151 lines
4.6 KiB
Python
151 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Inspect the last N llm_requests rows for a given model (default: deepseek).
|
|
Prints a structured summary without dumping raw payloads.
|
|
|
|
Usage:
|
|
python scripts/inspect_llm_requests.py [model_filter] [rows]
|
|
|
|
Examples:
|
|
python scripts/inspect_llm_requests.py deepseek 5
|
|
python scripts/inspect_llm_requests.py anthropic 3
|
|
"""
|
|
|
|
import json
|
|
import sqlite3
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
DB_PATH = Path(__file__).parent.parent / "database.db"
|
|
MODEL_FILTER = sys.argv[1] if len(sys.argv) > 1 else "deepseek"
|
|
ROWS = int(sys.argv[2]) if len(sys.argv) > 2 else 5
|
|
|
|
|
|
def fmt_len(s):
|
|
if s is None:
|
|
return "null"
|
|
return f"{len(s)} chars"
|
|
|
|
|
|
def summarize_message(i, msg):
|
|
role = msg.get("role", "?")
|
|
content = msg.get("content")
|
|
tool_calls = msg.get("tool_calls")
|
|
tool_call_id = msg.get("tool_call_id")
|
|
reasoning = msg.get("reasoning_content")
|
|
|
|
parts = []
|
|
|
|
if isinstance(content, str):
|
|
parts.append(f"{len(content)} chars")
|
|
elif isinstance(content, list):
|
|
total = sum(len(b.get("text", "")) for b in content if isinstance(b, dict))
|
|
cache_tags = [b for b in content if isinstance(b, dict) and "cache_control" in b]
|
|
parts.append(f"{total} chars (content array, {len(content)} blocks)")
|
|
if cache_tags:
|
|
parts.append(f"[cache_control on {len(cache_tags)} block(s)]")
|
|
elif content is None:
|
|
parts.append("(no content)")
|
|
|
|
if reasoning:
|
|
parts.append(f"[reasoning_content: {len(reasoning)} chars]")
|
|
|
|
if tool_calls:
|
|
names = [tc.get("function", {}).get("name", "?") for tc in tool_calls]
|
|
parts.append(f"[tool_calls: {', '.join(names)}]")
|
|
|
|
if tool_call_id:
|
|
parts.append(f"(tool_call_id={tool_call_id})")
|
|
|
|
detail = " ".join(parts)
|
|
print(f" {i:>3} {role:<12} {detail}")
|
|
|
|
|
|
def est_tokens(obj) -> int:
|
|
"""Rough token estimate: serialized chars / 4."""
|
|
return len(json.dumps(obj)) // 4
|
|
|
|
|
|
def summarize_request(row):
|
|
rid, model_name, req_json, req_headers, resp_json, input_tok, output_tok, duration_ms, created_at = row
|
|
|
|
print(f"\n{'='*70}")
|
|
print(f"id={rid} model={model_name} created={created_at}")
|
|
print(f"tokens: input={input_tok} output={output_tok} duration={duration_ms}ms")
|
|
|
|
try:
|
|
req = json.loads(req_json) if req_json else {}
|
|
except Exception as e:
|
|
print(f" [ERROR parsing request_json: {e}]")
|
|
return
|
|
|
|
# Top-level params (excluding messages and tools)
|
|
skip = {"messages", "tools", "model"}
|
|
params = {k: v for k, v in req.items() if k not in skip}
|
|
if params:
|
|
print(f"\n[params]")
|
|
for k, v in params.items():
|
|
print(f" {k} = {json.dumps(v)}")
|
|
|
|
# Tools
|
|
tools = req.get("tools", [])
|
|
if tools:
|
|
tool_names = [t.get("function", {}).get("name", "?") for t in tools]
|
|
tools_tok = est_tokens(tools)
|
|
print(f"\n[tools] {len(tools)} defined ~{tools_tok} tok est")
|
|
print(f" {', '.join(tool_names)}")
|
|
last = tools[-1]
|
|
if "cache_control" in last:
|
|
print(f" last tool has cache_control: {last['cache_control']}")
|
|
|
|
# Messages
|
|
messages = req.get("messages", [])
|
|
sys_msgs = [m for m in messages if m.get("role") == "system"]
|
|
sys_tok = est_tokens(sys_msgs)
|
|
conv_msgs = [m for m in messages if m.get("role") != "system"]
|
|
conv_tok = est_tokens(conv_msgs)
|
|
print(f"\n[messages] {len(messages)} total (~{est_tokens(messages)} tok est: {len(sys_msgs)} system ~{sys_tok} tok, {len(conv_msgs)} conv ~{conv_tok} tok)")
|
|
for i, msg in enumerate(messages):
|
|
summarize_message(i, msg)
|
|
|
|
# Response summary
|
|
if resp_json:
|
|
try:
|
|
resp = json.loads(resp_json)
|
|
usage = resp.get("usage", {})
|
|
if usage:
|
|
print(f"\n[usage]")
|
|
for k, v in usage.items():
|
|
print(f" {k} = {v}")
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def main():
|
|
conn = sqlite3.connect(DB_PATH)
|
|
rows = conn.execute(
|
|
"""
|
|
SELECT id, model_name, request_json, request_headers,
|
|
response_json, input_tokens, output_tokens, duration_ms, created_at
|
|
FROM llm_requests
|
|
WHERE model_name LIKE ?
|
|
ORDER BY id DESC
|
|
LIMIT ?
|
|
""",
|
|
(f"%{MODEL_FILTER}%", ROWS),
|
|
).fetchall()
|
|
conn.close()
|
|
|
|
if not rows:
|
|
print(f"No rows found for model filter '{MODEL_FILTER}'")
|
|
return
|
|
|
|
print(f"Last {len(rows)} request(s) matching '{MODEL_FILTER}' (newest first)")
|
|
for row in rows:
|
|
summarize_request(row)
|
|
print(f"\n{'='*70}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|