


















1-import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2-import * as authModule from "../../../../src/agents/model-auth.js";
3-4-vi.mock("../../../../src/infra/net/fetch-guard.js", () => ({
5-fetchWithSsrFGuard: async (params: {
6-url: string;
7-init?: RequestInit;
8-fetchImpl?: typeof fetch;
9-}) => {
10-const fetchImpl = params.fetchImpl ?? globalThis.fetch;
11-if (!fetchImpl) {
12-throw new Error("fetch is not available");
13-}
14-const response = await fetchImpl(params.url, params.init);
15-return {
16- response,
17-finalUrl: params.url,
18-release: async () => {},
19-};
20-},
21-}));
22-23-const { resolveApiKeyForProviderMock } = vi.hoisted(() => ({
24-resolveApiKeyForProviderMock: vi.fn(),
25-}));
26-27-vi.mock("../../../../src/agents/model-auth.js", () => {
28-return {
29-resolveApiKeyForProvider: resolveApiKeyForProviderMock,
30-requireApiKey: (auth: { apiKey?: string; mode?: string }, provider: string) => {
31-if (auth.apiKey) {
32-return auth.apiKey;
33-}
34-throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth.mode}).`);
35-},
36-};
37-});
38-39-const createGeminiFetchMock = (embeddingValues = [1, 2, 3]) =>
40-vi.fn(async (_input?: unknown, _init?: unknown) => ({
41-ok: true,
42-status: 200,
43-json: async () => ({ embedding: { values: embeddingValues } }),
44-}));
45-46-const createGeminiBatchFetchMock = (count: number, embeddingValues = [1, 2, 3]) =>
47-vi.fn(async (_input?: unknown, _init?: unknown) => ({
48-ok: true,
49-status: 200,
50-json: async () => ({
51-embeddings: Array.from({ length: count }, () => ({ values: embeddingValues })),
52-}),
53-}));
54-55-function installFetchMock(fetchMock: typeof globalThis.fetch) {
56-vi.stubGlobal("fetch", fetchMock);
57-}
58-59-function parseFetchBody(fetchMock: { mock: { calls: unknown[][] } }, callIndex = 0) {
60-const init = fetchMock.mock.calls[callIndex]?.[1] as RequestInit | undefined;
61-return JSON.parse((init?.body as string) ?? "{}") as Record<string, unknown>;
62-}
63-64-function magnitude(values: number[]) {
65-return Math.sqrt(values.reduce((sum, value) => sum + value * value, 0));
66-}
67-68-let buildGeminiEmbeddingRequest: typeof import("./embeddings-gemini.js").buildGeminiEmbeddingRequest;
69-let createGeminiEmbeddingProvider: typeof import("./embeddings-gemini.js").createGeminiEmbeddingProvider;
70-let DEFAULT_GEMINI_EMBEDDING_MODEL: typeof import("./embeddings-gemini.js").DEFAULT_GEMINI_EMBEDDING_MODEL;
71-let normalizeGeminiModel: typeof import("./embeddings-gemini.js").normalizeGeminiModel;
72-let resolveGeminiOutputDimensionality: typeof import("./embeddings-gemini.js").resolveGeminiOutputDimensionality;
73-74-beforeAll(async () => {
75-vi.doUnmock("undici");
76-({
77- buildGeminiEmbeddingRequest,
78- createGeminiEmbeddingProvider,
79-DEFAULT_GEMINI_EMBEDDING_MODEL,
80- normalizeGeminiModel,
81- resolveGeminiOutputDimensionality,
82-} = await import("./embeddings-gemini.js"));
83-});
84-85-beforeEach(() => {
86-vi.useRealTimers();
87-vi.doUnmock("undici");
88-});
89-90-afterEach(() => {
91-vi.doUnmock("undici");
92-vi.resetAllMocks();
93-vi.unstubAllGlobals();
94-});
95-96-function mockResolvedProviderKey(apiKey = "test-key") {
97-vi.mocked(authModule.resolveApiKeyForProvider).mockResolvedValue({
98- apiKey,
99-mode: "api-key",
100-source: "test",
101-});
102-}
103-104-type GeminiFetchMock =
105-| ReturnType<typeof createGeminiFetchMock>
106-| ReturnType<typeof createGeminiBatchFetchMock>;
107-108-async function createProviderWithFetch(
109-fetchMock: GeminiFetchMock,
110-options: Partial<Parameters<typeof createGeminiEmbeddingProvider>[0]> & { model: string },
111-) {
112-installFetchMock(fetchMock as unknown as typeof globalThis.fetch);
113-mockResolvedProviderKey();
114-const { provider } = await createGeminiEmbeddingProvider({
115-config: {} as never,
116-provider: "gemini",
117-fallback: "none",
118- ...options,
119-});
120-return provider;
121-}
122-123-function expectNormalizedThreeFourVector(embedding: number[]) {
124-expect(embedding[0]).toBeCloseTo(0.6, 5);
125-expect(embedding[1]).toBeCloseTo(0.8, 5);
126-expect(magnitude(embedding)).toBeCloseTo(1, 5);
127-}
128-129-describe("package Gemini embedding provider smoke", () => {
130-it("builds multimodal v2 requests and resolves dimensions", () => {
1+import { describe, expect, it } from "vitest";
2+import {
3+buildGeminiEmbeddingRequest,
4+DEFAULT_GEMINI_EMBEDDING_MODEL,
5+normalizeGeminiModel,
6+resolveGeminiOutputDimensionality,
7+} from "./embeddings-gemini-request.js";
8+9+describe("package Gemini embedding request helpers", () => {
10+it("builds multimodal v2 requests and resolves model settings", () => {
13111expect(
13212buildGeminiEmbeddingRequest({
13313input: {
@@ -158,57 +38,6 @@ describe("package Gemini embedding provider smoke", () => {
15838expect(() => resolveGeminiOutputDimensionality("gemini-embedding-2-preview", 512)).toThrow(
15939/Invalid outputDimensionality 512/,
16040);
161-});
162-163-it("handles legacy and v2 request/response behavior", async () => {
164-const legacyFetch = createGeminiBatchFetchMock(2);
165-const legacyProvider = await createProviderWithFetch(legacyFetch, {
166-model: "gemini-embedding-001",
167-});
168-169-await legacyProvider.embedQuery("test query");
170-await legacyProvider.embedBatch(["text1", "text2"]);
171-172-expect(parseFetchBody(legacyFetch, 0)).toMatchObject({
173-taskType: "RETRIEVAL_QUERY",
174-content: { parts: [{ text: "test query" }] },
175-});
176-expect(parseFetchBody(legacyFetch, 0)).not.toHaveProperty("outputDimensionality");
177-expect(parseFetchBody(legacyFetch, 1)).not.toHaveProperty("outputDimensionality");
178-179-const v2QueryFetch = createGeminiFetchMock([3, 4]);
180-const v2QueryProvider = await createProviderWithFetch(v2QueryFetch, {
181-model: "gemini-embedding-2-preview",
182-outputDimensionality: 768,
183-taskType: "SEMANTIC_SIMILARITY",
184-});
185-await expect(v2QueryProvider.embedQuery(" ")).resolves.toEqual([]);
186-await expect(v2QueryProvider.embedBatch([])).resolves.toEqual([]);
187-expectNormalizedThreeFourVector(await v2QueryProvider.embedQuery("test query"));
188-189-const v2BatchFetch = createGeminiBatchFetchMock(2, [3, 4]);
190-const v2BatchProvider = await createProviderWithFetch(v2BatchFetch, {
191-model: "gemini-embedding-2-preview",
192-outputDimensionality: 768,
193-taskType: "SEMANTIC_SIMILARITY",
194-});
195-const batch = await v2BatchProvider.embedBatch(["text1", "text2"]);
196-expect(batch).toHaveLength(2);
197-for (const embedding of batch) {
198-expectNormalizedThreeFourVector(embedding);
199-}
200-201-expect(parseFetchBody(v2QueryFetch)).toMatchObject({
202-outputDimensionality: 768,
203-taskType: "SEMANTIC_SIMILARITY",
204-});
205-expect(parseFetchBody(v2BatchFetch).requests).toEqual([
206-expect.objectContaining({ outputDimensionality: 768 }),
207-expect.objectContaining({ outputDimensionality: 768 }),
208-]);
209-});
210-211-it("normalizes known model prefixes and the default model", () => {
21241expect(normalizeGeminiModel("models/gemini-embedding-2-preview")).toBe(
21342"gemini-embedding-2-preview",
21443);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。