Refactor models and logging for nexus-claude-api

- Update default models in config to use `claude-opus-4.6`.
- Introduce logging configuration to write logs to a file.
- Add correlation ID to error responses for better traceability.
- Implement diagnostics for summarizing message requests.
- Normalize legacy system messages in the API.
- Enhance tests to cover new logging and error handling features.
- Update README and documentation to reflect changes in model defaults and logging behavior.
This commit is contained in:
2026-06-26 22:36:09 +08:00
parent 2851fa01cf
commit 0e98ce57d4
21 changed files with 573 additions and 78 deletions

View File

@@ -1,11 +1,14 @@
from __future__ import annotations
import logging
import uuid
from typing import Annotated
from fastapi import APIRouter, Depends, Request
from fastapi.responses import JSONResponse, Response, StreamingResponse
from pydantic import ValidationError
from nexus_claude_api.diagnostics import summarize_messages_request
from nexus_claude_api.errors import NexusClaudeError, anthropic_error_response
from nexus_claude_api.models import (
AnthropicMessagesRequest,
@@ -23,23 +26,64 @@ from nexus_claude_api.translators.stream import (
router = APIRouter()
logger = logging.getLogger(__name__)
def get_nexus_client(request: Request) -> NexusClient:
return request.app.state.nexus_client
def normalize_legacy_system_messages(raw: object) -> object:
if not isinstance(raw, dict):
return raw
messages = raw.get("messages")
if not isinstance(messages, list):
return raw
system_parts: list[object] = []
normalized_messages: list[object] = []
changed = False
for message in messages:
if isinstance(message, dict) and message.get("role") == "system":
system_parts.append(message.get("content", ""))
changed = True
else:
normalized_messages.append(message)
if not changed:
return raw
normalized = dict(raw)
normalized["messages"] = normalized_messages
if "system" not in normalized and system_parts:
normalized["system"] = system_parts[0] if len(system_parts) == 1 else system_parts
return normalized
@router.post("/v1/messages", response_model=None)
async def create_message(
request: Request,
client: Annotated[NexusClient, Depends(get_nexus_client)],
) -> Response:
correlation_id = uuid.uuid4().hex
try:
raw = await request.json()
raw = normalize_legacy_system_messages(await request.json())
payload = AnthropicMessagesRequest.model_validate(raw)
summary = summarize_messages_request(
payload,
correlation_id=correlation_id,
)
logger.info(
"anthropic_messages_request %s",
summary,
extra=summary,
)
bedrock_request = anthropic_to_bedrock_request(payload)
if payload.stream:
stream = client.converse_stream(bedrock_request)
stream = client.converse_stream(
bedrock_request,
correlation_id=correlation_id,
)
return StreamingResponse(
(
sse_frame(event)
@@ -51,26 +95,34 @@ async def create_message(
media_type="text/event-stream",
)
response = client.converse(bedrock_request)
response = client.converse(
bedrock_request,
correlation_id=correlation_id,
)
anthropic_response = bedrock_to_anthropic_response(
response,
model=payload.model,
)
return JSONResponse(content=anthropic_response.model_dump(exclude_none=True))
except ValidationError as exc:
return anthropic_error_response(str(exc), status_code=400)
return anthropic_error_response(
str(exc),
status_code=400,
correlation_id=correlation_id,
)
except NexusClaudeError as exc:
return anthropic_error_response(
exc.message,
status_code=exc.status_code,
error_type=exc.error_type,
correlation_id=correlation_id,
)
@router.post("/v1/messages/count_tokens")
async def count_tokens(request: Request) -> JSONResponse:
try:
raw = await request.json()
raw = normalize_legacy_system_messages(await request.json())
payload = CountTokensRequest.model_validate(raw)
response = CountTokensResponse(input_tokens=estimate_input_tokens(payload))
return JSONResponse(content=response.model_dump())