mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:10:45 +00:00
fix(agents): repair malformed tool-call args on openai-completions
This commit is contained in:
committed by
Peter Steinberger
parent
e54d0634c5
commit
49c7319ea5
@@ -96,6 +96,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Auto-reply/media: share one run-scoped reply media context between streamed block delivery and final payload filtering, so a local `MEDIA:` attachment is staged once and duplicate media sends are suppressed reliably. (#68111) Thanks @ayeshakhalid192007-dev.
|
||||
- Plugins/gateway hooks: expose startup config, workspace dir, and a live cron getter on the typed `gateway_start` hook, and move memory-core managed dreaming off the internal `gateway:startup` bridge so cron reconciliation stays on the public plugin hook path. Thanks @vincentkoc.
|
||||
- Plugins/config: read plugin trust decisions from the source config snapshot when a resolved runtime snapshot is active, so `plugins.allow` remains enforced and `doctor`/gateway startup no longer warn that the allowlist is empty when it is configured. Fixes #70161. Also fixes #70141.
|
||||
- Agents/openai-completions: enable malformed streamed tool-call argument repair for self-hosted OpenAI-compatible backends such as Kimi/SGLang, so fragmented tool-call arguments no longer reach tools as empty or unusable objects. Fixes #69672.
|
||||
- Gateway/restart: preserve group and channel chat context when resuming an agent turn after a Gateway restart, so continuation replies keep the same prompt, routing, and tool-status behavior as the original conversation.
|
||||
- Gateway/pairing: shared-secret loopback CLI clients now silently auto-approve `metadata-upgrade` pairing (platform / device family refresh) instead of being disconnected with `1008 pairing required`. This matches the scope-upgrade and role-upgrade behavior added in #69431 and unblocks non-interactive CLI automation when a paired-device record has a stale platform string (e.g. device key replicated across hosts, install migrated between OSes, or platform-string format changed between OpenClaw versions). Browser / Control-UI clients keep the existing approval-required flow for metadata changes.
|
||||
- Gateway/pairing: treat any forwarded-header evidence (`Forwarded`, `X-Forwarded-*`, or `X-Real-IP`) as proxied WebSocket traffic before pairing locality checks, so reverse-proxy topologies cannot use the loopback shared-secret helper auto-pairing path.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { shouldRepairMalformedToolCallArguments } from "./attempt.tool-call-argument-repair.js";
|
||||
|
||||
describe("shouldRepairMalformedToolCallArguments", () => {
|
||||
it("keeps the repair enabled for kimi providers on anthropic-messages", () => {
|
||||
expect(
|
||||
shouldRepairMalformedToolCallArguments({
|
||||
provider: "kimi-coding",
|
||||
modelApi: "anthropic-messages",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("enables the repair for openai-completions even when the provider is not kimi", () => {
|
||||
expect(
|
||||
shouldRepairMalformedToolCallArguments({
|
||||
provider: "openai-compatible",
|
||||
modelApi: "openai-completions",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not enable the repair for unrelated non-kimi transports", () => {
|
||||
expect(
|
||||
shouldRepairMalformedToolCallArguments({
|
||||
provider: "openai-compatible",
|
||||
modelApi: "openai-responses",
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -240,7 +240,7 @@ function wrapStreamRepairMalformedToolCallArguments(
|
||||
if (!loggedRepairIndices.has(event.contentIndex) && repair.kind === "repaired") {
|
||||
loggedRepairIndices.add(event.contentIndex);
|
||||
log.warn(
|
||||
`repairing Kimi tool call arguments with ${repair.leadingPrefix.length} leading chars and ${repair.trailingSuffix.length} trailing chars`,
|
||||
`repairing malformed tool call arguments with ${repair.leadingPrefix.length} leading chars and ${repair.trailingSuffix.length} trailing chars`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -294,8 +294,14 @@ export function wrapStreamFnRepairMalformedToolCallArguments(baseFn: StreamFn):
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldRepairMalformedAnthropicToolCallArguments(provider?: string): boolean {
|
||||
return normalizeProviderId(provider ?? "") === "kimi";
|
||||
export function shouldRepairMalformedToolCallArguments(params: {
|
||||
provider?: string;
|
||||
modelApi?: string | null;
|
||||
}): boolean {
|
||||
return (
|
||||
normalizeProviderId(params.provider ?? "") === "kimi" ||
|
||||
params.modelApi === "openai-completions"
|
||||
);
|
||||
}
|
||||
|
||||
export function wrapStreamFnDecodeXaiToolCallArguments(baseFn: StreamFn): StreamFn {
|
||||
|
||||
@@ -238,7 +238,7 @@ import {
|
||||
shouldUseOpenAIWebSocketTransport,
|
||||
} from "./attempt.thread-helpers.js";
|
||||
import {
|
||||
shouldRepairMalformedAnthropicToolCallArguments,
|
||||
shouldRepairMalformedToolCallArguments,
|
||||
wrapStreamFnDecodeXaiToolCallArguments,
|
||||
wrapStreamFnRepairMalformedToolCallArguments,
|
||||
} from "./attempt.tool-call-argument-repair.js";
|
||||
@@ -1464,8 +1464,10 @@ export async function runEmbeddedAttempt(
|
||||
);
|
||||
|
||||
if (
|
||||
params.model.api === "anthropic-messages" &&
|
||||
shouldRepairMalformedAnthropicToolCallArguments(params.provider)
|
||||
shouldRepairMalformedToolCallArguments({
|
||||
provider: params.provider,
|
||||
modelApi: params.model.api,
|
||||
})
|
||||
) {
|
||||
activeSession.agent.streamFn = wrapStreamFnRepairMalformedToolCallArguments(
|
||||
activeSession.agent.streamFn,
|
||||
|
||||
Reference in New Issue
Block a user