When MCP SDK hits root-level /register, /authorize, /token without
server name prefix, auto-resolve to the single configured OAuth2
server. Also fix WWW-Authenticate header to use correct public URL
behind reverse proxy.
* fix(prometheus): sanitize label values to prevent metric scrape failures
Unicode characters like U+2028 (Line Separator) in Prometheus label values
break the text exposition format, causing scrapers (e.g. Datadog) to fail
parsing the entire /metrics endpoint. One bad label value causes ALL metrics
to be lost, not just the affected metric.
Add _sanitize_prometheus_label_value() and apply it in prometheus_label_factory()
and all direct .labels() call sites.
* fix(prometheus): handle non-string label values in sanitization
Coerce non-string values (int, bool, float) to str before applying
sanitization, preventing AttributeError on .replace() calls.
* fix(prometheus): run sanitization on coerced non-string values
Non-string values should be coerced to str and then sanitized (not
returned early), so their string representations also get cleaned.
* fix(prometheus): widen type hint to Optional[Any] for label value sanitization
first_id and last_id in the video list response were returned as raw
provider IDs while data[].id was properly wrapped with
encode_video_id_with_provider(). This caused pagination to break when
clients passed unencoded cursors back as the `after` parameter.
- Encode first_id/last_id in transform_video_list_response
- Decode the `after` param in transform_video_list_request via
extract_original_video_id()
- Add 6 unit tests covering encoding, decoding, passthrough, and
full round-trip pagination
Fixes#20708
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vertex AI requires Anthropic beta flags in the request body
(anthropic_beta array), not as HTTP headers. The Bedrock handler
already extracts user-specified beta headers from the headers dict,
but the Vertex handler was missing this, causing extra_headers like
interleaved-thinking-2025-05-14 to be silently dropped.
This extracts anthropic-beta values from optional_params extra_headers
and merges them into the anthropic_beta request body field, and also
removes extra_headers from the request body since the parent's
transform_request spreads optional_params into data.
* feat: add support for anthropic_messages call type in prompt caching
* test: move anthropic_messages prompt caching test to main router test file
* add tutorial on using claude code with prompt cache routing
* support policy mapping on team key level
* update document
* update document
* address comments
* update document
* add unit test for new feature
* add more test case
* fix: empty guardrails/policies arrays should not trigger enterprise license check (#20304)
The UI sends empty arrays for enterprise-only fields (guardrails, policies,
logging) even when the user has not configured these features. The backend
`is not None` check treated `[]` as a truthy intent to use the feature,
falsely requiring an enterprise license for basic team operations.
Backend: Add `and updated_kv[field] != [] and updated_kv[field] != {}`
guards in `_update_metadata_fields` so empty collections are skipped.
UI: Conditionally omit guardrails, logging, and policies from the
payload when empty instead of defaulting to `[]`.
Fixes#20304
* fix: allow clearing fields with empty collections while skipping enterprise check
Address PR review feedback:
1. Move the empty-collection guard into _update_metadata_field (singular)
so that empty lists/dicts skip only the premium license check but still
get written into metadata. This lets users intentionally clear a
previously-set field (e.g. guardrails: []) without being blocked, while
the UI's default empty arrays still don't trigger a false enterprise
error.
2. Remove sys.path hack from test file; use standard imports that work
with pytest discovery.
3. Add tests verifying that empty collections are moved into metadata
(field clearing works) even though they bypass the premium check.
Fixes#20304
Fixes 15 failing tests in the MCP test suite:
1. **OAuth discoverable endpoints** (test_discoverable_endpoints.py):
- Added autouse fixture to mock IPAddressUtils.get_mcp_client_ip
- This bypasses IP-based access control which was blocking server lookup
- Fixes: test_authorize_*, test_token_*, test_oauth_*, test_register_*
2. **A2A endpoints** (test_a2a_endpoints.py):
- Fixed mock path for add_litellm_data_to_request
- Was patching litellm_pre_call_utils but function is called from common_request_processing
3. **MCP guardrail handler** (test_mcp_guardrail_handler.py):
- Updated tests to match new handler behavior
- Handler now passes tools (not texts) to guardrail
- Handler checks for mcp_tool_name (not messages array)
4. **MCP path-based segregation** (test_user_api_key_auth_mcp.py):
- Added client_ip to get_auth_context unpacking (7 values now)
- get_auth_context was updated to include client_ip
5. **MCP registry** (test_mcp_management_endpoints.py):
- Added mock for get_filtered_registry (not just get_registry)
- Registry endpoint uses get_filtered_registry for IP filtering
Co-authored-by: Shin <shin@openclaw.ai>
Add cheap .get() guards in should_run_callback() to short-circuit
the expensive EnterpriseCallbackControls.is_callback_disabled_dynamically()
call. When neither litellm_disabled_callbacks nor x-litellm-disable-callbacks
header is set (the common case), the enterprise function is never entered,
reducing should_run_callback from ~485ms to ~93-165ms across 54k calls.
CallTypes(call_type) was constructing an enum from string on every call,
taking ~4.6µs/call (69.6% of function time). Replace with a frozenset
membership test for ~0.8µs/call (8.3x faster).
* perf: Optimize get_litellm_params with sparse kwargs extraction
- Add _OPTIONAL_KWARGS_KEYS frozenset for O(1) lookups
- Replace 28 unconditional kwargs.get() calls with sparse extraction
- Only add kwargs keys that are actually present in the dict
- Simplify _get_base_model_from_litellm_call_metadata by removing redundant None checks
This reduces get_litellm_params() time by ~31% (743ms → 509ms across 6000 calls)
and Logging.__init__ total time by ~24% (1.61s → 1.23s).
* test: add unit tests for get_litellm_params sparse kwargs extraction
* perf: add early-exit guards in completion_cost for unused features
Skip function calls to get_cost_for_built_in_tools, _apply_cost_discount,
_apply_cost_margin, and _store_cost_breakdown_in_logging_obj when their
respective features are not configured. Reduces completion_cost() time
by ~20% (4.39s → 3.53s over 6K requests) for the common case where
built-in tools, discounts, margins, and logging object are not active.
* fix: always call get_cost_for_built_in_tools regardless of standard_built_in_tools_params
The function can detect web search usage from the usage object (e.g.
server_tool_use.web_search_requests, prompt_tokens_details.web_search_requests)
even when standard_built_in_tools_params is None, so guarding on it can
under-count cost for providers like Vertex AI and Anthropic.
Adds regression test for completion_cost with web search in usage but
no standard_built_in_tools_params.
* fix(tests): Mock async_container_create_handler for async router test
The test was mocking container_create_handler (sync), but
router.acreate_container uses _is_async=True which calls
async_container_create_handler. This caused the test to hit
the real OpenAI API.
Fixed by using AsyncMock on async_container_create_handler.
* fix(tests): Use uuid for unique model name in scientific notation test
The test was using a static "unique" model name which could cause
conflicts when running tests in parallel (-n 16 in CI). Using uuid
ensures truly unique names to prevent test pollution.
---------
Co-authored-by: Shin <shin@openclaw.ai>
* perf: Optimize get_standard_logging_metadata with set intersection
- Cache StandardLoggingMetadata.__annotations__.keys() as module-level frozenset
- Use set intersection to iterate only keys present in both metadata and supported keys
- Single lookup for user_api_key instead of 3 separate .get() calls
Results:
- get_standard_logging_metadata: 1.55s → 1.41s (9.2% faster)
* test: add unit tests for get_standard_logging_metadata non-string user_api_key handling
* fix(tests): Fix sendgrid email tests to properly mock httpx client
The tests were potentially hitting the real SendGrid API because the mock
was patching get_async_httpx_client() but the actual client could be cached
or the mock timing could be off.
Fix by directly replacing logger.async_httpx_client after instantiation,
which guarantees the mock is used regardless of caching or initialization
timing issues.
Changes:
- Replace mock_httpx_client fixture with simpler mock_async_client fixture
- Directly inject mock client into logger instance after creation
- Remove respx decorator (no longer needed with direct injection)
- Simplify test structure while maintaining same assertions
* fix(lint): remove unused imports from SendGrid test
Replace text-embedding-004 with gemini-embedding-001.
The old model was deprecated and returns 404:
'models/text-embedding-004 is not found for API version v1beta'
Co-authored-by: Shin <shin@openclaw.ai>