* feat(lens): add trace_agents rollup query
* feat(lens): register the trace_agents read query
* feat(lens): add trace_agents params and row types
* feat(lens): dispatch the trace_agents query
* feat(lens): export trace_agents wire schemas
* test(lens): pin the trace_agents query name
* test(lens): cover trace_agents scope, window and failure counts
* feat(lens): cap the agent list size
* feat(lens): declare the trace_agents bridge query
* feat(lens): read trace agents from clickhouse storage
* feat(lens): add trace agent response models
* feat(lens): list agents within the reader's trace scope
* feat(lens): add GET /v1/traces/agents
* chore(lens): regenerate trace models with trace_agents
* chore(lens): regenerate trace types with trace_agents
* chore(lens): regenerate read query name schema
* chore(lens): add trace agent row schema
* chore(lens): add trace agents params schema
* test(lens): cover the trace agents route
* test(lens): cover agent listing scope and timestamps
* chore(ui): regenerate api types with trace agents route
* feat(lens): derive trace agent types from the schema
* feat(lens): fetch the agent list from the traces api
* feat(lens): roll up demo runs into agents
* feat(lens): serve the agent list in demo data
* feat(lens): load agents seen in the last two weeks
* feat(lens): remember the selected agent per browser
* feat(lens): add the agent picker
* feat(lens): wire agent selection into the lens header
* feat(lens): show the agent picker next to the lens title
* refactor(lens): drop the toolbar agent filter in favor of the header picker
* test(lens): remove tests for the toolbar agent filter
* test(lens): cover agent resolution and rollup
* test(lens): cover scoping, switching and remembering the agent
* test(lens): read only the create request in guided setup
* test(lens): assert the toolbar agent filter is gone
* test(lens): read stubbed requests without their abort signal
* feat(lens): carry lens.source attributes on trace span rows
* feat(lens): read lens.source attributes in trace_spans query
* feat(lens): read lens.source attributes in trace_page_spans query
* feat(lens): read lens.source attributes in trace_span_batch query
* feat(lens): read lens.source attributes in trace_list_span_batch query
* feat(lens): decode lens.source columns from clickhouse span rows
* feat(lens): add run source to the trace summary contract
* feat(lens): export RunSource from litellm-traces
* feat(lens): resolve the https run source from the root span
* test(lens): cover run source resolution and https guard
* test(lens): add source fields to capture fixture rows
* test(lens): round-trip source fields on span row contract
* test(lens): add source fields to trace cache test rows
* test(lens): add source fields to trace cache read fixtures
* test(lens): add source fields to trace cache snapshot fixtures
* chore(lens): regenerate python trace types with run source
* chore(lens): regenerate trace json schema with run source
* chore(lens): regenerate trace page json schema with run source
* chore(ui): regenerate api types with trace run source
* feat(lens): add run source link with hover card
* test(lens): cover run source app detection and url guard
* feat(lens): show the run source next to the trace name
* feat(lens): label the run source link, e.g. Slack thread
* test(lens): assert run source link labels
* feat(lens): move the run source link into the trace stats row
* feat(lens): add a typed run source, e.g. slack or teams
* feat(lens): export RunSourceType
* feat(lens): carry the source type on trace span rows
* feat(lens): resolve the run source type, defaulting to custom
* feat(lens): read agent.source attributes in trace_spans query
* feat(lens): read agent.source attributes in trace_page_spans query
* feat(lens): read agent.source attributes in trace_span_batch query
* feat(lens): read agent.source attributes in trace_list_span_batch query
* feat(lens): decode the source type from clickhouse span rows
* test(lens): cover run source type parsing
* test(lens): add source type to capture fixture rows
* test(lens): round-trip source type on span row contract
* test(lens): add source type to trace cache test rows
* test(lens): add source type to trace cache read fixtures
* test(lens): add source type to trace cache snapshot fixtures
* chore(lens): regenerate python trace types with source type
* chore(lens): regenerate trace json schema with source type
* chore(lens): regenerate trace page json schema with source type
* chore(ui): regenerate api types with run source type
* feat(lens): pick the run source logo and label from its type
* test(lens): cover run source labels by type and slack logo
* feat(lens): show the run source as a Source stat with the app name
* test(lens): assert run source app names
* feat(lens): place the Source stat before Duration
* test(e2e): move the harness self-tests out of tests/e2e
The nightly Buildkite run copies tests/e2e into the runner image and runs
bare pytest, so the 672 tests of the harness itself (fixture parsing, JUnit
properties, the stack lock, the load aggregators, the Claude Code driver)
counted as e2e tests on the status page even though none of them reaches a
proxy. They now live in tests/e2e_harness, mirroring the tests/e2e layout,
and run in the GitHub Actions lint job and the CircleCI
provider_replay_harness job instead
* fix(ci): point the providers replay controls at tests/e2e_harness
The providers integration job still selected the four replay-control
tests under tests/e2e/test_provider_edge.py, so pytest exited before
they ran. The raw-HTTP check's file walk also drops to one loop per
comprehension
* style(tests): mark the raw-HTTP check's bindings Final
* perf(lens): bound single trace reads by the sampled start time
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
* test(lens): allow unused query fixture field in load tests
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
* test(lens): pass start_time in every lens content and evidence test
Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
* fix(lens): price agent traces by joining gen_ai.response.id to spend logs
Trace spend now joins each model call to spend_logs on one key: the
span's response id (gen_ai.response.id or the id the normalizers read
from OpenInference/LangChain output) against spend_logs.response_id or
the upstream id embedded in a managed resp_ id. The litellm.call_id and
traceparent transport join paths and the per-row ownership gate are
removed; the spend SQL still restricts rows to what the reader can see.
A run with some unpriced calls now reports the sum of its priced calls
plus priced_calls, instead of an unknown total.
* test(lens): cover response id spend join and partial trace totals
* chore(lens): regenerate trace types for priced_calls
* feat(lens): show partial run cost as a lower bound with priced call count
* fix(lens): treat litellm.call_id as the same assigned call id for spend joins
The id LiteLLM assigned to a call is either the response id it returned
(gen_ai.response.id -> spend_logs.response_id) or its gateway call id
(litellm.call_id -> spend_logs.litellm_call_id). Both are exact ids the
gateway mints and logs, so the join stays one rule. Transport span
matching and the ownership gate stay removed.
* test(lens): cover litellm.call_id spend joins and restore captured totals
* feat(lens): link each priced model call to its spend log
Spans gain spend_log_request_id, the spend_logs.request_id the call was
priced from, and spend_match, which says whether a model call matched or
why not (no assigned id on the span, no spend log with that id, or an
ambiguous match). A model call span is priced from the same ids as the
run total, so its cost and the total agree.
* test(lens): cover spend log links on model call spans
* chore(lens): regenerate trace types for spend log links
* feat(lens): open the matched spend log from an LLM step
An LLM step's header now shows a Spend log chip with the matched
request id and cost; clicking it opens the request log drawer over the
run, fetched by the exact spend_logs.request_id instead of the span's
own response id. Unpriced steps say why (no assigned id on the span, or
no spend log with it). Tree rows show each model call's cost, and a
partial run cost shows its priced call count inline.
* test(lens): cover the spend log link and unmatched cost reasons
* feat(lens): show the spend log link as a bordered LiteLLM Spend Log button
* feat(lens): add a back link from the spend log drawer to the agent trace
* feat(lens): label the spend log back link Back to Lens trace with the Lens icon
* fix(lens): ignore assigned ids that name no spend log when pricing a call
An id that names no row no longer vetoes the call, so a span carrying
both a response id and a call id still prices from a spend row logged
before litellm_call_id existed. An id naming two or more rows makes the
call ambiguous, and the match reason comes from the same per-id result,
so a single matched row with no cost is reported as matched.
* fix(lens): price a trace only from spend logs in its own team
A reader with several teams could see the same assigned id in another
team's spend log; only rows from the trace's team now price it. The
user and key ownership gate stays removed.
* perf(lens): resolve each model call's spend once per trace
Model call matches are computed once when the trace is resolved and
looked up by span index, instead of scanning the model call list for
every span and walking the graph again for spans, agents and the run
total.
* fix(lens): hide a step's Cost fact only when its spend log link shows the cost
* chore(lens): drop narrative doc comments from the spend join
* fix(lens): price a model call only when its ids agree on one spend log per span
* fix(lens): keep pricing spend logs written before litellm_call_id by their request id
* fix(lens): price every attempt a model call's ids name when they agree
* perf(types): defer pydantic schema builds via shared LiteLLMBaseModel
Add LiteLLMBaseModel with defer_build driven by DEFER_PYDANTIC_BUILD (default true) and move litellm and enterprise pydantic models onto it
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(types): build deferred models created by a parent validator; keep lens worker models litellm-free
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore(types): link pydantic issue on deferred-build rebuild hook
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: nate <nate@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(ci): namespace claude session ids in tracing seeds and allowlist /v1/logs on backend
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(proxy): allowlist /v1/logs on the gateway alongside /v1/traces
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test(tracing): pin HTTP request compatibility
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(tracing): generate existing HTTP request models from Rust schemas
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(tracing): bind trace query params to generated request models
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* ci(rust): raise the native wheel size gate to 48 MB
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf(tracing): read the trace list clock without a thread-pool dispatch
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(traces): share the trace page-size bounds between schema and reader
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(ui): type trace request queries against the generated OpenAPI schema
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* docs: encode the trace contract boundary in AGENTS.md
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* style(ui): format trace request aliases
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(lens): own trace reads behind a cached TraceStore port
Move storage-independent trace reads into litellm-traces-cache behind a
TraceStore port that ClickHouse implements. One keyset pager drives the
span, list span and spend reads, and a run list batch reads spend once.
Trace opens, pages and list summaries share one resolved read per trace
in an in-process cache with single-flight loading. Live traces and reads
with unknown spend expire after 5s, quiet traces after 10 minutes, failed
reads are never cached, and the accepted list page size is remembered per
scope.
Trace read failures map to their own status and code (400, 409, 413, 503
with Retry-After), and the trace drawer retries temporary failures while
offering only a refresh for changed or oversized traces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(lens): seed large profiles with server-side copies and long sessions
Replay one copy through the proxy, then copy it inside ClickHouse and
PostgreSQL with INSERT ... SELECT, rewriting trace, span and call IDs so
every copy keeps its own spend. Add three long single-trace sessions for
drawer paging and the oversized read path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chores
* style(lens): float the investigation setup badge on the tab edge
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(lens): restructure the trace drawer and polish its layout
Split the 547-line TraceDrawer into run/, tree/, span/, content/ and
conversation/ modules. Step rows now sit on one line with colored span
family tiles, and the per-row timing bar moved into an optional Waterfall
layout with a time axis. The steps and details panes are separated by the
shadcn Resizable handle, with the split remembered per orientation.
Span payloads go through one pure classifier (payloadView) that picks
messages, a tool result, a nested field tree or text. JSON-encoded field
values unfold into a tree, prose renders as markdown, repr and tracebacks
stay monospace, and every section offers a Raw view. LangChain's
serialized messages now render as conversation cards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* typesafety
* wip
* fmt
---------
Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat: seed Lens dev with configurable load profiles
* feat: run Lens UI live through the dev launcher
* fix: verify Lens UI startup before seeding
* fix(ui): render run timestamps on one compact line
The agent runs table printed the long locale form with timezone, which
wrapped to two lines per row. Use a fixed-width 24h form with
milliseconds that matches the timeline axis, keep the long form in the
hover title, and show the timezone once in the column header
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(ui): use the root query client for the Lens demo
Drop the demo's nested QueryClient. Cache keys are already partitioned by
scope, and the root client now skips retries on 4xx ApiErrors, which covers
the demo's not-in-demo and read-only rejections and live 4xx alike.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: drop unused synthetic_spend reference from query help
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(traces): drop the deeplite fixtures and map every SDK to a logo
The deeplite captures predate the example repository and carried
synthetic spend rows, which leaked a fixture-only column into the
query help SQL and pinned tests to its shape. Replace them with the
SDK captures in the ClickHouse round trip and query API tests, and
derive the seed tenant lookup from the capture metadata
The runs table only knew the two Anthropic framework slugs. Register
the slugs the normalizer emits for LangChain, LangGraph, Deep Agents,
CrewAI, Google ADK, LlamaIndex, OpenAI Agents, Pydantic AI, Strands,
Vercel AI SDK, Codex, Cursor and Copilot, and fall back to the generic
agent glyph when a run has no known framework
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(ui): inject the Lens sample through data sources instead of demo checks
Components no longer ask whether they are in the demo. The traces source
carries live and handoff, navigation state comes from a URL or memory
route, and the preview action comes from context instead of onDemo props
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): infinite scroll for the agent runs list
Replace the Load more button with a sentinel that fetches the next
cursor page as the list nears its end. Placeholder rows hold the tail
while more runs exist, and a failed page stops auto-loading until Retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): keep the whole Lens view in the URL
Lens navigation now lives entirely in query params through nuqs: the
sample session (demo=true, with a Demo data switch in the header), the
open run (trace, trace_ref), the selected step, view and detail section
(span, view, span_tab) and the list filters and range (q, agent, status,
hours). Any Lens view is a shareable link and the back button walks runs
RunView takes its selection injected: the drawer feeds it URL state and
the investigations evidence sheet keeps a local one, so a finding's
original run never writes step ids into the URL. Leaving the sample
session clears every Lens key except the tab so sample ids never point
at live data
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* style(traces): cargo fmt captures tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(docker): Windows quickstart in PowerShell, and a styled terminal for both quickstarts
scripts/quickstart.ps1 does what scripts/quickstart.sh does, for Windows:
powershell -c "irm https://raw.githubusercontent.com/BerriAI/litellm/main/scripts/quickstart.ps1 | iex"
It asks the same two questions, prints the same lines, writes the same .env
(UTF-8 without a byte order mark, LF endings, readable only by the current
Windows account), and runs in Windows PowerShell 5.1 and PowerShell 7.
Both scripts now give a person at a terminal colors, a check mark per step, a
spinner while Docker starts, and a framed summary. Agents, CI, log files, and
NO_COLOR get the same lines as plain text.
* feat(docker): support podman and rancher desktop in the quickstart scripts
Both quickstarts hard-required the docker CLI. They now pick the first
available engine among docker, podman, and nerdctl (Rancher Desktop in
containerd mode; its dockerd mode already provides a docker CLI), route
every invocation through it, and tailor the start hint (podman machine
start) and the printed stop/logs/volume commands to that engine
* fix(docker): test port availability by binding instead of connecting
On a WSL2-backed engine (Podman, Rancher Desktop), the Windows localhost
relay swallows connection refusals on closed ports, so every connect
waits out its 2-second timeout and Test-PortFree reported ports 4000 to
4099 all taken on a machine with none of them in use. Binding the port
answers instantly and accurately
* fix(quickstart): address review findings
- The PowerShell script names the project with the same POSIX cksum as the
shell script, so the earlier-install check sees the database from either
script.
- Both scripts stop when git tracks .env in the install folder.
- .env is written to a temp file with owner-only permissions and moved into
place, so a failed write never leaves a partial file.
- Errors stay plain text when stderr is redirected.
- The spinners remove their temp files on exit and Ctrl+C, and the shell
spinner no longer runs date on every frame.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(quickstart): offer to copy the admin password to the clipboard
After the summary, the quickstarts ask "What next?": copy the admin
password, open the admin UI, or finish. The password is piped to the
clipboard (pbcopy, wl-copy, xclip, xsel, clip.exe, or Set-Clipboard), so
it never appears on screen or in the process list. Over SSH, or with no
clipboard, the option is left out and the summary points to .env as
before. The PowerShell menu now honours Ctrl+C.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(quickstart): write .env through mktemp, let Ctrl+C cancel the PowerShell menu, drop redundant comments
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(quickstart): remove the in-progress .env temp file when interrupted
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Mubashir Osmani <mubashir.osmani777@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(lens): add scripts/lens_dev.sh for one-command lens local dev
* feat: add make lens-dev target
* chore: gitignore .lens-dev local state
* docs(lens): mention make lens-dev in the developer note
* fix(lens): resolve a relative LENS_DEV_CONFIG against the caller's cwd
* fix(lens): random lens-dev master key, verify reused services, drop inherited REDIS_*
* test(lens): cover lens-dev worker token, master key, env and cleanup
* fix(lens): skip compose postgres when LENS_DEV_DATABASE_URL is set
* test(lens): external LENS_DEV_DATABASE_URL never starts compose postgres
* feat(dev): seed linked tracing and spend fixtures
* chore(dev): use OpenAI model in tracing config
* chore(dev): align tracing credentials with UI E2E
* fix(dev): update fixture seeder query scope
* feat(dev): seed linked tracing and spend fixtures
* chore(dev): use OpenAI model in tracing config
* chore(dev): align tracing credentials with UI E2E
* fix(dev): update fixture seeder query scope
* wip
* wip
* wip
* chore(trace): checkpoint ongoing Rust migration
* refactor(trace): group Python bridge under trace package
* refactor(traces): read span conventions through a Convention trait
Each span format (Claude Code, LangSmith, OpenInference, gen_ai) now lives under
normalize/convention/ as a unit struct implementing Convention, owning both its
detection and its extraction. Precedence is one ordered registry instead of an
if-chain in mod.rs that reached into each module differently.
The modules now share one way to read attributes: present() for the first
non-empty key and Payload for a text that also reports the key it consumed,
replacing three different idioms and the &mut Vec threaded through payload
readers. Instrumentation::adjust returns a new Extraction instead of mutating
one, with each SDK rule as its own function, and the LangChain middleware
suffix list exists once.
* feat(trace): export Rust-owned wire schemas and enforce contract bounds
* fix(trace): bound quoted counts in ClickHouse wire schemas
* feat(trace): generate Python wire contracts with datamodel-code-generator
* test(trace): validate migrated callers and generated contracts at the native boundary
* refactor(traces): rename normalization convention to format
* fix(traces): reconcile spend evidence and preserve unknown costs
* feat(traces): normalize additional telemetry formats
* test(traces): cover captured normalization fixtures
* refactor(traces): isolate SDK normalization rules
* feat(tracing): seed all trace exports for local dashboard
* fix(clickhouse): preserve custom LiteLLM request metadata
* docs(traces): define normalization module boundaries
* docs(traces): define resolution and OTLP boundaries
* fix(ui): normalize nullable trace message names
* refactor(traces): split resolver modules and cover resolution behavior
* test(traces): replace normalization snapshots with behavior assertions
* fix(ui): align dashboard API contracts with generated types
* refactor(traces): type normalization and storage boundaries
* fix(traces): seed captured SDK spend and preserve provider identities
* wip
* test(traces): verify guide discovery and content ordering
---------
Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(docker): one-command quickstart that starts the gateway, Postgres, and the admin UI
scripts/quickstart.sh downloads the quickstart compose file into ~/litellm-gateway, generates the master key, salt key, and a random Postgres password into .env, picks a free port, starts the stack, waits for it to be ready, and prints where to log in. It asks at most two questions (install folder, open the browser) and asks nothing without a terminal, under CI or Claude Code, or with --yes
The compose file reads POSTGRES_PASSWORD and LITELLM_PORT from .env and falls back to the current values, so existing installs keep working unchanged
* fix(quickstart): address review findings on reinstall, ports, gitignore, and binding
Stop with instructions instead of generating a new password when a database volume from an earlier install is still there, since Postgres keeps the original password
Keep port 4000 for an existing .env that has no saved port, and only search for a free port on fresh installs
Only write the catch-all .gitignore into a folder the script created, and warn instead of writing into a folder that already existed
Add LITELLM_BIND to the compose port mapping. It is empty by default, so existing installs keep "4000:4000", and the script sets it to 127.0.0.1: so new installs listen on this machine only
* fix(quickstart): keep generated .env out of git in an existing repository folder
When LITELLM_DIR is inside a git repository that does not ignore .env, add /<path>/.env to the clone's local exclude list (.git/info/exclude) instead of only warning. Tracked files, including .gitignore, are not touched
* fix(quickstart): ignore an inherited LITELLM_BIND and keep .env ignored outside git
Clear LITELLM_BIND from the environment before starting Compose, like the keys and project name, so .env decides the bind address and new installs stay on 127.0.0.1
In an existing folder that is not inside a git repository, add a single .env line to its .gitignore (creating it if needed, without a duplicate), so the keys stay out of commits if the folder later becomes a repository
* fix(quickstart): keep an exported LITELLM_BIND for an .env without one, and show a read-first install
The bind address now follows .env only when .env sets it, which every install this script creates does. For an older .env without a bind line, a LITELLM_BIND exported in the shell is kept, so an intentional 127.0.0.1: is not dropped
The header shows how to download and read the script before running it
* fix(tracing): use ClickHouse URL for reads by default
* fix(tracing): unify ClickHouse storage configuration
* fix(tracing): update dashboard setup copy for one URL
* test(tracing): make tests/unit/tracing a package
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(config): drop legacy string tracing store variant
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(tracing): own ClickHouse defaults in constants and reject unset env references
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test(tracing): use raw regex patterns in config tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(tracing): read ClickHouse env defaults when tracing config resolves
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(ui): split audit log query guard to fit condition-chain budget
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore(lint): remove the LIT002 mutable-construction rule
Drop LIT002 from scripts/check_type_discipline.py along with its helpers,
its budget entry, its unit tests, and the AGENTS.md and gate docstring
mentions. `# mutable-ok` now only suppresses LIT001, so the markers that
only existed to silence LIT002 became LIT013 stale suppressions and are
removed. The files whose layout depended on those trailing comments are
reformatted with ruff format.
Every other LIT rule count is unchanged and the ASTs of all touched
litellm/ files match main apart from one docstring.
* chore(lint): keep the leftover mutable-ok markers for a follow-up
Restore the ~1.4k `# mutable-ok` markers stripped in the previous commit
so this PR only touches the checker, its tests, the budget, and docs.
Those markers no longer suppress anything, so `# mutable-ok` is exempt
from LIT013 until a follow-up strips them.
* Revert "chore(lint): keep the leftover mutable-ok markers for a follow-up"
This reverts commit c35bc0b84e.
---------
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
* feat(lint): LIT013 caps comprehensions at one for and one if clause
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* docs(lint): rewrap the type discipline gate rule list
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(lint): honor comprehension-ok on any line a comprehension spans
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(lint): scope comprehension-ok to the innermost comprehension spanning it
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(lint): break equal-span suppression ties toward the inner comprehension
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(lint): let single-line and only violating comprehensions own comprehension-ok markers
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test(lint): type tmp_path in LIT014 tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: mateo <mateo@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(ci): close open pull requests superseded by a merged fix on their linked issue
* fix(ci): replace the mixed-anchor release-line regex with a plain predicate
* fix(ci): recover a half-done close, page linked pull requests, trust only the workflow's marker
* fix(ci): close superseded pull requests whatever closed the issue and fail the job when the script throws
---------
Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
Shipped proxy configs now read general_settings.master_key from
os.environ/LITELLM_MASTER_KEY, the .env examples ship a blank value with
the openssl generate command above it, and READMEs, the missing env vars
page and Admin UI code snippets show a generate command or the
<your-master-key> placeholder instead of the literal sk-1234
The two CircleCI docker runs that mount proxy_server_config.yaml and
oai_misc_config.yaml now pass LITELLM_MASTER_KEY so their runtime key is
unchanged
Children spawned as [sys.executable, -c, ...] put the working directory first on sys.path, so under 'make test-rust-extension' a source checkout shadows the installed wheel and the child imports a litellm with no compiled extension. A shared helper spawns them with -I and asserts the child resolved the same litellm.__file__ as the parent, and a new TQ009 rule flags un-isolated sys.executable spawns.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
New matrix job across Python 3.10-3.14 verifies uv.lock against the
declared floors, installs the locked mcp+proxy extras and runs the MCP
unit suites, then resolves the same extras with uv's lowest-direct
strategy into a clean venv and runs scripts/check_mcp_sdk_install.py to
prove the floor still imports the SDK 2 API surface.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Everything this stack adds now carries issue in its file name, workflow name
and job id, so one search finds all of it:
ls .github/workflows/issue_*
grep -ril issue scripts .github/prompts .github/*.json
Renames: label_sync.yml -> issue_label_sync.yml, label_claude_code.yml ->
issue_label_claude_code.yml, .github/labels.json -> .github/issue-labels.json,
scripts/sync-labels(.test).ts -> scripts/sync-issue-labels(.test).ts. Job ids
now match the script they run: classify-issue-tests, classify-issue,
label-issue, sync-issue-labels-tests, sync-issue-labels, label-claude-code
An edit during the first run used to cancel it, and the edited run then
skipped because the webhook payload had no needs:template yet, so a
well-formed issue edited within the first minute was never labelled.
Runs for one issue now queue, and the script decides an edited event
against the live labels: a domain label means leave it alone, a gated
issue is re-run, and an unlabelled issue is re-run for its first hour
Split the issue body only on headings the two forms actually emit, keep the
first value when a heading repeats, cap each field on its own so a long config
cannot push the repro out of the model's view, and only treat comments from
github-actions[bot] as the template notice. The claude code keyword label the
deleted component labeler used to add gets its own small workflow.
Every issue opened from now on is gated on the template headings, sent once
through the LiteLLM proxy with a strict JSON schema, and labelled from the
manifest in .github/labels.json. Old-template issues are not touched. The bug
template shrinks to Description, Config, LiteLLM Version and Steps to Repro,
both templates gain a domain dropdown, and the labelers that keyed off the old
component dropdown go away.
The flag step never read it: parseVerdict destructures duplicate_of,
confidence and evidence only, so considered cost tokens on every issue
and went straight on the floor. The parse test now covers extra keys
being dropped instead of asserting a field that no longer exists.