mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:20:44 +00:00
fix: honor Ollama Modelfile num_ctx discovery
This commit is contained in:
@@ -3,6 +3,7 @@ import { jsonResponse, requestBodyText, requestUrl } from "../../../src/test-hel
|
||||
import {
|
||||
buildOllamaModelDefinition,
|
||||
enrichOllamaModelsWithContext,
|
||||
parseOllamaNumCtxParameter,
|
||||
resetOllamaModelShowInfoCacheForTest,
|
||||
resolveOllamaApiBase,
|
||||
type OllamaTagModel,
|
||||
@@ -42,6 +43,58 @@ describe("ollama provider models", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses Modelfile num_ctx when it expands the discovered context window", async () => {
|
||||
const models: OllamaTagModel[] = [{ name: "llama3-32k:latest" }];
|
||||
const fetchMock = vi.fn(async () =>
|
||||
jsonResponse({
|
||||
model_info: { "llama.context_length": 8192 },
|
||||
parameters: 'stop "<|eot_id|>"\nnum_ctx 32768\nnum_keep 5',
|
||||
capabilities: ["completion"],
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const enriched = await enrichOllamaModelsWithContext("http://127.0.0.1:11434", models);
|
||||
|
||||
expect(enriched).toEqual([
|
||||
{
|
||||
name: "llama3-32k:latest",
|
||||
contextWindow: 32768,
|
||||
capabilities: ["completion"],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps the larger native context window when Modelfile num_ctx is smaller", async () => {
|
||||
const models: OllamaTagModel[] = [{ name: "llama3.2:latest" }];
|
||||
const fetchMock = vi.fn(async () =>
|
||||
jsonResponse({
|
||||
model_info: { "llama.context_length": 131072 },
|
||||
parameters: "num_ctx 4096",
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const enriched = await enrichOllamaModelsWithContext("http://127.0.0.1:11434", models);
|
||||
|
||||
expect(enriched[0]?.contextWindow).toBe(131072);
|
||||
});
|
||||
|
||||
it("uses positive num_ctx when /api/show omits model context metadata", async () => {
|
||||
const models: OllamaTagModel[] = [{ name: "custom-model:latest" }];
|
||||
const fetchMock = vi.fn(async () =>
|
||||
jsonResponse({
|
||||
model_info: {},
|
||||
parameters: "num_ctx 16384",
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const enriched = await enrichOllamaModelsWithContext("http://127.0.0.1:11434", models);
|
||||
|
||||
expect(enriched[0]?.contextWindow).toBe(16384);
|
||||
});
|
||||
|
||||
it("sets models with vision capability from /api/show capabilities", async () => {
|
||||
const models: OllamaTagModel[] = [{ name: "kimi-k2.5:cloud" }, { name: "glm-5.1:cloud" }];
|
||||
const fetchMock = vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
|
||||
@@ -225,4 +278,11 @@ describe("ollama provider models", () => {
|
||||
expect(model.reasoning).toBe(false);
|
||||
expect(model.compat?.supportsTools).toBe(false);
|
||||
});
|
||||
|
||||
it("parses the last positive Modelfile num_ctx value", () => {
|
||||
expect(parseOllamaNumCtxParameter("num_ctx 8192\nnum_ctx 32768")).toBe(32768);
|
||||
expect(parseOllamaNumCtxParameter("temperature 0.8\nnum_ctx -1\nnum_ctx 0")).toBeUndefined();
|
||||
expect(parseOllamaNumCtxParameter('stop "<|eot_id|>"')).toBeUndefined();
|
||||
expect(parseOllamaNumCtxParameter({ num_ctx: 8192 })).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,6 +95,25 @@ function hasCachedOllamaModelShowInfo(info: OllamaModelShowInfo): boolean {
|
||||
return typeof info.contextWindow === "number" || (info.capabilities?.length ?? 0) > 0;
|
||||
}
|
||||
|
||||
export function parseOllamaNumCtxParameter(parameters: unknown): number | undefined {
|
||||
if (typeof parameters !== "string" || !parameters.trim()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let lastValue: number | undefined;
|
||||
for (const rawLine of parameters.split(/\r?\n/)) {
|
||||
const match = rawLine.trim().match(/^num_ctx\s+(-?\d+)\b/);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
const parsed = Number.parseInt(match[1], 10);
|
||||
if (Number.isFinite(parsed) && parsed > 0) {
|
||||
lastValue = parsed;
|
||||
}
|
||||
}
|
||||
return lastValue;
|
||||
}
|
||||
|
||||
export async function queryOllamaModelShowInfo(
|
||||
apiBase: string,
|
||||
modelName: string,
|
||||
@@ -119,6 +138,7 @@ export async function queryOllamaModelShowInfo(
|
||||
const data = (await response.json()) as {
|
||||
model_info?: Record<string, unknown>;
|
||||
capabilities?: unknown;
|
||||
parameters?: unknown;
|
||||
};
|
||||
|
||||
let contextWindow: number | undefined;
|
||||
@@ -138,6 +158,11 @@ export async function queryOllamaModelShowInfo(
|
||||
}
|
||||
}
|
||||
|
||||
const paramCtx = parseOllamaNumCtxParameter(data.parameters);
|
||||
if (paramCtx !== undefined && (contextWindow === undefined || paramCtx > contextWindow)) {
|
||||
contextWindow = paramCtx;
|
||||
}
|
||||
|
||||
const capabilities = Array.isArray(data.capabilities)
|
||||
? (data.capabilities as unknown[]).filter((c): c is string => typeof c === "string")
|
||||
: undefined;
|
||||
|
||||
Reference in New Issue
Block a user