fix(agents): respect explicit user compat overrides for non-native openai-completions (#44432)
Reviewed-by: @frankekn
This commit is contained in:
parent
84428bbba6
commit
60cb1d683c
@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/gateway startup: treat plain-text and transient `/gateway/bot` metadata fetch failures as transient startup errors so Discord gateway boot no longer crashes on unhandled rejections. (#44397) Thanks @jalehman.
|
||||
- Gateway/session reset: preserve `lastAccountId` and `lastThreadId` across gateway session resets so replies keep routing back to the same account and thread after `/reset`. (#44773) Thanks @Lanfei.
|
||||
- Agents/memory bootstrap: load only one root memory file, preferring `MEMORY.md` and using `memory.md` as a fallback, so case-insensitive Docker mounts no longer inject duplicate memory context. (#26054) Thanks @Lanfei.
|
||||
- Agents/OpenAI-compatible compat overrides: respect explicit user `models[].compat` opt-ins for non-native `openai-completions` endpoints so usage-in-streaming capability overrides no longer get forced off when the endpoint actually supports them. (#44432) Thanks @cheapestinference.
|
||||
|
||||
## 2026.3.12
|
||||
|
||||
|
||||
@ -251,7 +251,7 @@ describe("normalizeModelCompat", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("overrides explicit supportsDeveloperRole true on non-native endpoints", () => {
|
||||
it("respects explicit supportsDeveloperRole true on non-native endpoints", () => {
|
||||
const model = {
|
||||
...baseModel(),
|
||||
provider: "custom-cpa",
|
||||
@ -259,10 +259,10 @@ describe("normalizeModelCompat", () => {
|
||||
compat: { supportsDeveloperRole: true },
|
||||
};
|
||||
const normalized = normalizeModelCompat(model);
|
||||
expect(supportsDeveloperRole(normalized)).toBe(false);
|
||||
expect(supportsDeveloperRole(normalized)).toBe(true);
|
||||
});
|
||||
|
||||
it("overrides explicit supportsUsageInStreaming true on non-native endpoints", () => {
|
||||
it("respects explicit supportsUsageInStreaming true on non-native endpoints", () => {
|
||||
const model = {
|
||||
...baseModel(),
|
||||
provider: "custom-cpa",
|
||||
@ -270,6 +270,18 @@ describe("normalizeModelCompat", () => {
|
||||
compat: { supportsUsageInStreaming: true },
|
||||
};
|
||||
const normalized = normalizeModelCompat(model);
|
||||
expect(supportsUsageInStreaming(normalized)).toBe(true);
|
||||
});
|
||||
|
||||
it("still forces flags off when not explicitly set by user", () => {
|
||||
const model = {
|
||||
...baseModel(),
|
||||
provider: "custom-cpa",
|
||||
baseUrl: "https://proxy.example.com/v1",
|
||||
};
|
||||
delete (model as { compat?: unknown }).compat;
|
||||
const normalized = normalizeModelCompat(model);
|
||||
expect(supportsDeveloperRole(normalized)).toBe(false);
|
||||
expect(supportsUsageInStreaming(normalized)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@ -55,17 +55,22 @@ export function normalizeModelCompat(model: Model<Api>): Model<Api> {
|
||||
// The `developer` role and stream usage chunks are OpenAI-native behaviors.
|
||||
// Many OpenAI-compatible backends reject `developer` and/or emit usage-only
|
||||
// chunks that break strict parsers expecting choices[0]. For non-native
|
||||
// openai-completions endpoints, force both compat flags off.
|
||||
// openai-completions endpoints, force both compat flags off — unless the
|
||||
// user has explicitly opted in via their model config.
|
||||
const compat = model.compat ?? undefined;
|
||||
// When baseUrl is empty the pi-ai library defaults to api.openai.com, so
|
||||
// leave compat unchanged and let default native behavior apply.
|
||||
// Note: explicit true values are intentionally overridden for non-native
|
||||
// endpoints for safety.
|
||||
const needsForce = baseUrl ? !isOpenAINativeEndpoint(baseUrl) : false;
|
||||
if (!needsForce) {
|
||||
return model;
|
||||
}
|
||||
if (compat?.supportsDeveloperRole === false && compat?.supportsUsageInStreaming === false) {
|
||||
|
||||
// Respect explicit user overrides: if the user has set a compat flag to
|
||||
// true in their model definition, they know their endpoint supports it.
|
||||
const forcedDeveloperRole = compat?.supportsDeveloperRole === true;
|
||||
const forcedUsageStreaming = compat?.supportsUsageInStreaming === true;
|
||||
|
||||
if (forcedDeveloperRole && forcedUsageStreaming) {
|
||||
return model;
|
||||
}
|
||||
|
||||
@ -73,7 +78,11 @@ export function normalizeModelCompat(model: Model<Api>): Model<Api> {
|
||||
return {
|
||||
...model,
|
||||
compat: compat
|
||||
? { ...compat, supportsDeveloperRole: false, supportsUsageInStreaming: false }
|
||||
? {
|
||||
...compat,
|
||||
supportsDeveloperRole: forcedDeveloperRole || false,
|
||||
supportsUsageInStreaming: forcedUsageStreaming || false,
|
||||
}
|
||||
: { supportsDeveloperRole: false, supportsUsageInStreaming: false },
|
||||
} as typeof model;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user