Skip to content

fix(tools): preserve provider-compatible descriptions - #886

Closed
seonghobae wants to merge 2 commits into
mainfrom
fix/provider-tool-description-compat
Closed

fix(tools): preserve provider-compatible descriptions#886
seonghobae wants to merge 2 commits into
mainfrom
fix/provider-tool-description-compat

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • preserve OpenCode tool descriptions longer than the provider-compatible 1024-character field in a versioned system-message document
  • replace only the constrained field with a short reference; do not truncate or summarize semantic content
  • record the runtime evidence and accepted structured-tool boundary

Evidence

  • hourly-loop run 32986962178 reached OpenCode through the gateway, then returned 400 invalid_tools for the overlong description
  • 48 passed across tool-shape, null/omit, passthrough, and exact HTTP forwarding regressions
  • git diff --check

Governance

Normal protected merge only. No bypass, self-approval, or force push.


Open in Devin Review

@seonghobae
seonghobae enabled auto-merge (squash) August 26, 2026 16:22
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 15 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 710cd23f-ef43-4da9-a1fc-fea48486ad41

📥 Commits

Reviewing files that changed from the base of the PR and between 5a01759 and e43fbdd.

📒 Files selected for processing (5)
  • contextual_orchestrator/server.py
  • docs/architecture.md
  • docs/planning/adrs/0035-structured-provider-orchestration.md
  • docs/product-technical-gap-baseline.md
  • tests/test_tool_description_length_http_honesty.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Superseded by #887 after comparing both exact implementations and the current official OpenAI schemas.

The API schemas define function.description as a string but do not establish the local 1,024-character maximum. PR #887 removes only that unsupported gateway restriction while retaining the existing 64 KiB request-body bound and all structural tool validation. That is the smaller root fix and preserves the caller's tool schema unchanged.

This PR's lossless system-message indirection was a careful compatibility attempt, but it changes message semantics and is unnecessary once the unsupported local cap is removed. Its review findings and tests informed the comparison; no check, review, or approval evidence transfers to #887. #887 must complete its own exact-head protected gates before merge.

@seonghobae seonghobae closed this Aug 26, 2026
auto-merge was automatically disabled August 26, 2026 16:34

Pull request was closed

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 3 new potential issues.

Open in Devin Review

"tool_choice requires tools on /v1/responses",
)
if "tools" in body:
_preserve_long_tool_descriptions(body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Empty Responses input passes fail-closed check

On /v1/responses, _preserve_long_tool_descriptions runs before the input non-empty check. When a tool has an overlong description and input is an empty/blank string or empty list, it wraps input into a non-empty list first. The invalid_input guard then passes and the request is forwarded instead of rejected.

Prompt for agents
In the /v1/responses handler in contextual_orchestrator/server.py, the call to _preserve_long_tool_descriptions(body) at the tools block (around line 6360) mutates body['input']: when input is an empty/blank string or empty list and a tool has an overlong description, it wraps input into a non-empty list. This happens before the input presence/non-empty validation (around lines 6399-6409, which rejects blank strings and empty arrays with invalid_input). As a result an invalid empty input is silently accepted and forwarded to the provider. Fix by validating input (the 'input is required' and 'input must be a non-empty string or non-empty array' checks) BEFORE invoking _preserve_long_tool_descriptions, or otherwise ensure preservation does not run against invalid/empty input. Chat Completions is unaffected because its message validation already runs before preservation.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +4176 to +4189
for tool in tools:
if not isinstance(tool, dict):
continue
function = tool.get("function") if isinstance(tool.get("function"), dict) else tool
description = function.get("description")
name = function.get("name")
if not isinstance(description, str) or len(description) <= 1024 or not isinstance(name, str):
continue
normalized_name = name.strip()
preserved.append({"name": normalized_name, "description": description})
function["description"] = (
f"Full documentation for {normalized_name} is preserved in the "
"contextual_orchestrator_tool_descriptions_v1 system message."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📝 Info: Preservation mutates body before tool-name validation

_preserve_long_tool_descriptions rewrites the description and inserts the system message before _validate_chat_tools checks the tool name. When a tool has both an overlong description and an invalid name, the body is mutated and then the request is rejected by _validate_chat_tools. Harmless since the request fails, but preservation operates on unvalidated tool objects.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +4202 to +4206
generated_body = {"messages": [documentation_message]}
_validate_chat_message_known_fields(generated_body)
_validate_chat_tool_message_ids(generated_body)
_validate_chat_assistant_tool_calls(generated_body)
_validate_chat_message_audio_function_call(generated_body)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📝 Info: Synthesized preface is self-validated

The synthesized documentation message is run through the chat message validators via generated_body. Since the list holds the same dict later inserted, any mutation by those validators carries through. The message is a fixed system role with JSON string content, so it should pass; confirm none of these validators reject a plain system message.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant