Skip to content

Commit 93185ec

Browse files
jopemachineclaude
andcommitted
refactor(BA-5528): consolidate OpenAI-compat extra="allow" and split history None vs empty
- Extract `_OpenAICompatModel` base class so all OpenAI-compat response DTOs share a single `ConfigDict(extra="allow")` declaration instead of repeating it on each subclass. - In `history_show`, distinguish "no history record" (`messages is None`) from the invariant-violating "record exists but empty list" case so the CLI message reflects the actual state instead of conflating both as falsy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9e14188 commit 93185ec

2 files changed

Lines changed: 27 additions & 26 deletions

File tree

src/ai/backend/client/cli/v2/deployment/chat/commands.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,12 @@ def history_show(deployment_id: DeploymentID, limit: int | None) -> None:
385385
"""Print the persisted transcript for a deployment."""
386386
history = DeploymentChatHistory.load()
387387
messages = history.get(deployment_id)
388-
if not messages:
388+
if messages is None:
389389
print(f"No chat history for deployment {deployment_id}.")
390390
return
391+
if not messages:
392+
print(f"Chat history for deployment {deployment_id} is empty.")
393+
return
391394
visible = messages if limit is None else messages[-limit:]
392395
print(f"deployment_id : {deployment_id}")
393396
print(f"messages : {len(messages)} persisted (showing {len(visible)})")

src/ai/backend/common/dto/clients/openai_compat/response.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,47 @@
55
from pydantic import BaseModel, ConfigDict
66

77

8-
class ModelEntry(BaseModel):
9-
"""One entry in an OpenAI-compat ``GET /v1/models`` response.
10-
11-
Runtimes (vLLM, SGLang, NIM) typically include extra fields such as
12-
``created`` or ``owned_by``; ``extra="allow"`` keeps them on the
13-
model so future additions don't break parsing.
8+
class _OpenAICompatModel(BaseModel):
9+
"""Base for OpenAI-compat response DTOs.
10+
11+
Runtimes (vLLM, SGLang, NIM, TGI) ship runtime-specific extras
12+
(``usage``, ``system_fingerprint``, ``tool_calls``,
13+
``reasoning_content``, ``prompt_logprobs``, ``owned_by``, ...).
14+
``extra="allow"`` keeps them on the model so ``model_dump_json``
15+
round-trips faithfully back to the CLI's stdout pretty-print.
1416
"""
1517

1618
model_config = ConfigDict(extra="allow")
1719

20+
21+
class ModelEntry(_OpenAICompatModel):
22+
"""One entry in an OpenAI-compat ``GET /v1/models`` response."""
23+
1824
id: str
1925
object: str = "model"
2026

2127

22-
class ListModelsResponse(BaseModel):
28+
class ListModelsResponse(_OpenAICompatModel):
2329
"""Body of ``GET /v1/models`` on an OpenAI-compat endpoint."""
2430

25-
model_config = ConfigDict(extra="allow")
26-
2731
object: str = "list"
2832
data: list[ModelEntry]
2933

3034

31-
class ChatCompletionResponseMessage(BaseModel):
35+
class ChatCompletionResponseMessage(_OpenAICompatModel):
3236
"""The ``message`` payload inside one OpenAI-compat choice.
3337
3438
Only ``content`` is consumed by the CLI (for chat-history persistence);
35-
``extra="allow"`` keeps runtime-specific fields like ``tool_calls`` or
36-
``reasoning_content`` (DeepSeek-R1, Qwen-QwQ) on the model so they pass
37-
through to the JSON pretty-printed output.
39+
runtime-specific fields like ``tool_calls`` or ``reasoning_content``
40+
(DeepSeek-R1, Qwen-QwQ) pass through to the JSON pretty-printed output
41+
via the inherited ``extra="allow"``.
3842
"""
3943

40-
model_config = ConfigDict(extra="allow")
41-
4244
role: str | None = None
4345
content: str | None = None
4446

4547

46-
class ChatCompletionResponseChoice(BaseModel):
48+
class ChatCompletionResponseChoice(_OpenAICompatModel):
4749
"""One entry in ``choices[]`` on a non-streaming chat-completion response.
4850
4951
Streaming responses use ``delta`` instead of ``message``; this model
@@ -52,24 +54,20 @@ class ChatCompletionResponseChoice(BaseModel):
5254
``ValidationError`` rather than corrupting persisted history.
5355
"""
5456

55-
model_config = ConfigDict(extra="allow")
56-
5757
message: ChatCompletionResponseMessage
5858

5959

60-
class ChatCompletionResponse(BaseModel):
60+
class ChatCompletionResponse(_OpenAICompatModel):
6161
"""Body of ``POST /v1/chat/completions`` (OpenAI-compatible).
6262
6363
Only the path used by chat-history bookkeeping
64-
(``choices[0].message.content``) is typed here. The remaining
65-
top-level fields (``id``, ``object``, ``created``, ``model``,
66-
``usage``, ``system_fingerprint``, runtime-specific extras) ride
67-
through via ``extra="allow"`` so they survive the round-trip back
64+
(``choices[0].message.content``) is typed here. Top-level extras
65+
(``id``, ``object``, ``created``, ``model``, ``usage``,
66+
``system_fingerprint``, runtime-specific fields) ride through via
67+
the inherited ``extra="allow"`` so they survive the round-trip back
6868
to the user's stdout when the CLI pretty-prints the response.
6969
"""
7070

71-
model_config = ConfigDict(extra="allow")
72-
7371
choices: list[ChatCompletionResponseChoice]
7472

7573
@property

0 commit comments

Comments
 (0)