fix(claude-code): fix remaining CI failures on allowed_skills addition

- Modernize Optional[X]/List[X]/Dict[X,Y] to X | None/list/dict on every
  line the strict-rule ruff budget flagged as newly added, instead of
  raising the ceiling.
- Fix a golden-fixture test in test_customer_endpoints.py that hardcoded
  the full object_permission response shape and didn't account for the
  new allowed_skills field.
- Regenerate schema.d.ts with a Python 3.12 venv synced the same way CI's
  check-ui-api-types workflow does (uv sync --frozen --group ci --group
  proxy-dev --extra google --extra proxy --extra semantic-router); the
  prior regeneration used a mismatched local Python 3.11/deps and produced
  spurious diffs unrelated to this PR.
This commit is contained in:
Krrish Dholakia 2026-07-11 18:49:00 -07:00
parent 73042573c6
commit 59f2a8ed67
8 changed files with 1117 additions and 92 deletions

View file

@ -25,4 +25,4 @@ class LiteLLM_ObjectPermissionTable(LiteLLMPydanticObjectBase):
blocked_tools: Optional[List[str]] = []
search_tools: Optional[List[str]] = []
mcp_tool_search_enabled: Optional[bool] = None
allowed_skills: Optional[List[str]] = None
allowed_skills: list[str] | None = None

View file

@ -1009,7 +1009,7 @@ class LiteLLM_ObjectPermissionBase(LiteLLMPydanticObjectBase):
models: Optional[List[str]] = None
search_tools: Optional[List[str]] = None
mcp_tool_search_enabled: Optional[bool] = None
allowed_skills: Optional[List[str]] = None
allowed_skills: list[str] | None = None
from litellm.types.object_permission import ( # noqa: E402

View file

@ -18,7 +18,7 @@ Endpoints:
import json
import re
from datetime import datetime, timezone
from typing import Any, Dict, FrozenSet, Optional
from typing import Any, Dict
from fastapi import APIRouter, Depends, HTTPException, Request, Security
from fastapi.responses import JSONResponse
@ -54,11 +54,11 @@ async def _optional_user_api_key_auth(
request: Request,
api_key: str = Security(api_key_header),
azure_api_key: str = Security(azure_api_key_header),
anthropic_api_key: Optional[str] = Security(anthropic_api_key_header),
google_ai_studio_api_key: Optional[str] = Security(google_ai_studio_api_key_header),
azure_apim_key: Optional[str] = Security(azure_apim_header),
custom_litellm_key: Optional[str] = Security(custom_litellm_key_header),
) -> Optional[UserAPIKeyAuth]:
anthropic_api_key: str | None = Security(anthropic_api_key_header),
google_ai_studio_api_key: str | None = Security(google_ai_studio_api_key_header),
azure_apim_key: str | None = Security(azure_apim_header),
custom_litellm_key: str | None = Security(custom_litellm_key_header),
) -> UserAPIKeyAuth | None:
"""
Resolve UserAPIKeyAuth if a key is present (header or, for this route,
the `key` query param wired up via RouteChecks.is_claude_code_marketplace_route),
@ -95,7 +95,7 @@ async def _get_prisma_client():
tags=["Claude Code Marketplace"],
)
async def get_marketplace(
user_api_key_dict: Optional[UserAPIKeyAuth] = Depends(_optional_user_api_key_auth), # noqa: B008 # DI idiom
user_api_key_dict: UserAPIKeyAuth | None = Depends(_optional_user_api_key_auth), # noqa: B008 # DI idiom
):
"""
Serve marketplace.json for Claude Code plugin discovery.
@ -120,7 +120,7 @@ async def get_marketplace(
try:
prisma_client = await _get_prisma_client()
allowed_skills: FrozenSet[str] = (
allowed_skills: frozenset[str] = (
await get_allowed_skills(user_api_key_dict, prisma_client) if user_api_key_dict is not None else frozenset()
)
where = (

View file

@ -13,7 +13,6 @@ Endpoints:
"""
import re
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
@ -100,7 +99,7 @@ async def _count_plugins(prisma_client, marketplace_id: str) -> int:
return await ClaudeCodePluginRepository(prisma_client).table.count(where={"marketplace_id": marketplace_id})
def _to_marketplace_source_response(marketplace, plugin_count: Optional[int]) -> MarketplaceSourceResponse:
def _to_marketplace_source_response(marketplace, plugin_count: int | None) -> MarketplaceSourceResponse:
return MarketplaceSourceResponse(
id=marketplace.id,
name=marketplace.name,

View file

@ -1,5 +1,3 @@
from typing import Optional
from litellm._logging import verbose_logger
from litellm.proxy._types import UI_TEAM_ID, UserAPIKeyAuth
from litellm.proxy.utils import PrismaClient
@ -7,7 +5,7 @@ from litellm.proxy.utils import PrismaClient
async def _get_allowed_skills_for_key(
user_api_key_dict: UserAPIKeyAuth,
prisma_client: Optional[PrismaClient],
prisma_client: PrismaClient | None,
) -> frozenset[str]:
"""Key's own allowed_skills ceiling from its object_permission.
@ -36,7 +34,7 @@ async def _get_allowed_skills_for_key(
async def _get_allowed_skills_for_team(
user_api_key_dict: UserAPIKeyAuth,
prisma_client: Optional[PrismaClient],
prisma_client: PrismaClient | None,
) -> frozenset[str]:
"""Team's allowed_skills ceiling from team.object_permission."""
from litellm.proxy.auth.auth_checks import get_team_object
@ -63,7 +61,7 @@ async def _get_allowed_skills_for_team(
async def _get_allowed_skills_for_org(
user_api_key_dict: UserAPIKeyAuth,
prisma_client: Optional[PrismaClient],
prisma_client: PrismaClient | None,
) -> frozenset[str]:
"""Org's allowed_skills ceiling from org.object_permission."""
from litellm.proxy.auth.auth_checks import get_object_permission, get_org_object
@ -97,7 +95,7 @@ async def _get_allowed_skills_for_org(
async def get_allowed_skills(
user_api_key_dict: UserAPIKeyAuth,
prisma_client: Optional[PrismaClient],
prisma_client: PrismaClient | None,
) -> frozenset[str]:
"""
Resolve the set of Claude Code skill names (already-namespaced, e.g.

View file

@ -708,7 +708,7 @@ async def validate_key_vector_stores_against_team(
def _extract_requested_allowed_skills(
object_permission: Optional[ObjectPermissionDict],
object_permission: ObjectPermissionDict | None,
) -> set[str]:
"""Return allowed_skills names from a key's object_permission dict."""
if not object_permission or not isinstance(object_permission, dict):
@ -720,8 +720,8 @@ def _extract_requested_allowed_skills(
async def validate_key_allowed_skills_against_team(
object_permission: Optional[ObjectPermissionDict],
team_obj: Optional["LiteLLM_TeamTableCachedObj"],
object_permission: ObjectPermissionDict | None,
team_obj: "LiteLLM_TeamTableCachedObj | None",
is_proxy_admin: bool = False,
) -> None:
"""
@ -752,7 +752,7 @@ async def validate_key_allowed_skills_against_team(
},
)
team_skills: List[str] = []
team_skills: list[str] = []
if team_obj is not None and team_obj.object_permission is not None:
skills = team_obj.object_permission.allowed_skills
if skills:

View file

@ -719,6 +719,7 @@ _EXPECTED_CUSTOMER = {
"blocked_tools": [],
"search_tools": [],
"mcp_tool_search_enabled": None,
"allowed_skills": None,
},
}

File diff suppressed because it is too large Load diff