From b285beb3bb5e468f672783134ead94472a2218fc Mon Sep 17 00:00:00 2001 From: udemex10 <80661519+udemex10@users.noreply.github.com> Date: Thu, 22 Oct 2026 22:15:25 -0500 Subject: [PATCH] feat: add Veqen cost ledger callback --- .../generic_api_compatible_callbacks.json | 9 ++++++ .../generic_api/test_generic_api_callback.py | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/litellm/integrations/generic_api/generic_api_compatible_callbacks.json b/litellm/integrations/generic_api/generic_api_compatible_callbacks.json index 900f75b1d54..f8f23daa424 100644 --- a/litellm/integrations/generic_api/generic_api_compatible_callbacks.json +++ b/litellm/integrations/generic_api/generic_api_compatible_callbacks.json @@ -34,6 +34,15 @@ }, "environment_variables": ["QUALIFIRE_API_KEY", "QUALIFIRE_WEBHOOK_URL"] }, + "veqen": { + "event_types": ["llm_api_success", "llm_api_failure"], + "endpoint": "https://veqen-app.com/v1/integrations/litellm", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer {{environment_variables.VEQEN_API_KEY}}" + }, + "environment_variables": ["VEQEN_API_KEY"] + }, "ramp": { "event_types": ["llm_api_success"], "endpoint": "https://api.ramp.com/developer/v1/ai-usage/litellm", diff --git a/tests/unit/integrations/generic_api/test_generic_api_callback.py b/tests/unit/integrations/generic_api/test_generic_api_callback.py index afaae02321d..dc9413f14d5 100644 --- a/tests/unit/integrations/generic_api/test_generic_api_callback.py +++ b/tests/unit/integrations/generic_api/test_generic_api_callback.py @@ -731,6 +731,34 @@ class _InProcessLogSink: return httpx.Response(200) +@pytest.mark.asyncio +async def test_veqen_delivers_success_and_failure_as_authenticated_json(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("VEQEN_API_KEY", "synthetic-ingest-key") + sink: Final = _InProcessLogSink() + handler: Final = AsyncHTTPHandler(transport=httpx.MockTransport(sink.respond)) + before_tasks: Final = frozenset(asyncio.all_tasks()) + logger: Final = GenericAPILogger( + callback_name="veqen", async_httpx_client=handler, batch_size=1000, flush_interval=3600 + ) + owned_tasks: Final = frozenset(asyncio.all_tasks()) - before_tasks + try: + await logger.async_log_success_event({"standard_logging_object": {"id": "success"}}, None, None, None) + await logger.async_log_failure_event({"standard_logging_object": {"id": "failure"}}, None, None, None) + await logger.flush_queue() + assert tuple(_received_log_ids(sink.requests, "json_array")) == ("success", "failure") + assert len(sink.requests) == 1 + request: Final = sink.requests[0] + assert request.method == "POST" + assert str(request.url) == "https://veqen-app.com/v1/integrations/litellm" + assert request.headers["authorization"] == "Bearer synthetic-ingest-key" + assert request.headers["content-type"] == "application/json" + finally: + for task in owned_tasks: + task.cancel() + await asyncio.gather(*owned_tasks, return_exceptions=True) + await handler.close() + + @pytest.mark.asyncio @pytest.mark.parametrize("log_format", ("json_array", "ndjson", "single")) async def test_generic_api_keeps_failure_arriving_during_success_batch(log_format: LOG_FORMAT_TYPES) -> None: