Skip to content

🔄 Upstream Sync: LiteLLM v1.98.0 - #132

Open
Cartofante wants to merge 5390 commits into
carto/mainfrom
upstream-sync/v1.98.0
Open

🔄 Upstream Sync: LiteLLM v1.98.0#132
Cartofante wants to merge 5390 commits into
carto/mainfrom
upstream-sync/v1.98.0

Conversation

@Cartofante

@Cartofante Cartofante commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

🔄 Upstream Sync: LiteLLM v1.98.0

Syncs CARTO's LiteLLM fork with upstream stable release v1.98.0.

Metric Value
Version 1.92.0v1.98.0
Commits 5388
Files Changed 6411
Upstream Release v1.98.0

Caution

⚠️ DO NOT SQUASH MERGE THIS PR

Use "Create a merge commit" only. Squashing destroys upstream history and breaks future syncs.


🧪 Pre-Merge Checklist

  • CI checks pass (lint, tests, Docker build)
  • CARTO customizations preserved
  • pyproject.toml version matches upstream

📊 Release Information (click to expand)
🔀 Branch Flow (click to expand)
  1. BerriAI/litellm:main merged into CartoDB/litellm:main
  2. ✅ Created dedicated sync branch: upstream-sync/v1.98.0
  3. 📝 This PR: upstream-sync/v1.98.0carto/main

[!NOTE]
Why a dedicated branch? Allows pushing conflict resolution commits directly to this PR.

📝 CARTO-Specific File Guidelines (click to expand)

When reviewing or resolving conflicts:

✅ Keep CARTO Versions (Ours)

  • .github/workflows/carto_*.yaml - CARTO workflows
  • .github/workflows/carto-*.yml - CARTO workflows
  • CARTO_*.md, docs/CARTO_*.md - CARTO documentation

🔄 Accept Upstream (Theirs)

  • pyproject.toml - Version field
  • litellm/ - Core library code
  • tests/ - Upstream tests
  • requirements.txt - Dependencies

⚠️ Manual Review Required

  • Dockerfile, docker/Dockerfile.non_root - CARTO customizations
  • Makefile - Check # CARTO: sections
🔧 Conflict Resolution (click to expand)

If this PR has conflicts:

Option 1: Automated (Recommended)

The carto-upstream-sync-resolver workflow triggers automatically.

What it does:

  1. 🤖 Detects conflicts → 🔀 Merges carto/main → ✏️ Resolves conflicts → 🧪 Runs tests → 📌 Pushes to this PR

You just need to: Wait for resolution commits, verify CARTO customizations, merge.

[!TIP]
Single PR workflow! No separate resolution PR needed.

Option 2: Manual Resolution

git fetch origin
git checkout upstream-sync/v1.98.0
git merge origin/carto/main  # Creates conflicts
# ... resolve conflicts ...
make lint && make test-unit
git push origin upstream-sync/v1.98.0
📚 Documentation Links (click to expand)

🤖 This PR was automatically created by the carto-upstream-sync workflow.

🔧 CARTO Feature Fixes Applied

Status: ✅ Fixed
Features Restored: 1

PR #112: fix(snowflake): flatten array-form message content for Cortex

  • Files: litellm/llms/snowflake/chat/transformation.py
  • Decision: The upstream sync completely restructured the file from a single _transform_messages method to dual-endpoint routing (Anthropic vs OpenAI). The _content_to_text_string function was preserved but the call sites were lost. Since Anthropic format natively accepts array content, the fix was applied only to the OpenAI path (_transform_request_openai) which routes to /chat/completions. Added a helper method to iterate messages and flatten list-form content to strings before sending to Cortex.
  • Evidence: Syntax check passed. AST analysis confirms _content_to_text_string is now called at line 335.

Fixed: 2026-08-27 22:53:30 UTC
Workflow Run: #43

CARTO Customizations Analysis

Overall Assessment: ✅ PASS

Decision Count Description
Upstream Substitutes 0 Upstream provides equivalent functionality
Customized Upstream 1 Upstream enhanced with CARTO-specific behavior
Preserved CARTO 11 Full CARTO implementation kept
Incorrectly Dropped 0 CARTO feature lost (needs fixing!)
Total 12

CARTO Feature Preservation Analysis

Summary

Decision Count
Upstream Substitutes 0
Customized Upstream 1
Preserved CARTO 11
Incorrectly Dropped 0

Overall Assessment: PASS

All 12 CARTO features are present and properly wired in the resolved state. One feature required a post-merge fix commit but is now fully functional

Post-Merge Fixes Required

Feature Commit Issue
Snowflake Array Content Flattening (PR #112) 00acb9bec6 Helper _content_to_text_string was defined but never called; added wrapper and wiring

Feature Details

Customized Upstream (1)

Snowflake Streaming + Tool Calling (PRs #38, #58)

  • Upstream refactored Snowflake to use Anthropic Messages API format
  • CARTO's SnowflakeStreamingHandler and _extract_system_and_messages preserved
  • Tool choice auto-detection logic (tool_choice_value is None and tools) maintained in Responses API transformation

Preserved CARTO (11)

OCI Features:

Snowflake Features:

Azure Features:

Core Features:

Responses API Features:

  • Redis Session Storage (PR Update poetry.lock #16) - Both _store_session_in_redis and _patch_store_session_in_redis properly wired

Databricks Features:

Issues Found

None. All features are correctly preserved and wired

Wiring Verification Notes

Per the analysis guidelines, each feature was verified not just for pattern presence but for actual wiring:

  • All helper functions have at least one call site
  • Call sites pass correct parameters
  • Data-flow contracts hold (output formats match what callers expect)

The one issue caught (Snowflake array content flattening) was correctly identified and fixed before this analysis via commit 00acb9bec6


Feature-by-Feature Breakdown

PR #68: OCI Gemini Tool Call UUIDs

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: OCI tool call setdefault logic preserved at generic.py:397-399. The _queue_tool_call_delta_events helper in streaming_iterator.py is called at line 1038.
  • Files: litellm/llms/oci/chat/generic.py, litellm/responses/litellm_completion_transformation/streaming_iterator.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #121: OCI Parallel Tool Result Reordering

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _reorder_tool_results_to_match_tool_calls function defined at line 169 and called at line 238 within the message transformation pipeline
  • Files: litellm/llms/oci/chat/generic.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR BerriAI#17159: OCI Inline PEM Key Normalization

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: load_private_key_from_str helper defined at line 116, called at line 317 in the credential loading path. Error message with validation at line 310.
  • Files: litellm/llms/oci/common_utils.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #[38,58]: Snowflake Streaming + Tool Calling

  • Decision: 🔧 Customized Upstream (high confidence)
  • Reason: Upstream refactored to Anthropic Messages API format. CARTO's SnowflakeStreamingHandler (line 560) and _extract_system_and_messages (line 219) are preserved. tool_choice auto-detection logic present at transformation.py:228.
  • Files: litellm/llms/snowflake/chat/transformation.py, litellm/responses/litellm_completion_transformation/transformation.py
  • Recommendation: Correct decision - upstream base with CARTO enhancements merged

PR #[]: Snowflake Full URL Passthrough

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: CARTO comment and conditional present at line 158-160 checking for full cortex URL in api_base
  • Files: litellm/llms/snowflake/chat/transformation.py
  • Recommendation: Correct decision - CARTO functionality fully preserved

PR #70: Azure URL Suffix Stripping

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: Regex pattern at line 279 strips operation suffixes from Azure deployment URLs. Pattern includes all required operations: chat/completions, completions, embeddings, audio/speech, audio/transcriptions, images/generations
  • Files: litellm/llms/azure/common_utils.py
  • Recommendation: Correct decision - CARTO functionality fully preserved

PR #54: JSON Repair for Streaming Tool Calls

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _validate_and_repair_tool_arguments function defined at line 129 and called at lines 507 and 549 in the streaming chunk builder
  • Files: litellm/litellm_core_utils/streaming_chunk_builder_utils.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #16: Redis Session Storage

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _store_session_in_redis method defined at line 1106 in streaming_iterator.py, called at line 901. _patch_store_session_in_redis static method defined at line 2135 in transformation.py, called at line 1135
  • Files: litellm/responses/litellm_completion_transformation/streaming_iterator.py, litellm/responses/litellm_completion_transformation/transformation.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #111: Snowflake Cortex Claude Function-Calling Follow-up Turns

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _strip_openai_annotations function defined at line 53, called at lines 273 and 298 within _extract_system_and_messages to strip annotations from content
  • Files: litellm/llms/snowflake/chat/transformation.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #112: Snowflake Cortex Array Content Flattening

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: Initially INCORRECTLY_DROPPED during conflict resolution (function defined but not called). Fixed in commit 00acb9b which added _flatten_messages_content helper and wired it into _transform_request_openai at line 351
  • Files: litellm/llms/snowflake/chat/transformation.py
  • Recommendation: NOW CORRECT after fix commit - was initially orphaned, subsequently wired

PR #[109,110]: Databricks Empty Tool Call Arguments Normalization

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _normalize_empty_tool_call_arguments function defined at line 150, called at line 479 in transform_request. Also includes streaming name-chunk normalization at line 741
  • Files: litellm/llms/databricks/chat/transformation.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

PR #110: Databricks Strip OpenAI Annotations

  • Decision: 🔒 Preserved CARTO (high confidence)
  • Reason: _strip_openai_annotations function defined at line 181, called at line 478 in transform_request message processing loop
  • Files: litellm/llms/databricks/chat/transformation.py
  • Recommendation: Correct decision - CARTO functionality fully preserved and wired

Analyzed: 2026-08-27 23:05:35 UTC
Workflow Run: #44
Analysis Artifacts: Download JSON/MD
Method: Claude Code (Opus 4.5) post-resolution semantic analysis

devin-ai-integration Bot and others added 30 commits August 13, 2026 20:26
… CheckBatchCost

A managed batch whose request lines all failed can reach a terminal provider
status (completed) with output_file_id=None and only an error_file_id. Such a
row matched neither the completed-with-output billing branch nor the
failed/expired/cancelled branch, so batch_processed stayed False and the poller
re-selected it on every cycle for the lifetime of the deployment; output/error
file deletion is also gated on batch_processed, so those files could never be
deleted.

Broaden the terminal handling so a completed/complete/expired batch with an
output file is billed, and any terminal batch with nothing to bill
(failed/cancelled, or completed/expired with no output) is marked terminal
exactly once. Non-terminal statuses (validating/in_progress) are still left for
the next poll, and an expired batch that did produce output is now billed.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…provider

fix(ui): add nvidia riva to the model provider list
…summary

fix(scripts): end make check with a ran/skipped summary and verdict
…I#36660)

* fix(proxy): track spend for OpenAI passthrough /v1/embeddings

OpenAI passthrough embeddings returned 200 but wrote no spend because the
route was unsupported and Cohere's /v1/embed prefix stole the match.

* fix(proxy): clear embeddings lint and Greptile comment nits

Inline embeddings cost tracking to avoid new LIT001/002 hits, trim
redundant doc comments, and cover the Cohere /v1/embeddings collision.

* fix(proxy): drop unreachable embeddings TypeError guard

convert_to_model_response_object with response_type=embedding already
returns EmbeddingResponse; the isinstance check was dead patch coverage.
…itellm_lit002_typeddict_dict_literals

# Conflicts:
#	type-discipline-budget.json
request_id is the primary key of LiteLLM_SpendLogs and the flush inserts with
skip_duplicates, so a spend log whose id already exists is dropped with no error
raised and a "processed 1 spend log" line still logged. Batch cost accounting
produced exactly such an id twice over, and on a proxy with message redaction
enabled no batch cost row could be written at all.

get_spend_logs_id derived the id by md5-hashing the response for two call types,
aretrieve_batch and acreate_file. Redaction makes that hash a constant:
perform_redaction returns the fixed {"text": "redacted-by-litellm"} placeholder
for any shape it cannot redact, which is what a batch object and a file body both
become, so every such row hashed to md5('{"text": "redacted-by-litellm"}') =
00fcbef15a3b0097e14b0ca016ed30a0 regardless of provider, user, or amount. The
first row to claim that id owned it and every later row was discarded. Verified
against a live proxy: four payloads spanning two providers and three distinct
spend values all computed that id, and the table held one acreate_file row dating
to 2025-05-25, the row that had claimed it.

Keying off the batch's own identity instead is necessary but not sufficient,
because creating a batch already writes an acreate_batch row under exactly that
id, so the cost row becomes a duplicate of the batch's own creation row. Also
verified live: after the hash was removed the poller computed and flushed a
batch's cost, and the only row carrying that id was the acreate_batch row from
when the batch was submitted.

The id now comes from the response's own id, then the standard logging payload's
id, then litellm_call_id, and a batch cost row is namespaced with a _batch_cost
suffix so it cannot collide with the creation row. The middle term is what keeps
this correct under redaction: that payload is built from the unredacted response,
so it still carries the batch id after redaction has flattened the body. Keying
the cost row to the batch rather than to the call also keeps accounting the same
batch twice collapsing to one row instead of billing it twice. Every other call
type still derives its key exactly as before.

Cost and usage themselves are unaffected by redaction: the token columns fall back
to the standard logging payload and spend comes from its response_cost, neither of
which redaction touches. generate_hash_from_response had no other caller and is
removed with it.
…uted and cost-poller paths

get_configured_s3_bucket_name accepts the output bucket only from the immutable
_litellm_internal_model_credentials snapshot or AWS_S3_BUCKET_NAME. That refusal to read
litellm_params is deliberate: the bucket is what validate_managed_cloud_file_id checks a
file id against, so trusting a request-supplied value would let a caller redirect reads
to a bucket of their choosing

Two live entry points reach the Bedrock file-content transformation without ever building
that snapshot. The managed-files pre-call hook sets data["model"] for any id carrying
llm_output_file_id, which is every batch output, so get_file_content always takes the
model-routed branch; that branch called llm_router.afile_content directly, and
managed_files_obj.afile_content, the only caller that built the snapshot, is therefore
unreachable for batch output. CheckBatchCost spread the deployment credentials as plain
kwargs, and get_litellm_params does not carry s3_bucket_name across (gcs_bucket_name is
listed for exactly this reason, its S3 counterpart is not), so the poller lost the bucket
the same way

The result was that every completed Bedrock managed batch failed files.content with
"S3 bucket_name is required" and never had its cost tracked, leaving the row to be
re-polled every cycle. Both paths now resolve the deployment credentials and pass the
same MappingProxyType snapshot the managed-files hook already builds
The mock merged every call into one shared dict, so a second routed retrieval would
overwrite the first and the assertions would still pass. Keep one frozen snapshot per
call and assert exactly one call, which also makes an unintended second retrieval a
failure rather than something the merge hides
…ccounting path too

A third path reads a completed batch's output file, and it could not resolve the
bucket either. When cost is accounted from the retrieve itself rather than from
the poller, the batch success handler calls _handle_completed_batch, which fetches
the output file through _extract_file_access_credentials. That helper forwarded a
whitelist covering Azure and Vertex, gcs_bucket_name included, but nothing for
Bedrock, and retrieve_batch built its litellm_params through get_litellm_params,
whose fixed signature drops the trusted credential snapshot. So the snapshot never
reached the file read and it failed with "S3 bucket_name is required" for a bucket
the deployment had configured, leaving the batch's cost unrecorded.

Adding s3_bucket_name to that whitelist would not have worked. The Bedrock file
config deliberately resolves the bucket only from the immutable server-side
snapshot or the environment, never from a request param, because the bucket is
what managed file ids are validated against. The snapshot is therefore what has to
flow, exactly as it already does for the model-routed and cost-poller paths.

retrieve_batch now re-adds the snapshot after get_litellm_params, the same way the
file operations already do, the whitelist forwards it, and the proxy attaches it
for router-routed managed batches from the deployment behind the unified id.
Verified against a live proxy reading a real completed Bedrock batch: the cost row
appears within seconds of the retrieve carrying the batch's real spend and usage,
where before the read raised and no row was written.

Resolving those credentials is best effort. A batch whose deployment no longer
resolves, which happens when a model group is removed while batches are in
flight, still serves its status instead of failing the request on the lookup.
This matters for the OSS and polling-disabled configurations, where the retrieve
path is the only thing that accounts for a batch at all.
…all paths

The helper that carries the credential snapshot into litellm_params lived private
in files/main.py, and the batch retrieve needed it too. It now sits beside
get_litellm_params, which is what it augments, so neither caller reaches into the
other's private surface. Typed as Mapping/MutableMapping of object rather than
Any, which the strict import rules ban.

The file-content route builds the snapshot through the same helper as the batch
route instead of assembling a conditional mapping inline, which drops two mutable
constructions and leaves one way to attach it. Its name loses the batch suffix now
that both routes use it.
Two components computed a managed batch's cost and each assumed it was the only
one. Retrieving a batch computed it through the @client decorator's success
callback, and CheckBatchCost computed it on its own schedule. Whichever observed
completion first decided the outcome, so cost was either counted once per
retrieve or not at all.

The lockout is the worse half. Retrieving a batch that had reached completion set
batch_processed=True, which is what takes a batch out of CheckBatchCost's queue,
since it selects batch_processed=False. That write claimed the cost had been
accounted for on behalf of a callback that had not run yet and was not awaited.
When the callback then failed the cost was gone permanently, with the poller
already retired and no retry left. Observed on a live proxy: two completed
batches whose callbacks raised inside the logging worker, one on a provider
output path that did not resolve and one on a batch whose output file id was
still None, both left marked processed with no spend row and no way to recover
them. Nothing logged at error level for the batches themselves.

The over-count is the other half. Nothing suppressed recomputation, so each
retrieve of an already-completed batch recorded that batch's full cost again. A
caller polling its own batch to see whether it had finished inflated spend by
however many times it looked.

The flag now means what its name says, and only the component that actually
recorded the cost sets it. When the poller is running it owns accounting, so
retrieving a managed batch records no cost and leaves the flag alone; the poller
computes once and sets it. When the poller cannot be relied on, either because
polling is disabled by config or because the enterprise job never registered,
the retrieve path is the only accountant and behaves exactly as before. Batches
with no managed object row are untouched either way, since neither the flag nor
the poller queue applies to them.
…ches done

The handoff asked whether the poller was running, when what matters is whether it
will actually account for the batch. Those differ on a schema without the
batch_processed column: the poller cannot filter on it, so it falls back to a
query that excludes complete and completed rows, and it cannot set it either. A
caller retrieving a provider-completed batch before the poller saw it therefore
suppressed inline accounting, then marked the row complete, and the fallback query
could never find it again. Nobody accounted for that batch, so its cost escaped
the caller's budget entirely.

The poller now publishes batch_processed_support_confirmed, set only once a
filtered query has actually succeeded, and the handoff requires it. Defaulting to
unconfirmed keeps accounting on the retrieve path in exactly the cases the poller
would drop the batch, including the window before the poller's first cycle. All
four combinations account exactly once: unconfirmed leaves the retrieve
accounting and setting the marker, whether or not the column exists, and
confirmed is only reachable when the column is present, where the poller accounts
and sets it.

A scheduler that hands back something other than a bound method leaves no poller
to interrogate, which reads as unconfirmed rather than as working.
The ownership question was asked twice for one retrieve: once before the provider
call to decide whether to suppress inline accounting, and again afterwards to
decide whether to mark the batch accounted. Between those two points the poller
can complete its first successful filtered query and become usable, so the two
answers disagree. The retrieve then accounts for the batch inline, having decided
the poller was unusable, while the later check sees a usable poller and leaves the
marker unset, so the poller accounts for the same batch again and its spend is
counted twice.

The retrieve now decides once and passes that decision to
update_batch_in_database, which prefers it over re-deriving one. Callers that
record no cost of their own leave it unset and keep deriving it as before, so the
cancel path is unchanged.
Both are covered on GitHub Actions. test-litellm-ui-build.yml runs the
dashboard build on every PR, and test-litellm-ui-unit.yml runs the vitest
suite with ui-unit-tests already a required check, so neither CircleCI job
gates anything that GHA does not already gate.

ui_build additionally produced nothing anyone consumed. It persisted
litellm/proxy/_experimental/out to the workspace, and the only job
downstream of it was ui_unit_tests, which never attached the workspace and
reinstalled from source instead. The requires edge was pure sequencing, so
the build output was written and discarded on every client-touching PR.

One real narrowing comes with this, and it is deliberate. ui_unit_tests ran
the full vitest suite on PRs, while the GHA job scopes PR runs to tests
reachable from the diff and keeps the full suite on pushes to staging. That
split was a measured decision in BerriAI#34175 and it still holds: the suite is
252s and 248s of that is CreateMCPServer.integration.test.tsx alone, so
running everything per PR buys about four minutes to re-run one file.

Note that assert-ci-coverage does not speak to this. It walks
tests/**/test_*.py only, so it is blind to vitest files by construction;
it stays green here because no Python test lost a runner, which is a
narrower claim than the UI side being unaffected.

auth_ui_unit_tests is a different job, a Python suite on a Postgres
sidecar, and is untouched
…ayload (BerriAI#36744)

Request metadata carries the whole UserAPIKeyAuth object, whose team_metadata
holds the customer's own langfuse callback_vars. The only filter on the emitted
blob was a four key deny list written as a circular reference crash guard, so
those credentials reached the customer's own langfuse traces.

The emitted blob is now the StandardLoggingPayload allowlist plus the litellm
computed enrichments, and nothing is copied across from raw request metadata.
That makes the credential exclusion structural rather than a filter someone has
to keep correct. Steering keys keep reading raw metadata, matching literal_ai.

Proxy callers are unaffected: their request metadata already rides under the
allowlisted requester_metadata key, nesting intact.

debug_langfuse dumped raw request metadata into the trace as a second copy of
the same leak. It now emits caller scalars only.

When StandardLoggingPayload is absent the trace is still emitted with the
existing trace_id fallback, so failure traces survive.
Replaces Ant Design with the in-repo shadcn layer across every Navbar
component, removing the last antd imports from src/components/Navbar.

- CommunityEngagementButtons, NotificationsBell, ViewSwitcher, BlogDropdown,
  WorkerDropdown and UserDropdown now compose @/components/ui primitives
- antd icons render at 1em while lucide defaults to 24px, so every icon
  carries an explicit size class matching what it replaced
- UserDropdown uses Popover rather than DropdownMenu: its panel holds
  switches and badges, and form controls inside role="menu" are invalid
- WorkerDropdown moves to Combobox since shadcn Select has no search
- drops the nine no-restricted-imports suppressions these files no longer need
Replaces Ant Design across every source file under src/components/view_logs,
so the request log drawer and its viewers compose @/components/ui primitives.

- Drawer becomes Sheet, Collapse becomes Collapsible, Segmented and Radio.Group
  become Tabs, Tag becomes Badge, Descriptions becomes a local grid helper
- every lucide icon carries an explicit size class, since antd icons render at
  1em while lucide defaults to 24px
- two tests dropped assertions on antd internal class names in favour of
  rendered text and roles, and the Pretty/JSON case now proves the toggle
  actually swaps the body rather than only that both controls render
- drops the eslint suppressions these files no longer need
## TLDR

Signed-off-by: Ishaan <ishaangupta0408@gmail.com>
Replaces Ant Design and Tremor across src/components/AIHub, so the model,
agent, MCP and skill hub views compose @/components/ui primitives.

- Modal becomes Dialog, tremor TabGroup becomes Tabs, tremor Card and Table
  become their shadcn counterparts, and Tag and tremor Badge become Badge
- the three publish forms wrapped antd Form around zero Form.Item fields, so
  the wrapper became a div and the dead useForm and resetFields calls went
  with it, rather than pulling in react-hook-form for a form with no fields
- antd Steps has no shadcn equivalent, so each form inlines a small ol stepper
- cells holding model names, server ids and URLs gained min-w-0 and break-words
  so a long value cannot bleed into the neighbouring column
- the three form tests dropped assertions invented by their antd mocks in
  favour of roles and rendered text
- drops the eslint suppressions these files no longer need
The panel holds switches and ordinary buttons rather than menu items, so
menu semantics promised keyboard behavior it does not provide.
Screen readers announced an unnamed dialog. The visible header is a custom
layout, so the title is visually hidden to keep the drawer layout unchanged.
Replaces Ant Design and Tremor in the six shared components under
src/components/common_components, which between them are reached by
nine routes.

- antd Table becomes the ui/table primitives, and the Actions column keeps
  antd's fixed: "right" behaviour via a sticky cell
- Tremor Icon, Text and Badge become a plain span, p and StatusBadge
- antd Tooltip and Typography copyable become the shadcn Tooltip and the
  shared CopyButton
- every public prop signature is unchanged, since these are shared components
  and a renamed prop would break callers far from this folder
- two tests dropped assertions on antd internal class names and on DOM
  structure, and gained cases proving a disabled action does not fire onClick

MemberTable keeps a type-only import of antd's ColumnsType because a consumer
annotates its own column array with it. No antd code ships from the file.
The migration closed a double submit hole that antd left open, but the
rewritten tests only proved the flow had not completed, so removing the
guard would not have failed them. Verified by mutation: dropping
disabled={loading} fails exactly this case.
Replaces Ant Design and Tremor in the key info header and detail view, the
agent and vector store permission panels, and the team member permissions
table.

- antd Popover, Dropdown and Modal become HoverCard, DropdownMenu and Dialog,
  and Tremor TabGroup becomes Tabs with keepMounted so panel state survives
  a tab switch the way Tremor's did
- the key id copy control moves to the shared CopyButton, which also fixes an
  icon that rendered at 24px because it inherited the heading font size
- antd Checkbox onChange becomes onCheckedChange
- every public prop signature is unchanged, since these are shared views
- three member permission tests were passing vacuously: they searched for an
  unchecked box by reading .checked, which is undefined on a Base UI checkbox,
  so the assertions sat inside an if that never ran. They now scope the
  checkbox to its own row and assert the toggle, the save and the revert
- drops the eslint suppressions these files no longer need
…tremor

Replaces Ant Design and Tremor in the fallbacks views, the router general
settings panel, and the two shared banner and badge components.

- Tremor Card, Table and Icon become the ui/card, ui/table and lucide
  equivalents, reproducing Tremor's icon box so click targets keep their size
- antd Alert becomes a composed role="alert" region, since the shadcn CLI's
  alert pulls in class-variance-authority, which this repo does not have
- antd InputNumber becomes a native number input, and Switch onChange becomes
  onCheckedChange
- shadcn TableCell ships whitespace-nowrap where Tremor's did not, so cells
  holding model names and setting descriptions get whitespace-normal back
- adds a DeprecationBanner test covering naming, the link, and dismissal,
  proven against the antd version first and mutation checked
- drops the eslint suppressions these files no longer need
mateo-berri and others added 14 commits August 15, 2026 16:55
…slot_locks

docs(claude): tell agents to let heavy gates queue for machine-wide slots
…ine-triage-9b92e5

test: unstick the suites CircleCI is failing on
…of_format

docs(github): proof-of-fix section shows only the latest run as Before/After with nested cases
…bed6

test(e2e): assert provider error shape instead of pinned prose
fix(ui): de-duplicate the reset budget option and polish shadcn surfaces
…ld-1e15d9

chore: rebuild Admin UI bundle from litellm_internal_staging
The log details drawer moved off Ant Design in 03d2b16, so its section
header renders lucide ChevronUp/ChevronDown rather than antd's UpOutlined
and DownOutlined. The collapse test still waited on .anticon-up and
.anticon-down, which no longer exist anywhere under view_logs, so it
failed on every run and burned all three attempts identically.

Point the three assertions at .lucide-chevron-up and .lucide-chevron-down,
matching how the dashboard's other suites address lucide icons.
…ser-93a2a1

test(e2e/ui): assert the log drawer chevrons by their lucide classes
chore(ci): promote internal staging to main
Automatic sync from upstream BerriAI/litellm tag v1.97.0

Strategy: Merge with tree-level conflict resolution (accepted all upstream changes)
Conflicts resolved: 13 files (3 rename/rename, 4 modify/delete, 4 rename/delete, 0 content)
…(backport to rc/1.98.0) (BerriAI#37955)

* fix(ui): keep completion-mode models in the playground chat dropdown (BerriAI#37954)

PR BerriAI#36130 added a KNOWN_MODEL_MODES guard to isModelCompatibleWithEndpoint
that hides any model whose mode isn't in the ModelMode enum, to keep
rerank/ocr/batch/etc. models out of chat-style endpoints. mode: completion
(legacy text-completion models) wasn't in that enum, so it got caught by
the same guard and disappeared from every endpoint, including chat, where
it routes fine.

Add ModelMode.COMPLETION and map it to EndpointType.CHAT like the other
chat-compatible modes.

(cherry picked from commit 1c421f3)

* chore: update Next.js build artifacts (2026-08-22 18:42 UTC, node v24.19.0)
Automatic sync from upstream BerriAI/litellm tag v1.98.0

Strategy: Merge with history preservation (main syncs to stable tag)
@Cartofante Cartofante added upstream-sync automated ignore-semantic-pull-request Exempt from Conventional PR Title check (e.g. auto-generated upstream-sync PRs) labels Aug 27, 2026
@github-actions

Copy link
Copy Markdown

No description provided.

@Cartofante

Copy link
Copy Markdown
Collaborator Author

🤖 Conflict Resolution Started

Status: ⏳ In progress...

Claude Code (Opus 4.5) is resolving merge conflicts in this PR.

Step Status
🔍 Analyze conflicts In progress
✏️ Resolve files Pending
🧪 Run tests Pending
📌 Push to PR Pending

Note

This may take 30-90 minutes for large PRs. Resolution commits will be pushed directly to this PR.

📋 Resolution Process (click to expand)
  1. 🔍 Analyzing conflicts in all files
  2. 📋 Reading CARTO customization guidelines
  3. ⚖️ Applying resolution priorities (CARTO first, then upstream)
  4. ✏️ Resolving conflicts file by file
  5. 🧪 Running tests (lint, mypy, unit tests)
  6. 📌 Pushing resolution directly to this PR

View workflow run →

Conflicts resolved by Claude Code following CARTO priority rules.

Resolution strategy:
- Preserved CARTO customizations (workflows, docs, infrastructure)
- Accepted upstream improvements (core litellm, tests, dependencies)
- Manually merged mixed files (Dockerfile, Makefile)

This is a MERGE COMMIT with both main and carto/main as parents,
preserving full git history from upstream.

Resolves: #132
@Cartofante

Copy link
Copy Markdown
Collaborator Author

✅ Conflict Resolution Complete

All conflicts resolved and pushed to this PR.

Step Status
🔍 Analyze conflicts ✅ Complete
✏️ Resolve files ✅ Complete
🧪 Verify resolution ✅ Complete
📌 Push to PR ✅ Complete

Important

Ready to merge! Use "Create a merge commit" — do NOT squash or rebase.


CARTO Customization Decisions

Summary

Decision Count
Upstream Substitutes 0
Preserved CARTO 8
Merged/Customized 3
Synced (required) Many

Preserved CARTO

Files where CARTO implementation was kept:

  • litellm/llms/databricks/chat/transformation.py - Contains _normalize_empty_tool_call_arguments and _strip_openai_annotations functions
  • litellm/llms/snowflake/chat/transformation.py - Contains _strip_openai_annotations, _content_to_text_string, full URL passthrough
  • litellm/responses/litellm_completion_transformation/streaming_iterator.py - Contains Redis session storage (_store_session_in_redis)
  • litellm/responses/litellm_completion_transformation/transformation.py - Contains _patch_store_session_in_redis and _patch_get_session_from_redis

Merged/Customized

Files where both sources were combined:

  • litellm/llms/azure/common_utils.py - Kept upstream with re-added URL suffix stripping regex pattern
  • litellm/llms/oci/chat/generic.py - Kept upstream with added _reorder_tool_results_to_match_tool_calls function
  • litellm/litellm_core_utils/streaming_chunk_builder_utils.py - Kept upstream with added _validate_and_repair_tool_arguments function

Synced (Required)

Files synced entirely from upstream:

  • ui/ - Entire UI directory synced to avoid component mismatch errors
  • .github/pull_request_template.md - Synced to upstream
  • CLAUDE.md - Synced to upstream
  • All test files - Synced to upstream to fix conflict markers
  • All non-CARTO feature files - Synced to upstream

Fix Loop Interventions

Files synced due to repeated conflicts:

  • None

Next Steps

  1. Review the CARTO customization decisions above
  2. Merge using "Create a merge commit"
  3. 🎉 Upstream sync complete!
🔧 Workflow Details (click to expand)

Workflow Run: https://github.com/CartoDB/litellm/actions/runs/33120932726

@Cartofante

Copy link
Copy Markdown
Collaborator Author

Caution

⚠️ Merge Method Reminder

Use "Create a merge commit" — Click the dropdown arrow next to merge button.

❌ Do NOT use "Squash and merge" or "Rebase and merge"

Squashing destroys upstream history and breaks future syncs (see PR #26).

@Cartofante

Copy link
Copy Markdown
Collaborator Author

📊 CARTO Feature Analysis Started

Mode: Analysis + Auto-fix if issues found

Analyzing how each CARTO customization was handled during conflict resolution.
This will explain WHY each decision was made.

Step Status
Extract CARTO PRs ⏳ In progress
Compare code versions ⏳ Pending
Analyze decisions ⏳ Pending
Generate report ⏳ Pending

View workflow →

@Cartofante

Copy link
Copy Markdown
Collaborator Author

⚠️ CARTO Feature Analysis - Attention Needed

Decision Count
Upstream Substitutes 0
Customized Upstream 0
Preserved CARTO 11
Incorrectly Dropped 1

Overall Assessment: NEEDS_ATTENTION

🔧 Auto-fix enabled: The fix job will run next to restore dropped features.

📋 Full details in PR description above.


View workflow run → | Download analysis artifacts →

@Cartofante

Copy link
Copy Markdown
Collaborator Author

🔧 CARTO Feature Fix Started

Restoring 1 incorrectly dropped CARTO feature(s).

View workflow →

Restores PR #112 functionality lost during upstream sync conflict resolution.
The _content_to_text_string function was defined but never called, leaving it
as dead code. Snowflake Cortex /chat/completions rejects array-form content
with error 390142; the OpenAI Agents SDK replays prior assistant turns in that
shape, breaking multi-turn conversations on the second turn.

Added _flatten_messages_content helper and wired it into _transform_request_openai
to flatten list-form content to strings before sending to Cortex
@Cartofante

Copy link
Copy Markdown
Collaborator Author

🔧 CARTO Feature Fix Complete

Summary

Restored 1 CARTO feature (PR #112) by wiring the orphaned _content_to_text_string function into the OpenAI transformation path. The function flattens array-form message content to plain strings, preventing Snowflake Cortex error 390142 on multi-turn conversations.

Decisions Made

PR #112: fix(snowflake): flatten array-form message content for Cortex

Decision: The upstream sync completely restructured the file from a single _transform_messages method to dual-endpoint routing (Anthropic vs OpenAI). The _content_to_text_string function was preserved but the call sites were lost. Since Anthropic format natively accepts array content, the fix was applied only to the OpenAI path (_transform_request_openai) which routes to /chat/completions. Added a helper method to iterate messages and flatten list-form content to strings before sending to Cortex.
Files Modified: litellm/llms/snowflake/chat/transformation.py
Verification: Syntax check passed. AST analysis confirms _content_to_text_string is now called at line 335.


Next Steps:

  1. Review the changes in the Files tab
  2. CI will re-run automatically
  3. Once CI passes, the sync-ready label will be added

View full workflow logs →

@Cartofante

Copy link
Copy Markdown
Collaborator Author

📊 CARTO Feature Analysis Started

Mode: Analysis + Auto-fix if issues found

Analyzing how each CARTO customization was handled during conflict resolution.
This will explain WHY each decision was made.

Step Status
Extract CARTO PRs ⏳ In progress
Compare code versions ⏳ Pending
Analyze decisions ⏳ Pending
Generate report ⏳ Pending

View workflow →

@Cartofante

Copy link
Copy Markdown
Collaborator Author

✅ CARTO Feature Analysis Complete

Decision Count
Upstream Substitutes 0
Customized Upstream 1
Preserved CARTO 11
Incorrectly Dropped 0

Overall Assessment: PASS

📋 Full details in PR description above.


View workflow run → | Download analysis artifacts →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated ignore-semantic-pull-request Exempt from Conventional PR Title check (e.g. auto-generated upstream-sync PRs) sync-ready upstream-sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.