openai-function-calling

code_execution · unverified · null · json · download .py

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")