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

95
tests/test_diagnostics.py Normal file
View File

@@ -0,0 +1,95 @@
from __future__ import annotations
import base64
from nexus_claude_api.diagnostics import summarize_messages_request
from nexus_claude_api.models import AnthropicMessagesRequest
def test_text_request_summary_omits_prompt_text() -> None:
payload = AnthropicMessagesRequest.model_validate(
{
"model": "claude-opus-4.6",
"messages": [{"role": "user", "content": "secret prompt"}],
"max_tokens": 32,
}
)
summary = summarize_messages_request(payload, correlation_id="corr-1")
assert summary["correlation_id"] == "corr-1"
assert summary["model"] == "claude-opus-4.6"
assert summary["backend_model"] == "claude-opus-4.6"
assert summary["stream"] is False
assert summary["message_count"] == 1
assert summary["content_block_types"] == {"text": 1}
assert "secret prompt" not in repr(summary)
def test_image_request_summary_omits_base64_and_records_size() -> None:
image_data = base64.b64encode(b"image-bytes").decode("ascii")
payload = AnthropicMessagesRequest.model_validate(
{
"model": "claude-opus-4.6",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "describe"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data,
},
},
],
}
],
"max_tokens": 32,
}
)
summary = summarize_messages_request(payload, correlation_id="corr-2")
assert summary["content_block_types"] == {"image": 1, "text": 1}
assert summary["images"] == [{"media_type": "image/png", "byte_size": 11}]
assert image_data not in repr(summary)
assert "image-bytes" not in repr(summary)
def test_tool_request_summary_omits_tool_inputs() -> None:
payload = AnthropicMessagesRequest.model_validate(
{
"model": "claude-opus-4.6",
"messages": [
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_1",
"name": "lookup_secret",
"input": {"api_key": "secret-tool-input"},
}
],
}
],
"tools": [
{
"name": "lookup_secret",
"input_schema": {"type": "object"},
}
],
"tool_choice": {"type": "tool", "name": "lookup_secret"},
"max_tokens": 32,
}
)
summary = summarize_messages_request(payload, correlation_id="corr-3")
assert summary["tools"] == {"count": 1, "names": ["lookup_secret"]}
assert summary["tool_choice_type"] == "tool"
assert "secret-tool-input" not in repr(summary)
assert "api_key" not in repr(summary)