Commit graph

10 commits

Author SHA1 Message Date
Yassin Kortam
7a5d6a0548
ci: fail the build when a test file or Dockerfile is invoked by no job (#35991)
The absence of a CI signal is indistinguishable from a passing one, and
that shape has now produced several independent holes: whole test
directories no job runs, and shipped images no job builds. Nothing was
watching for either, so each was found by accident.

assert_ci_coverage.py enumerates every test_*.py under tests/ and every
Dockerfile in the repo, then credits only what the workflows and the
CircleCI config actually invoke. Paths filters and lint steps that merely
name a directory do not count as coverage, because crediting a mention is
the same mistake one level up. Anything neither invoked nor listed in
.github/ci-coverage-allowlist.yml with a written reason fails the job.

The guard found 275 uncovered test files and 6 unbuilt Dockerfiles. Fixed
here: the root Dockerfile, the primary published image, now gets an
image-scan leg that builds it and runs the offline migration check against
it, and 8 tests/test_litellm subdirectories join the shards that already
enumerate their siblings. Everything else is allowlisted per file, so a
new file cannot inherit an exemption, and the remaining decisions are
tracked rather than invisible.

The job reports but is not in branch protection, so it does not block
merges; promoting it is a separate change once the allowlist has survived
contact with a few pull requests.
2026-08-05 14:52:48 -07:00
Yuneng Jiang
37ca659f84
ci: run unit tests on pushes to main and litellm_internal_staging
The unit workflows trigger on pull_request only, so a commit that
actually lands on a gated branch ends up with no unit-test check runs at
all. The commit status API reports success for those commits, which a
release gate reads as "nothing failed" rather than "never tested". PR
checks also only ever ran against the merge preview, not the commit that
landed, so two branches that are each green can still land broken
together

Add a push trigger on the two gated branches to the twelve unit
workflows and to the code-quality workflow, so every landed commit gets
check runs addressable by its SHA

test-linting.yml is deliberately left on pull_request only; six of its
steps gate on a diff against github.event.pull_request.base.sha, which
is empty outside a pull request, and a "what did this branch add" check
has no meaning on a merge commit

Also key the concurrency group on github.sha and restrict
cancel-in-progress to pull_request. The previous group was stable across
pushes to a branch, so consecutive merges would cancel the in-flight run
for the earlier commit and leave that SHA without a result, which is the
same blind spot this change is meant to close
2026-08-01 11:58:49 -07:00
Yuneng Jiang
82fd456b94
Revert "ci: skip unit test workflows when only ui or markdown files change (#32422)"
This reverts commit 6df5e1b263.
2026-07-08 11:55:59 -07:00
Mateo Wang
6df5e1b263
ci: skip unit test workflows when only ui or markdown files change (#32422)
* ci: skip unit test workflows when only docs or ui files change

Mirror the CircleCI backend path filter (.circleci/scripts/classify_changes.sh)
in the GitHub Actions unit test workflows by adding paths-ignore for ui/**,
docs/**, *.md and *.mdx to every test-unit-*.yml pull_request trigger

* ci: drop docs/** from unit test paths-ignore since the folder no longer exists
2026-07-07 22:10:58 -07:00
Sameer Kankute
c712c20d0f
fix(ci): point OSS contributor workflows to litellm_oss_staging (#31270)
* fix(ci): point OSS contributor workflows to litellm_oss_staging

Workflow triggers and guard error messages incorrectly referenced litellm_oss_branch; update them to the branch we actually use for external contributions.

* fix(ci): include test-rust.yml in litellm_oss_staging rename

Missed test-rust.yml when updating OSS contributor target branch references.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 21:07:59 -07:00
ishaan-berri
4efce809d0
feat(proxy): add POST /v1/callbacks/logs to replay logging payloads through callbacks (#31134)
* feat(proxy): add logging_endpoints package init

* feat(proxy): add POST /v1/callbacks/logs to replay logging payloads through the success/failure callback fan-out

* feat(proxy): register callback_logs_router

* test(proxy): add logging_endpoints test package init

* test(proxy): cover /v1/callbacks/logs replay, admin guard, and partial-failure handling

* refactor(proxy): move callback-logs request/response models to litellm/types/proxy

* refactor(proxy): wrap callback-logs replay in CallbackLogsReplayer class with payload logging

* test(proxy): update callback-logs tests for class-based replayer and separated types

* fix(proxy): cover /v1/callbacks/ in backend component allowlist

The new /v1/callbacks/logs route was dropped by both component
allowlists, failing test_gateway_plus_backend_covers_full_app. It's an
admin-only spend-logging route, so it belongs on the backend (control
plane) alongside the existing /callbacks family.

* refactor(proxy): use builtin dict/list generics in callback-logs endpoint

Switch Dict/List from typing to builtin dict/list to satisfy the ruff
strict-rule budget (UP006).

* refactor(proxy): use builtin dict/list generics in callback-logs types

UP006: builtin generics over typing.Dict/List.

* chore(ui): regenerate schema.d.ts for /v1/callbacks/logs

Run npm run gen:api to add the CallbackLogRecord/CallbackLogsRequest/
CallbackLogsResponse types and the /v1/callbacks/logs path, keeping the
dashboard types in sync with the proxy OpenAPI spec.

* fix(proxy): force stream=False when replaying callback logs

A replayed StandardLoggingPayload is a terminal, fully-aggregated event —
the producer (e.g. the rust realtime gateway) already collected the whole
session before POSTing. Marking the rebuilt Logging object as streaming made
async_success_handler wait for a complete_streaming_response that never
arrives, so the spend log was never written. Realtime sessions now land in
LiteLLM_SpendLogs.

* feat(litellm-rust): CustomLogger callback layer posting to /v1/callbacks/logs

integrations/ mirrors litellm/integrations/: a sync, typed CustomLogger trait
(base contract), a typed StandardLoggingPayload, and LiteLLMPythonProxyAPILogger
— the first concrete logger, owning a bounded channel + background worker that
batches and POSTs to the Python proxy's /v1/callbacks/logs.

* feat(litellm-rust): RealTimeStreaming per-session log collector

1:1 with Python's RealTimeStreaming: observe() accumulates O(1) usage/model/id
per event (never buffers frames); log_messages() builds one StandardLoggingPayload
on session close and fans out to the CustomLogger callbacks. request_id == the
OpenAI realtime session id (sess_…), with the gateway id as fallback.

* feat(litellm-rust): wire realtime logging into the splice (lock-free observe)

The collector is owned on the splice task and observed via a synchronous &mut
callback threaded through providers::realtime::realtime() — no Arc/Mutex/atomic
on the per-frame hot path. On session close the bridge flushes one payload.
AppState carries the registered loggers; main spawns the proxy logger.

* docs(litellm-rust): ai-gateway realtime logging architecture

* docs(litellm-rust): document request-log egress to the LiteLLM control plane

Add a 'Request logging' guide to the ai-gateway README: how to point the gateway
at a LiteLLM proxy via LITELLM_PROXY_BASE_URL (+ LITELLM_MASTER_KEY for the
admin-only /v1/callbacks/logs POST), and the non-blocking / one-payload-per-session
behavior.

* feat(litellm-rust): make log-egress tunables env-overridable

Channel capacity, batch size, and flush interval now read from
LITELLM_LOG_CHANNEL_CAPACITY / LITELLM_LOG_BATCH_SIZE / LITELLM_LOG_FLUSH_INTERVAL_MS,
falling back to the DEFAULT_* consts on missing/invalid/non-positive values.
Grouped behind an EgressTunables::from_env() read once at logger construction.

* docs(litellm-rust): document log-egress tuning env vars

* docs(litellm-rust): require constants in a crate-level constants.rs

Mirror of Python's litellm/constants.py rule — magic numbers and fixed strings
go in src/constants.rs, not inline in feature modules; env-overridable tunables
keep their DEFAULT_* value there.

* refactor(litellm-rust): move ai-gateway constants into constants.rs

Per the new rule: the log-egress defaults (proxy base, ingest path, channel
capacity, batch size, flush interval) and the realtime provider default move to
crates/ai-gateway/src/constants.rs; modules import from it.

* ci: run logging_endpoints tests in the proxy-infra coverage shard

tests/test_litellm/proxy/logging_endpoints wasn't in any coverage-uploading
job, so callback_logs_endpoints.py showed only import-level coverage (~35%) on
codecov/patch despite being ~98% covered locally. Add it to proxy-infra's
test-path so the test is exercised under --cov.

* fix(litellm-rust): hash the master key before logging — never send the raw credential

Greptile/Veria P1: user_api_key_hash was the plaintext LITELLM_MASTER_KEY, which
fans out to spend logs and every callback (Langfuse/Datadog) and could be
recovered from logs. SHA-256 it (auth::hash_token, matching the proxy's
hash_token); the field is named *_hash and the proxy stores it verbatim when it
isn't sk-prefixed, so the DB value is identical with zero plaintext exposure.

* fix(litellm-rust): observe realtime logging on upstream events only

Greptile P1: observe ran on the client->upstream arm too, so an authenticated
client could send a fabricated response.done and inflate its own spend log.
session.created/response.done are server->client events; observe the upstream
arm only.

* feat(proxy): bound callback-logs batch + return per-record failures

Greptile P2: cap /v1/callbacks/logs at MAX_CALLBACK_LOG_RECORDS (default 1000,
env-overridable) so one POST can't trigger an unbounded callback/DB fan-out; and
return per-record {index, error} failures so a caller (the rust gateway) can
distinguish a transient callback error from a structurally bad payload.

* chore(ui): regenerate schema.d.ts for CallbackLogFailure / failures field

* fix(constants): make MAX_CALLBACK_LOG_RECORDS a plain constant

It doesn't need to be env-configurable (only the rust egress tunables are). As an
os.getenv var it tripped tests/documentation_tests/test_env_keys.py, which requires
every env key to be documented in the (separate-repo) config_settings.md. Plain
constant → not scanned → code-quality + documentation checks pass.

* docs(litellm-rust): trim ai-gateway ARCHITECTURE.md to one diagram + notes

* docs(litellm-rust): tighten the README request-logging section

* docs(litellm-rust): ARCHITECTURE.md is just the diagram (gateway = inference, spend = callback)

* docs(litellm-rust): drop em-dashes from the request-logging section

---------

Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
2026-06-24 15:25:10 -07:00
Yuneng Jiang
aff4717494
[Infra] Expand CI branch filters for non-main PR targets
Required test-unit-* and related workflows only triggered on PRs targeting
main, so feature PRs routed through litellm_internal_staging or
litellm_oss_branch never dispatched the full suite. Branch protection
reported BLOCKED even when CircleCI was green.

Expand pull_request and push branch filters to also match
litellm_internal_staging, litellm_oss_branch, and "litellm_**" (using **
so branch names containing "/" also match).
2026-04-15 15:39:57 -07:00
yuneng-jiang
d132b1bf51
[Infra] Remove Redundant Matrix Unit Test Workflow (#25251)
* Remove redundant matrix unit test workflow

All test paths in test-litellm-matrix.yml are fully covered by the
newer semantic unit test workflows (test-unit-*.yml), making the
matrix workflow redundant CI spend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add Codecov coverage reporting to semantic unit test workflows

Add coverage collection (--cov) and Codecov OIDC upload to both
reusable base workflows and all 12 caller workflows, replacing the
coverage reporting that was previously only in the matrix workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Move id-token/pull-requests permissions to job level for multi-job workflows

For workflows with multiple jobs (llm-providers, proxy-db), move
id-token: write and pull-requests: write from workflow level to job
level so permissions are scoped to only the jobs that need them.
Removes zizmor inline suppressions that were masking the issue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:52:38 -07:00
Yuneng Jiang
a34ed20901
[Infra] Fix job naming in reusable workflow callers
Rename job keys from generic 'test' to descriptive names (e.g.,
'core-utils', 'proxy-auth', 'router') so GitHub checks display as
'core-utils / run' instead of 'test / test'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 10:07:32 -07:00
Yuneng Jiang
3d527b722d
[Infra] Add isolated unit test workflows with hardened security posture
Replace monolithic matrix workflow with individual, descriptively-named
workflow files. Each workflow uses a shared reusable base and follows
least-privilege security: zero secrets, read-only permissions, SHA-pinned
actions, persist-credentials: false, and env-var indirection to prevent
template injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:56:58 -07:00