mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 12:02:49 +05:30
refactor!(llm): implement new model registration system
This commit is contained in:
@@ -1,7 +1,34 @@
|
||||
import { describe, test, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
const mockConfig: Record<string, string | undefined> = {};
|
||||
|
||||
vi.mock("../src/config.js", () => ({
|
||||
getLlmConfig: () => mockConfig,
|
||||
resetLlmConfig: () => {
|
||||
for (const key of Object.keys(mockConfig)) {
|
||||
delete mockConfig[key];
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const { getActiveMock } = vi.hoisted(() => ({
|
||||
getActiveMock: vi.fn().mockReturnValue(null),
|
||||
}));
|
||||
|
||||
vi.mock("../src/provider-manager.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../src/provider-manager.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ProviderManager: {
|
||||
...actual.ProviderManager,
|
||||
getActive: getActiveMock,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { DeepSeekProvider } from "../src/providers/deepseek.js";
|
||||
import { llmConfig } from "../src/config.js";
|
||||
|
||||
// Mock the ChatDeepSeek class
|
||||
vi.mock("@langchain/deepseek", () => {
|
||||
@@ -41,27 +68,30 @@ describe("DeepSeekProvider Unit Tests (Tier 1)", () => {
|
||||
});
|
||||
|
||||
test("initializes successfully with apiKey from config", () => {
|
||||
const originalKey = llmConfig.DEEPSEEK_API_KEY;
|
||||
llmConfig.DEEPSEEK_API_KEY = "env-dummy-key";
|
||||
const originalKey = process.env.DEEPSEEK_API_KEY;
|
||||
process.env.DEEPSEEK_API_KEY = "env-dummy-key";
|
||||
mockConfig.DEEPSEEK_API_KEY = "env-dummy-key";
|
||||
|
||||
try {
|
||||
const provider = new DeepSeekProvider();
|
||||
expect(provider.providerName).toBe("DeepSeek");
|
||||
} finally {
|
||||
llmConfig.DEEPSEEK_API_KEY = originalKey;
|
||||
process.env.DEEPSEEK_API_KEY = originalKey;
|
||||
delete mockConfig.DEEPSEEK_API_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
test("throws error if no API key is provided or in config", () => {
|
||||
const originalKey = llmConfig.DEEPSEEK_API_KEY;
|
||||
llmConfig.DEEPSEEK_API_KEY = undefined;
|
||||
const originalKey = process.env.DEEPSEEK_API_KEY;
|
||||
process.env.DEEPSEEK_API_KEY = undefined;
|
||||
mockConfig.DEEPSEEK_API_KEY = undefined;
|
||||
|
||||
try {
|
||||
expect(() => new DeepSeekProvider()).toThrow(
|
||||
"DEEPSEEK_API_KEY is required to initialize DeepSeekProvider",
|
||||
);
|
||||
} finally {
|
||||
llmConfig.DEEPSEEK_API_KEY = originalKey;
|
||||
process.env.DEEPSEEK_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,34 @@
|
||||
import { describe, test, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
const mockConfig: Record<string, string | undefined> = {};
|
||||
|
||||
vi.mock("../src/config.js", () => ({
|
||||
getLlmConfig: () => mockConfig,
|
||||
resetLlmConfig: () => {
|
||||
for (const key of Object.keys(mockConfig)) {
|
||||
delete mockConfig[key];
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const { getActiveMock } = vi.hoisted(() => ({
|
||||
getActiveMock: vi.fn().mockReturnValue(null),
|
||||
}));
|
||||
|
||||
vi.mock("../src/provider-manager.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../src/provider-manager.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ProviderManager: {
|
||||
...actual.ProviderManager,
|
||||
getActive: getActiveMock,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { GroqProvider } from "../src/providers/groq.js";
|
||||
import { llmConfig } from "../src/config.js";
|
||||
|
||||
// Mock the ChatGroq class
|
||||
vi.mock("@langchain/groq", () => {
|
||||
@@ -41,27 +68,30 @@ describe("GroqProvider Unit Tests (Tier 1)", () => {
|
||||
});
|
||||
|
||||
test("initializes successfully with apiKey from config", () => {
|
||||
const originalKey = llmConfig.GROQ_API_KEY;
|
||||
llmConfig.GROQ_API_KEY = "env-dummy-key";
|
||||
const originalKey = process.env.GROQ_API_KEY;
|
||||
process.env.GROQ_API_KEY = "env-dummy-key";
|
||||
mockConfig.GROQ_API_KEY = "env-dummy-key";
|
||||
|
||||
try {
|
||||
const provider = new GroqProvider();
|
||||
expect(provider.providerName).toBe("Groq");
|
||||
} finally {
|
||||
llmConfig.GROQ_API_KEY = originalKey;
|
||||
process.env.GROQ_API_KEY = originalKey;
|
||||
delete mockConfig.GROQ_API_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
test("throws error if no API key is provided or in config", () => {
|
||||
const originalKey = llmConfig.GROQ_API_KEY;
|
||||
llmConfig.GROQ_API_KEY = undefined;
|
||||
const originalKey = process.env.GROQ_API_KEY;
|
||||
process.env.GROQ_API_KEY = undefined;
|
||||
mockConfig.GROQ_API_KEY = undefined;
|
||||
|
||||
try {
|
||||
expect(() => new GroqProvider()).toThrow(
|
||||
"GROQ_API_KEY is required to initialize GroqProvider",
|
||||
);
|
||||
} finally {
|
||||
llmConfig.GROQ_API_KEY = originalKey;
|
||||
process.env.GROQ_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,37 @@
|
||||
import { describe, test, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
const mockConfig: Record<string, string | undefined> = {};
|
||||
|
||||
vi.mock("../src/config.js", () => ({
|
||||
getLlmConfig: () => mockConfig,
|
||||
resetLlmConfig: () => {
|
||||
for (const key of Object.keys(mockConfig)) {
|
||||
delete mockConfig[key];
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const { getActiveMock } = vi.hoisted(() => ({
|
||||
getActiveMock: vi.fn().mockReturnValue(null),
|
||||
}));
|
||||
|
||||
vi.mock("../src/provider-manager.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../src/provider-manager.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ProviderManager: {
|
||||
...actual.ProviderManager,
|
||||
getActive: getActiveMock,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import {
|
||||
OpenAIProvider,
|
||||
OpenAIEmbeddingProvider,
|
||||
} from "../src/providers/openai.js";
|
||||
import { llmConfig } from "../src/config.js";
|
||||
|
||||
// Mock the ChatOpenAI and OpenAIEmbeddings classes
|
||||
vi.mock("@langchain/openai", () => {
|
||||
@@ -53,27 +80,30 @@ describe("OpenAIProvider Unit Tests (Tier 1)", () => {
|
||||
});
|
||||
|
||||
test("initializes successfully with apiKey from config", () => {
|
||||
const originalKey = llmConfig.OPENAI_API_KEY;
|
||||
llmConfig.OPENAI_API_KEY = "env-dummy-key";
|
||||
const originalKey = process.env.OPENAI_API_KEY;
|
||||
process.env.OPENAI_API_KEY = "env-dummy-key";
|
||||
mockConfig.OPENAI_API_KEY = "env-dummy-key";
|
||||
|
||||
try {
|
||||
const provider = new OpenAIProvider();
|
||||
expect(provider.providerName).toBe("OpenAI");
|
||||
} finally {
|
||||
llmConfig.OPENAI_API_KEY = originalKey;
|
||||
process.env.OPENAI_API_KEY = originalKey;
|
||||
delete mockConfig.OPENAI_API_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
test("throws error if no API key is provided or in config", () => {
|
||||
const originalKey = llmConfig.OPENAI_API_KEY;
|
||||
llmConfig.OPENAI_API_KEY = undefined;
|
||||
const originalKey = process.env.OPENAI_API_KEY;
|
||||
process.env.OPENAI_API_KEY = undefined;
|
||||
mockConfig.OPENAI_API_KEY = undefined;
|
||||
|
||||
try {
|
||||
expect(() => new OpenAIProvider()).toThrow(
|
||||
"OPENAI_API_KEY is required to initialize OpenAIProvider",
|
||||
);
|
||||
} finally {
|
||||
llmConfig.OPENAI_API_KEY = originalKey;
|
||||
process.env.OPENAI_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -116,14 +146,16 @@ describe("OpenAIEmbeddingProvider Unit Tests (Tier 1)", () => {
|
||||
});
|
||||
|
||||
test("initializes successfully with apiKey from config", () => {
|
||||
const originalKey = llmConfig.OPENAI_API_KEY;
|
||||
llmConfig.OPENAI_API_KEY = "env-dummy-key";
|
||||
const originalKey = process.env.OPENAI_API_KEY;
|
||||
process.env.OPENAI_API_KEY = "env-dummy-key";
|
||||
mockConfig.OPENAI_API_KEY = "env-dummy-key";
|
||||
|
||||
try {
|
||||
const provider = new OpenAIEmbeddingProvider();
|
||||
expect(provider.providerName).toBe("OpenAI");
|
||||
} finally {
|
||||
llmConfig.OPENAI_API_KEY = originalKey;
|
||||
process.env.OPENAI_API_KEY = originalKey;
|
||||
delete mockConfig.OPENAI_API_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,34 @@
|
||||
import { describe, test, expect, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
const mockConfig: Record<string, string | undefined> = {};
|
||||
|
||||
vi.mock("../src/config.js", () => ({
|
||||
getLlmConfig: () => mockConfig,
|
||||
resetLlmConfig: () => {
|
||||
for (const key of Object.keys(mockConfig)) {
|
||||
delete mockConfig[key];
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const { getActiveMock } = vi.hoisted(() => ({
|
||||
getActiveMock: vi.fn().mockReturnValue(null),
|
||||
}));
|
||||
|
||||
vi.mock("../src/provider-manager.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../src/provider-manager.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ProviderManager: {
|
||||
...actual.ProviderManager,
|
||||
getActive: getActiveMock,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { OpenRouterProvider } from "../src/providers/openrouter.js";
|
||||
import { llmConfig } from "../src/config.js";
|
||||
|
||||
// Mock the ChatOpenRouter class
|
||||
vi.mock("@langchain/openrouter", () => {
|
||||
@@ -14,7 +41,6 @@ vi.mock("@langchain/openrouter", () => {
|
||||
withStructuredOutput = vi.fn().mockImplementation(() => {
|
||||
return {
|
||||
invoke: vi.fn().mockImplementation(async () => {
|
||||
// Return a mock output that matches the includeRaw: true structure
|
||||
return {
|
||||
parsed: {
|
||||
name: "mocked response",
|
||||
@@ -42,29 +68,30 @@ describe("OpenRouterProvider Unit Tests (Tier 1)", () => {
|
||||
});
|
||||
|
||||
test("initializes successfully with apiKey from config", () => {
|
||||
// Save current config
|
||||
const originalKey = llmConfig.OPENROUTER_API_KEY;
|
||||
llmConfig.OPENROUTER_API_KEY = "env-dummy-key";
|
||||
const originalKey = process.env.OPENROUTER_API_KEY;
|
||||
process.env.OPENROUTER_API_KEY = "env-dummy-key";
|
||||
mockConfig.OPENROUTER_API_KEY = "env-dummy-key";
|
||||
|
||||
try {
|
||||
const provider = new OpenRouterProvider();
|
||||
expect(provider.providerName).toBe("OpenRouter");
|
||||
} finally {
|
||||
llmConfig.OPENROUTER_API_KEY = originalKey;
|
||||
process.env.OPENROUTER_API_KEY = originalKey;
|
||||
delete mockConfig.OPENROUTER_API_KEY;
|
||||
}
|
||||
});
|
||||
|
||||
test("throws error if no API key is provided or in config", () => {
|
||||
// Save current config
|
||||
const originalKey = llmConfig.OPENROUTER_API_KEY;
|
||||
llmConfig.OPENROUTER_API_KEY = undefined;
|
||||
const originalKey = process.env.OPENROUTER_API_KEY;
|
||||
process.env.OPENROUTER_API_KEY = undefined;
|
||||
mockConfig.OPENROUTER_API_KEY = undefined;
|
||||
|
||||
try {
|
||||
expect(() => new OpenRouterProvider()).toThrow(
|
||||
"OPENROUTER_API_KEY is required to initialize OpenRouterProvider",
|
||||
);
|
||||
} finally {
|
||||
llmConfig.OPENROUTER_API_KEY = originalKey;
|
||||
process.env.OPENROUTER_API_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {
|
||||
ProviderManager,
|
||||
setDbPathOverride,
|
||||
resetHasBootstrapped,
|
||||
} from "../src/index.js";
|
||||
import { ProviderManager, setDbPathOverride } from "../src/index.js";
|
||||
|
||||
describe("ProviderManager Bootstrapping & CRUD Unit Tests", () => {
|
||||
let tempDbPath: string;
|
||||
let originalGoogle: string | undefined;
|
||||
let originalOpenRouter: string | undefined;
|
||||
let savedEnv: Record<string, string | undefined>;
|
||||
|
||||
beforeEach(() => {
|
||||
originalGoogle = process.env.GOOGLE_API_KEY;
|
||||
originalOpenRouter = process.env.OPENROUTER_API_KEY;
|
||||
savedEnv = {
|
||||
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY,
|
||||
OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY,
|
||||
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
|
||||
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
GROQ_API_KEY: process.env.GROQ_API_KEY,
|
||||
DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY,
|
||||
};
|
||||
delete process.env.GOOGLE_API_KEY;
|
||||
delete process.env.OPENROUTER_API_KEY;
|
||||
|
||||
resetHasBootstrapped();
|
||||
delete process.env.ANTHROPIC_API_KEY;
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
delete process.env.GROQ_API_KEY;
|
||||
delete process.env.DEEPSEEK_API_KEY;
|
||||
|
||||
// Generate a unique temp database path for this test run
|
||||
tempDbPath = path.resolve(
|
||||
@@ -37,47 +40,223 @@ describe("ProviderManager Bootstrapping & CRUD Unit Tests", () => {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (originalGoogle !== undefined) {
|
||||
process.env.GOOGLE_API_KEY = originalGoogle;
|
||||
} else {
|
||||
delete process.env.GOOGLE_API_KEY;
|
||||
}
|
||||
if (originalOpenRouter !== undefined) {
|
||||
process.env.OPENROUTER_API_KEY = originalOpenRouter;
|
||||
} else {
|
||||
delete process.env.OPENROUTER_API_KEY;
|
||||
for (const [key, value] of Object.entries(savedEnv)) {
|
||||
if (value !== undefined) {
|
||||
process.env[key] = value;
|
||||
} else {
|
||||
delete process.env[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("auto-bootstraps Gemini and OpenRouter when database is empty and environment variables are present", () => {
|
||||
process.env.GOOGLE_API_KEY = "mock-google-key-123";
|
||||
process.env.OPENROUTER_API_KEY = "mock-openrouter-key-456";
|
||||
test("auto-bootstraps all environment-variable providers when database is empty", () => {
|
||||
process.env.GOOGLE_API_KEY = "mock-google-key";
|
||||
process.env.OPENROUTER_API_KEY = "mock-openrouter-key";
|
||||
process.env.ANTHROPIC_API_KEY = "mock-anthropic-key";
|
||||
process.env.OPENAI_API_KEY = "mock-openai-key";
|
||||
process.env.GROQ_API_KEY = "mock-groq-key";
|
||||
process.env.DEEPSEEK_API_KEY = "mock-deepseek-key";
|
||||
|
||||
const list = ProviderManager.list();
|
||||
expect(list.length).toBe(3);
|
||||
|
||||
const gemini = list.find((p) => p.providerName === "google-genai");
|
||||
expect(gemini).toBeDefined();
|
||||
expect(gemini?.name).toBe("Gemini (Env)");
|
||||
expect(gemini?.apiKey).toBe("mock-google-key-123");
|
||||
expect(gemini?.modelName).toBe("gemini-2.5-flash");
|
||||
expect(gemini?.isActive).toBe(true); // first inserted is active
|
||||
const providers = list.map((p) => p.providerName);
|
||||
expect(providers).toContain("google-genai");
|
||||
expect(providers).toContain("openrouter");
|
||||
expect(providers).toContain("anthropic");
|
||||
expect(providers).toContain("openai");
|
||||
expect(providers).toContain("groq");
|
||||
expect(providers).toContain("deepseek");
|
||||
|
||||
const openrouter = list.find((p) => p.providerName === "openrouter");
|
||||
expect(openrouter).toBeDefined();
|
||||
expect(openrouter?.name).toBe("OpenRouter (Env)");
|
||||
expect(openrouter?.apiKey).toBe("mock-openrouter-key-456");
|
||||
expect(openrouter?.modelName).toBe("google/gemini-2.5-flash");
|
||||
expect(openrouter?.isActive).toBe(false); // second inserted is inactive
|
||||
// Gemini should have both generative + embedding (2 entries)
|
||||
const geminiEntries = list.filter((p) => p.providerName === "google-genai");
|
||||
expect(geminiEntries.length).toBe(2);
|
||||
expect(geminiEntries.some((p) => p.type === "generative")).toBe(true);
|
||||
expect(geminiEntries.some((p) => p.type === "embedding")).toBe(true);
|
||||
|
||||
// OpenAI should have both generative + embedding (2 entries)
|
||||
const openaiEntries = list.filter((p) => p.providerName === "openai");
|
||||
expect(openaiEntries.length).toBe(2);
|
||||
expect(openaiEntries.some((p) => p.type === "generative")).toBe(true);
|
||||
expect(openaiEntries.some((p) => p.type === "embedding")).toBe(true);
|
||||
|
||||
// First generative provider inserted should be active
|
||||
const activeGenerative = list.filter(
|
||||
(p) => p.type === "generative" && p.isActive,
|
||||
);
|
||||
expect(activeGenerative.length).toBe(1);
|
||||
|
||||
// First embedding provider inserted should be active
|
||||
const activeEmbedding = list.filter(
|
||||
(p) => p.type === "embedding" && p.isActive,
|
||||
);
|
||||
expect(activeEmbedding.length).toBe(1);
|
||||
});
|
||||
|
||||
test("getActive returns null when no providers exist and no env vars", () => {
|
||||
const active = ProviderManager.getActive("generative");
|
||||
expect(active).toBeNull();
|
||||
const activeEmbed = ProviderManager.getActive("embedding");
|
||||
expect(activeEmbed).toBeNull();
|
||||
});
|
||||
|
||||
test("getActive falls back to env var when DB operations fail or return null", () => {
|
||||
process.env.GOOGLE_API_KEY = "mock-google-key-123";
|
||||
// DB is empty, getActive should bootstrap and find or create from env
|
||||
const active = ProviderManager.getActive("generative");
|
||||
expect(active).not.toBeNull();
|
||||
expect(active?.providerName).toBe("google-genai");
|
||||
expect(active?.apiKey).toBe("mock-google-key-123");
|
||||
expect(active?.type).toBe("generative");
|
||||
|
||||
const activeEmbed = ProviderManager.getActive("embedding");
|
||||
expect(activeEmbed).not.toBeNull();
|
||||
expect(activeEmbed?.providerName).toBe("google-genai");
|
||||
expect(activeEmbed?.type).toBe("embedding");
|
||||
});
|
||||
|
||||
test("getActive returns first instance of type when none is active", () => {
|
||||
// Manually create instances without any env var bootstrap
|
||||
const inst1 = ProviderManager.create("Test Gemini", "google-genai", "key1");
|
||||
const inst2 = ProviderManager.create(
|
||||
"Test OpenAI",
|
||||
"openai",
|
||||
"key2",
|
||||
"gpt-4o",
|
||||
"generative",
|
||||
128000,
|
||||
);
|
||||
expect(inst1.isActive).toBe(true); // first created auto-activates
|
||||
expect(inst2.isActive).toBe(false);
|
||||
|
||||
// Deactivate both
|
||||
ProviderManager.setActive("__nonexistent__"); // no-op for nonexistent
|
||||
|
||||
// Deactivate inst1 by setting another as active, then delete that
|
||||
ProviderManager.setActive(inst2.id);
|
||||
expect(
|
||||
ProviderManager.list().find((p) => p.id === inst2.id)?.isActive,
|
||||
).toBe(true);
|
||||
expect(
|
||||
ProviderManager.list().find((p) => p.id === inst1.id)?.isActive,
|
||||
).toBe(false);
|
||||
|
||||
// Delete the active one → auto-promotes inst1
|
||||
ProviderManager.delete(inst2.id);
|
||||
const promoted = ProviderManager.list().find((p) => p.id === inst1.id);
|
||||
expect(promoted?.isActive).toBe(true);
|
||||
});
|
||||
|
||||
test("setActive correctly deactivates siblings and activates target", () => {
|
||||
const inst1 = ProviderManager.create(
|
||||
"First Gemini",
|
||||
"google-genai",
|
||||
"key1",
|
||||
undefined,
|
||||
"generative",
|
||||
);
|
||||
const inst2 = ProviderManager.create(
|
||||
"Second Gemini",
|
||||
"google-genai",
|
||||
"key2",
|
||||
undefined,
|
||||
"generative",
|
||||
);
|
||||
|
||||
expect(inst1.isActive).toBe(true);
|
||||
expect(inst2.isActive).toBe(false);
|
||||
|
||||
ProviderManager.setActive(inst2.id);
|
||||
|
||||
const list = ProviderManager.list();
|
||||
const updated1 = list.find((p) => p.id === inst1.id);
|
||||
const updated2 = list.find((p) => p.id === inst2.id);
|
||||
expect(updated1?.isActive).toBe(false);
|
||||
expect(updated2?.isActive).toBe(true);
|
||||
});
|
||||
|
||||
test("getMappings returns empty object initially, setMapping persists mappings", () => {
|
||||
const mappings = ProviderManager.getMappings();
|
||||
expect(mappings).toEqual({});
|
||||
|
||||
const inst = ProviderManager.create(
|
||||
"Test Provider",
|
||||
"google-genai",
|
||||
"key1",
|
||||
);
|
||||
|
||||
ProviderManager.setMapping("actor-prose", inst.id);
|
||||
ProviderManager.setMapping("embeddings", inst.id);
|
||||
|
||||
const updated = ProviderManager.getMappings();
|
||||
expect(updated["actor-prose"]).toBe(inst.id);
|
||||
expect(updated["embeddings"]).toBe(inst.id);
|
||||
});
|
||||
|
||||
test("setMapping with empty providerInstanceId deletes the mapping", () => {
|
||||
const inst = ProviderManager.create(
|
||||
"Test Provider",
|
||||
"google-genai",
|
||||
"key1",
|
||||
);
|
||||
|
||||
ProviderManager.setMapping("test-task", inst.id);
|
||||
expect(ProviderManager.getMappings()["test-task"]).toBe(inst.id);
|
||||
|
||||
ProviderManager.setMapping("test-task", "");
|
||||
expect(ProviderManager.getMappings()["test-task"]).toBeUndefined();
|
||||
});
|
||||
|
||||
test("create returns instance with correct fields and endpointUrl support", () => {
|
||||
const inst = ProviderManager.create(
|
||||
"Ollama Local",
|
||||
"ollama",
|
||||
"",
|
||||
"llama3.1",
|
||||
"generative",
|
||||
32768,
|
||||
"http://localhost:11434",
|
||||
);
|
||||
|
||||
expect(inst.id).toMatch(/^provider-/);
|
||||
expect(inst.name).toBe("Ollama Local");
|
||||
expect(inst.providerName).toBe("ollama");
|
||||
expect(inst.modelName).toBe("llama3.1");
|
||||
expect(inst.endpointUrl).toBe("http://localhost:11434");
|
||||
});
|
||||
|
||||
test("update preserves apiKey when not provided", () => {
|
||||
const inst = ProviderManager.create(
|
||||
"Original",
|
||||
"openai",
|
||||
"original-key",
|
||||
"gpt-4o",
|
||||
"generative",
|
||||
128000,
|
||||
);
|
||||
|
||||
ProviderManager.update(
|
||||
inst.id,
|
||||
"Renamed",
|
||||
"openai",
|
||||
undefined, // no apiKey → preserve existing
|
||||
"gpt-4o-mini",
|
||||
"generative",
|
||||
64000,
|
||||
);
|
||||
|
||||
const updated = ProviderManager.list().find((p) => p.id === inst.id);
|
||||
expect(updated?.name).toBe("Renamed");
|
||||
expect(updated?.apiKey).toBe("original-key"); // preserved
|
||||
expect(updated?.modelName).toBe("gpt-4o-mini");
|
||||
expect(updated?.maxContext).toBe(64000);
|
||||
});
|
||||
test("treats bootstrapped instances as normal provider instances (editable and deletable)", () => {
|
||||
process.env.GOOGLE_API_KEY = "mock-google-key-123";
|
||||
|
||||
// Trigger bootstrap
|
||||
const list = ProviderManager.list();
|
||||
expect(list.length).toBe(2);
|
||||
const bootstrapped = list.find((p) => p.name === "Gemini (Env)");
|
||||
const bootstrapped = list.find((p) => p.name === "Google Gemini (Env)");
|
||||
expect(bootstrapped).toBeDefined();
|
||||
if (!bootstrapped) return;
|
||||
expect(bootstrapped.isActive).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user