Files
llm-server-setup/tool_call_demo.py
T

107 lines
3.5 KiB
Python

import json
from pathlib import Path
SYSTEM_PROMPT = (
"You are a helpful assistant. "
"When outputting Windows file paths in JSON arguments, you must strictly escape all backslashes "
"(for example: C:\\\\Users\\\\name\\\\file.txt). "
"If you need to read a file, emit a tool call with the path field."
)
def read_file(path: str) -> str:
"""Read a file from disk and return its contents."""
file_path = Path(path)
try:
return file_path.read_text(encoding="utf-8")
except FileNotFoundError:
return f"ERROR: File not found: {path}"
except Exception as exc: # pragma: no cover - demo only
return f"ERROR: {type(exc).__name__}: {exc}"
def sanitize_tool_arguments(raw_arguments: str):
"""Repair malformed JSON emitted by the model when Windows paths are not escaped."""
try:
return json.loads(raw_arguments)
except json.JSONDecodeError:
repaired = raw_arguments.replace("\\", "\\\\")
return json.loads(repaired)
def handle_tool_call(response: dict) -> dict:
"""Detect a tool call in the LLM response and execute it locally."""
tool_calls = response.get("tool_calls") or response.get("function_call")
if tool_calls is None:
return {"status": "final_response", "content": response}
if isinstance(tool_calls, dict):
tool_calls = [tool_calls]
for tool_call in tool_calls:
function_data = tool_call.get("function", tool_call)
name = function_data.get("name")
arguments = function_data.get("arguments", {})
if isinstance(arguments, str):
try:
arguments = json.loads(arguments)
except json.JSONDecodeError:
try:
arguments = sanitize_tool_arguments(arguments)
except json.JSONDecodeError:
return {
"status": "invalid_arguments",
"raw_arguments": arguments,
"message": "The model emitted malformed JSON. Ensure backslashes are escaped.",
}
if name == "read_file":
file_path = arguments.get("path")
content = read_file(file_path)
return {
"status": "tool_result",
"tool_call_id": tool_call.get("id"),
"content": content,
}
return {"status": "unsupported_tool_call", "raw": response}
if __name__ == "__main__":
# A valid OpenAI-style tool call with a Windows path escaped correctly.
valid_response = {
"tool_calls": [
{
"id": "call_read_001",
"type": "function",
"function": {
"name": "read_file",
"arguments": '{"path": "c:\\Workspace\\llm-server-setup\\README.md"}',
},
}
]
}
# This reproduces the common bug: malformed Windows path in JSON.
broken_response = {
"tool_calls": [
{
"id": "call_read_002",
"type": "function",
"function": {
"name": "read_file",
"arguments": '{"path": "c:\Workspace\llm-server-setup\README.md"}',
},
}
]
}
result = handle_tool_call(valid_response)
print(json.dumps(result, ensure_ascii=False, indent=2))
print("\n--- malformed-path fallback sample ---")
fallback = handle_tool_call(broken_response)
print(json.dumps(fallback, ensure_ascii=False, indent=2))