Skip to content

fix(ai): force tool calls so tag generation stops returning prose - #333

Merged
EyJunge1 merged 2 commits into
tickernelz:mainfrom
share121:fix/tag-choice-required
Oct 1, 2026
Merged

EyJunge1 merged 2 commits into
tickernelz:mainfrom
share121:fix/tag-choice-required

Conversation

@share121

@share121 share121 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Addresses the prose-without-tool-call failure mode from #303 (complementary with #319 for soft-fail accounting).

The OpenAI chat-completion provider hard-coded tool_choice: "auto", which lets a model answer a tool-requesting prompt in plain text. The tag-migration prompt asked for a tool call (save_tags) but also said "Return ONLY a comma-separated list of tags", so models like deepseek-flash / deepseek-v4-flash return prose with no tool_calls. executeToolCall then returns success: false, handleRunTagMigrationBatch silently skips the tag write, and the migration reports success while memories stay untagged — so the modal reappears on every page load.

Verified directly against deepseek-flash with the exact migration prompt:

request model response
tool_choice: "auto" (previous) "Manim, 数学动画引擎, Python, Manim CE" — no tool_calls
tool_choice: "required" save_tags called with {"tags": [...]}

Changes

  • base-provider.ts — add forceToolChoice?: boolean to ProviderConfig.
  • openai-chat-completion.ts — send tool_choice: "required" unless forceToolChoice === false.
  • config.ts / provider-config.ts — wire forceToolChoice through user config ("forceToolChoice": false opt-out).
  • openai-chat-completion.ts — 400 hint when thinking mode rejects tool_choice.
  • api-handlers.ts — migration prompt asks for the save_tags tool call.
  • Tests for default, config opt-out, and 400 hint.

Relation to #319

#319 fixes soft-fail accounting / UI false success. This PR reduces the prose failure mode. Merge both. Empty arguments: "{}" remains a separate path.

Does not solely close #303. Rebased onto current main.

Test plan

  • bunx tsc --noEmit — clean
  • bun test tests/openai-chat-completion-provider.test.ts tests/orcarouter-provider.test.ts tests/ai-provider-config.test.ts — pass
  • Both merge orders with fix: do not mark failed tag migrations as processed #319 onto main verified clean
  • Manual: rebuilt plugin, tag-migration modal on 7 untagged memories — all written, detect 7 → 0

share121 and others added 2 commits October 1, 2026 21:37
The OpenAI chat-completion provider hard-coded tool_choice: "auto", which
lets a model answer a tool-requesting prompt in plain text. deepseek-flash
and similar models do exactly that: on the tag-migration prompt the assistant
reply is a comma-separated list with no tool_calls, so executeToolCall
returns success:false and handleRunTagMigrationBatch silently skips the tag
write. The migration then reports success while memories stay untagged and
the modal reappears on every load (#303).

Default tool_choice to "required" so the model must emit the tool call, with
an opt-out (forceToolChoice:false) for providers that reject it. Also align
the migration prompt with the tool it is meant to call instead of asking for
a comma-separated list.
Make the tool_choice required opt-out reachable from opencode.json and
surface a clear config hint when thinking models reject forced tool calls.
@EyJunge1
EyJunge1 force-pushed the fix/tag-choice-required branch from 96d5a46 to 48a22aa Compare October 1, 2026 19:38
@EyJunge1
EyJunge1 merged commit dd7972c into tickernelz:main Oct 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tag migration batch marks failed items as processed — memories stay untagged forever and the migration modal reappears on every page load

2 participants