Commit graph

8758 commits

Author SHA1 Message Date
Sameer Kankute
72fc2f77cf
Merge pull request #23252 from BerriAI/main
merge main
2026-03-10 17:24:19 +05:30
Sameer Kankute
3f17a63b81
Merge branch 'main' into litellm_oss_staging_03_02_2026 2026-03-10 17:19:37 +05:30
Harshit28j
631aefea18 fix: req changes on feedback from greptile 2026-03-10 16:46:18 +05:30
Harshit28j
212059cd11 fix: presidio improvements 2026-03-10 16:33:41 +05:30
Harshit28j
12de8a724f fix: clean approach instead of UUID 2026-03-10 15:51:11 +05:30
Harshit28j
14ecc79760 fix: req changes greptile hallucinates 2026-03-10 15:51:11 +05:30
Harshit28j
1bfd88a33c fix: req changes 2026-03-10 15:51:10 +05:30
Harshit28j
861db111a0 fix: presidio edge case with antropic handle on pii token leak 2026-03-10 15:51:10 +05:30
Sameer Kankute
0122abfa2b fix(bedrock): populate completion_tokens_details in converse _transform_usage
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-10 13:17:28 +05:30
Harshit28j
afb117f5d3 fix: req changes from greptile 2026-03-10 13:03:29 +05:30
Harshit28j
b731e432f8 feat: record silent metrics 2026-03-10 12:54:32 +05:30
Sameer Kankute
f33f446476
Merge pull request #23240 from BerriAI/litellm_bedrock-nova-output-config-spend-tracking
fix(bedrock): strip output_config from Converse requests; fix spend tracking redaction test
2026-03-10 12:15:06 +05:30
yuneng-jiang
b8886568f0
Merge pull request #23236 from BerriAI/litellm_/flamboyant-leakey
[Fix] All Proxy Models Not Including Model Access Groups in Key Creation
2026-03-09 23:27:38 -07:00
Sameer Kankute
200b001633 fix(bedrock): strip output_config from Converse requests; fix spend tracking redaction test
Made-with: Cursor
2026-03-10 11:53:36 +05:30
Sameer Kankute
db99fdeff3 fix(mcp): OpenAPI tool listing and execution for relative URLs and camelCase
- Fix case-insensitive tool name matching in _tool_name_matches() so that
  OpenAPI operationIds (camelCase) match lowercase registered tool names
  when filtering by allowed_tools
- Fix get_base_url() to resolve relative server URLs (e.g. /api/v3) by
  deriving full base URL from spec_path when OpenAPI spec has relative URLs
- Add tests for case-insensitive matching and filter_tools_by_allowed_tools

Made-with: Cursor
2026-03-10 11:34:23 +05:30
yuneng-jiang
1755a281bd Fix mutation bug: copy lists in get_key_models to prevent corrupting cached UserAPIKeyAuth
`all_models = user_api_key_dict.models` was creating an alias, so
`_get_models_from_access_groups` (which uses `.pop()`/`.extend()`) would
mutate the cached object in-place. Now both `.models` and `.team_models`
assignments create copies via `list()`.

Added test to verify the input is not mutated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:55:53 -07:00
yuneng-jiang
1cf191d9ad [Fix] Deduplicate model lists and remove dead assignment
Adds dedup to get_key_models and get_team_models to prevent duplicate
entries when access group member models overlap with proxy_model_list.
Removes dead assignment of all_models in get_team_models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:44:45 -07:00
yuneng-jiang
c829733200 [Fix] Include model access groups when expanding All Proxy Models
When a team has "all-proxy-models", the model list expansion now includes
model access group names so they appear in the UI key creation form.
Also fixes get_key_models not forwarding include_model_access_groups to
_get_models_from_access_groups, and removes unused _unfurl_all_proxy_models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:30:07 -07:00
Sameer Kankute
504e66ccd4
Revert "fix: forward recognized OpenAI params from kwargs in completion() (#2…"
This reverts commit dd6f0d6c55.
2026-03-10 10:22:00 +05:30
Sameer Kankute
c1b860b3c1
Revert "fix: strip empty text content blocks in /v1/messages endpoint (#23097)"
This reverts commit 2c738cc939.
2026-03-10 09:53:19 +05:30
jymmi
33d4b93bee
fix(sagemaker): Add role assumption support for embedding endpoint (#20435)
The SageMaker embedding handler was not using _load_credentials(),
which meant aws_role_name and aws_session_name parameters were
ignored. This prevented cross-account role assumption for embeddings
while it worked for completions.

Changes:
- Replace direct boto3 client creation with _load_credentials() call
- Create boto3.Session with assumed credentials
- Add comprehensive unit tests for role assumption

This aligns the embedding handler behavior with the completion handler,
which already supports role assumption via the BaseAWSLLM.get_credentials()
method.

Fixes cross-account SageMaker embedding access where users need to
assume a role in another account to invoke endpoints.
2026-03-09 20:58:14 -07:00
Krish Dholakia
dd6f0d6c55
fix: forward recognized OpenAI params from kwargs in completion() (#23224)
Any param in DEFAULT_CHAT_COMPLETION_PARAM_VALUES that arrives via
completion(**kwargs) is now automatically forwarded to
get_optional_params(), even if it's not a named parameter of
completion().

Previously, get_non_default_completion_params() excluded params in
OPENAI_CHAT_COMPLETION_PARAMS (assuming they'd be forwarded via the
named-param path), while optional_param_args only contained explicitly
named params. Params like 'store' that were in the known-params list
but not named params fell through both paths and were silently dropped.

The fix adds a 7-line loop after building optional_param_args that
forwards any kwargs present in DEFAULT_CHAT_COMPLETION_PARAM_VALUES.
This means new OpenAI params only need to be added to the constants
dict — no boilerplate changes to 3+ function signatures required.

Fixes #23087

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-03-09 20:56:27 -07:00
mubashir1osmani
23cf360be7 fix: explicit type conversion for prompt caching 2026-03-09 23:53:55 -04:00
tristanolive
30b82c3a0c
feat(charity_engine): add Charity Engine provider (#23223)
* feat(charity_engine): add Charity Engine provider

Charity Engine is a crowdsourced distributed computing platform that
donates processing power to charitable causes. Its inference API
provides OpenAI-compatible chat, completions, and embeddings endpoints.

* test(charity_engine): add provider config and resolution tests

Verify JSONProviderRegistry config, provider list membership,
model routing for charity_engine/<model>, and Router compatibility.

* feat(charity_engine): add Charity Engine to LlmProviders enum

Enables provider_list membership and LlmProviders.CHARITY_ENGINE
resolution required by the provider and test suite.

* fix(charity_engine): remove api_base_env to fix non-deterministic test

The CHARITY_ENGINE_API_BASE env var could override the base_url in CI,
causing test_charity_engine_provider_resolution to fail intermittently.

* fix(charity_engine): remove trailing slash from base_url
2026-03-09 20:46:43 -07:00
Maxwell Calkin
2c738cc939
fix: strip empty text content blocks in /v1/messages endpoint (#23097)
Claude's API returns assistant messages with empty text blocks
({"type": "text", "text": ""}) alongside tool_use blocks during
multi-turn tool-use conversations. These blocks are rejected when
sent back to the API with "text content blocks must be non-empty".

Sanitization already exists for other code paths (/v1/chat/completions
for both Anthropic and Bedrock), but NOT for the /v1/messages native
path. This adds the same treatment by stripping empty text blocks
from messages in async_anthropic_messages_handler before they are
forwarded to the provider.

Fixes #22930
2026-03-09 19:51:25 -07:00
zxshen
9314963697
fix(fireworks): strip duplicate /v1 from models endpoint URL (#23113)
_get_openai_compatible_provider_info already returns an api_base
ending in /v1, but get_models prepended another /v1, producing
.../inference/v1/v1/accounts/... which 404s.

Strip the trailing /v1 from api_base before re-adding it so that
both the default and any user-supplied base work correctly.

Add parametrized tests covering the default URL, trailing-slash,
custom base with /v1, and custom base without /v1.

Fixes #23106
2026-03-09 19:49:58 -07:00
Krish Dholakia
992510154c
Merge branch 'main' into litellm_oss_staging_02_18_2026 2026-03-09 19:48:46 -07:00
Krish Dholakia
9500fc18d1
Fix TypeError: LiteLLM_Params.__init__() got multiple values for argument 'self' (#23220)
The bug occurred when user data inadvertently contained reserved Python
keywords like 'self', 'params', or '__class__' as keys. When such a dict
was unpacked via **kwargs to LiteLLM_Params() or GenericLiteLLMParams(),
Python raised TypeError because 'self' was passed both implicitly and
as a keyword argument.

The fix:
- Add a Pydantic model_validator(mode='before') to GenericLiteLLMParams
  that filters out reserved keys ('self', 'params', '__class__') before
  validation
- Move the max_retries str-to-int conversion into the same validator
- Remove the custom __init__ methods from both GenericLiteLLMParams and
  LiteLLM_Params, since the validator now handles the preprocessing
- Clean up unused VERTEX_CREDENTIALS_TYPES import

This fix applies to all classes that inherit from GenericLiteLLMParams,
including LiteLLM_Params and updateLiteLLMParams.

Added comprehensive tests in tests/test_litellm/test_litellm_params_reserved_keys.py

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-03-09 19:33:52 -07:00
Chesars
2ea2660ba5 feat(images): support input_fidelity parameter for image edit API
Add input_fidelity parameter ("high"/"low") to the image edit pipeline,
allowing users to control how much effort the model exerts to match
input image style and features. Fixes #22813.
2026-03-09 19:51:31 -03:00
mubashir1osmani
7ffaa6f74e fix(tag-usage): remove broken compute_tag_metadata_totals causing cost panel to show 0
The tag daily activity endpoint used compute_tag_metadata_totals which
deduplicates by request_id, but LiteLLM_DailyTagSpend stores aggregated
daily records where request_id is either NULL or stale. This caused all
metadata totals (total_spend, total_requests, etc.) to be 0 in the UI
cost panel. Now uses the same standard totals as every other entity type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:48:27 -04:00
yuneng-jiang
ffd1eb18e0 Merge remote main and resolve conflicts
Kept our sync test fix, accepted upstream's xdist_group marker on
the async handler test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:34:50 -07:00
yuneng-jiang
74ed6a16ac Fix flaky test_watsonx_gpt_oss_prompt_transformation
The test was flaky under pytest-xdist parallel execution because it used
async acompletion (which runs completion() in a thread pool via
run_in_executor) and relied on shared global state (known_tokenizer_config,
iam_token_cache, module_level_client) that could be modified by other tests
running in parallel. Failures were silently swallowed by a broad try/except,
causing mock_post.call_count to remain 0.

Fix:
- Convert from async acompletion to sync completion, matching every other
  test in the file. The test's intent is verifying prompt transformation,
  not async behavior.
- Use monkeypatch.setitem for known_tokenizer_config to ensure proper
  teardown isolation.
- Remove unnecessary mock layers (async template fetchers, iam_token_cache
  pre-population, mock completion response) that were only needed for the
  async code path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:32:30 -07:00
yuneng-jiang
8ecac84789
Revert "feat(proxy): add Prisma DB pool and engine health metrics to Promethe…"
This reverts commit 0bb26c3f1b.
2026-03-09 14:55:11 -07:00
yuneng-jiang
be9d1798b2
Merge pull request #23182 from BerriAI/litellm_/exciting-swanson
[Fix] Model pricing schema test missing output_cost_per_image_token_batches
2026-03-09 14:26:21 -07:00
yuneng-jiang
379ce1aae5 [Fix] Add output_cost_per_image_token_batches to model pricing schema test
The gemini-3.1-flash-image-preview model introduced a new pricing field
that was missing from the test's validation schema and cost_fields list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 14:17:52 -07:00
michelligabriele
c47f77a348
fix(agentcore): handle JSON responses from agents using sync return (#23165)
* fix(agentcore): handle JSON responses from agents using sync return

BedrockAgentCoreApp agents that use synchronous `return` (instead of
async `yield`) respond with Content-Type: application/json instead of
text/event-stream. The streaming parser only handles SSE format, silently
discarding the JSON body and returning empty content to the client.

This adds Content-Type detection in both sync and async streaming
wrappers — when application/json is received, the response is parsed
and converted to a single-chunk stream. Also extends _parse_json_response
with a fallback chain supporting multiple agent response schemas (standard
AgentCore, Strands framework, plain string, raw JSON fallback).

* fix(agentcore): add dict-type guard to _parse_json_response

Prevent AttributeError when json.loads() returns a non-dict
(e.g. JSON array or primitive) by adding an isinstance check
at the top of _parse_json_response. Non-dict values fall back
to raw JSON string content.

* fix(agentcore): handle malformed JSON and split streaming chunks

- Wrap json.loads() in try/except in both sync and async streaming
  wrappers so malformed JSON bodies raise a structured BedrockError
  instead of a raw JSONDecodeError
- Split the JSON-fallback streaming path into two chunks (content
  chunk with finish_reason=None, then stop sentinel with empty delta)
  to match the SSE path convention

* fix(agentcore): catch IO errors in streaming JSON path + async error test

- Broaden except clause to catch both json.JSONDecodeError and IO-level
  exceptions (httpx.ReadError, etc.) from response.read()/aread(), so
  all failures surface as structured BedrockError
- Add async malformed-JSON test to mirror the sync test coverage
2026-03-09 10:22:36 -07:00
yuneng-jiang
c1b3640310 feat: add audit log export to external callbacks (S3, Datadog, etc.)
Audit logs (CRUD events on keys, teams, users, models) were only stored in
the Prisma DB. This adds a pluggable callback system so audit logs can be
forwarded to external services like S3 for ingestion into security monitoring
tools.

New config key `audit_log_callbacks` under `litellm_settings` reuses the
existing callback infrastructure. Any CustomLogger subclass can opt in by
overriding `async_log_audit_log_event()`. S3Logger (s3_v2) is implemented
as the first handler, storing audit logs under `audit_logs/{date}/` prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:48:10 -07:00
Varad Khonde
9b15f639a4
fix(responses): merge parallel function_call items into single assistant message (#23116) 2026-03-09 09:01:31 -07:00
Aarish Alam
e21b06265a
fix fkey violation on deleting user (#23115) 2026-03-09 08:53:11 -07:00
ohadgur
0bb26c3f1b
feat(proxy): add Prisma DB pool and engine health metrics to Prometheus (#22655)
* feat(proxy): add Prisma DB pool and engine health metrics to Prometheus

Add a PrismaMetricsCollector that periodically queries pg_stat_activity
and the Prisma engine process to expose connection pool and engine health
as Prometheus gauges/counters. Auto-enabled when prometheus_system is in
service_callback.

New metrics:
- litellm_db_pool_active_connections (Gauge)
- litellm_db_pool_idle_connections (Gauge)
- litellm_db_pool_total_connections (Gauge)
- litellm_db_pool_waiting_connections (Gauge)
- litellm_db_engine_up (Gauge)
- litellm_db_engine_restarts_total (Counter)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Greptile review feedback

- Only increment engine_restarts counter on heavy reconnects (engine
  actually dead), not lightweight network-blip reconnects
- Fix potential KeyError in _get_or_create_gauge/counter fallback path
  when REGISTRY._names_to_collectors is absent
- Rename litellm_db_pool_waiting_connections to
  litellm_db_pool_lock_waiting_connections to clarify it measures lock
  contention, not pool slot queuing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: warn when prometheus_system enabled but watchdog disabled

Log a warning when users have prometheus_system in service_callback
but PRISMA_HEALTH_WATCHDOG_ENABLED=false, since DB pool and engine
metrics won't be collected in that configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: retrigger CI checks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use labeled gauge for DB pool connection metrics

Replace 3 separate pool gauges (active, idle, total) with a single
`litellm_db_pool_connections` gauge using a `state` label. This is more
Prometheus-idiomatic and exposes all pg_stat_activity states (active,
idle, idle in transaction, etc.) without ambiguity about what "total"
includes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address Greptile review — stale labels and fallback re-registration

- Zero out known pg_stat_activity states that are absent from the current
  query result, preventing stale gauge values from persisting.
- Simplify _get_or_create_gauge/counter by removing the fallback loop
  that could re-register an already-registered metric (ValueError).
- Add test for stale label clearing across collection cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include "unknown" in _PG_STATES for stale label clearing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: collect immediately on start and consolidate into single query

- Move sleep to end of loop so metrics appear on /metrics immediately
  after startup instead of after a 30s delay.
- Combine pool state and lock waiting queries into a single SQL query
  using conditional aggregation, halving per-cycle DB overhead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent tight spin loop on collection error

Move asyncio.sleep outside the try/except so it always executes even
when _collect_engine_health() or _collect_pool_metrics() raises.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add multiprocess_mode to _get_or_create_gauge initialization

- Include `multiprocess_mode` parameter to properly support multiprocessing in Gauge creation.
- Ensure consistent behavior for labeled and unlabeled Gauges.

* fix: handle invalid env var and document watchdog prerequisite

- Add try/except ValueError for PRISMA_METRICS_COLLECTION_INTERVAL_SECONDS
  to prevent proxy startup crash on non-numeric values (e.g. "30s")
- Document that DB metrics require both prometheus_system callback and
  PRISMA_HEALTH_WATCHDOG_ENABLED=true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use defensive null coalescing for query_raw row values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add invalid env var fallback test and fix mock signature

- Add test for non-numeric PRISMA_METRICS_COLLECTION_INTERVAL_SECONDS
- Add **kwargs to mock _patched_get_or_create_gauge for forward compat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:49:46 -07:00
milan-berri
df2e1bca46
feat: allow JWT and OAuth2 auth to coexist on the same instance (#23153)
When both enable_jwt_auth and enable_oauth2_auth are True, the proxy now
routes tokens based on their format:
- JWT tokens (3 dot-separated parts) -> JWT auth handler
- Opaque tokens -> OAuth2 auth handler

This enables using JWT for human users and OAuth2 for M2M (machine) clients
on the same LiteLLM instance. Previously, enabling OAuth2 would intercept
all tokens on LLM API routes before JWT auth could run.

When only one auth method is enabled, behavior is unchanged (backward compatible).
2026-03-09 08:41:27 -07:00
Joe Reyna
36e04b6efe
fix(tests): restore litellm_params=None on mock agent in a2a invoke test (#23125) 2026-03-09 07:16:02 -07:00
Joe Reyna
0bc1bd6871
fix(tests): use AsyncMock for prisma find_unique in agent get-by-id test (#23122) 2026-03-09 07:13:50 -07:00
Sameer Kankute
ee3ecb5994 fix(openai): preserve reasoning_effort summary + fix xhigh/none guards for dict inputs
- Add _get_effort_level() to extract effective effort from string or dict
- Use effective_effort for xhigh validation, tool-drop, sampling, temperature guards
- Preserve dict format when it has summary/generate_summary for Responses API
- Add tests: xhigh-dict validation, none-dict for tools/sampling/temperature
- Update tests: dict-with-summary now preserved (not normalized)

Made-with: Cursor
2026-03-09 18:44:05 +05:30
Sameer Kankute
8cf80a14d9 fix(openai): preserve reasoning_effort summary field for Responses API
When reasoning_effort is passed as a dict with additional fields like 'summary' or 'generate_summary', preserve the full dict format instead of normalizing it to a string. This ensures that when requests are routed to the OpenAI Responses API, all reasoning parameters are correctly included.

The normalization to string format now only happens for simple dicts with just the 'effort' key, which is appropriate for the Chat Completions API.

Fixes issue where summary field was being dropped when routing gpt-5.4+ requests with tools + reasoning to Responses API.

Made-with: Cursor
2026-03-09 18:28:11 +05:30
Giulio Leone
556c64875e fix(models): set gpt-5.4-pro mode to responses instead of chat
gpt-5.4-pro and gpt-5.4-pro-2026-03-05 do not support the
/v1/chat/completions endpoint — OpenAI returns a 404 with
"This is not a chat model". These models are responses-only,
like o3-pro and o1-pro.

Changes:
- Set mode from "chat" to "responses" for both model entries
- Update supported_endpoints to ["/v1/responses", "/v1/batch"]
- Add regression test for responses API bridge routing

Fixes BerriAI/litellm#23014
2026-03-09 12:10:08 +01:00
Sameer Kankute
4dc277e427 fix(vertex_ai): strip LiteLLM-internal keys from extra_body before merging to Gemini request
PR #20950 added extra_body forwarding to Vertex AI Gemini. LiteLLM-internal
keys (cache, tags) were being merged into the request body, causing Vertex AI
to reject with 400: 'Unknown name "cache": Cannot find field.'

- Add _LITELLM_INTERNAL_EXTRA_BODY_KEYS frozenset (cache, tags)
- Skip these keys in _pop_and_merge_extra_body before merging
- Add regression tests for cache and tags stripping

Fixes regression from 1.79.3 → 1.81.12 when using proxy cache with
extra_body={"cache": {"use-cache": True, "ttl": 86400}}

Made-with: Cursor
2026-03-09 10:21:29 +05:30
netbrah
ffc6d84f27 fix: shallow copy input_schema to avoid caller mutation + add mutation guard test
Addresses Greptile review:
- dict(_input_schema) before mutation prevents cross-provider state leakage
- Test asserts original tool parameters dict is unchanged after call
2026-03-08 10:04:29 -04:00
netbrah
5d1106f018 fix(anthropic): deduplicate tool_result messages by tool_call_id
Anthropic requires exactly one tool_result per tool_use. When
conversation history (e.g. from session resume/checkpoint restore)
contains duplicate tool result messages with the same tool_call_id,
the API rejects with: 'each tool_use must have a single result.
Found multiple tool_result blocks with id: <id>'.

This is already handled for Bedrock via _deduplicate_bedrock_tool_content()
but was missing from the Anthropic direct and Vertex AI partner paths,
which share sanitize_messages_for_tool_calling().

Fix: Add Case D to sanitize_messages_for_tool_calling() — after the
existing orphan detection passes, scan for duplicate tool_call_ids
and keep only the last occurrence (most complete result).

Added 3 unit tests: dedup with duplicates, no-op with unique IDs,
and behavior when modify_params=False.

Related issues: #11804, #11029, #6836, #1782, #151
2026-03-08 10:02:45 -04:00
netbrah
78159212d9 fix(anthropic): enforce type:'object' on tool input schemas
Anthropic's API requires all tool input_schema to have type:'object'
at the root level. When OpenAI-format tools have parameters with a
missing or non-'object' type field (common with MCP tool servers),
the schema was passed through unchanged, causing Anthropic to reject
with: 'tools.N.custom.input_schema.type: Input should be object'.

The existing default handles the case where parameters is entirely
missing, but does not normalize schemas that ARE provided with a
wrong or absent type field.

Fix: After extracting _input_schema in _map_tool_helper(), ensure
type is set to 'object' and properties exists. This matches the
normalization already done implicitly by the Bedrock handler.

Added 4 unit tests covering: missing type, wrong type, valid schema
(no-op), and entirely missing parameters.

Related issues: #12020, #64, #1671
2026-03-08 07:52:07 -04:00