-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Studio: inject synthetic respond tool into agentic loop #5706
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 | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| import ast | ||||||||||||||||||||||||||||||||||||||||||||||
| import http.client | ||||||||||||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||
| import signal | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -505,6 +506,73 @@ def _get_workdir(session_id: str | None = None) -> str: | |||||||||||||||||||||||||||||||||||||||||||||
| ALL_TOOLS = [WEB_SEARCH_TOOL, PYTHON_TOOL, TERMINAL_TOOL] | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Synthetic respond tool. Adapted from forge | ||||||||||||||||||||||||||||||||||||||||||||||
| # (https://github.com/antoinezambelli/forge, MIT). The model calls | ||||||||||||||||||||||||||||||||||||||||||||||
| # respond(message=...) instead of producing bare text so the agentic | ||||||||||||||||||||||||||||||||||||||||||||||
| # loop has a structured exit. The unwrap path strips the call from the | ||||||||||||||||||||||||||||||||||||||||||||||
| # response and emits the message as plain assistant content. | ||||||||||||||||||||||||||||||||||||||||||||||
| RESPOND_TOOL_NAME = "respond" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| RESPOND_TOOL = { | ||||||||||||||||||||||||||||||||||||||||||||||
| "type": "function", | ||||||||||||||||||||||||||||||||||||||||||||||
| "function": { | ||||||||||||||||||||||||||||||||||||||||||||||
| "name": RESPOND_TOOL_NAME, | ||||||||||||||||||||||||||||||||||||||||||||||
| "description": ( | ||||||||||||||||||||||||||||||||||||||||||||||
| "Respond to the user with a message. Use this when the user " | ||||||||||||||||||||||||||||||||||||||||||||||
| "is chatting, asking a question, when you need to ask a " | ||||||||||||||||||||||||||||||||||||||||||||||
| "clarifying question before proceeding, or when no other " | ||||||||||||||||||||||||||||||||||||||||||||||
| "tool action is needed. Also use this after completing the " | ||||||||||||||||||||||||||||||||||||||||||||||
| "user's request to report the result." | ||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||
| "parameters": { | ||||||||||||||||||||||||||||||||||||||||||||||
| "type": "object", | ||||||||||||||||||||||||||||||||||||||||||||||
| "properties": { | ||||||||||||||||||||||||||||||||||||||||||||||
| "message": { | ||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string", | ||||||||||||||||||||||||||||||||||||||||||||||
| "description": "The message to send to the user.", | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| "required": ["message"], | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| def inject_respond_tool(tools: list[dict] | None) -> tuple[list[dict] | None, bool]: | ||||||||||||||||||||||||||||||||||||||||||||||
| """Defensively append ``RESPOND_TOOL`` to ``tools``. Skips when the | ||||||||||||||||||||||||||||||||||||||||||||||
| list is empty/None or a tool named ``respond`` already exists. | ||||||||||||||||||||||||||||||||||||||||||||||
| Returns ``(new_tools, was_injected)``. The input list is not mutated. | ||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||
| if not tools: | ||||||||||||||||||||||||||||||||||||||||||||||
| return tools, False | ||||||||||||||||||||||||||||||||||||||||||||||
| for t in tools: | ||||||||||||||||||||||||||||||||||||||||||||||
| if (t.get("function") or {}).get("name") == RESPOND_TOOL_NAME: | ||||||||||||||||||||||||||||||||||||||||||||||
| return tools, False | ||||||||||||||||||||||||||||||||||||||||||||||
| return list(tools) + [RESPOND_TOOL], True | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+541
to
+551
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
References
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| def is_respond_call(tc: dict) -> bool: | ||||||||||||||||||||||||||||||||||||||||||||||
| """True if ``tc`` is a tool call to the synthetic ``respond`` tool.""" | ||||||||||||||||||||||||||||||||||||||||||||||
| return (tc.get("function") or {}).get("name") == RESPOND_TOOL_NAME | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| def extract_respond_message(tc: dict) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||
| """Pull the ``message`` arg out of a respond call. Tolerates | ||||||||||||||||||||||||||||||||||||||||||||||
| arguments arriving as either a JSON string or a dict. Returns ``""`` | ||||||||||||||||||||||||||||||||||||||||||||||
| if missing or malformed. | ||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||
| args = (tc.get("function") or {}).get("arguments", "") | ||||||||||||||||||||||||||||||||||||||||||||||
| if isinstance(args, str): | ||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||
| args = json.loads(args) | ||||||||||||||||||||||||||||||||||||||||||||||
| except (json.JSONDecodeError, ValueError): | ||||||||||||||||||||||||||||||||||||||||||||||
| return "" | ||||||||||||||||||||||||||||||||||||||||||||||
| if not isinstance(args, dict): | ||||||||||||||||||||||||||||||||||||||||||||||
| return "" | ||||||||||||||||||||||||||||||||||||||||||||||
| msg = args.get("message", "") | ||||||||||||||||||||||||||||||||||||||||||||||
| return msg if isinstance(msg, str) else "" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| _TIMEOUT_UNSET = object() | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
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.
The early
returnhere skips the final metadata yield (usage and timings) that normally occurs at the end of thegenerate_chat_completion_with_toolsfunction (lines 5331-5340). This will cause the request to report zero or missing token counts and performance metrics in the UI when therespondtool is used.Consider yielding the accumulated metadata before returning, or refactoring the loop to allow a clean exit that reaches the final metadata block to ensure all computed values are returned for callers to reuse.
References