fix(apodex): satisfy CI quality gates

This commit is contained in:
zhanghanduo 2026-08-26 09:38:19 +08:00
parent bb6cf2e2ef
commit a665d2e336
8 changed files with 48 additions and 12 deletions

View file

@ -644,7 +644,7 @@ snowflake_models: Set = set()
gradient_ai_models: Set = set()
llama_models: Set = set()
nscale_models: Set = set()
apodex_models: Set = set()
apodex_models: Set = set() # mutable-ok: provider model registry is populated during initialization
nebius_models: Set = set()
nebius_embedding_models: Set = set()
aiml_models: Set = set()

View file

@ -74,7 +74,7 @@ class AnthropicResponsesStreamWrapper:
if self._open_block_index is None:
return
self._chunk_queue.append(
{
{ # mutable-ok: queued Anthropic event payload
"type": "content_block_stop",
"index": self._open_block_index,
}
@ -88,7 +88,7 @@ class AnthropicResponsesStreamWrapper:
{
"type": "content_block_start",
"index": block_idx,
"content_block": dict(content_block),
"content_block": dict(content_block), # mutable-ok: queued Anthropic event payload
}
)
self._open_block_index = block_idx
@ -143,7 +143,11 @@ class AnthropicResponsesStreamWrapper:
block_idx = self._next_block_index()
if item_id:
self._item_id_to_block_index[item_id] = block_idx
self._start_block(block_idx, "text", {"type": "text", "text": ""})
self._start_block(
block_idx,
"text",
{"type": "text", "text": ""}, # mutable-ok: Anthropic content block payload
)
elif item_type == "function_call":
call_id: Final = (
getattr(item, "call_id", None) or (item.get("call_id") if isinstance(item, dict) else None) or ""
@ -172,7 +176,7 @@ class AnthropicResponsesStreamWrapper:
text_block_idx: Final = self._get_or_start_block(
item_id=item_id,
block_type="text",
content_block={"type": "text", "text": ""},
content_block={"type": "text", "text": ""}, # mutable-ok: Anthropic content block payload
)
self._chunk_queue.append(
{
@ -192,7 +196,11 @@ class AnthropicResponsesStreamWrapper:
thinking_block_idx: Final = self._get_or_start_block(
item_id=item_id,
block_type="thinking",
content_block={"type": "thinking", "thinking": "", "signature": ""},
content_block={ # mutable-ok: Anthropic content block payload
"type": "thinking",
"thinking": "",
"signature": "",
},
)
self._chunk_queue.append(
{

View file

@ -125,13 +125,15 @@ class ApodexChatConfig(OpenAIGPTConfig):
def transform_request(
self,
model: str,
messages: list[AllMessageValues],
messages: list[AllMessageValues], # mutable-ok: matches the base-class signature
optional_params: dict, # mutable-ok: matches the base-class signature
litellm_params: dict, # mutable-ok: matches the base-class signature
headers: dict, # mutable-ok: matches the base-class signature
) -> dict: # mutable-ok: JSON request body
pin_non_streaming: Final = bool(optional_params.get(_PIN_NON_STREAMING, False))
forwarded_params: Final = {key: value for key, value in optional_params.items() if key != _PIN_NON_STREAMING}
forwarded_params: Final = { # mutable-ok: base transformer requires a request dict
key: value for key, value in optional_params.items() if key != _PIN_NON_STREAMING
}
transformed: Final = super().transform_request(
model=model,
messages=messages,

View file

@ -117,13 +117,13 @@ class ApodexResponsesConfig(OpenAIResponsesAPIConfig):
status_code=raw_response.status_code,
# Content-Encoding and Content-Length describe the body being replaced here;
# carrying them over makes httpx try to decompress plain JSON on read.
headers={
headers={ # mutable-ok: httpx requires mutable response headers
name: value for name, value in raw_response.headers.items() if name.lower() not in _BODY_FRAMING_HEADERS
},
json={
json={ # mutable-ok: httpx requires a JSON-compatible response dict
**payload,
"created_at": payload.get("created_at", int(time())),
"output": payload.get("output", []),
"output": payload.get("output", []), # mutable-ok: Responses payload requires an array default
},
)
return super().transform_cancel_response_api_response(

View file

@ -245,10 +245,16 @@ class TestProcessEventTextDeltaWithoutOutputItemAdded:
assert chunks[1]["delta"] == {"type": "text_delta", "text": "Hel"}
def test_process_event_delta_without_item_id_never_yields_negative_index(self):
chunks = _process_all([{"type": "response.output_text.delta", "delta": "Hi"}])
chunks = _process_all(
[
{"type": "response.output_text.delta", "delta": "Hi"},
{"type": "response.output_text.delta", "delta": " again"},
]
)
assert [(c["type"], c["index"]) for c in chunks] == [
("content_block_start", 0),
("content_block_delta", 0),
("content_block_delta", 0),
]
def test_process_event_unregistered_item_id_opens_new_text_block(self):

View file

@ -72,6 +72,13 @@ def _chat_config(model: str):
class TestProviderResolution:
def test_openai_compatible_provider_info_uses_apodex_credentials(self):
config = _chat_config("apodex-1.1")
assert config._get_openai_compatible_provider_info("https://override.test/v1", "sk-override") == (
"https://override.test/v1",
"sk-override",
)
def test_prefixed_model_resolves_to_the_default_base(self):
model, provider, api_key, api_base = litellm.get_llm_provider(model=CORE_MODEL)
assert (model, provider, api_key, api_base) == (

View file

@ -55,6 +55,7 @@ class TestNativePassthroughRouting:
assert config is not None
assert type(config).__name__ == "ApodexAnthropicMessagesConfig"
assert config.custom_llm_provider == "apodex"
assert config.should_strip_billing_metadata() is True
@pytest.mark.parametrize("model", DEEP_RESEARCH_MODELS)
def test_deep_research_models_fall_back_to_translation(self, model: str):

View file

@ -367,6 +367,18 @@ class TestDeepResearchKeepsState:
assert event.type == "response.swarm.llm_delta"
def test_non_string_delta_is_not_claimed(self):
config = _responses_config("apodex-1-1-deep-research")
assert (
config._map_swarm_delta(
{
"type": "response.swarm.llm_delta",
"swarm": {"data": {"channel": "output_text", "delta": None}},
}
)
is None
)
@pytest.mark.parametrize(
"event_type",
(