-
Notifications
You must be signed in to change notification settings - Fork 154
fix: enforce tool_choice and prevent data loss for local LLM compatibility #1292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,11 @@ import { | |||||||||||||||||||||||||||||||||||||||
| * Without this patch, text-based function calls are treated as assistant | ||||||||||||||||||||||||||||||||||||||||
| * messages, causing `enforceFunctionCall` checks to fail. | ||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||
| * The wrapping is also used to enforce `tool_choice: "required"` for | ||||||||||||||||||||||||||||||||||||||||
| * non-streaming requests that include tools. The request body passed by | ||||||||||||||||||||||||||||||||||||||||
| * MicroAgentica is a frozen/sealed object, so we spread it into a new object | ||||||||||||||||||||||||||||||||||||||||
| * to safely set additional fields. | ||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||
| * The wrapping is idempotent — calling this multiple times with the same vendor | ||||||||||||||||||||||||||||||||||||||||
| * will only wrap once (guarded by a Symbol). | ||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,10 +43,23 @@ export const supportFunctionCallFallback = ( | |||||||||||||||||||||||||||||||||||||||
| body: ICreateBody, | ||||||||||||||||||||||||||||||||||||||||
| options?: Record<string, unknown>, | ||||||||||||||||||||||||||||||||||||||||
| ): Promise<unknown> { | ||||||||||||||||||||||||||||||||||||||||
| // Enforce function calling: require the model to call one of the tools. | ||||||||||||||||||||||||||||||||||||||||
| // The body object from MicroAgentica may be frozen/sealed, so we spread | ||||||||||||||||||||||||||||||||||||||||
| // into a new object rather than mutating directly. | ||||||||||||||||||||||||||||||||||||||||
| // NOTE: Apply to both streaming and non-streaming requests — Ollama and | ||||||||||||||||||||||||||||||||||||||||
| // compatible local model servers support tool_choice on streaming calls too. | ||||||||||||||||||||||||||||||||||||||||
| const effectiveBody: ICreateBody = | ||||||||||||||||||||||||||||||||||||||||
| body.tools?.length | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+52
|
||||||||||||||||||||||||||||||||||||||||
| // Enforce function calling: require the model to call one of the tools. | |
| // The body object from MicroAgentica may be frozen/sealed, so we spread | |
| // into a new object rather than mutating directly. | |
| // NOTE: Apply to both streaming and non-streaming requests — Ollama and | |
| // compatible local model servers support tool_choice on streaming calls too. | |
| const effectiveBody: ICreateBody = | |
| body.tools?.length | |
| // Enforce function calling by default when tools are present, but do not | |
| // override an explicit upstream tool_choice and respect vendor capability | |
| // flags for models/providers that do not support tool_choice. | |
| // The body object from MicroAgentica may be frozen/sealed, so we spread | |
| // into a new object rather than mutating directly. | |
| // NOTE: Apply to both streaming and non-streaming requests when supported | |
| // — Ollama and compatible local model servers support tool_choice on | |
| // streaming calls too. | |
| const effectiveBody: ICreateBody = | |
| body.tools?.length && | |
| body.tool_choice === undefined && | |
| (vendor.useToolChoice ?? true) |
Copilot
AI
Apr 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
console.log here will run on every completion request and can create noisy logs / performance overhead in production. Prefer gating behind an env/config debug flag (similar to other debug logging in this repo) or removing it once validated.
| console.log( | |
| `[FunctionCallFallback] request: tools=${body.tools?.length ?? 0} stream=${body.stream ?? false} tool_choice=${(effectiveBody as any).tool_choice ?? "none"}`, | |
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
supportFunctionCallFallbackappears to be unused in the current codebase (all imports/calls are commented out). If the goal is to fix local-LLM tool calling in the main pipeline, this wrapper needs to be enabled from the agent construction path(s); otherwise these fixes won’t take effect at runtime.