From 84c6636c778c89306b7d13cdc7e887ebef776b77 Mon Sep 17 00:00:00 2001 From: Xiang Shen Date: Thu, 15 Oct 2026 19:05:07 +0000 Subject: [PATCH] smart_routing: Log routes:select requests to ~/.ucode/routing-requests.jsonl Debug aid for inspecting the exact route menu ug sends to the AI Gateway router. select_route is the single call site for the Claude first prompt, Claude subagent, and Codex menus, so each call appends one JSON line with the URL and request body. The bearer token is not recorded. Output goes to a file because stdout belongs to the TUI and the hook protocol. Co-authored-by: Isaac --- src/ucode/smart_routing/routing.py | 10 +++++++++- tests/test_claude_routing.py | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ucode/smart_routing/routing.py b/src/ucode/smart_routing/routing.py index 6286c767d..79f48f41b 100644 --- a/src/ucode/smart_routing/routing.py +++ b/src/ucode/smart_routing/routing.py @@ -23,11 +23,13 @@ from pathlib import Path from typing import Any +from ucode import config_io from ucode.smart_routing.rationale import format_rationale ROUTER_NAME = "task_v3" ROUTER_NAME_ENV_VAR = "SMART_ROUTER_NAME" ROUTING_PATH = "/ai-gateway/routing/v1/routes:select" +ROUTING_REQUEST_LOG_NAME = "routing-requests.jsonl" REQUEST_TIMEOUT_S = 30.0 SUBAGENT_ROUTING_DISCLAIMER = ( "Spawned subagents are routed independently based on their own complexity." @@ -234,8 +236,14 @@ def select_route( "task": {"prompt": task}, "route_selector": {"router_name": router_name}, } + url = workspace.rstrip("/") + ROUTING_PATH + # Written to a file because stdout belongs to the TUI or the hook protocol. + _append_jsonl( + config_io.APP_DIR / ROUTING_REQUEST_LOG_NAME, + {"at": time.time(), "url": url, "body": body}, + ) request = urllib.request.Request( - workspace.rstrip("/") + ROUTING_PATH, + url, data=json.dumps(body).encode("utf-8"), headers={ "Authorization": f"Bearer {token}", diff --git a/tests/test_claude_routing.py b/tests/test_claude_routing.py index 5b2b5307d..1bb694124 100644 --- a/tests/test_claude_routing.py +++ b/tests/test_claude_routing.py @@ -5,6 +5,7 @@ import json import urllib.error +from ucode import config_io from ucode.smart_routing import claude_routing, routing WS = "https://example.databricks.com" @@ -79,6 +80,30 @@ def fake_urlopen(request, timeout): assert recorded["router_name"] == captured["body"]["route_selector"]["router_name"] +def test_routing_request_is_logged(monkeypatch): + sent = {} + + def fake_urlopen(request, timeout): + sent["url"] = request.full_url + sent["body"] = json.loads(request.data) + return _Response({"route_selection": []}) + + monkeypatch.setattr(claude_routing.urllib.request, "urlopen", fake_urlopen) + + claude_routing.request_routing_decision( + WS, + "dapi-secret", + "Refactor the parser", + ["system.ai.claude-opus-4-8", "system.ai.claude-sonnet-5"], + ) + + log_path = config_io.APP_DIR / routing.ROUTING_REQUEST_LOG_NAME + [record] = [json.loads(line) for line in log_path.read_text().splitlines()] + assert record["url"] == sent["url"] + assert record["body"] == sent["body"] + assert "dapi-secret" not in log_path.read_text() + + def test_missing_arm_short_circuits_without_calling_router(monkeypatch): def fail(*args, **kwargs): raise AssertionError("router must not be called when an arm is missing")