telemetry-dev-bedrock 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- telemetry_dev_bedrock-0.1.0/PKG-INFO +165 -0
- telemetry_dev_bedrock-0.1.0/README.md +143 -0
- telemetry_dev_bedrock-0.1.0/pyproject.toml +67 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/__init__.py +7 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_fields.py +102 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_instrument.py +231 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_invoke_model.py +241 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_messages.py +162 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_registry.py +409 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/_streams.py +575 -0
- telemetry_dev_bedrock-0.1.0/src/telemetry_dev_bedrock/py.typed +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: telemetry-dev-bedrock
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AWS Bedrock integration for telemetry.dev Python SDK
|
|
5
|
+
Keywords: telemetry,opentelemetry,bedrock,aws,boto3,genai,tracing
|
|
6
|
+
Author: telemetry.dev
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: telemetry-dev>=0.1.0
|
|
17
|
+
Requires-Dist: boto3>=1.36
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Project-URL: Homepage, https://telemetry.dev
|
|
20
|
+
Project-URL: Repository, https://github.com/telemetry-dev/telemetry.dev
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# telemetry-dev-bedrock
|
|
24
|
+
|
|
25
|
+
AWS Bedrock instrumentation for the telemetry.dev Python SDK.
|
|
26
|
+
|
|
27
|
+
This package instruments boto3/botocore clients for:
|
|
28
|
+
|
|
29
|
+
- `bedrock-runtime`
|
|
30
|
+
- `Converse`
|
|
31
|
+
- `ConverseStream`
|
|
32
|
+
- `InvokeModel`
|
|
33
|
+
- `InvokeModelWithResponseStream`
|
|
34
|
+
- `ApplyGuardrail`
|
|
35
|
+
- `bedrock-agent-runtime`
|
|
36
|
+
- `InvokeAgent`
|
|
37
|
+
- `InvokeInlineAgent`
|
|
38
|
+
- `Retrieve`
|
|
39
|
+
- `RetrieveAndGenerate`
|
|
40
|
+
- `RetrieveAndGenerateStream`
|
|
41
|
+
- `InvokeFlow`
|
|
42
|
+
|
|
43
|
+
Telemetry is emitted through `telemetry_dev.start_span`. No AWS request parameters are mutated.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
uv add telemetry-dev telemetry-dev-bedrock boto3
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or with pip:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
pip install telemetry-dev telemetry-dev-bedrock boto3
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
```py
|
|
60
|
+
import os
|
|
61
|
+
import boto3
|
|
62
|
+
import telemetry_dev
|
|
63
|
+
from telemetry_dev_bedrock import wrap_bedrock
|
|
64
|
+
|
|
65
|
+
telemetry_dev.init(
|
|
66
|
+
api_key=os.getenv("TELEMETRY_DEV_API_KEY"),
|
|
67
|
+
service_name="bedrock-app",
|
|
68
|
+
environment="production",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
bedrock = wrap_bedrock(boto3.client("bedrock-runtime", region_name="us-east-1"))
|
|
72
|
+
|
|
73
|
+
bedrock.converse(
|
|
74
|
+
modelId="anthropic.claude-3-5-haiku-20241022-v1:0",
|
|
75
|
+
messages=[{"role": "user", "content": [{"text": "Hello"}]}],
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
telemetry_dev.shutdown()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`wrap_bedrock` accepts either a `bedrock-runtime` or `bedrock-agent-runtime` boto3 client. It patches the operation methods on that instance and is idempotent.
|
|
82
|
+
|
|
83
|
+
## Global instrumentation
|
|
84
|
+
|
|
85
|
+
```py
|
|
86
|
+
from telemetry_dev_bedrock import instrument_bedrock, uninstrument_bedrock
|
|
87
|
+
|
|
88
|
+
instrument_bedrock()
|
|
89
|
+
# bedrock-runtime and bedrock-agent-runtime clients created before or after this point are covered.
|
|
90
|
+
uninstrument_bedrock()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Global instrumentation wraps `botocore.client.BaseClient._make_api_call` and filters by service name, so unrelated boto3 clients pass through untouched. Per-client `wrap_bedrock()` clients are skipped by the global wrapper to avoid double spans.
|
|
94
|
+
|
|
95
|
+
## Options
|
|
96
|
+
|
|
97
|
+
| Option | Default | Applies to | Notes |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| `capture_agent_trace` | `False` | Agent Runtime streams | Aggregates trace usage and counts by default. When enabled, also attaches raw trace events as `td.metadata.agent_trace` after SDK masking/truncation. |
|
|
100
|
+
|
|
101
|
+
```py
|
|
102
|
+
agent = wrap_bedrock(
|
|
103
|
+
boto3.client("bedrock-agent-runtime"),
|
|
104
|
+
capture_agent_trace=True,
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Signal coverage
|
|
109
|
+
|
|
110
|
+
All instrumented AWS responses/errors include `gen_ai.response.id` from the AWS request id, plus `aws.http.status_code`, `aws.request.attempts` when retries occurred, and `aws.request.total_retry_delay_ms` when the SDK exposes them.
|
|
111
|
+
|
|
112
|
+
| Operation | Span type | Span name | Captured fields |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| `Converse` | `generation` | `chat {modelId}` | normalized input/output messages, system instructions, sampling params, usage/cache usage, finish reason, request id, retry attempts, prompt-router response model, server latency, guardrail metadata |
|
|
115
|
+
| `ConverseStream` | `generation` | `chat {modelId}` | pull-through stream output, time-to-first-chunk, usage/latency from `metadata`, partial output on early break/error |
|
|
116
|
+
| `InvokeModel` | `generation` or `embedding` | `chat {modelId}` / `embeddings {modelId}` | native JSON request/response bodies, provider-native sampling, provider-native usage, embedding output type, HTTP-header token fallback |
|
|
117
|
+
| `InvokeModelWithResponseStream` | `generation` or `embedding` | `chat {modelId}` / `embeddings {modelId}` | provider-native chunk text where known, optional final `amazon-bedrock-invocationMetrics` usage |
|
|
118
|
+
| `ApplyGuardrail` | `span` | `apply_guardrail {guardrailIdentifier}` | guardrail input/output, action, action reason, request id |
|
|
119
|
+
| `InvokeAgent` / `InvokeInlineAgent` | `agent` | `invoke_agent {agentId}` / `invoke_agent {agentName}` | user input, streamed answer, session/memory ids, alias id, trace usage aggregation, trace event count, return-control output |
|
|
120
|
+
| `Retrieve` | `span` | `retrieve {knowledgeBaseId}` | query, retrieval results, guardrail action, result count metadata |
|
|
121
|
+
| `RetrieveAndGenerate` / stream | `generation` | `retrieve_and_generate {modelArn basename}` | input text, generated text, citations count, session id, guardrail action |
|
|
122
|
+
| `InvokeFlow` | `agent` | `invoke_flow {flowIdentifier}` | inputs, flow output events, completion reason |
|
|
123
|
+
|
|
124
|
+
## StreamingBody behavior
|
|
125
|
+
|
|
126
|
+
`InvokeModel` returns a botocore `StreamingBody`. The integration reads it once to capture output and token usage, then replaces it with a new `StreamingBody` over the same bytes. Caller code can still call `response["body"].read()` normally.
|
|
127
|
+
|
|
128
|
+
Event streams are wrapped lazily. The wrapper does not pre-read events, preserves backpressure, forwards unknown attributes to the inner stream, and calls `close()` on the inner stream when closed.
|
|
129
|
+
|
|
130
|
+
## Message normalization
|
|
131
|
+
|
|
132
|
+
Converse messages follow the OpenTelemetry GenAI non-normative LLM-call examples:
|
|
133
|
+
|
|
134
|
+
- text blocks become `{ "type": "text", "content": ... }`
|
|
135
|
+
- `toolUse` becomes `{ "type": "tool_call", "id", "name", "arguments" }`
|
|
136
|
+
- `toolResult` becomes `{ "type": "tool_call_response", "id", "response" }`
|
|
137
|
+
- reasoning text becomes `{ "type": "reasoning", "content" }`
|
|
138
|
+
- image/document/video/audio bytes become blob parts without byte content
|
|
139
|
+
- S3/URI sources become `{ "type": "uri", "uri", "modality" }`
|
|
140
|
+
|
|
141
|
+
InvokeModel captures provider-native JSON bodies verbatim. Non-JSON bodies are not captured.
|
|
142
|
+
|
|
143
|
+
## Semantics and guarantees
|
|
144
|
+
|
|
145
|
+
- Provider is emitted as `amazon-bedrock`. This intentionally differs from the OpenTelemetry registry value `aws.bedrock` so telemetry.dev pricing keys match Bedrock model IDs exactly. The raw caller `modelId`, `modelArn`, or `foundationModel` is emitted as the model.
|
|
146
|
+
- Instrumentation is fail-open. Normalization, span updates, and span endings are guarded so telemetry failures do not break caller code.
|
|
147
|
+
- Requests are never modified.
|
|
148
|
+
- Streams end spans exactly once. Exhaustion records full output and finish reason; early `break`, `close()`, `GeneratorExit`, or stream errors record partial output.
|
|
149
|
+
- boto3 has no async client. `aiobotocore` is a separate package and is out of scope.
|
|
150
|
+
|
|
151
|
+
## Coverage gaps
|
|
152
|
+
|
|
153
|
+
The following calls pass through unless a supported operation above is used:
|
|
154
|
+
|
|
155
|
+
- `CountTokens`
|
|
156
|
+
- `InvokeGuardrailChecks`
|
|
157
|
+
- `InvokeModelWithBidirectionalStream`
|
|
158
|
+
- `StartAsyncInvoke`, `GetAsyncInvoke`, `ListAsyncInvokes`
|
|
159
|
+
- Agent Runtime session CRUD
|
|
160
|
+
- `Rerank`
|
|
161
|
+
- `GenerateQuery`
|
|
162
|
+
- `OptimizePrompt`
|
|
163
|
+
- `AgenticRetrieveStream`
|
|
164
|
+
|
|
165
|
+
Provider-native `InvokeModel` usage is best-effort. Anthropic Claude, Amazon Titan/Nova, Meta Llama, and Titan embeddings expose known token fields. Cohere and Mistral text models do not consistently include usage in the JSON body; spans still capture request/response bodies and finish reasons where present.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# telemetry-dev-bedrock
|
|
2
|
+
|
|
3
|
+
AWS Bedrock instrumentation for the telemetry.dev Python SDK.
|
|
4
|
+
|
|
5
|
+
This package instruments boto3/botocore clients for:
|
|
6
|
+
|
|
7
|
+
- `bedrock-runtime`
|
|
8
|
+
- `Converse`
|
|
9
|
+
- `ConverseStream`
|
|
10
|
+
- `InvokeModel`
|
|
11
|
+
- `InvokeModelWithResponseStream`
|
|
12
|
+
- `ApplyGuardrail`
|
|
13
|
+
- `bedrock-agent-runtime`
|
|
14
|
+
- `InvokeAgent`
|
|
15
|
+
- `InvokeInlineAgent`
|
|
16
|
+
- `Retrieve`
|
|
17
|
+
- `RetrieveAndGenerate`
|
|
18
|
+
- `RetrieveAndGenerateStream`
|
|
19
|
+
- `InvokeFlow`
|
|
20
|
+
|
|
21
|
+
Telemetry is emitted through `telemetry_dev.start_span`. No AWS request parameters are mutated.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
uv add telemetry-dev telemetry-dev-bedrock boto3
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or with pip:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pip install telemetry-dev telemetry-dev-bedrock boto3
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```py
|
|
38
|
+
import os
|
|
39
|
+
import boto3
|
|
40
|
+
import telemetry_dev
|
|
41
|
+
from telemetry_dev_bedrock import wrap_bedrock
|
|
42
|
+
|
|
43
|
+
telemetry_dev.init(
|
|
44
|
+
api_key=os.getenv("TELEMETRY_DEV_API_KEY"),
|
|
45
|
+
service_name="bedrock-app",
|
|
46
|
+
environment="production",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
bedrock = wrap_bedrock(boto3.client("bedrock-runtime", region_name="us-east-1"))
|
|
50
|
+
|
|
51
|
+
bedrock.converse(
|
|
52
|
+
modelId="anthropic.claude-3-5-haiku-20241022-v1:0",
|
|
53
|
+
messages=[{"role": "user", "content": [{"text": "Hello"}]}],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
telemetry_dev.shutdown()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`wrap_bedrock` accepts either a `bedrock-runtime` or `bedrock-agent-runtime` boto3 client. It patches the operation methods on that instance and is idempotent.
|
|
60
|
+
|
|
61
|
+
## Global instrumentation
|
|
62
|
+
|
|
63
|
+
```py
|
|
64
|
+
from telemetry_dev_bedrock import instrument_bedrock, uninstrument_bedrock
|
|
65
|
+
|
|
66
|
+
instrument_bedrock()
|
|
67
|
+
# bedrock-runtime and bedrock-agent-runtime clients created before or after this point are covered.
|
|
68
|
+
uninstrument_bedrock()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Global instrumentation wraps `botocore.client.BaseClient._make_api_call` and filters by service name, so unrelated boto3 clients pass through untouched. Per-client `wrap_bedrock()` clients are skipped by the global wrapper to avoid double spans.
|
|
72
|
+
|
|
73
|
+
## Options
|
|
74
|
+
|
|
75
|
+
| Option | Default | Applies to | Notes |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| `capture_agent_trace` | `False` | Agent Runtime streams | Aggregates trace usage and counts by default. When enabled, also attaches raw trace events as `td.metadata.agent_trace` after SDK masking/truncation. |
|
|
78
|
+
|
|
79
|
+
```py
|
|
80
|
+
agent = wrap_bedrock(
|
|
81
|
+
boto3.client("bedrock-agent-runtime"),
|
|
82
|
+
capture_agent_trace=True,
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Signal coverage
|
|
87
|
+
|
|
88
|
+
All instrumented AWS responses/errors include `gen_ai.response.id` from the AWS request id, plus `aws.http.status_code`, `aws.request.attempts` when retries occurred, and `aws.request.total_retry_delay_ms` when the SDK exposes them.
|
|
89
|
+
|
|
90
|
+
| Operation | Span type | Span name | Captured fields |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `Converse` | `generation` | `chat {modelId}` | normalized input/output messages, system instructions, sampling params, usage/cache usage, finish reason, request id, retry attempts, prompt-router response model, server latency, guardrail metadata |
|
|
93
|
+
| `ConverseStream` | `generation` | `chat {modelId}` | pull-through stream output, time-to-first-chunk, usage/latency from `metadata`, partial output on early break/error |
|
|
94
|
+
| `InvokeModel` | `generation` or `embedding` | `chat {modelId}` / `embeddings {modelId}` | native JSON request/response bodies, provider-native sampling, provider-native usage, embedding output type, HTTP-header token fallback |
|
|
95
|
+
| `InvokeModelWithResponseStream` | `generation` or `embedding` | `chat {modelId}` / `embeddings {modelId}` | provider-native chunk text where known, optional final `amazon-bedrock-invocationMetrics` usage |
|
|
96
|
+
| `ApplyGuardrail` | `span` | `apply_guardrail {guardrailIdentifier}` | guardrail input/output, action, action reason, request id |
|
|
97
|
+
| `InvokeAgent` / `InvokeInlineAgent` | `agent` | `invoke_agent {agentId}` / `invoke_agent {agentName}` | user input, streamed answer, session/memory ids, alias id, trace usage aggregation, trace event count, return-control output |
|
|
98
|
+
| `Retrieve` | `span` | `retrieve {knowledgeBaseId}` | query, retrieval results, guardrail action, result count metadata |
|
|
99
|
+
| `RetrieveAndGenerate` / stream | `generation` | `retrieve_and_generate {modelArn basename}` | input text, generated text, citations count, session id, guardrail action |
|
|
100
|
+
| `InvokeFlow` | `agent` | `invoke_flow {flowIdentifier}` | inputs, flow output events, completion reason |
|
|
101
|
+
|
|
102
|
+
## StreamingBody behavior
|
|
103
|
+
|
|
104
|
+
`InvokeModel` returns a botocore `StreamingBody`. The integration reads it once to capture output and token usage, then replaces it with a new `StreamingBody` over the same bytes. Caller code can still call `response["body"].read()` normally.
|
|
105
|
+
|
|
106
|
+
Event streams are wrapped lazily. The wrapper does not pre-read events, preserves backpressure, forwards unknown attributes to the inner stream, and calls `close()` on the inner stream when closed.
|
|
107
|
+
|
|
108
|
+
## Message normalization
|
|
109
|
+
|
|
110
|
+
Converse messages follow the OpenTelemetry GenAI non-normative LLM-call examples:
|
|
111
|
+
|
|
112
|
+
- text blocks become `{ "type": "text", "content": ... }`
|
|
113
|
+
- `toolUse` becomes `{ "type": "tool_call", "id", "name", "arguments" }`
|
|
114
|
+
- `toolResult` becomes `{ "type": "tool_call_response", "id", "response" }`
|
|
115
|
+
- reasoning text becomes `{ "type": "reasoning", "content" }`
|
|
116
|
+
- image/document/video/audio bytes become blob parts without byte content
|
|
117
|
+
- S3/URI sources become `{ "type": "uri", "uri", "modality" }`
|
|
118
|
+
|
|
119
|
+
InvokeModel captures provider-native JSON bodies verbatim. Non-JSON bodies are not captured.
|
|
120
|
+
|
|
121
|
+
## Semantics and guarantees
|
|
122
|
+
|
|
123
|
+
- Provider is emitted as `amazon-bedrock`. This intentionally differs from the OpenTelemetry registry value `aws.bedrock` so telemetry.dev pricing keys match Bedrock model IDs exactly. The raw caller `modelId`, `modelArn`, or `foundationModel` is emitted as the model.
|
|
124
|
+
- Instrumentation is fail-open. Normalization, span updates, and span endings are guarded so telemetry failures do not break caller code.
|
|
125
|
+
- Requests are never modified.
|
|
126
|
+
- Streams end spans exactly once. Exhaustion records full output and finish reason; early `break`, `close()`, `GeneratorExit`, or stream errors record partial output.
|
|
127
|
+
- boto3 has no async client. `aiobotocore` is a separate package and is out of scope.
|
|
128
|
+
|
|
129
|
+
## Coverage gaps
|
|
130
|
+
|
|
131
|
+
The following calls pass through unless a supported operation above is used:
|
|
132
|
+
|
|
133
|
+
- `CountTokens`
|
|
134
|
+
- `InvokeGuardrailChecks`
|
|
135
|
+
- `InvokeModelWithBidirectionalStream`
|
|
136
|
+
- `StartAsyncInvoke`, `GetAsyncInvoke`, `ListAsyncInvokes`
|
|
137
|
+
- Agent Runtime session CRUD
|
|
138
|
+
- `Rerank`
|
|
139
|
+
- `GenerateQuery`
|
|
140
|
+
- `OptimizePrompt`
|
|
141
|
+
- `AgenticRetrieveStream`
|
|
142
|
+
|
|
143
|
+
Provider-native `InvokeModel` usage is best-effort. Anthropic Claude, Amazon Titan/Nova, Meta Llama, and Titan embeddings expose known token fields. Cohere and Mistral text models do not consistently include usage in the JSON body; spans still capture request/response bodies and finish reasons where present.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "telemetry-dev-bedrock"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "AWS Bedrock integration for telemetry.dev Python SDK"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [{ name = "telemetry.dev" }]
|
|
9
|
+
keywords = ["telemetry", "opentelemetry", "bedrock", "aws", "boto3", "genai", "tracing"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.10",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"telemetry-dev>=0.1.0",
|
|
22
|
+
"boto3>=1.36",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://telemetry.dev"
|
|
27
|
+
Repository = "https://github.com/telemetry-dev/telemetry.dev"
|
|
28
|
+
|
|
29
|
+
[tool.uv.sources]
|
|
30
|
+
telemetry-dev = { path = "../python", editable = true }
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = [
|
|
34
|
+
"pytest>=8.3",
|
|
35
|
+
"ruff>=0.9",
|
|
36
|
+
"pyright>=1.1.390",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
41
|
+
build-backend = "uv_build"
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 100
|
|
48
|
+
target-version = "py310"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = ["E", "F", "I", "UP", "B", "RUF"]
|
|
52
|
+
|
|
53
|
+
[tool.pyright]
|
|
54
|
+
include = ["src", "tests"]
|
|
55
|
+
typeCheckingMode = "strict"
|
|
56
|
+
pythonVersion = "3.10"
|
|
57
|
+
reportMissingTypeStubs = false
|
|
58
|
+
reportPrivateUsage = false
|
|
59
|
+
reportUnknownArgumentType = false
|
|
60
|
+
reportUnknownLambdaType = false
|
|
61
|
+
reportUnknownMemberType = false
|
|
62
|
+
reportUnknownParameterType = false
|
|
63
|
+
reportUnknownVariableType = false
|
|
64
|
+
reportArgumentType = false
|
|
65
|
+
reportAssignmentType = false
|
|
66
|
+
reportOptionalMemberAccess = false
|
|
67
|
+
reportOptionalSubscript = false
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
PROVIDER = "amazon-bedrock"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def clean(fields: dict[str, Any]) -> dict[str, Any]:
|
|
10
|
+
return {key: value for key, value in fields.items() if value is not None}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def metadata_fields(response: dict[str, Any] | BaseException | None) -> dict[str, Any]:
|
|
14
|
+
metadata: dict[str, Any] | None = None
|
|
15
|
+
if isinstance(response, dict) and isinstance(response.get("ResponseMetadata"), dict):
|
|
16
|
+
metadata = response["ResponseMetadata"]
|
|
17
|
+
else:
|
|
18
|
+
error_response = getattr(response, "response", None)
|
|
19
|
+
if isinstance(error_response, dict) and isinstance(
|
|
20
|
+
error_response.get("ResponseMetadata"), dict
|
|
21
|
+
):
|
|
22
|
+
metadata = error_response["ResponseMetadata"]
|
|
23
|
+
if metadata is None:
|
|
24
|
+
return {}
|
|
25
|
+
retry_attempts = metadata.get("RetryAttempts")
|
|
26
|
+
attempts = retry_attempts + 1 if isinstance(retry_attempts, int) else None
|
|
27
|
+
total_retry_delay = metadata.get("TotalRetryDelay")
|
|
28
|
+
if total_retry_delay is None:
|
|
29
|
+
total_retry_delay = metadata.get("totalRetryDelay")
|
|
30
|
+
attributes = clean(
|
|
31
|
+
{
|
|
32
|
+
"aws.http.status_code": metadata.get("HTTPStatusCode"),
|
|
33
|
+
"aws.request.attempts": attempts if attempts and attempts > 1 else None,
|
|
34
|
+
"aws.request.total_retry_delay_ms": total_retry_delay,
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
return clean(
|
|
38
|
+
{
|
|
39
|
+
"response_id": metadata.get("RequestId"),
|
|
40
|
+
"attributes": attributes or None,
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def error_fields(error: BaseException) -> dict[str, Any]:
|
|
46
|
+
fields = metadata_fields(error)
|
|
47
|
+
response = getattr(error, "response", None)
|
|
48
|
+
code = None
|
|
49
|
+
if isinstance(response, dict) and isinstance(response.get("Error"), dict):
|
|
50
|
+
code = response["Error"].get("Code")
|
|
51
|
+
fields["error"] = error
|
|
52
|
+
if code:
|
|
53
|
+
attrs = dict(fields.get("attributes") or {})
|
|
54
|
+
attrs["aws.error.code"] = code
|
|
55
|
+
fields["attributes"] = attrs
|
|
56
|
+
return fields
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def usage_from_converse(value: Any) -> dict[str, int] | None:
|
|
60
|
+
if not isinstance(value, dict):
|
|
61
|
+
return None
|
|
62
|
+
usage = clean(
|
|
63
|
+
{
|
|
64
|
+
"input_tokens": value.get("inputTokens"),
|
|
65
|
+
"output_tokens": value.get("outputTokens"),
|
|
66
|
+
"total_tokens": value.get("totalTokens"),
|
|
67
|
+
"cache_read_input_tokens": value.get("cacheReadInputTokens"),
|
|
68
|
+
"cache_creation_input_tokens": value.get("cacheWriteInputTokens"),
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
return usage or None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def metadata_attr_value(value: Any) -> Any:
|
|
75
|
+
if value is None or isinstance(value, str):
|
|
76
|
+
return value
|
|
77
|
+
return json.dumps(value, default=repr, ensure_ascii=False)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def merge_fields(*parts: dict[str, Any]) -> dict[str, Any]:
|
|
81
|
+
merged: dict[str, Any] = {}
|
|
82
|
+
for part in parts:
|
|
83
|
+
metadata = part.get("metadata")
|
|
84
|
+
attributes = part.get("attributes")
|
|
85
|
+
usage = part.get("usage")
|
|
86
|
+
for key, value in part.items():
|
|
87
|
+
if key not in {"metadata", "attributes", "usage"} and value is not None:
|
|
88
|
+
merged[key] = value
|
|
89
|
+
if isinstance(metadata, dict):
|
|
90
|
+
merged["metadata"] = {
|
|
91
|
+
**merged.get("metadata", {}),
|
|
92
|
+
**{
|
|
93
|
+
key: normalized
|
|
94
|
+
for key, value in metadata.items()
|
|
95
|
+
if (normalized := metadata_attr_value(value)) is not None
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
if isinstance(attributes, dict):
|
|
99
|
+
merged["attributes"] = {**merged.get("attributes", {}), **attributes}
|
|
100
|
+
if isinstance(usage, dict):
|
|
101
|
+
merged["usage"] = {**merged.get("usage", {}), **usage}
|
|
102
|
+
return merged
|