165 lines · 5.2 KB · TypeScript
RawDownload
import { afterEach, beforeEach, describe, expect, test } from "vitest";import { ChatAnthropic } from "@langchain/anthropic";import { ChatGoogle } from "@langchain/google/node";import { ChatOpenAI } from "@langchain/openai";import { createModel } from "../src/agent/index.ts"; // Constructing a LangChain chat model makes no network calls (auth/clients// resolve lazily on first request), so these assert the gemini-enterprise// surface dispatch and the project-id guard without needing GCP credentials. const PROJECT_KEY = "GOOGLE_CLOUD_PROJECT";const LOCATION_KEY = "GOOGLE_CLOUD_LOCATION";const GEMINI_KEY = "GEMINI_API_KEY"; function modelName(model: unknown): string | undefined {  return (model as { model?: string }).model;} describe("createModel gemini-enterprise surface dispatch", () => {  let savedProject: string | undefined;  let savedLocation: string | undefined;   beforeEach(() => {    savedProject = process.env[PROJECT_KEY];    savedLocation = process.env[LOCATION_KEY];    process.env[PROJECT_KEY] = "test-project";    process.env[LOCATION_KEY] = "us-central1";  });   afterEach(() => {    restoreEnv(PROJECT_KEY, savedProject);    restoreEnv(LOCATION_KEY, savedLocation);  });   test("routes Claude IDs to ChatAnthropic and strips the publisher path", () => {    const model = createModel(      "gemini-enterprise",      "publishers/anthropic/models/claude-sonnet-4-5@20250929",      0,    );     expect(model).toBeInstanceOf(ChatAnthropic);    expect(modelName(model)).toBe("claude-sonnet-4-5@20250929");  });   test("routes MaaS IDs to ChatOpenAI and normalizes to publisher/model", () => {    const model = createModel(      "gemini-enterprise",      "publishers/meta/models/llama-3.3-70b-instruct-maas",      0,    );     expect(model).toBeInstanceOf(ChatOpenAI);    expect(model).not.toBeInstanceOf(ChatGoogle);    expect(modelName(model)).toBe("meta/llama-3.3-70b-instruct-maas");  });   test("routes Gemini IDs to ChatGoogle", () => {    const model = createModel("gemini-enterprise", "gemini-3.1-pro", 0);     expect(model).toBeInstanceOf(ChatGoogle);    expect(modelName(model)).toBe("gemini-3.1-pro");  });   test("routes Gemma IDs to ChatGoogle (default surface)", () => {    const model = createModel("gemini-enterprise", "gemma-3-27b-it", 0);     expect(model).toBeInstanceOf(ChatGoogle);    expect(modelName(model)).toBe("gemma-3-27b-it");  });   test("strips the publisher path on the Gemini surface", () => {    const model = createModel(      "gemini-enterprise",      "publishers/google/models/gemini-3-pro",      0,    );     expect(model).toBeInstanceOf(ChatGoogle);    expect(modelName(model)).toBe("gemini-3-pro");  });   test("resolves the global endpoint for the MaaS surface when location is unset", () => {    delete process.env[LOCATION_KEY];     const model = createModel(      "gemini-enterprise",      "publishers/meta/models/llama-3.3-70b-instruct-maas",      0,    );     // The global endpoint uses the unprefixed host (not global-aiplatform…).    const baseURL = (model as { clientConfig?: { baseURL?: string } })      .clientConfig?.baseURL;    expect(baseURL).toBe(      "https://aiplatform.googleapis.com/v1/projects/test-project/locations/global/endpoints/openapi",    );  });   test("throws a clear error when the project ID is missing", () => {    delete process.env[PROJECT_KEY];     expect(() => createModel("gemini-enterprise", "gemini-3.1-pro", 0)).toThrow(      /GOOGLE_CLOUD_PROJECT is required/u,    );  });}); describe("createModel gemini (AI Studio)", () => {  let savedGeminiKey: string | undefined;   beforeEach(() => {    savedGeminiKey = process.env[GEMINI_KEY];    process.env[GEMINI_KEY] = "test-gemini-key";  });   afterEach(() => {    restoreEnv(GEMINI_KEY, savedGeminiKey);  });   test("builds a ChatGoogle AI Studio client with v0 output pinned", () => {    const model = createModel("gemini", "gemini-3.1-pro", 0);     expect(model).toBeInstanceOf(ChatGoogle);    expect(modelName(model)).toBe("gemini-3.1-pro");     // Thought-signature workaround: streaming disabled + v0 output, on the AI    // Studio ("gai") platform. (The API key is stored privately by ChatGoogle    // and is asserted via the constructor mock in gemini-retry.test.ts.)    const config = model as {      _platform?: string;      disableStreaming?: boolean;      outputVersion?: string;    };    expect(config.disableStreaming).toBe(true);    expect(config.outputVersion).toBe("v0");    expect(config._platform).toBe("gai");  });}); describe("createModel OpenAI-compatible transport selection", () => {  test("routes Copilot GPT-5 models through the Responses API", () => {    const model = createModel("copilot", "gpt-5.5", 0) as {      useResponsesApi?: boolean;    };     expect(model.useResponsesApi).toBe(true);  });   test("keeps non-GPT-5 Copilot models on chat completions", () => {    const model = createModel("copilot", "claude-sonnet-5", 0) as {      useResponsesApi?: boolean;    };     expect(model.useResponsesApi).toBe(false);  });}); function restoreEnv(key: string, value: string | undefined): void {  if (value === undefined) {    delete process.env[key];  } else {    process.env[key] = value;  }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t