feat: strengthen tool emulation prompting
Improve proxy-side tool instructions so models more reliably emit structured tool actions, and add focused tests covering prompt guidance and default action limits. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,7 @@ from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse, Response, StreamingResponse
|
||||
|
||||
from app.anthropic_schema import AnthropicMessagesRequest
|
||||
from app.http.tool_emulation import EmulatedToolChoice, EmulatedToolDef, inject_tooling, parse_action_blocks
|
||||
from app.openai_schema import ChatCompletionsRequest, ResponsesRequest
|
||||
import app.main as main
|
||||
|
||||
@@ -2789,6 +2790,54 @@ class AdminIntrospectionEndpointTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(ctx.exception.status_code, 401)
|
||||
|
||||
|
||||
class ToolEmulationPromptTests(unittest.TestCase):
|
||||
def test_inject_tooling_adds_routing_hints_and_examples(self) -> None:
|
||||
tools = [
|
||||
EmulatedToolDef(
|
||||
name="read_file",
|
||||
description="Read a file",
|
||||
input_schema={"type": "object", "properties": {"path": {"type": "string"}}},
|
||||
),
|
||||
EmulatedToolDef(
|
||||
name="bash",
|
||||
description="Run shell commands",
|
||||
input_schema={"type": "object", "properties": {"command": {"type": "string"}}},
|
||||
),
|
||||
]
|
||||
|
||||
injected = inject_tooling("system prompt", tools, EmulatedToolChoice(mode="auto"))
|
||||
|
||||
self.assertIn("Tool routing guide:", injected)
|
||||
self.assertIn("use read_file", injected)
|
||||
self.assertIn("use bash", injected)
|
||||
self.assertIn("Core tool syntax examples.", injected)
|
||||
self.assertIn("Example valid action block", injected)
|
||||
self.assertIn("tool_choice=auto means you must decide whether the user request needs a tool", injected)
|
||||
|
||||
def test_inject_tooling_does_not_modify_plain_system_when_no_tools(self) -> None:
|
||||
injected = inject_tooling("system prompt", [], EmulatedToolChoice(mode="auto"))
|
||||
self.assertEqual(injected, "system prompt")
|
||||
|
||||
def test_parse_action_blocks_limits_default_to_five_calls(self) -> None:
|
||||
tools = [
|
||||
EmulatedToolDef(
|
||||
name="lookup",
|
||||
description="Lookup data",
|
||||
input_schema={"type": "object", "properties": {"q": {"type": "string"}}},
|
||||
)
|
||||
]
|
||||
text = "\n".join(
|
||||
f"```json action\n{{\"tool\":\"lookup\",\"parameters\":{{\"q\":\"item-{i}\"}}}}\n```"
|
||||
for i in range(6)
|
||||
)
|
||||
|
||||
calls, remaining = parse_action_blocks(text, tools)
|
||||
|
||||
self.assertEqual(len(calls), 5)
|
||||
self.assertEqual([call.arguments["q"] for call in calls], [f"item-{i}" for i in range(5)])
|
||||
self.assertIn('"q":"item-5"', remaining)
|
||||
|
||||
|
||||
class SessionCacheToolFingerprintTests(unittest.TestCase):
|
||||
def test_build_key_changes_with_tool_config(self) -> None:
|
||||
from app.session_cache import SessionCache
|
||||
|
||||
Reference in New Issue
Block a user