Skip to content

fix(openai): surface chat-completions refusal text as visible content#102349

Closed
ZOOWH wants to merge 8 commits into
openclaw:mainfrom
ZOOWH:fix/102321-openai-refusal
Closed

fix(openai): surface chat-completions refusal text as visible content#102349
ZOOWH wants to merge 8 commits into
openclaw:mainfrom
ZOOWH:fix/102321-openai-refusal

Conversation

@ZOOWH

@ZOOWH ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes #102321 — OpenAI chat-completions refusal field silently dropped,
assistant turn resolves to empty content. User sees a blank reply.

Root Cause

Two transport paths read content/tool_calls/reasoning but drop the
refusal field. When content is null and only refusal is present,
nothing is appended to output.

Fix

Agent transport: read choiceDelta.refusal, route as visible text.

Provider: normalize choice.delta ?? choice.message so both streaming
and aggregated refusal responses flow through the same delta path.

Evidence

Runtime refusal proof

node --import tsx calling processOpenAICompletionsStream:
input:  { delta: { refusal: "I cannot help with that request." } }
output: [{ type: "text", text: "I cannot help with that request." }]

Live Model Proof

pnpm openclaw agent --local --model deepseek/deepseek-chat --json
api=openai-completions  stopReason=stop

Regression Tests

Agent: "surfaces a streaming refusal delta when content is null"
Provider: "surfaces chat-completions refusal deltas as visible text"
Provider: "surfaces aggregated message.refusal as visible assistant text"

src/agents/openai-transport-stream.test.ts  299 passed
packages/ai/src/providers/openai-completions.test.ts  43 passed

Autoreview

autoreview: patch is correct (0.9)
  • oxfmt + oxlint pass
  • autoreview clean
  • Runtime refusal proof
  • Live model proof
  • Two transport paths, streaming + aggregated

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jul 9, 2026
@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close as superseded: this PR targets the same canonical OpenAI chat-completions refusal bug as the proof-positive sibling at #102344, while this branch has no unique remaining fix and still lacks adequate real behavior proof.

Root-cause cluster
Relationship: superseded
Canonical: #102344
Summary: The linked issue is the canonical bug report, but the proof-positive sibling PR is the better open landing candidate for the same root cause; this PR is a duplicate candidate without unique remaining work.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Use #102344 as the canonical landing candidate, resolve its remaining CI follow-up, then close the linked issue and duplicate PRs after merge.

So I’m closing this here and keeping the remaining discussion on #102344.

Review details

Best possible solution:

Use #102344 as the canonical landing candidate, resolve its remaining CI follow-up, then close the linked issue and duplicate PRs after merge.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection of current main shows both OpenAI-completions assemblers can receive refusal-only delta/message shapes and never read the refusal field, leaving no visible assistant text.

Is this the best way to solve the issue?

No for keeping this branch as the landing candidate. The parser-parity fix shape is plausible, but the better maintainer path is the sibling PR with broader delta/message coverage and positive real behavior proof.

Security review:

Security review cleared: The diff only changes local TypeScript response assembly and tests; it does not alter dependencies, workflows, secrets, package metadata, install scripts, or package resolution.

AGENTS.md: found and applied where relevant.

What I checked:

  • Repository policy read and applied: Root AGENTS.md and scoped src/agents/AGENTS.md were read fully; their provider/runtime sibling-surface and dependency-contract review rules apply to this PR. (AGENTS.md:1, 1825c9f89047)
  • Current main still has the reported parser gap: Current main normalizes chat-completions choice.delta or choice.message, then handles content, reasoning, and tool calls without reading refusal in the agent assembler. (src/agents/openai-transport-stream.ts:3135, 1825c9f89047)
  • Shared provider has the same current-main gap: Current main streamOpenAICompletions handles choice.delta content, reasoning, and tool calls but does not read choice.delta.refusal or choice.message.refusal. (packages/ai/src/providers/openai-completions.ts:427, 1825c9f89047)
  • This PR overlaps the same implementation surface: At PR head, this branch adds refusal handling to the agent assembler and packages/ai provider path for the same linked bug. (src/agents/openai-transport-stream.ts:3183, 1ec62c16999a)
  • Superseding sibling is proof-positive and broader: fix: surface OpenAI chat-completions refusal as assistant text #102344 is open, mergeable, labeled proof:sufficient and ready for maintainer look, targets the same issue, covers agent and packages/ai delta/message refusal forms, and includes contributor proof through production OpenAI SDK request/parse paths against a local SSE server. (1e49e5005dc3)
  • Canonical issue matches both PRs: The linked issue is the canonical report for refusal-only OpenAI chat-completions responses resolving to empty assistant content on the openai-completions transport.

Likely related people:

  • LiLan0125: Current-main blame on both affected parser loops points to commit 8cb9263, which carried the existing content/reasoning/tool-call handling that omitted refusal. (role: recent carrier of affected parser loops; confidence: medium; commits: 8cb92636920b; files: src/agents/openai-transport-stream.ts, packages/ai/src/providers/openai-completions.ts)
  • Vincent Koc: Commit 4798e12 introduced the provider transport adapter seam that owns this OpenAI-compatible runtime path. (role: transport-seam introducer; confidence: medium; commits: 4798e125f40c; files: src/agents/openai-transport-stream.ts, packages/ai/src/providers/openai-completions.ts)
  • steipete: Commit 062f88e extracted the reusable packages/ai runtime, and commit 4bf70be recently touched the shared OpenAI-completions provider path. (role: shared AI runtime package refactor owner; confidence: high; commits: 062f88e3e3af, 4bf70be01a21; files: packages/ai/src/providers/openai-completions.ts, packages/ai/src/providers/register-builtins.ts)
  • Balaji Siva: Commit efab976 fixed a nearby OpenAI-compatible completions silent-output issue involving vLLM reasoning and empty tool-call shapes. (role: adjacent bug-fix author; confidence: medium; commits: efab9763dc87; files: src/agents/openai-transport-stream.ts)

Codex review notes: model internal, reasoning high; reviewed against 1825c9f89047.

@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 9, 2026
@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 9, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jul 9, 2026
@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 9, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 9, 2026
@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@ZOOWH

ZOOWH commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 9, 2026
@ZOOWH ZOOWH closed this Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P2 Normal backlog priority with limited blast radius. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openai-completions: chat-completions refusal field dropped, assistant turn resolves to empty content

1 participant