style: apply ruff's automatic fixes and formatter
Mechanical only, and separated from the judgment calls that follow so the reviewable changes are not buried in a 98-file whitespace diff. 227 automatic fixes: 60 blank lines carrying whitespace, 60 unsorted import blocks, 34 Optional[X] to X | None, 28 unused imports, 16 deprecated typing imports, 12 datetime.timezone.utc to datetime.UTC, and assorted smaller modernisations. Then `ruff format` over src and tests: 98 files reformatted, 35 already conforming. No file among the unused-import findings defines __all__ or is an __init__.py, so nothing here removes a re-export. `make test`: 658 passed, unchanged from HEAD. Two things observed while verifying, neither addressed here: `pytest tests/` cannot collect — tests/e2e/test_orchestration_e2e.py uses an `e2e` marker that is not registered, and the config is strict about markers. This fails identically at HEAD, so it predates this change; `make test` passes because it ignores tests/e2e, tests/integration and tests/contracts. test_tatlock_tool_call_logging_calculator is flaky. It failed once in a full run with these changes and passed on the next, passes in isolation with them, and fails in isolation at HEAD. It is order- or timing-dependent, not a regression from this commit — established by running the full suite both ways rather than by reasoning about which change could have caused it. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -2,9 +2,10 @@
|
||||
Chat completion router.
|
||||
OpenAI-compatible /v1/chat/completions endpoint.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import APIRouter
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
OpenAI-compatible chat completion schemas.
|
||||
Following OpenAI API specification for compatibility.
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
@@ -11,6 +12,7 @@ from src.core.models import CustomBaseModel
|
||||
|
||||
class ChatMessage(CustomBaseModel):
|
||||
"""OpenAI-compatible chat message."""
|
||||
|
||||
role: Literal["system", "user", "assistant"]
|
||||
content: str
|
||||
name: str | None = None
|
||||
@@ -18,6 +20,7 @@ class ChatMessage(CustomBaseModel):
|
||||
|
||||
class ChatCompletionRequest(CustomBaseModel):
|
||||
"""OpenAI-compatible chat completion request."""
|
||||
|
||||
model: str = Field(..., description="Model to use for completion")
|
||||
messages: list[ChatMessage] = Field(..., description="List of messages")
|
||||
temperature: float | None = Field(default=0.7, ge=0.0, le=2.0)
|
||||
@@ -29,6 +32,7 @@ class ChatCompletionRequest(CustomBaseModel):
|
||||
|
||||
class ChatCompletionChoice(CustomBaseModel):
|
||||
"""Choice in chat completion response."""
|
||||
|
||||
index: int
|
||||
message: ChatMessage
|
||||
finish_reason: str | None
|
||||
@@ -36,6 +40,7 @@ class ChatCompletionChoice(CustomBaseModel):
|
||||
|
||||
class ChatCompletionUsage(CustomBaseModel):
|
||||
"""Token usage information."""
|
||||
|
||||
prompt_tokens: int
|
||||
completion_tokens: int
|
||||
total_tokens: int
|
||||
@@ -43,6 +48,7 @@ class ChatCompletionUsage(CustomBaseModel):
|
||||
|
||||
class ChatCompletionResponse(CustomBaseModel):
|
||||
"""OpenAI-compatible chat completion response."""
|
||||
|
||||
id: str
|
||||
object: str = "chat.completion"
|
||||
created: int
|
||||
@@ -53,6 +59,7 @@ class ChatCompletionResponse(CustomBaseModel):
|
||||
|
||||
class ChatCompletionChunkDelta(CustomBaseModel):
|
||||
"""Delta in streaming chunk."""
|
||||
|
||||
role: str | None = None
|
||||
content: str | None = None
|
||||
reasoning_content: str | None = None # For thinking/reasoning (DeepSeek R1 format)
|
||||
@@ -60,6 +67,7 @@ class ChatCompletionChunkDelta(CustomBaseModel):
|
||||
|
||||
class ChatCompletionChunkChoice(CustomBaseModel):
|
||||
"""Choice in streaming chunk."""
|
||||
|
||||
index: int
|
||||
delta: ChatCompletionChunkDelta
|
||||
finish_reason: str | None = None
|
||||
@@ -67,6 +75,7 @@ class ChatCompletionChunkChoice(CustomBaseModel):
|
||||
|
||||
class ChatCompletionChunk(CustomBaseModel):
|
||||
"""OpenAI-compatible streaming chunk."""
|
||||
|
||||
id: str
|
||||
object: str = "chat.completion.chunk"
|
||||
created: int
|
||||
|
||||
+12
-14
@@ -4,17 +4,17 @@ Chat completion service.
|
||||
Wrapper around Responses API that converts to Chat Completions format.
|
||||
Embeds reasoning in <think> tags for Open WebUI compatibility.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
import time
|
||||
import uuid
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from src.chat import constants
|
||||
from src.chat.schemas import (
|
||||
ChatCompletionChoice,
|
||||
ChatCompletionChunk,
|
||||
ChatCompletionChunkChoice,
|
||||
ChatCompletionChunkDelta,
|
||||
ChatCompletionChoice,
|
||||
ChatCompletionRequest,
|
||||
ChatCompletionResponse,
|
||||
ChatCompletionUsage,
|
||||
@@ -43,10 +43,7 @@ async def create_chat_completion(
|
||||
created_at = int(time.time())
|
||||
|
||||
# Convert Chat request to Responses request
|
||||
input_messages = [
|
||||
{"role": msg.role, "content": msg.content}
|
||||
for msg in request.messages
|
||||
]
|
||||
input_messages = [{"role": msg.role, "content": msg.content} for msg in request.messages]
|
||||
|
||||
response_request = ResponseRequest(
|
||||
model=request.model,
|
||||
@@ -54,7 +51,9 @@ async def create_chat_completion(
|
||||
reasoning={"effort": "medium", "summary": "auto"}, # Enable reasoning
|
||||
temperature=request.temperature or 1.0,
|
||||
max_output_tokens=request.max_tokens,
|
||||
stop=request.stop if isinstance(request.stop, list) else ([request.stop] if request.stop else None),
|
||||
stop=request.stop
|
||||
if isinstance(request.stop, list)
|
||||
else ([request.stop] if request.stop else None),
|
||||
)
|
||||
|
||||
# Call Responses API (will use Steward for Tatlock)
|
||||
@@ -118,16 +117,13 @@ async def create_chat_completion_stream(
|
||||
Yields:
|
||||
Chat completion chunks with reasoning as <think> tags
|
||||
"""
|
||||
from src.responses.streaming import StreamingCoordinator, StreamEventType
|
||||
from src.responses.streaming import StreamEventType, StreamingCoordinator
|
||||
|
||||
completion_id = f"chatcmpl-{uuid.uuid4().hex[:24]}"
|
||||
created_at = int(time.time())
|
||||
|
||||
# Convert Chat request to Responses request
|
||||
input_messages = [
|
||||
{"role": msg.role, "content": msg.content}
|
||||
for msg in request.messages
|
||||
]
|
||||
input_messages = [{"role": msg.role, "content": msg.content} for msg in request.messages]
|
||||
|
||||
response_request = ResponseRequest(
|
||||
model=request.model,
|
||||
@@ -135,7 +131,9 @@ async def create_chat_completion_stream(
|
||||
reasoning={"effort": "medium", "summary": "auto"},
|
||||
temperature=request.temperature or 1.0,
|
||||
max_output_tokens=request.max_tokens,
|
||||
stop=request.stop if isinstance(request.stop, list) else ([request.stop] if request.stop else None),
|
||||
stop=request.stop
|
||||
if isinstance(request.stop, list)
|
||||
else ([request.stop] if request.stop else None),
|
||||
stream=True,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user