项目文件夹

文件
wehub-resource-sync f3d80b4628
Auto Release / auto-release (push) Failing after 1s
CI / lint (push) Failing after 0s
CI / screenshot-quality (push) Failing after 3s
CI / pr-policy (push) Has been skipped
CI / test (3.11) (push) Failing after 0s
CI / test (3.12) (push) Failing after 0s
CI / test (3.13) (push) Failing after 3s
CI / coverage (push) Failing after 1s
Legibility / legibility (push) Failing after 3s
chore: import upstream snapshot with attribution
2026-07-13 12:31:48 +08:00

192 行
7.5 KiB
Python

"""Tests for Bedrock-specific dashboard helpers (usage, model, events)."""
from __future__ import annotations
import base64
import json
from claude_tap.dashboard import (
_bedrock_events,
_record_model,
_record_response_text,
_record_usage,
)
def _bedrock_frame(payload: dict) -> str:
encoded = base64.b64encode(json.dumps(payload, separators=(",", ":")).encode()).decode()
return "\x00\x00binary-prefix" + json.dumps({"bytes": encoded, "p": "abcdefghijk"}) + ""
def _bedrock_body(*payloads: dict) -> str:
return "".join(_bedrock_frame(p) for p in payloads)
def _wrapped_bedrock_frame(payload: dict) -> str:
encoded = base64.b64encode(json.dumps(payload, separators=(",", ":")).encode()).decode()
return "\x00\x00binary-prefix" + json.dumps({"chunk": {"bytes": encoded}}) + ""
class TestBedrockEvents:
def test_decodes_eventstream_body(self):
body = _bedrock_body(
{"type": "message_start", "message": {"model": "claude-opus-4-6"}},
{"type": "message_delta", "usage": {"output_tokens": 5}},
)
record = {"response": {"body": body}}
events = _bedrock_events(record)
assert len(events) == 2
assert events[0]["event"] == "message_start"
assert events[1]["event"] == "message_delta"
def test_decodes_chunk_wrapped_eventstream_body(self):
body = _wrapped_bedrock_frame({"type": "message_start", "message": {"model": "claude-sonnet-4-6"}})
record = {"response": {"body": body}}
events = _bedrock_events(record)
assert events == [
{"event": "message_start", "data": {"type": "message_start", "message": {"model": "claude-sonnet-4-6"}}}
]
def test_decodes_converse_stream_eventstream_body(self):
body = _bedrock_body(
{"messageStart": {"role": "assistant"}},
{"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"text": "OK"}}},
{"metadata": {"usage": {"inputTokens": 6, "outputTokens": 2}}},
)
record = {"response": {"body": body}}
events = _bedrock_events(record)
assert [event["event"] for event in events] == ["message_start", "content_block_delta", "message_delta"]
assert events[1]["data"]["delta"] == {"type": "text_delta", "text": "OK"}
assert events[2]["data"]["usage"] == {"inputTokens": 6, "outputTokens": 2}
def test_decodes_raw_converse_stream_payloads(self):
body = "".join(
json.dumps(payload, separators=(",", ":"))
for payload in (
{"messageStart": {"role": "assistant"}},
{"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"text": "OK"}}},
{"metadata": {"usage": {"inputTokens": 6, "outputTokens": 2}}},
)
)
record = {"response": {"body": body}}
events = _bedrock_events(record)
assert [event["event"] for event in events] == ["message_start", "content_block_delta", "message_delta"]
def test_preserves_bedrock_error_events(self):
record = {"response": {"body": json.dumps({"modelStreamErrorException": {"message": "stream failed"}})}}
events = _bedrock_events(record)
assert events == [{"event": "modelStreamErrorException", "data": {"message": "stream failed"}}]
def test_returns_empty_for_non_bedrock_response(self):
record = {"response": {"body": {"content": [], "usage": {}}}}
assert _bedrock_events(record) == []
def test_returns_empty_for_missing_response(self):
assert _bedrock_events({}) == []
assert _bedrock_events({"response": None}) == []
class TestBedrockRecordUsage:
def test_extracts_usage_from_bedrock_events(self):
body = _bedrock_body(
{
"type": "message_start",
"message": {
"model": "claude-opus-4-6",
"usage": {"input_tokens": 10, "cache_read_input_tokens": 5, "output_tokens": 0},
},
},
{"type": "message_delta", "usage": {"output_tokens": 7}},
)
record = {"response": {"body": body}}
usage = _record_usage(record)
assert usage["input_tokens"] == 10
assert usage["output_tokens"] == 7
assert usage["cache_read_input_tokens"] == 5
def test_bedrock_usage_not_used_when_standard_usage_exists(self):
record = {"response": {"body": {"usage": {"input_tokens": 3, "output_tokens": 2}}}}
usage = _record_usage(record)
assert usage["input_tokens"] == 3
assert usage["output_tokens"] == 2
def test_extracts_usage_from_bedrock_converse_response_body(self):
record = {
"response": {
"body": {
"usage": {
"inputTokens": 9,
"outputTokens": 4,
"totalTokens": 13,
"cacheReadInputTokens": 3,
"cacheWriteInputTokens": 2,
}
}
}
}
usage = _record_usage(record)
assert usage["input_tokens"] == 9
assert usage["output_tokens"] == 4
assert usage["total_tokens"] == 13
assert usage["cache_read_input_tokens"] == 3
assert usage["cache_creation_input_tokens"] == 2
def test_extracts_usage_from_bedrock_converse_stream_metadata(self):
body = _bedrock_body(
{"messageStart": {"role": "assistant"}},
{"contentBlockDelta": {"contentBlockIndex": 0, "delta": {"text": "OK"}}},
{"metadata": {"usage": {"inputTokens": 6, "outputTokens": 2, "totalTokens": 8}}},
)
record = {"response": {"body": body}}
usage = _record_usage(record)
assert usage["input_tokens"] == 6
assert usage["output_tokens"] == 2
assert usage["total_tokens"] == 8
def test_record_response_text_reads_bedrock_converse_output_message() -> None:
record = {
"response": {
"body": {
"output": {
"message": {
"role": "assistant",
"content": [{"text": "Bedrock says OK"}],
}
}
}
}
}
assert _record_response_text(record) == "Bedrock says OK"
class TestBedrockRecordModel:
def test_extracts_model_from_bedrock_message_start(self):
body = _bedrock_body(
{"type": "message_start", "message": {"model": "claude-opus-4-6", "usage": {}}},
)
record = {"response": {"body": body}}
assert _record_model(record) == "claude-opus-4-6"
def test_extracts_model_from_bedrock_path(self):
record = {
"request": {"path": "/model/us.anthropic.claude-opus-4-6-v1/invoke-with-response-stream"},
"response": {"body": ""},
}
assert _record_model(record) == "us.anthropic.claude-opus-4-6-v1"
def test_preserves_bedrock_model_version_suffix_from_path(self):
record = {
"request": {"path": "/model/anthropic.claude-sonnet-4-20250514-v1:0/invoke"},
"response": {"body": ""},
}
assert _record_model(record) == "anthropic.claude-sonnet-4-20250514-v1:0"
def test_extracts_model_from_bedrock_converse_paths(self):
for suffix in ("converse", "converse-stream"):
record = {
"request": {"path": f"/model/anthropic.claude-sonnet-4-20250514-v1:0/{suffix}"},
"response": {"body": ""},
}
assert _record_model(record) == "anthropic.claude-sonnet-4-20250514-v1:0"