telemetry-dev-openrouter 0.1.1__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_openrouter-0.1.1/PKG-INFO +142 -0
- telemetry_dev_openrouter-0.1.1/README.md +120 -0
- telemetry_dev_openrouter-0.1.1/pyproject.toml +78 -0
- telemetry_dev_openrouter-0.1.1/pyproject.toml.orig +58 -0
- telemetry_dev_openrouter-0.1.1/src/telemetry_dev_openrouter/__init__.py +1583 -0
- telemetry_dev_openrouter-0.1.1/src/telemetry_dev_openrouter/py.typed +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: telemetry-dev-openrouter
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: OpenRouter integration for telemetry.dev Python SDK
|
|
5
|
+
Keywords: telemetry,opentelemetry,openrouter,llm,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.2.0
|
|
17
|
+
Requires-Dist: openrouter>=1.1.0,<2
|
|
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-openrouter
|
|
24
|
+
|
|
25
|
+
OpenRouter SDK instrumentation for telemetry.dev. It wraps the official `openrouter` Python SDK
|
|
26
|
+
and emits telemetry.dev generation and embedding spans through `telemetry-dev`.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pip install telemetry-dev-openrouter
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Initialize the core SDK first:
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
import telemetry_dev
|
|
38
|
+
|
|
39
|
+
telemetry_dev.init(
|
|
40
|
+
api_key="td_live_...",
|
|
41
|
+
service_name="my-service",
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Per-client wrapping
|
|
46
|
+
|
|
47
|
+
```py
|
|
48
|
+
from openrouter import OpenRouter
|
|
49
|
+
from telemetry_dev_openrouter import wrap_open_router
|
|
50
|
+
|
|
51
|
+
client = wrap_open_router(OpenRouter(api_key="sk-or-v1-..."))
|
|
52
|
+
|
|
53
|
+
client.chat.send(
|
|
54
|
+
model="openai/gpt-4o-mini",
|
|
55
|
+
messages=[{"role": "user", "content": "Tell me a joke about OpenTelemetry"}],
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use this when you want explicit control over which clients are instrumented. The same wrapped
|
|
60
|
+
client supports synchronous methods and their `*_async` counterparts.
|
|
61
|
+
|
|
62
|
+
## Global instrumentation
|
|
63
|
+
|
|
64
|
+
```py
|
|
65
|
+
from openrouter import OpenRouter
|
|
66
|
+
from telemetry_dev_openrouter import instrument_openrouter, uninstrument_openrouter
|
|
67
|
+
|
|
68
|
+
instrument_openrouter()
|
|
69
|
+
client = OpenRouter(api_key="sk-or-v1-...")
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
client.responses.send(model="openai/gpt-4o-mini", input="Explain OpenTelemetry briefly")
|
|
73
|
+
finally:
|
|
74
|
+
uninstrument_openrouter()
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Use this as the app-wide one-liner at startup when all OpenRouter clients should be instrumented.
|
|
78
|
+
Repeated wrapping or global instrumentation is safe, and `uninstrument_openrouter()` restores the
|
|
79
|
+
original SDK methods.
|
|
80
|
+
|
|
81
|
+
## Instrumented surfaces
|
|
82
|
+
|
|
83
|
+
Synchronous and asynchronous variants are covered:
|
|
84
|
+
|
|
85
|
+
- `client.chat.send(...)` and `client.chat.send_async(...)`
|
|
86
|
+
- `client.responses.send(...)` and `client.responses.send_async(...)`
|
|
87
|
+
- `client.embeddings.generate(...)` and `client.embeddings.generate_async(...)`
|
|
88
|
+
|
|
89
|
+
All spans use `gen_ai.provider.name = "openrouter"`. Generation spans capture the request model and
|
|
90
|
+
input, supported sampling parameters, response model and ID, finish reasons, output messages, and
|
|
91
|
+
token usage. Embedding spans capture the request input, request and response models, and token
|
|
92
|
+
usage; embedding vectors are intentionally not captured as output.
|
|
93
|
+
|
|
94
|
+
When OpenRouter returns cost information, the integration records `gen_ai.usage.cost` from
|
|
95
|
+
`usage.cost`. If that field is absent, it falls back to
|
|
96
|
+
`usage.cost_details.upstream_inference_cost`. The cost attribute is omitted when neither value is
|
|
97
|
+
available.
|
|
98
|
+
|
|
99
|
+
## Streaming
|
|
100
|
+
|
|
101
|
+
Chat and Responses API streams are traced for sync and async callers. The integration accumulates
|
|
102
|
+
captured output, records time to first chunk, and maps terminal usage and cost when OpenRouter
|
|
103
|
+
provides them. Terminal Responses API events close the span, including completed, failed,
|
|
104
|
+
incomplete, and error events.
|
|
105
|
+
|
|
106
|
+
Responses stream output can include reconstructed output items followed by bounded
|
|
107
|
+
`{"type": "telemetry.dev.response_stream_event", "event_type": ..., "payload": ...}` entries for
|
|
108
|
+
consumed image-generation lifecycle and partial-image events, apply-patch diff events, fusion
|
|
109
|
+
lifecycle, analysis, and panel events, web-search lifecycle events, and debug timing events.
|
|
110
|
+
OpenRouter debug upstream request-body echoes are not retained. These provider-event entries share
|
|
111
|
+
the stream capture budget and set
|
|
112
|
+
`telemetry.dev.capture.truncated = true` when only a prefix can be retained.
|
|
113
|
+
|
|
114
|
+
Requests are passed to OpenRouter unchanged. The integration does not add or modify
|
|
115
|
+
`stream_options`, so streaming usage and cost are available only when they are present in the
|
|
116
|
+
stream returned by OpenRouter.
|
|
117
|
+
|
|
118
|
+
## Failure behavior
|
|
119
|
+
|
|
120
|
+
Instrumentation is fail-open when `telemetry_dev.init()` has not been called: OpenRouter requests
|
|
121
|
+
still execute and return their normal SDK values without exporting telemetry.
|
|
122
|
+
|
|
123
|
+
## Limitations
|
|
124
|
+
|
|
125
|
+
- Unconsumed streams end their spans only when the stream is exhausted, errors, or is closed.
|
|
126
|
+
- Streaming output is retained only up to the core SDK capture budget; terminal metadata and usage
|
|
127
|
+
can still be recorded after the budget is reached. A span with partial captured output includes
|
|
128
|
+
`telemetry.dev.capture.truncated = true`.
|
|
129
|
+
- Only the SDK methods listed above are instrumented in this version.
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
From `sdks/python-openrouter`:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
uv sync
|
|
137
|
+
uv run ruff format .
|
|
138
|
+
uv run pytest
|
|
139
|
+
uv run ruff format --check .
|
|
140
|
+
uv run ruff check .
|
|
141
|
+
uv run pyright
|
|
142
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# telemetry-dev-openrouter
|
|
2
|
+
|
|
3
|
+
OpenRouter SDK instrumentation for telemetry.dev. It wraps the official `openrouter` Python SDK
|
|
4
|
+
and emits telemetry.dev generation and embedding spans through `telemetry-dev`.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pip install telemetry-dev-openrouter
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Initialize the core SDK first:
|
|
13
|
+
|
|
14
|
+
```py
|
|
15
|
+
import telemetry_dev
|
|
16
|
+
|
|
17
|
+
telemetry_dev.init(
|
|
18
|
+
api_key="td_live_...",
|
|
19
|
+
service_name="my-service",
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Per-client wrapping
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
from openrouter import OpenRouter
|
|
27
|
+
from telemetry_dev_openrouter import wrap_open_router
|
|
28
|
+
|
|
29
|
+
client = wrap_open_router(OpenRouter(api_key="sk-or-v1-..."))
|
|
30
|
+
|
|
31
|
+
client.chat.send(
|
|
32
|
+
model="openai/gpt-4o-mini",
|
|
33
|
+
messages=[{"role": "user", "content": "Tell me a joke about OpenTelemetry"}],
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use this when you want explicit control over which clients are instrumented. The same wrapped
|
|
38
|
+
client supports synchronous methods and their `*_async` counterparts.
|
|
39
|
+
|
|
40
|
+
## Global instrumentation
|
|
41
|
+
|
|
42
|
+
```py
|
|
43
|
+
from openrouter import OpenRouter
|
|
44
|
+
from telemetry_dev_openrouter import instrument_openrouter, uninstrument_openrouter
|
|
45
|
+
|
|
46
|
+
instrument_openrouter()
|
|
47
|
+
client = OpenRouter(api_key="sk-or-v1-...")
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
client.responses.send(model="openai/gpt-4o-mini", input="Explain OpenTelemetry briefly")
|
|
51
|
+
finally:
|
|
52
|
+
uninstrument_openrouter()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use this as the app-wide one-liner at startup when all OpenRouter clients should be instrumented.
|
|
56
|
+
Repeated wrapping or global instrumentation is safe, and `uninstrument_openrouter()` restores the
|
|
57
|
+
original SDK methods.
|
|
58
|
+
|
|
59
|
+
## Instrumented surfaces
|
|
60
|
+
|
|
61
|
+
Synchronous and asynchronous variants are covered:
|
|
62
|
+
|
|
63
|
+
- `client.chat.send(...)` and `client.chat.send_async(...)`
|
|
64
|
+
- `client.responses.send(...)` and `client.responses.send_async(...)`
|
|
65
|
+
- `client.embeddings.generate(...)` and `client.embeddings.generate_async(...)`
|
|
66
|
+
|
|
67
|
+
All spans use `gen_ai.provider.name = "openrouter"`. Generation spans capture the request model and
|
|
68
|
+
input, supported sampling parameters, response model and ID, finish reasons, output messages, and
|
|
69
|
+
token usage. Embedding spans capture the request input, request and response models, and token
|
|
70
|
+
usage; embedding vectors are intentionally not captured as output.
|
|
71
|
+
|
|
72
|
+
When OpenRouter returns cost information, the integration records `gen_ai.usage.cost` from
|
|
73
|
+
`usage.cost`. If that field is absent, it falls back to
|
|
74
|
+
`usage.cost_details.upstream_inference_cost`. The cost attribute is omitted when neither value is
|
|
75
|
+
available.
|
|
76
|
+
|
|
77
|
+
## Streaming
|
|
78
|
+
|
|
79
|
+
Chat and Responses API streams are traced for sync and async callers. The integration accumulates
|
|
80
|
+
captured output, records time to first chunk, and maps terminal usage and cost when OpenRouter
|
|
81
|
+
provides them. Terminal Responses API events close the span, including completed, failed,
|
|
82
|
+
incomplete, and error events.
|
|
83
|
+
|
|
84
|
+
Responses stream output can include reconstructed output items followed by bounded
|
|
85
|
+
`{"type": "telemetry.dev.response_stream_event", "event_type": ..., "payload": ...}` entries for
|
|
86
|
+
consumed image-generation lifecycle and partial-image events, apply-patch diff events, fusion
|
|
87
|
+
lifecycle, analysis, and panel events, web-search lifecycle events, and debug timing events.
|
|
88
|
+
OpenRouter debug upstream request-body echoes are not retained. These provider-event entries share
|
|
89
|
+
the stream capture budget and set
|
|
90
|
+
`telemetry.dev.capture.truncated = true` when only a prefix can be retained.
|
|
91
|
+
|
|
92
|
+
Requests are passed to OpenRouter unchanged. The integration does not add or modify
|
|
93
|
+
`stream_options`, so streaming usage and cost are available only when they are present in the
|
|
94
|
+
stream returned by OpenRouter.
|
|
95
|
+
|
|
96
|
+
## Failure behavior
|
|
97
|
+
|
|
98
|
+
Instrumentation is fail-open when `telemetry_dev.init()` has not been called: OpenRouter requests
|
|
99
|
+
still execute and return their normal SDK values without exporting telemetry.
|
|
100
|
+
|
|
101
|
+
## Limitations
|
|
102
|
+
|
|
103
|
+
- Unconsumed streams end their spans only when the stream is exhausted, errors, or is closed.
|
|
104
|
+
- Streaming output is retained only up to the core SDK capture budget; terminal metadata and usage
|
|
105
|
+
can still be recorded after the budget is reached. A span with partial captured output includes
|
|
106
|
+
`telemetry.dev.capture.truncated = true`.
|
|
107
|
+
- Only the SDK methods listed above are instrumented in this version.
|
|
108
|
+
|
|
109
|
+
## Development
|
|
110
|
+
|
|
111
|
+
From `sdks/python-openrouter`:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
uv sync
|
|
115
|
+
uv run ruff format .
|
|
116
|
+
uv run pytest
|
|
117
|
+
uv run ruff format --check .
|
|
118
|
+
uv run ruff check .
|
|
119
|
+
uv run pyright
|
|
120
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "telemetry-dev-openrouter"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "OpenRouter integration for telemetry.dev Python SDK"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
keywords = [
|
|
9
|
+
"telemetry",
|
|
10
|
+
"opentelemetry",
|
|
11
|
+
"openrouter",
|
|
12
|
+
"llm",
|
|
13
|
+
"genai",
|
|
14
|
+
"tracing",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"telemetry-dev>=0.2.0",
|
|
28
|
+
"openrouter>=1.1.0,<2",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[project.authors]]
|
|
32
|
+
name = "telemetry.dev"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://telemetry.dev"
|
|
36
|
+
Repository = "https://github.com/telemetry-dev/telemetry.dev"
|
|
37
|
+
|
|
38
|
+
[tool.uv.sources.telemetry-dev]
|
|
39
|
+
path = "../python"
|
|
40
|
+
editable = true
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
asyncio_mode = "auto"
|
|
44
|
+
testpaths = ["tests"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 100
|
|
48
|
+
target-version = "py310"
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
select = [
|
|
52
|
+
"E",
|
|
53
|
+
"F",
|
|
54
|
+
"I",
|
|
55
|
+
"UP",
|
|
56
|
+
"B",
|
|
57
|
+
"RUF",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.pyright]
|
|
61
|
+
include = [
|
|
62
|
+
"src",
|
|
63
|
+
"tests",
|
|
64
|
+
]
|
|
65
|
+
typeCheckingMode = "strict"
|
|
66
|
+
pythonVersion = "3.10"
|
|
67
|
+
|
|
68
|
+
[dependency-groups]
|
|
69
|
+
dev = [
|
|
70
|
+
"pytest>=8.3",
|
|
71
|
+
"pytest-asyncio>=0.25",
|
|
72
|
+
"ruff>=0.9",
|
|
73
|
+
"pyright>=1.1.390",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[build-system]
|
|
77
|
+
requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
78
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "telemetry-dev-openrouter"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "OpenRouter 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", "openrouter", "llm", "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.2.0",
|
|
22
|
+
"openrouter>=1.1.0,<2",
|
|
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
|
+
"pytest-asyncio>=0.25",
|
|
36
|
+
"ruff>=0.9",
|
|
37
|
+
"pyright>=1.1.390",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
42
|
+
build-backend = "uv_build"
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
asyncio_mode = "auto"
|
|
46
|
+
testpaths = ["tests"]
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
line-length = 100
|
|
50
|
+
target-version = "py310"
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = ["E", "F", "I", "UP", "B", "RUF"]
|
|
54
|
+
|
|
55
|
+
[tool.pyright]
|
|
56
|
+
include = ["src", "tests"]
|
|
57
|
+
typeCheckingMode = "strict"
|
|
58
|
+
pythonVersion = "3.10"
|