mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
131 lines
4.7 KiB
Python
Executable File
131 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Empirical end-to-end PDF roundtrip probe.
|
|
|
|
Verifies that an Anthropic-shape `document` content block flows through
|
|
anthropic_proxy → 9router → upstream provider → response. Use one of
|
|
the model presets below; the script picks the right backend lane.
|
|
|
|
Usage:
|
|
1. Configure the matching API key in OpenSwarm Settings (or env).
|
|
2. Start the dev stack: bash run.sh
|
|
3. python3 scripts/probe-pdf-roundtrip.py <provider> <pdf-path>
|
|
|
|
Providers:
|
|
anthropic direct Anthropic API key (claude-opus-4-7)
|
|
gemini direct Google AI Studio key (gemini-3-pro-preview)
|
|
openai direct OpenAI key (gpt-5.5)
|
|
openrouter OpenRouter free model with file-parser plugin (openrouter/openai/gpt-5)
|
|
|
|
A 2xx response with non-empty content blocks confirms the full
|
|
translator chain works for that provider. A 4xx with a provider-specific
|
|
error message tells you exactly which step broke.
|
|
"""
|
|
import base64
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.request
|
|
import urllib.error
|
|
|
|
# Each model_id below targets a SPECIFIC routing lane so a probe failure
|
|
# pinpoints exactly which auth path is broken. "anthropic" uses the SDK
|
|
# model_id form that lands on the cc/ OAuth subscription lane; if you
|
|
# only have an Anthropic API key (no Pro subscription), use the explicit
|
|
# direct-API model_id by editing PRESETS or pass --model.
|
|
PRESETS = {
|
|
"anthropic": "claude-opus-4-7", # cc/ lane via 9router; needs Pro OAuth
|
|
"anthropic-api": "claude-opus-4-7", # same wire shape; routes by settings
|
|
"gemini": "gemini-3-pro-preview",
|
|
"openai": "gpt-5.5",
|
|
"openrouter": "openrouter/openai/gpt-5",
|
|
}
|
|
PROVIDER_CAPS_MB = {
|
|
"anthropic": 28,
|
|
"gemini": 14,
|
|
"openai": 45,
|
|
"openrouter": 45,
|
|
}
|
|
|
|
|
|
def probe(provider: str, pdf_path: str) -> int:
|
|
if provider not in PRESETS:
|
|
print(f"FAIL: unknown provider '{provider}'. Use one of: {', '.join(PRESETS)}", file=sys.stderr)
|
|
return 2
|
|
if not os.path.isfile(pdf_path):
|
|
print(f"FAIL: file not found: {pdf_path}", file=sys.stderr)
|
|
return 2
|
|
size = os.path.getsize(pdf_path)
|
|
cap = PROVIDER_CAPS_MB[provider] * 1024 * 1024
|
|
if size > cap:
|
|
print(f"FAIL: PDF is {size // (1024*1024)} MB, over {provider}'s {PROVIDER_CAPS_MB[provider]} MB inline cap.", file=sys.stderr)
|
|
return 2
|
|
|
|
with open(pdf_path, "rb") as fh:
|
|
data_b64 = base64.b64encode(fh.read()).decode("ascii")
|
|
|
|
token_path = os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
"backend", "data", "auth.token",
|
|
)
|
|
if not os.path.exists(token_path):
|
|
print(f"FAIL: auth.token not found at {token_path}. Start the dev backend first (bash run.sh).", file=sys.stderr)
|
|
return 2
|
|
with open(token_path) as fh:
|
|
token = fh.read().strip()
|
|
|
|
body = {
|
|
"model": PRESETS[provider],
|
|
"max_tokens": 300,
|
|
"messages": [{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "In one sentence, what is this PDF about?"},
|
|
{"type": "document", "source": {
|
|
"type": "base64",
|
|
"media_type": "application/pdf",
|
|
"data": data_b64,
|
|
}},
|
|
],
|
|
}],
|
|
}
|
|
|
|
req = urllib.request.Request(
|
|
"http://127.0.0.1:8324/api/anthropic-proxy/v1/messages",
|
|
data=json.dumps(body).encode("utf-8"),
|
|
headers={
|
|
"Content-Type": "application/json",
|
|
"x-api-key": token,
|
|
"anthropic-version": "2023-06-01",
|
|
},
|
|
method="POST",
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=120) as resp:
|
|
status = resp.status
|
|
payload = resp.read(4096).decode("utf-8", errors="replace")
|
|
except urllib.error.HTTPError as e:
|
|
status = e.code
|
|
payload = e.read(4096).decode("utf-8", errors="replace")
|
|
except Exception as e:
|
|
print(f"FAIL: network error: {e}", file=sys.stderr)
|
|
return 3
|
|
|
|
print(f"=== {provider} ({PRESETS[provider]}) ===")
|
|
print(f"HTTP {status}")
|
|
print(payload[:800])
|
|
if 200 <= status < 300:
|
|
if any(k in payload.lower() for k in ("content", "text", "completion", "choice")):
|
|
print(f"\n✓ {provider} accepted the PDF document block end-to-end.")
|
|
return 0
|
|
print(f"\n⚠ HTTP 200 but no recognizable content; inspect response above.")
|
|
return 1
|
|
print(f"\n✗ {provider} PDF roundtrip failed at HTTP {status}. See response above.")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 3:
|
|
print(f"usage: probe-pdf-roundtrip.py <{'|'.join(PRESETS)}> path/to/test.pdf", file=sys.stderr)
|
|
sys.exit(2)
|
|
sys.exit(probe(sys.argv[1], sys.argv[2]))
|