Skip to content

Commit d2dc4c9

Browse files
committed
R2-3: export TAOS_TRACE_URL from proxy and fall back to TAOS_PORT in callback
ACCEPTANCE: proxy env now carries TAOS_TRACE_URL on the bound port; callback falls back to TAOS_PORT (default 6969) when TAOS_TRACE_URL is unset; proxy logs the trace URL once at start. ```text FAILED tests/test_litellm_callback.py::test_callback_uses_taos_port_as_trace_url_fallback FAILED tests/test_litellm_callback.py::test_success_posts_trace_to_taos_port FAILED tests/test_llm_proxy.py::TestTraceUrlPropagation::test_start_exports_trace_url_with_proxy_port FAILED tests/test_llm_proxy.py::TestTraceUrlPropagation::test_start_logs_trace_url 4 failed in 6.47s ``` ```text tests/test_litellm_callback.py::test_callback_uses_taos_port_as_trace_url_fallback PASSED tests/test_litellm_callback.py::test_success_posts_trace_to_taos_port PASSED tests/test_llm_proxy.py::TestTraceUrlPropagation::test_start_exports_trace_url_with_proxy_port PASSED tests/test_llm_proxy.py::TestTraceUrlPropagation::test_start_logs_trace_url PASSED 4 passed in 5.68s ```
1 parent c8ccdb5 commit d2dc4c9

5 files changed

Lines changed: 128 additions & 2 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
### Fixed
2+
- `LLMProxy.start()` in `tinyagentos/llm_proxy.py` now exports `TAOS_TRACE_URL`
3+
derived from the controller's bound port so the `TaosLiteLLMCallback` inside
4+
the LiteLLM subprocess can POST trace, lifecycle and spend events to the
5+
correct controller instead of silently dropping them on non-default ports
6+
(#tsk-j2l2qy).
7+
- `TaosLiteLLMCallback` in `tinyagentos/litellm_callback.py` now falls back to
8+
`TAOS_PORT` (defaulting to 6969) when `TAOS_TRACE_URL` is unset, so standalone
9+
callers and custom-port installs still emit traces (#tsk-j2l2qy).

tests/test_litellm_callback.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,46 @@ async def test_success_event_without_budget_env_does_not_crash(monkeypatch):
342342

343343
# Must not raise even though TAOS_AGENT_BUDGETS is unset.
344344
await cb.async_log_success_event(kwargs, resp, t0, t1)
345+
346+
347+
def test_callback_uses_taos_port_as_trace_url_fallback(monkeypatch):
348+
"""When TAOS_TRACE_URL is unset, the callback falls back to TAOS_PORT
349+
(defaulting to 6969) so non-default controller ports are not silently
350+
dropped."""
351+
try:
352+
from tinyagentos.litellm_callback import TaosLiteLLMCallback
353+
except ImportError:
354+
pytest.skip("litellm not installed")
355+
monkeypatch.setenv("TAOS_PORT", "7117")
356+
monkeypatch.delenv("TAOS_TRACE_URL", raising=False)
357+
cb = TaosLiteLLMCallback()
358+
assert cb._trace_url == "http://127.0.0.1:7117/api/trace"
359+
monkeypatch.delenv("TAOS_PORT", raising=False)
360+
361+
362+
@pytest.mark.asyncio
363+
async def test_success_posts_trace_to_taos_port(monkeypatch):
364+
"""Trace POST must target the TAOS_PORT-derived URL, not the hard-coded
365+
6969 fallback, so agents on non-default ports still emit trace events."""
366+
try:
367+
from tinyagentos.litellm_callback import TaosLiteLLMCallback
368+
except ImportError:
369+
pytest.skip("litellm not installed")
370+
monkeypatch.setenv("TAOS_PORT", "7117")
371+
monkeypatch.delenv("TAOS_TRACE_URL", raising=False)
372+
cb = TaosLiteLLMCallback()
373+
posted = []
374+
375+
async def _mock_post(url, payload):
376+
posted.append(url)
377+
378+
cb._post = _mock_post
379+
380+
from datetime import datetime, timezone
381+
t0 = datetime(2024, 1, 1, 10, 0, 0, tzinfo=timezone.utc)
382+
t1 = datetime(2024, 1, 1, 10, 0, 1, tzinfo=timezone.utc)
383+
await cb.async_log_success_event(_make_kwargs(), _make_response(), t0, t1)
384+
385+
assert len(posted) >= 1
386+
assert "7117" in posted[0]
387+
monkeypatch.delenv("TAOS_PORT", raising=False)

tests/test_llm_proxy.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,79 @@ def __init__(self, *args, **kwargs):
360360
assert "DATABASE_URL" not in captured["env"]
361361

362362

363+
class TestTraceUrlPropagation:
364+
@pytest.mark.asyncio
365+
async def test_start_exports_trace_url_with_proxy_port(self, tmp_path, monkeypatch):
366+
"""The proxy must export TAOS_TRACE_URL pointing at its bound port so the
367+
LiteLLM callback inside the subprocess can POST traces to the right
368+
controller, not silently drop them on non-default ports."""
369+
import shutil
370+
import tinyagentos.llm_proxy as mod
371+
372+
class _FakeResp:
373+
status_code = 200
374+
375+
class _FakeClient:
376+
def __init__(self, *a, **kw): pass
377+
async def __aenter__(self): return self
378+
async def __aexit__(self, *exc): return False
379+
async def get(self, url): return _FakeResp()
380+
381+
monkeypatch.setattr(mod.httpx, "AsyncClient", _FakeClient)
382+
monkeypatch.setattr(mod, "_pids_listening_on", lambda port: [])
383+
monkeypatch.setattr(shutil, "which", lambda _: "/fake/litellm")
384+
385+
captured = {}
386+
387+
class _FakePopen:
388+
def __init__(self, *args, **kwargs):
389+
captured["env"] = kwargs.get("env") or {}
390+
391+
monkeypatch.setattr(mod.subprocess, "Popen", _FakePopen)
392+
393+
p = mod.LLMProxy(port=7117)
394+
await p.start(backends=[])
395+
396+
assert captured["env"]["TAOS_TRACE_URL"] == "http://127.0.0.1:7117/api/trace"
397+
398+
@pytest.mark.asyncio
399+
async def test_start_logs_trace_url(self, tmp_path, monkeypatch, caplog):
400+
"""On successful start, the proxy must log the trace URL it exported so
401+
operators can verify traces are targeting the correct port."""
402+
import logging
403+
import shutil
404+
import tinyagentos.llm_proxy as mod
405+
406+
class _FakeResp:
407+
status_code = 200
408+
409+
class _FakeClient:
410+
def __init__(self, *a, **kw): pass
411+
async def __aenter__(self): return self
412+
async def __aexit__(self, *exc): return False
413+
async def get(self, url): return _FakeResp()
414+
415+
monkeypatch.setattr(mod.httpx, "AsyncClient", _FakeClient)
416+
monkeypatch.setattr(mod, "_pids_listening_on", lambda port: [])
417+
monkeypatch.setattr(shutil, "which", lambda _: "/fake/litellm")
418+
419+
class _FakePopen:
420+
def __init__(self, *a, **kw):
421+
pass
422+
423+
monkeypatch.setattr(mod.subprocess, "Popen", _FakePopen)
424+
425+
p = mod.LLMProxy(port=7117)
426+
with caplog.at_level(logging.INFO, logger="tinyagentos.llm_proxy"):
427+
result = await p.start(backends=[])
428+
429+
assert result is True
430+
assert any(
431+
"trace URL" in rec.getMessage() and "7117" in rec.getMessage()
432+
for rec in caplog.records
433+
), [rec.getMessage() for rec in caplog.records]
434+
435+
363436
class TestLLMProxyOwnership:
364437
def test_is_running_false_by_default(self):
365438
from tinyagentos.llm_proxy import LLMProxy

tinyagentos/litellm_callback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class TaosLiteLLMCallback(_CustomLogger):
7878

7979
def __init__(self) -> None:
8080
super().__init__()
81-
self._trace_url: str = os.environ.get("TAOS_TRACE_URL", "http://127.0.0.1:6969/api/trace")
81+
self._trace_url: str = os.environ.get("TAOS_TRACE_URL", f"http://127.0.0.1:{os.environ.get('TAOS_PORT', '6969')}/api/trace")
8282
self._notify_url: str = self._trace_url.replace("/api/trace", "/api/lifecycle/notify")
8383

8484
async def _post(self, url: str, payload: dict) -> None:

tinyagentos/llm_proxy.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ async def start(
472472
# deployer uses when auth'ing /key/generate and agent requests.
473473
env = os.environ.copy()
474474
env["LITELLM_MASTER_KEY"] = get_litellm_master_key(self._data_dir)
475+
env["TAOS_TRACE_URL"] = f"http://127.0.0.1:{self.port}/api/trace"
475476
# Forward the local auth token so the TaosLiteLLMCallback inside
476477
# the subprocess can POST to taOS's /api/trace (otherwise 401).
477478
if self.local_token:
@@ -559,7 +560,7 @@ async def start(
559560
async with httpx.AsyncClient(timeout=3) as client:
560561
resp = await client.get(f"{self.url}/health/readiness")
561562
if resp.status_code == 200:
562-
logger.info(f"LiteLLM proxy started on port {self.port}")
563+
logger.info("LiteLLM proxy started on port %d (trace URL: %s)", self.port, env.get("TAOS_TRACE_URL"))
563564
return True
564565
except Exception:
565566
pass

0 commit comments

Comments
 (0)