Skip to content

fix(responses): return error to model on hallucinated tool calls instead of silently exiting#6231

Merged
mattf merged 4 commits into
ogx-ai:mainfrom
matteo-grimaldi:fix/hallucinated-tool-call-silent-exit
Jul 8, 2026
Merged

fix(responses): return error to model on hallucinated tool calls instead of silently exiting#6231
mattf merged 4 commits into
ogx-ai:mainfrom
matteo-grimaldi:fix/hallucinated-tool-call-silent-exit

Conversation

@matteo-grimaldi

Copy link
Copy Markdown
Contributor

Bug: OGX Silently Exits Inference Loop on Unrecognized Tool Calls

Component: OGX Responses API (ogx.providers.inline.responses.builtin.responses.streaming)
Severity: High (agent run terminates silently — no error surfaced to the user or caller)
Type: Bug

Description

When the LLM generates a tool call for a function name that does not match any tool registered in the MCP servers, OGX classifies it as a "client-side function call" and exits the inference loop. Because there is no client waiting to handle the call, the agent run simply stops. No error is returned to the caller, no diagnostic is written to the response object, and the MCP sessions are torn down.

From the user's perspective the agent appears to hang or silently stop producing output mid-diagnosis.

Observed Behaviour

Pod: ocp-troubleshooter-6db9d4f7c9-hpzfs
Timestamp: 2026-06-16 12:25:44 UTC
Model: nemotron/nemotron-3-nano-30b-a3b

Time (UTC) Event Detail
12:25:44.354 LLM response received finish_reason: "tool_calls"
12:25:44.401 WARNING — service tier Service tier is None, setting to default
12:25:44.403 WARNING — unrecognized tool Model called unrecognized tool; treating as a client-side function call. name=services_list
12:25:44.447 Inference loop exit Exiting inference loop since there is a function (client-side) tool call
12:25:44.610 SSE stream disconnect GET stream disconnected, reconnecting in 1000ms...
12:25:44.611–.677 MCP session cleanup DELETE requests sent to all three MCP servers (openshift-mcp, prometheus-mcp, ticketing-mcp)
12:25:53+ Idle Only /v1/models health checks continue

The model called services_list({"namespace": "demo-app"}). This tool does not exist in any of the three registered MCP servers:

  • OpenShift MCP (kubernetes-mcp-server) exposes: pods_list_in_namespace, pods_get, pods_log, pods_delete, and other Kubernetes resource tools — but not services_list.
  • Prometheus MCP exposes: query_prometheus, query_prometheus_range.
  • Ticketing MCP exposes: create_incident, list_incidents, get_incident, update_incident, add_work_note.

The nemotron model hallucinated a plausible but non-existent tool name — a known failure mode with smaller models, especially when tool schemas contain many functions with similar naming patterns (e.g. pods_list_in_namespace → the model infers services_list should also exist).

…ead of silently exiting

When the LLM generates a tool call for a function name that does not
match any registered server-side tool in a server-only agentic loop,
OGX now feeds a structured error back to the model listing available
tools, allowing it to self-correct. Previously the call was classified
as a client-side function call causing the inference loop to exit
silently with no error surfaced to the caller.

A retry counter (_MAX_HALLUCINATED_TOOL_RETRIES=3) caps consecutive
hallucinated attempts and exits with status "incomplete" to prevent
infinite loops.

Signed-off-by: Matteo Grimaldi <matteo@redhat.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Matteo Grimaldi <mgrimald@redhat.com>
@mattf

mattf commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@matteo-grimaldi great idea! please address ci failures.

matteo-grimaldi and others added 3 commits July 8, 2026 15:24
Existing tests unpacked 4 values from _separate_tool_calls but the
hallucinated-tool-call change added a 5th (has_hallucinated_retries).
Also fix import ordering to satisfy ruff's isort rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Matteo Grimaldi <mgrimald@redhat.com>
…st refactor

Upstream PR ogx-ai#6227 changed create_openai_response to accept a single
CreateResponseRequest object instead of individual keyword arguments.
Update the two new tests to use the new calling convention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Matteo Grimaldi <mgrimald@redhat.com>
@mattf

mattf commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@matteo-grimaldi couple more

@mattf mattf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice idea, good start

@mattf mattf added this pull request to the merge queue Jul 8, 2026
Merged via the queue into ogx-ai:main with commit 0509e92 Jul 8, 2026
56 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.

2 participants