Add Chat Completions wrapper with reasoning conversion

Implements Phase 5: OpenAI Chat Completions compatibility layer

Features:
- Wraps Responses API for single source of truth
- Automatically enables reasoning generation
- Converts reasoning items to <think> tags for Open WebUI
- Maintains OpenAI-compatible chat completion format
- Supports both streaming and non-streaming modes
- Pipeline prefix preservation for model names
- System message handling

Architecture:
- Service layer calls Responses API internally
- Streams word-by-word for smooth UX
- Reasoning displayed in thought bubbles (Open WebUI)
- Main response shown separately from thinking

Error Handling:
- Enhanced exception types (RateLimitError, ContextLengthError)
- OpenAI-compatible error format
- Graceful error propagation from Responses API

Testing:
- 6 unit tests for chat router functionality
- 6 unit tests for streaming wrapper behavior
- Total: 12 tests with comprehensive coverage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-06 19:38:35 +01:00
co-authored by Claude
parent ff6c3cf1b5
commit 5e40704d91
4 changed files with 509 additions and 46 deletions
+22 -1
View File
@@ -47,6 +47,27 @@ class ModelNotFoundError(AppException):
class ValidationError(AppException):
"""Raised for validation errors."""
def __init__(self, message: str, details: dict[str, Any] | None = None):
super().__init__(message=message, status_code=422, details=details)
class RateLimitError(AppException):
"""Raised when rate limit is exceeded."""
def __init__(self, message: str = "Rate limit exceeded"):
super().__init__(message=message, status_code=429)
class ContextLengthError(AppException):
"""Raised when context length exceeds model limits."""
def __init__(self, message: str = "Context length exceeded"):
super().__init__(message=message, status_code=400)
class APIError(AppException):
"""Generic API error."""
def __init__(self, message: str, status_code: int = 500):
super().__init__(message=message, status_code=status_code)