























@@ -20,6 +20,21 @@ const promptAndConfigureOllamaMock = vi.hoisted(() =>
2020);
2121const ensureOllamaModelPulledMock = vi.hoisted(() => vi.fn(async () => {}));
2222const buildOllamaProviderMock = vi.hoisted(() => vi.fn());
23+const queryOllamaModelShowInfoMock = vi.hoisted(() => vi.fn());
24+const buildOllamaModelDefinitionMock = vi.hoisted(() =>
25+vi.fn((modelId: string, contextWindow?: number, capabilities?: string[]) => ({
26+id: modelId,
27+name: modelId,
28+reasoning: capabilities?.includes("thinking") ?? false,
29+input: capabilities?.includes("vision") ? ["text", "image"] : ["text"],
30+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
31+contextWindow: contextWindow ?? 8192,
32+maxTokens: 8192,
33+compat: capabilities
34+ ? { supportsTools: capabilities.includes("tools"), supportsUsageInStreaming: true }
35+ : { supportsUsageInStreaming: true },
36+})),
37+);
2338const createConfiguredOllamaStreamFnMock = vi.hoisted(() =>
2439vi.fn((_params: { model: unknown; providerBaseUrl?: string }) => ({}) as never),
2540);
@@ -29,6 +44,8 @@ vi.mock("./api.js", () => ({
2944ensureOllamaModelPulled: ensureOllamaModelPulledMock,
3045configureOllamaNonInteractive: vi.fn(),
3146buildOllamaProvider: buildOllamaProviderMock,
47+queryOllamaModelShowInfo: queryOllamaModelShowInfoMock,
48+buildOllamaModelDefinition: buildOllamaModelDefinitionMock,
3249}));
33503451vi.mock("./src/stream.js", async (importOriginal) => {
@@ -43,6 +60,8 @@ beforeEach(() => {
4360promptAndConfigureOllamaMock.mockClear();
4461ensureOllamaModelPulledMock.mockClear();
4562buildOllamaProviderMock.mockReset();
63+queryOllamaModelShowInfoMock.mockReset();
64+buildOllamaModelDefinitionMock.mockClear();
4665createConfiguredOllamaStreamFnMock.mockClear();
4766});
4867@@ -420,6 +439,102 @@ describe("ollama plugin", () => {
420439}
421440});
422441442+it("resolves requested Ollama cloud models that are omitted from tags but confirmed by show", async () => {
443+const provider = registerProvider();
444+const previous = process.env.OLLAMA_API_KEY;
445+process.env.OLLAMA_API_KEY = "ollama-local";
446+buildOllamaProviderMock.mockResolvedValueOnce({
447+baseUrl: "http://127.0.0.1:11434",
448+api: "ollama",
449+models: [
450+{
451+id: "kimi-k2.5:cloud",
452+name: "kimi-k2.5:cloud",
453+reasoning: true,
454+input: ["text"],
455+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
456+contextWindow: 262144,
457+maxTokens: 8192,
458+},
459+],
460+});
461+queryOllamaModelShowInfoMock.mockResolvedValueOnce({
462+contextWindow: 1048576,
463+capabilities: ["completion", "tools", "thinking"],
464+});
465+466+try {
467+await provider.prepareDynamicModel?.({
468+config: {},
469+provider: "ollama",
470+modelId: "deepseek-v4-pro:cloud",
471+modelRegistry: { find: vi.fn(() => null) },
472+} as never);
473+474+expect(queryOllamaModelShowInfoMock).toHaveBeenCalledWith(
475+"http://127.0.0.1:11434",
476+"deepseek-v4-pro:cloud",
477+);
478+expect(
479+provider.resolveDynamicModel?.({
480+config: {},
481+provider: "ollama",
482+modelId: "deepseek-v4-pro:cloud",
483+modelRegistry: { find: vi.fn(() => null) },
484+} as never),
485+).toMatchObject({
486+provider: "ollama",
487+id: "deepseek-v4-pro:cloud",
488+api: "ollama",
489+baseUrl: "http://127.0.0.1:11434",
490+reasoning: true,
491+compat: { supportsTools: true },
492+});
493+} finally {
494+if (previous === undefined) {
495+delete process.env.OLLAMA_API_KEY;
496+} else {
497+process.env.OLLAMA_API_KEY = previous;
498+}
499+}
500+});
501+502+it("keeps unknown requested Ollama models unresolved when show has no metadata", async () => {
503+const provider = registerProvider();
504+const previous = process.env.OLLAMA_API_KEY;
505+process.env.OLLAMA_API_KEY = "ollama-local";
506+buildOllamaProviderMock.mockResolvedValueOnce({
507+baseUrl: "http://127.0.0.1:11434",
508+api: "ollama",
509+models: [],
510+});
511+queryOllamaModelShowInfoMock.mockResolvedValueOnce({});
512+513+try {
514+await provider.prepareDynamicModel?.({
515+config: {},
516+provider: "ollama",
517+modelId: "depseek-v4-pro:cloud",
518+modelRegistry: { find: vi.fn(() => null) },
519+} as never);
520+521+expect(
522+provider.resolveDynamicModel?.({
523+config: {},
524+provider: "ollama",
525+modelId: "depseek-v4-pro:cloud",
526+modelRegistry: { find: vi.fn(() => null) },
527+} as never),
528+).toBeUndefined();
529+} finally {
530+if (previous === undefined) {
531+delete process.env.OLLAMA_API_KEY;
532+} else {
533+process.env.OLLAMA_API_KEY = previous;
534+}
535+}
536+});
537+423538it("skips implicit localhost discovery when a custom remote Ollama provider is configured", async () => {
424539const provider = registerProvider();
425540此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。