Ghostlight/tools/check_qwen_chat_tools.py
2026-05-02 03:26:13 +01:00

93 lines
3.4 KiB
Python

"""Smoke-test Qwen thinking plus native Ollama tool calls."""
from __future__ import annotations
import argparse
import json
import urllib.request
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_ENDPOINT = "http://192.168.1.84:11434/api/chat"
def post_json(endpoint: str, payload: dict[str, Any]) -> dict[str, Any]:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
request = urllib.request.Request(
endpoint,
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request, timeout=120) as response:
return json.loads(response.read().decode("utf-8"))
def main() -> int:
parser = argparse.ArgumentParser(description="Check Qwen /api/chat thinking plus tool calling.")
parser.add_argument("--endpoint", default=DEFAULT_ENDPOINT)
parser.add_argument("--model", default="qwen3.5:9b")
parser.add_argument("--think", action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--out", type=Path, default=ROOT / "experiments" / "ink" / "qwen-chat-tools-smoke.json")
args = parser.parse_args()
payload = {
"model": args.model,
"stream": False,
"think": args.think,
"messages": [
{
"role": "user",
"content": "Think privately if supported, then call the tool with action_type show_object and note qwen-chat-tools-smoke.",
}
],
"tools": [
{
"type": "function",
"function": {
"name": "record_action",
"description": "Record one canonical Ghostlight action.",
"parameters": {
"type": "object",
"required": ["action_type", "note"],
"properties": {
"action_type": {"type": "string", "enum": ["speak", "show_object", "withhold_object"]},
"note": {"type": "string"},
},
},
},
}
],
}
raw = post_json(args.endpoint, payload)
message = raw.get("message", {})
tool_calls = message.get("tool_calls") or []
if not tool_calls:
raise SystemExit("Qwen did not return tool_calls")
arguments = tool_calls[0].get("function", {}).get("arguments", {})
if arguments.get("action_type") != "show_object":
raise SystemExit(f"Unexpected tool arguments: {arguments}")
if args.think and not message.get("thinking"):
raise SystemExit("Qwen did not return a thinking field while think=true")
receipt = {
"schema_version": "ghostlight.qwen_chat_tools_smoke.v0",
"endpoint": args.endpoint,
"model": args.model,
"think_requested": args.think,
"has_thinking": bool(message.get("thinking")),
"tool_name": tool_calls[0].get("function", {}).get("name"),
"tool_arguments": arguments,
"raw": raw,
}
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(json.dumps(receipt, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
print(f"ok: thinking={receipt['has_thinking']} tool={receipt['tool_name']} args={receipt['tool_arguments']}")
print(f"wrote {args.out.relative_to(ROOT)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())