127 lines
5.5 KiB
Python
127 lines
5.5 KiB
Python
import json, sys, time, uuid, os
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
from urllib.parse import urlparse
|
|
|
|
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dump_output.txt")
|
|
|
|
def log(msg):
|
|
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
f.write(msg + "\n")
|
|
f.flush()
|
|
|
|
class DumpHandler(BaseHTTPRequestHandler):
|
|
def do_POST(self):
|
|
length = int(self.headers.get('Content-Length', 0))
|
|
body = self.rfile.read(length)
|
|
path = urlparse(self.path).path
|
|
|
|
log(f"\n{'='*70}")
|
|
log(f"REQUEST: POST {path}")
|
|
log(f"HEADERS: {json.dumps(dict(self.headers))}")
|
|
log(f"BODY SIZE: {len(body)} bytes")
|
|
try:
|
|
parsed = json.loads(body)
|
|
sys_prompt = ""
|
|
raw_system = parsed.get("system")
|
|
if raw_system is not None:
|
|
if isinstance(raw_system, str):
|
|
sys_prompt = raw_system
|
|
elif isinstance(raw_system, list):
|
|
parts = []
|
|
for block in raw_system:
|
|
if isinstance(block, dict):
|
|
text = block.get("text", "") or ""
|
|
parts.append(text)
|
|
sys_prompt = "\n".join(parts)
|
|
if not sys_prompt:
|
|
for msg in parsed.get("messages", []):
|
|
if msg.get("role") == "system":
|
|
c = msg.get("content", "")
|
|
sys_prompt = c if isinstance(c, str) else str(c)
|
|
break
|
|
system_chars = len(sys_prompt)
|
|
system_tokens = system_chars // 4
|
|
|
|
tools = parsed.get("tools", [])
|
|
all_messages = parsed.get("messages", [])
|
|
non_sys_msgs = [m for m in all_messages if m.get("role") != "system"]
|
|
|
|
msg_chars = sum(len(json.dumps(m, ensure_ascii=False)) for m in non_sys_msgs) if non_sys_msgs else 0
|
|
tool_chars = sum(len(json.dumps(t, ensure_ascii=False)) for t in tools) if tools else 0
|
|
|
|
log(f"\n=== SIZE BREAKDOWN ===")
|
|
log(f"System prompt: {system_chars:>6} chars / ~{system_tokens:>5} tokens")
|
|
log(f"Messages ({len(non_sys_msgs)}): {msg_chars:>6} bytes")
|
|
log(f"Tools ({len(tools)}): {tool_chars:>6} bytes")
|
|
log(f"Total body: {len(body):>6} bytes")
|
|
log(f'Model: {parsed.get("model", "N/A")}')
|
|
log(f'Stream: {parsed.get("stream", "N/A")}')
|
|
log(f'Max tokens: {parsed.get("max_tokens", parsed.get("max_completion_tokens", "N/A"))}')
|
|
|
|
if sys_prompt:
|
|
log(f"\n=== SYSTEM PROMPT (full) ===")
|
|
log(sys_prompt)
|
|
|
|
if tools:
|
|
log(f"\n=== TOOLS ({len(tools)}) ===")
|
|
for t in tools:
|
|
fname = t.get("name") or t.get("function", {}).get("name", "?")
|
|
fdesc = t.get("description") or t.get("function", {}).get("description", "")
|
|
log(f" - {fname}: {fdesc}")
|
|
|
|
if non_sys_msgs:
|
|
log(f"\n=== MESSAGES ===")
|
|
for m in non_sys_msgs:
|
|
role = m.get("role", "?")
|
|
c = m.get("content", "")
|
|
if isinstance(c, list):
|
|
parts = [p.get("type","?")[:20] for p in c if isinstance(p,dict)]
|
|
content_str = f"[{'|'.join(parts)}]"
|
|
else:
|
|
content_str = str(c)
|
|
log(f" [{role}]: {content_str}")
|
|
|
|
log(f"\n=== FULL JSON BODY (pretty) ===")
|
|
pretty = json.dumps(parsed, indent=2, ensure_ascii=False)
|
|
log(pretty)
|
|
log(f" skip_tools: {parsed.get('tools') is None}")
|
|
except Exception as e:
|
|
log(f"\nPARSE ERROR: {e}")
|
|
import traceback
|
|
traceback.print_exc(file=open(LOG_FILE, "a"))
|
|
log(f" skip_tools: True (unparseable)")
|
|
|
|
# Send Anthropic-compatible SSE (/v1/messages format)
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "text/event-stream")
|
|
self.send_header("Cache-Control", "no-cache")
|
|
self.send_header("Connection", "close")
|
|
self.send_header("Access-Control-Allow-Origin", "*")
|
|
self.end_headers()
|
|
|
|
msg_id = str(uuid.uuid4())
|
|
|
|
# Always return a single end_turn text response; no tool_use round trip.
|
|
events = [
|
|
{"type": "message_start", "message": {"id": msg_id, "type": "message", "role": "assistant", "content": [], "model": "local-model", "stop_reason": None, "stop_sequence": None, "usage": {"input_tokens": 10, "output_tokens": 5}}},
|
|
{"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}},
|
|
{"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Request received."}},
|
|
{"type": "content_block_stop", "index": 0},
|
|
{"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 5}},
|
|
{"type": "message_stop"},
|
|
]
|
|
for evt in events:
|
|
self.wfile.write(f"data: {json.dumps(evt)}\n\n".encode())
|
|
self.wfile.flush()
|
|
self.wfile.write(b"data: [DONE]\n\n")
|
|
self.wfile.flush()
|
|
time.sleep(0.1)
|
|
|
|
def log_message(self, format, *args):
|
|
pass
|
|
|
|
port = 1234
|
|
log(f"Dump server starting on port {port}")
|
|
server = HTTPServer(("0.0.0.0", port), DumpHandler)
|
|
server.serve_forever()
|