A 4B model miscopied 8-hex listing ids (picked a tutoring id for a kids' bike). The local prompt now shows L-refs, the server maps them back before tools/hydration (unknown refs are dropped as before), tool results are aliased the same way, and refs are scrubbed from replies. Hosted models still see real ids.
64 lines
4.5 KiB
Python
Executable File
64 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Fake OpenAI-compatible endpoint to exercise the LLM proxy path without a real key.
|
|
Round 1 asks for a search tool; round 2 returns show_cards with ids from the tool results."""
|
|
import json, re, sys, time
|
|
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
|
|
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8798
|
|
SEEN = {"auth_ok": 0, "calls": {}, "auth": {}, "grammar": {}, "local_prompt": {}}
|
|
|
|
class H(BaseHTTPRequestHandler):
|
|
def log_message(self, *a): pass
|
|
def do_GET(self):
|
|
b = json.dumps(SEEN).encode(); self.send_response(200); self.end_headers(); self.wfile.write(b)
|
|
def do_POST(self):
|
|
body = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
|
|
SEEN["calls"][body.get("model")] = SEEN["calls"].get(body.get("model"), 0) + 1
|
|
if body.get("model") == "slow-local":
|
|
time.sleep(3)
|
|
if body.get("model") in ("busy-model", "busy-model-2"):
|
|
b = b'{"error":{"message":"rate-limited upstream","code":429}}'
|
|
self.send_response(429); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(b)
|
|
return
|
|
mdl = body.get("model")
|
|
if self.headers.get("Authorization") == "Bearer test-dummy-key":
|
|
SEEN["auth_ok"] += 1
|
|
SEEN["auth"][mdl] = SEEN["auth"].get(mdl, 0) + 1
|
|
if body.get("grammar"):
|
|
SEEN["grammar"][mdl] = SEEN["grammar"].get(mdl, 0) + 1
|
|
if "EXAMPLES" in body["messages"][0]["content"] and "VISITOR MESSAGE" in body["messages"][-1]["content"]:
|
|
SEEN["local_prompt"][mdl] = SEEN["local_prompt"].get(mdl, 0) + 1
|
|
if body.get("model") == "daily-model":
|
|
b = json.dumps({"error": {"message": "Rate limit exceeded: free-models-per-day.", "code": 429,
|
|
"metadata": {"headers": {"X-RateLimit-Limit": "50", "X-RateLimit-Remaining": "0", "X-RateLimit-Reset": "4102444800000"}}}}).encode()
|
|
self.send_response(429); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(b)
|
|
return
|
|
last = body["messages"][-1]["content"]
|
|
if "new internet" in last.lower():
|
|
# answers like a model that read the prompt: it only knows "top left" = copy.logo if the prompt says so
|
|
sysmsg = body["messages"][0]["content"]
|
|
knows = "TOP-LEFT" in sysmsg and '"logo": "Agent Market"' in sysmsg
|
|
out = ({"reply": "Renamed the top-left logo to New Internet.", "actions": [{"type": "set_theme", "payload": {"copy": {"logo": "New Internet"}}}]}
|
|
if knows else {"reply": "I can't change that text.", "actions": []})
|
|
b = json.dumps({"choices": [{"message": {"role": "assistant", "content": json.dumps(out)}}]}).encode()
|
|
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(b)
|
|
return
|
|
if "background" in last:
|
|
out = {"reply": "Done! Sunset gradient applied (lst_deadbeef).", "actions": [{"type": "set_theme", "payload": {
|
|
"background": {"kind": "gradient", "colors": ["#ff7e5f", "#feb47b", "javascript:alert(1)"], "angle": 180},
|
|
"palette": {"accent": "orange"}, "customCss": "@import url(https://evil.example/x.css); .card{border-width:3px;position:fixed}",
|
|
"copy": {"headline": "<img src=x onerror=alert(1)>Sunset"}}}]}
|
|
b = json.dumps({"choices": [{"message": {"role": "assistant", "content": json.dumps(out)}}]}).encode()
|
|
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(b)
|
|
return
|
|
if last.startswith("TOOL_RESULTS"):
|
|
ids = re.findall(r'"id": "(lst_[a-f0-9]+|L\d+)"', last)[:3] # local model sees short refs (L1, L2, ...)
|
|
out = {"reply": "Here are bikes I found (mock LLM). Payments are TEST MODE ONLY.",
|
|
"actions": [{"type": "show_cards", "payload": {"heading": "Bikes", "listing_ids": ids}},
|
|
{"type": "not_allowed_component", "payload": {}}]}
|
|
else:
|
|
out = {"reply": "searching", "tools": [{"name": "search", "args": {"q": "bike"}}]}
|
|
b = json.dumps({"choices": [{"message": {"role": "assistant", "content": json.dumps(out)}}]}).encode()
|
|
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(b)
|
|
|
|
ThreadingHTTPServer(("127.0.0.1", PORT), H).serve_forever()
|