


















@@ -126,6 +126,17 @@ describe("normalizeCompatibilityConfigValues", () => {
126126expect(res.changes).toStrictEqual([]);
127127};
128128129+const ollamaModel = (overrides: Record<string, unknown> = {}) => ({
130+id: "llama3.3",
131+name: "Llama 3.3",
132+reasoning: false,
133+input: ["text"] as Array<"text">,
134+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
135+contextWindow: 81920,
136+maxTokens: 8192,
137+ ...overrides,
138+});
139+129140beforeAll(() => {
130141previousOauthDir = process.env.OPENCLAW_OAUTH_DIR;
131142tempOauthDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-oauth-"));
@@ -1229,6 +1240,214 @@ describe("normalizeCompatibilityConfigValues", () => {
12291240]);
12301241});
123112421243+it("sets native Ollama params.num_ctx from explicit model contextWindow budgets", () => {
1244+const res = normalizeCompatibilityConfigValues({
1245+models: {
1246+providers: {
1247+ollama: {
1248+baseUrl: "http://localhost:11434",
1249+api: "ollama",
1250+models: [
1251+ollamaModel({
1252+params: {
1253+temperature: 0.2,
1254+},
1255+}),
1256+ollamaModel({
1257+id: "llama3.3-small",
1258+contextWindow: 32768,
1259+maxTokens: 4096,
1260+params: {
1261+num_ctx: 16384,
1262+},
1263+}),
1264+],
1265+},
1266+},
1267+},
1268+});
1269+1270+expect(res.config.models?.providers?.ollama?.models?.map((model) => model.params)).toEqual([
1271+{ temperature: 0.2, num_ctx: 81920 },
1272+{ num_ctx: 16384 },
1273+]);
1274+expect(res.changes).toEqual([
1275+"Set models.providers.ollama.models[0].params.num_ctx to 81920 for native Ollama compatibility.",
1276+]);
1277+});
1278+1279+it("sets native Ollama params.num_ctx from custom provider maxTokens budgets", () => {
1280+const res = normalizeCompatibilityConfigValues({
1281+models: {
1282+providers: {
1283+localOllama: {
1284+baseUrl: "http://ollama-box:11434",
1285+api: "ollama",
1286+models: [
1287+ollamaModel({
1288+contextWindow: 0,
1289+maxTokens: 24576,
1290+}),
1291+],
1292+},
1293+},
1294+},
1295+});
1296+1297+expect(res.config.models?.providers?.localOllama?.models?.[0]?.params).toEqual({
1298+num_ctx: 24576,
1299+});
1300+expect(res.changes).toEqual([
1301+"Set models.providers.localOllama.models[0].params.num_ctx to 24576 for native Ollama compatibility.",
1302+]);
1303+});
1304+1305+it("prefers provider contextWindow over model maxTokens for native Ollama params.num_ctx", () => {
1306+const modelWithoutContextWindow = ollamaModel({
1307+contextWindow: undefined,
1308+maxTokens: 4096,
1309+});
1310+const res = normalizeCompatibilityConfigValues({
1311+models: {
1312+providers: {
1313+ollama: {
1314+baseUrl: "http://localhost:11434",
1315+api: "ollama",
1316+contextWindow: 65536,
1317+models: [modelWithoutContextWindow],
1318+},
1319+},
1320+},
1321+});
1322+1323+expect(res.config.models?.providers?.ollama?.models?.[0]?.params).toBeUndefined();
1324+expect(res.config.models?.providers?.ollama?.params).toEqual({
1325+num_ctx: 65536,
1326+});
1327+expect(res.changes).toEqual([
1328+"Set models.providers.ollama.params.num_ctx to 65536 for native Ollama compatibility.",
1329+]);
1330+});
1331+1332+it("sets provider-level native Ollama params.num_ctx when auto-discovered models use provider budgets", () => {
1333+const res = normalizeCompatibilityConfigValues({
1334+models: {
1335+providers: {
1336+ollama: {
1337+baseUrl: "http://localhost:11434",
1338+api: "ollama",
1339+contextWindow: 65536,
1340+models: [],
1341+},
1342+},
1343+},
1344+});
1345+1346+expect(res.config.models?.providers?.ollama?.params).toEqual({
1347+num_ctx: 65536,
1348+});
1349+expect(res.changes).toEqual([
1350+"Set models.providers.ollama.params.num_ctx to 65536 for native Ollama compatibility.",
1351+]);
1352+});
1353+1354+it("sets provider-level native Ollama params.num_ctx when explicit model entries also exist", () => {
1355+const res = normalizeCompatibilityConfigValues({
1356+models: {
1357+providers: {
1358+ollama: {
1359+baseUrl: "http://localhost:11434",
1360+api: "ollama",
1361+contextWindow: 65536,
1362+models: [
1363+ollamaModel({
1364+contextWindow: 32768,
1365+}),
1366+],
1367+},
1368+},
1369+},
1370+});
1371+1372+expect(res.config.models?.providers?.ollama?.params).toEqual({
1373+num_ctx: 65536,
1374+});
1375+expect(res.config.models?.providers?.ollama?.models?.[0]?.params).toEqual({
1376+num_ctx: 32768,
1377+});
1378+expect(res.changes).toEqual([
1379+"Set models.providers.ollama.params.num_ctx to 65536 for native Ollama compatibility.",
1380+"Set models.providers.ollama.models[0].params.num_ctx to 32768 for native Ollama compatibility.",
1381+]);
1382+});
1383+1384+it("keeps existing provider-level native Ollama params.num_ctx ahead of inherited provider budgets", () => {
1385+const res = normalizeCompatibilityConfigValues({
1386+models: {
1387+providers: {
1388+ollama: {
1389+baseUrl: "http://localhost:11434",
1390+api: "ollama",
1391+contextWindow: 65536,
1392+params: {
1393+num_ctx: 32768,
1394+},
1395+models: [
1396+ollamaModel({
1397+contextWindow: undefined,
1398+maxTokens: undefined,
1399+}),
1400+],
1401+},
1402+},
1403+},
1404+});
1405+1406+expect(res.config.models?.providers?.ollama?.params).toEqual({
1407+num_ctx: 32768,
1408+});
1409+expect(res.config.models?.providers?.ollama?.models?.[0]?.params).toBeUndefined();
1410+expect(res.changes).toEqual([]);
1411+});
1412+1413+it("does not set native Ollama params for OpenAI-compatible Ollama configs", () => {
1414+const input = {
1415+models: {
1416+providers: {
1417+ollama: {
1418+baseUrl: "http://localhost:11434/v1",
1419+api: "openai-completions" as const,
1420+models: [ollamaModel()],
1421+},
1422+},
1423+},
1424+};
1425+1426+const res = normalizeCompatibilityConfigValues(input);
1427+1428+expect(res.config).toEqual(input);
1429+expect(res.changes).toEqual([]);
1430+});
1431+1432+it("does not set native Ollama params for implicit OpenAI-compatible Ollama configs", () => {
1433+const input = {
1434+models: {
1435+providers: {
1436+ollama: {
1437+baseUrl: "http://localhost:11434/v1",
1438+contextWindow: 65536,
1439+models: [ollamaModel()],
1440+},
1441+},
1442+},
1443+};
1444+1445+const res = normalizeCompatibilityConfigValues(input);
1446+1447+expect(res.config).toEqual(input);
1448+expect(res.changes).toEqual([]);
1449+});
1450+12321451it("normalizes persisted mistral model maxTokens that matched the old context-sized defaults", () => {
12331452const res = normalizeCompatibilityConfigValues({
12341453models: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。