Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion claude_tap/cli_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,10 @@ def reverse_strip_path_prefix(self, target: str) -> str:
default_proxy_mode="forward",
auto_trust_ca_macos=True,
forward_trace_methods=("POST", "WEBSOCKET"),
forward_trace_path_prefixes=("/backend-api/codex/responses",),
forward_trace_path_prefixes=(
"/backend-api/codex/responses",
"/v1/responses",
),
),
"kimi": ClientConfig(
cmd="kimi",
Expand Down
5 changes: 4 additions & 1 deletion tests/test_client_config_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ def test_codexapp_declares_raw_backend_capture_mode() -> None:
assert cfg.default_target == "https://chatgpt.com/backend-api/codex"
assert cfg.default_proxy_mode == "forward"
assert cfg.forward_trace_methods == ("POST", "WEBSOCKET")
assert cfg.forward_trace_path_prefixes == ("/backend-api/codex/responses",)
assert cfg.forward_trace_path_prefixes == (
"/backend-api/codex/responses",
"/v1/responses",
)
assert cfg.transcript_only is False


Expand Down
75 changes: 75 additions & 0 deletions tests/test_forward_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import pytest

from claude_tap.cli_clients import CLIENT_CONFIGS
from claude_tap.forward_proxy import ForwardProxyServer, _decode_request_body_for_trace
from claude_tap.trace import TraceWriter
from claude_tap.trace_store import get_trace_store, reset_trace_store
Expand All @@ -36,6 +37,80 @@ async def drain(self) -> None:
return None


@pytest.mark.parametrize(
("method", "path", "expected"),
[
("POST", "/backend-api/codex/responses", True),
("POST", "/v1/responses", True),
("WEBSOCKET", "/v1/responses", True),
("GET", "/v1/responses", False),
("POST", "/v1/chat/completions", False),
("POST", "/v1/responses-other", False),
],
)
def test_codexapp_capture_filter(method: str, path: str, expected: bool) -> None:
cfg = CLIENT_CONFIGS["codexapp"]
server = ForwardProxyServer(
host="127.0.0.1",
port=0,
ca=object(),
writer=object(),
session=object(),
trace_methods=cfg.forward_trace_methods,
trace_path_prefixes=cfg.forward_trace_path_prefixes,
)

assert server._should_trace_request(method, path) is expected


@pytest.mark.asyncio
async def test_forward_proxy_captures_codexapp_custom_responses_request(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("CLOUDTAP_DB", str(tmp_path / "traces.sqlite3"))
reset_trace_store()
store = get_trace_store()
session_id = store.create_session(client="codexapp", proxy_mode="forward")
trace_writer = TraceWriter(session_id, store=store)
client_writer = _MemoryWriter()
cfg = CLIENT_CONFIGS["codexapp"]
server = ForwardProxyServer(
host="127.0.0.1",
port=0,
ca=object(),
writer=trace_writer,
session=_UnexpectedSession(),
trace_methods=cfg.forward_trace_methods,
trace_path_prefixes=cfg.forward_trace_path_prefixes,
capture_only=True,
)
request_body = {
"model": "MiniMax-M3",
"input": [{"role": "user", "content": [{"type": "input_text", "text": "hello"}]}],
"stream": False,
}

try:
await server._forward_and_record(
"POST",
"/v1/responses",
{"Content-Type": "application/json"},
json.dumps(request_body).encode(),
"https://api.minimaxi.com/v1/responses",
client_writer,
)
trace_writer.close()

records = store.load_records(session_id)
assert len(records) == 1
assert records[0]["request"]["path"] == "/v1/responses"
assert records[0]["request"]["body"] == request_body
assert records[0]["response"]["status"] == 200
finally:
reset_trace_store()


@pytest.mark.parametrize(
("encoding", "compress"),
[
Expand Down
Loading