openai-function-calling
Model to use (must support tool use)
import sys
import os
import subprocess
import time
import urllib.request
import json
# ─────────────────────────────────────────
# PRE_EXECUTION
# ─────────────────────────────────────────
for attempt in range(2):
try:
req = urllib.request.Request(
"https://checklist.day/api/registry/openai",
headers={"User-Agent": "checklist-agent/1.0"}
)
with urllib.request.urlopen(req, timeout=10) as resp:
registry = json.loads(resp.read())
break
except Exception as e:
if attempt == 1:
print(f"ABORT: registry unreachable — {e}")
sys.exit(1)
time.sleep(2)
warnings = registry.get("warnings", [])
if warnings:
print("[openai] WARNINGS:")
for w in warnings if isinstance(warnings, list) else [warnings]:
print(f" ⚠ {w}")
# ─────────────────────────────────────────
# EXECUTION
# ─────────────────────────────────────────
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "openai>=1.0.0"])
from openai import OpenAI
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
if not OPENAI_API_KEY:
print("ABORT: OPENAI_API_KEY env var not set")
sys.exit(1)
client = OpenAI(api_key=OPENAI_API_KEY)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature units"},
},
"required": ["city"],
},
},
}
]
response = client.chat.completions.create(
model=OPENAI_MODEL,
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
tools=tools,
# FOOTGUN: tool_choice="auto" lets model skip tools — use "required" to force a tool call
tool_choice="required",
)
# FOOTGUN: finish_reason is "tool_calls" not "stop" when model called a tool
finish_reason = response.choices[0].finish_reason
message = response.choices[0].message
# FOOTGUN: tool calls are in message.tool_calls, not message.content
tool_calls = message.tool_calls
assert tool_calls, "FAIL: no tool calls in response"
tool_call = tool_calls[0]
tool_called = tool_call.function.name
# FOOTGUN: arguments is a JSON string, not a dict — must parse with json.loads
raw_args = tool_call.function.arguments
arguments = json.loads(raw_args)
arguments_parsed = "city" in arguments
print(f" tool called: {tool_called}")
print(f" arguments: {arguments}")
print(f" finish_reason: {finish_reason}")
# ─────────────────────────────────────────
# POST_EXECUTION
# ─────────────────────────────────────────
assert tool_called == "get_weather", f"FAIL: expected 'get_weather', got '{tool_called}'"
assert arguments_parsed, f"FAIL: 'city' not in parsed arguments: {arguments}"
assert finish_reason == "tool_calls", f"FAIL: expected finish_reason='tool_calls', got '{finish_reason}'"
assert "paris" in arguments.get("city", "").lower(), f"FAIL: expected city=Paris, got {arguments.get('city')}"
result = {
"tool_called": tool_called,
"arguments": arguments,
"arguments_parsed": arguments_parsed,
"finish_reason": finish_reason,
}
print(json.dumps(result, indent=2))
print("PASS")