Repository navigation
Expand file tree
/
Copy pathparamSupport.js
More file actions
90 lines (85 loc) · 4.38 KB
/
Copy pathparamSupport.js
File metadata and controls
90 lines (85 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { getCapabilitiesForModel } from "../../providers/capabilities.js";
// Strip request params a given provider/model rejects upstream (e.g. HTTP 400).
// Config-driven: add a rule instead of scattering `delete body.x` across executors.
// Each rule: optional provider, regex match on model, list of params to drop.
// A param is removed only when it is present (!== undefined).
const STRIP_RULES = [
// All Claude models: temperature deprecated/rejected upstream (Anthropic 400). #1748
{ match: /claude/i, drop: ["temperature"] },
// GitHub Copilot gpt-5.4: temperature unsupported.
{ provider: "github", match: /gpt-5\.4/i, drop: ["temperature"] },
// GitHub Copilot Claude (except opus/sonnet 4.6): thinking + reasoning_effort rejected. #713
{ provider: "github", match: (m) => /claude/i.test(m) && !/claude.*(opus|sonnet).*4\.6/i.test(m), drop: ["thinking", "reasoning_effort"] },
// Cloudflare Workers AI: content must be plain string, rejects OpenAI content-part array (#1926)
{ provider: "cloudflare-ai", flattenContent: true },
{ provider: "volcengine-ark", match: /glm-5/i, clampToModelMaxOutput: true },
// VolcEngine Ark caps the Kimi family at max_tokens <= 32768, but the model's
// advertised ceiling is far higher (Kimi-K2.7-Code resolves to maxOutput 262144),
// so clampToModelMaxOutput alone leaves it uncapped and the request 400s with
// "integer above maximum value, expected <= 32768". Pin an explicit endpoint cap;
// min() with the model ceiling still applies if a variant's own limit is lower.
{ provider: "volcengine-ark", match: /kimi/i, maxOutputCap: 32768, clampToModelMaxOutput: true },
// OpenAI gpt-5 family + o-series reject `max_tokens` upstream (HTTP 400:
// "Use 'max_completion_tokens' instead.") — rename so the output cap survives.
// Provider-agnostic: the OpenAI API contract applies to resellers/proxies too
// (openai, github copilot, azure, openrouter, ...). #2742
{ match: /(^|[^a-z0-9])(gpt-5|o[134])([^a-z0-9]|$)/i, rename: { max_tokens: "max_completion_tokens" } },
];
// Test a rule's match (regex or predicate) against the model id.
function matches(rule, model) {
if (!rule.match) return true;
return typeof rule.match === "function" ? rule.match(model) : rule.match.test(model);
}
function clampNumber(body, key, ceiling) {
if (typeof body[key] === "number" && Number.isFinite(body[key]) && body[key] > ceiling) {
body[key] = ceiling;
}
}
// Remove unsupported params from body in place; returns body.
export function stripUnsupportedParams(provider, model, body) {
if (!model || !body || typeof body !== "object") return body;
for (const rule of STRIP_RULES) {
if (rule.provider && rule.provider !== provider) continue;
if (!matches(rule, model)) continue;
for (const key of rule.drop || []) {
if (body[key] !== undefined) delete body[key];
}
// CF Workers AI oneOf root schema only accepts content as plain string (#1926)
if (rule.flattenContent && Array.isArray(body.messages)) {
for (const msg of body.messages) {
if (msg && Array.isArray(msg.content)) {
msg.content = msg.content
.map(b => (b?.type === "text" && typeof b.text === "string") ? b.text : "")
.join("");
}
}
}
if (rule.clampToModelMaxOutput || Number.isFinite(rule.maxOutputCap)) {
const modelCeiling = getCapabilitiesForModel(provider, model).maxOutput;
const candidates = [];
if (rule.clampToModelMaxOutput && Number.isFinite(modelCeiling) && modelCeiling > 0) {
candidates.push(modelCeiling);
}
if (Number.isFinite(rule.maxOutputCap) && rule.maxOutputCap > 0) {
candidates.push(rule.maxOutputCap);
}
if (candidates.length > 0) {
const ceiling = Math.min(...candidates);
clampNumber(body, "max_tokens", ceiling);
clampNumber(body, "max_completion_tokens", ceiling);
clampNumber(body, "max_output_tokens", ceiling);
}
}
// Rename params the upstream rejects under their old name (e.g. OpenAI
// gpt-5/o-series: max_tokens → max_completion_tokens). Prefer keeping an
// already-present target name, then drop the rejected one.
if (rule.rename) {
for (const [from, to] of Object.entries(rule.rename)) {
if (body[from] === undefined) continue;
if (body[to] === undefined) body[to] = body[from];
delete body[from];
}
}
}
return body;
}