The S3 Vectors ingestion embedded every chunk with the request's
embedding.model or the default, never the embedding_model the store was
registered with, while search on the same store embeds with the
registered model. A registered store uploaded to by id alone therefore
embedded with the wrong model and AWS rejected the vectors on the
dimension mismatch. The store's embedding model now wins for S3 Vectors
ingestion through a helper next to the one search already uses
The ingest-side bucket and index precedence now sits next to the shared
store id split instead of under litellm/rag/, where provider-specific
parsing does not belong.
Hosted Responses API tools with no Chat Completions equivalent were forwarded
verbatim, so Codex 0.140+ got a 400 from the provider on every turn. The bridge
now drops tool_search and local_shell the same way it drops computer_use,
image_generation, and shell, and also drops parallel_tool_calls when no chat
tools remain, since chat completions only accepts it alongside tools
A "bucket:" or ":index" id split into an empty name, so ingestion silently
generated a fresh index and search sent the empty name to AWS. Both sides now
raise the existing format error through the shared helper.
A registered S3 Vectors store usually carries only its "bucket:index" id,
and the previous commit stopped forwarding the caller's bucket and index for
a managed store, so ingesting into one raised KeyError 'vector_bucket_name'.
The ingestion now derives both from vector_store_id with the rule the search
side already uses, explicit keys still winning. The caller's
litellm_credential_name is dropped for a managed store too, since it expands
into api_key and api_base, and max_embedding_requests_per_min joins the
per-upload options a caller may still set.
The cost tracking callback f-stringed chosen_metadata, litellm_metadata,
and old_metadata into the failed_tracking_spend alert on every failure,
at every log level, so one 250-byte request produced a 23 KB alert
carrying the client's metadata, headers, and key-auth reprs four times
over. The alert now carries the exception, the traceback, the model, and
the call type; the metadata keys are logged once at debug level through
lazy formatting, so nothing is built at warning level
With store_prompts_in_spend_logs on, the persisted request body kept the client's model string even when the row's model, model_group, and error text had been replaced by the unknown-model placeholder. The body's model now takes the same placeholder on those rows. Also annotates the new test locals with Final and wraps the four test lines that ran past 120 characters.
Guardrails created through POST /guardrails on older releases have api_version "v1" saved in the database, because the writer persists every default. Azure Content Safety never accepts that value, so those guardrails kept answering 404 after the default moved to None. The Azure base now resolves "v1" to 2024-09-01 the same way it resolves a missing value. Also restores the OpenAPI snapshot line that a Python 3.14 regeneration had dedented
When a deployment priced one OCR batch family and the other still needed a
published rate, a failed cost-map lookup returned zero for the whole line and
discarded the deployment rate that was already resolved. Those pages were
billed as free. The lookup failure now only logs, and the families the
deployment prices are billed at the configured rate
LitellmParams mixes every provider config model into one class, so the
Javelin api_version default of "v1" reached the Azure Content Safety
guardrails whenever config.yaml omitted api_version and Azure answered 404.
The shared field now defaults to None, Javelin keeps filling in "v1" itself,
and the Azure guardrails fall back to the documented 2024-09-01 at request
time so a DB update that omits api_version stays on the default too.
POST /v1/rag/ingest authorized the managed vector store the request
named but then handed the raw request options to the ingestion
pipeline, which defaults to OpenAI. A request naming only a registered
store id uploaded the document to OpenAI Files, got an OpenAI 400, and
answered HTTP 200 with status "failed"; naming azure_ai explicitly
escaped as a 500.
The store's provider and litellm_params now merge into the request the
way /v1/rag/query already does (store wins, None values dropped), the
merged provider is checked against the ingestion registry before any
upload so unsupported providers get a 400 naming the supported ones, and
persistence keeps reading the caller's original options so registry
credentials never reach the database. A registry store with no database
row is no longer written as a new row.
The router coverage gate in code-quality flags every router.py function
no router test calls by name, and the two helpers get_credential_deployment
gained (the team public-name lookup and the team-aware wildcard lookup)
were only reached through it. Each now has a test of its own: the
public-name lookup resolves only for the owning team, and the wildcard
lookup prefers the team's own pattern over the shared one and never hands
another team's wildcard deployment to a caller outside that team.