bryel 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.
- bryel-0.1.0/.gitignore +7 -0
- bryel-0.1.0/PKG-INFO +101 -0
- bryel-0.1.0/README.md +73 -0
- bryel-0.1.0/examples/openai_chat.py +23 -0
- bryel-0.1.0/pyproject.toml +39 -0
- bryel-0.1.0/src/bryel/__init__.py +131 -0
- bryel-0.1.0/tests/test_bryel.py +48 -0
bryel-0.1.0/.gitignore
ADDED
bryel-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bryel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OpenTelemetry-native tracing for LLM apps and agents — ships OpenInference/OTLP traces to bryel.
|
|
5
|
+
Project-URL: Homepage, https://docs.bryel.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.bryel.ai
|
|
7
|
+
Author: bryel
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: bryel,genai,llm,observability,openinference,opentelemetry,tracing
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Requires-Dist: opentelemetry-api>=1.20.0
|
|
15
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20.0
|
|
16
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
17
|
+
Provides-Extra: anthropic
|
|
18
|
+
Requires-Dist: openinference-instrumentation-anthropic; extra == 'anthropic'
|
|
19
|
+
Provides-Extra: bedrock
|
|
20
|
+
Requires-Dist: openinference-instrumentation-bedrock; extra == 'bedrock'
|
|
21
|
+
Provides-Extra: langchain
|
|
22
|
+
Requires-Dist: openinference-instrumentation-langchain; extra == 'langchain'
|
|
23
|
+
Provides-Extra: llama-index
|
|
24
|
+
Requires-Dist: openinference-instrumentation-llama-index; extra == 'llama-index'
|
|
25
|
+
Provides-Extra: openai
|
|
26
|
+
Requires-Dist: openinference-instrumentation-openai; extra == 'openai'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# bryel (Python)
|
|
30
|
+
|
|
31
|
+
OpenTelemetry-native tracing for LLM apps and agents. One call ships your model
|
|
32
|
+
calls, tool calls, tokens, and cost to [bryel](https://docs.bryel.ai) as
|
|
33
|
+
OpenInference traces — from any framework.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add bryel # or: pip install bryel
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quickstart
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import bryel
|
|
43
|
+
bryel.init(api_key="bk_…", service_name="my-app")
|
|
44
|
+
|
|
45
|
+
# Trace OpenAI (any OpenInference instrumentor works the same way):
|
|
46
|
+
from openinference.instrumentation.openai import OpenAIInstrumentor
|
|
47
|
+
OpenAIInstrumentor().instrument()
|
|
48
|
+
|
|
49
|
+
from openai import OpenAI
|
|
50
|
+
OpenAI().chat.completions.create(
|
|
51
|
+
model="gpt-4o-mini",
|
|
52
|
+
messages=[{"role": "user", "content": "hello"}],
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Prefer bryel to wire up everything it can find? Let it instrument every installed
|
|
57
|
+
OpenInference library for you:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import bryel
|
|
61
|
+
bryel.init(api_key="bk_…", instrument=True) # instruments openai, anthropic, langchain, …
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## How it works
|
|
65
|
+
|
|
66
|
+
bryel is just the exporter. It configures an OpenTelemetry `TracerProvider` with
|
|
67
|
+
an OTLP/HTTP exporter pointed at the bryel ingest, authenticated with your API
|
|
68
|
+
key. The [OpenInference instrumentors](https://github.com/Arize-ai/openinference)
|
|
69
|
+
emit the spans; bryel's ingest normalizes both the OpenInference (`llm.*`) and
|
|
70
|
+
OpenTelemetry GenAI (`gen_ai.*`) conventions — so there's nothing to map.
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
### `bryel.init(api_key=None, *, service_name=None, endpoint=None, headers=None, set_global=True, instrument=False)`
|
|
75
|
+
|
|
76
|
+
Configures the exporter and returns the `TracerProvider`.
|
|
77
|
+
|
|
78
|
+
- `api_key` — your project API key (`bk_…`). Falls back to `$BRYEL_KEY`.
|
|
79
|
+
- `service_name` — OpenTelemetry `service.name`.
|
|
80
|
+
- `endpoint` — OTLP/HTTP traces URL. Defaults to the bryel ingest, or
|
|
81
|
+
`$BRYEL_INGEST_URL` for self-hosting.
|
|
82
|
+
- `headers` — extra headers, merged after auth.
|
|
83
|
+
- `set_global` — register as the global OTel provider (default `True`).
|
|
84
|
+
- `instrument` — also auto-instrument installed OpenInference libraries.
|
|
85
|
+
|
|
86
|
+
### `bryel.auto_instrument(tracer_provider=None) -> list[str]`
|
|
87
|
+
|
|
88
|
+
Best-effort instrument every installed OpenInference library. Returns the ones it
|
|
89
|
+
wired up. Safe to call with none installed.
|
|
90
|
+
|
|
91
|
+
### `bryel.shutdown()`
|
|
92
|
+
|
|
93
|
+
Flush pending spans and shut down. Call on process exit.
|
|
94
|
+
|
|
95
|
+
## Feedback and grouping
|
|
96
|
+
|
|
97
|
+
Group turns into a conversation and attach 👍/👎 by stamping metadata on your
|
|
98
|
+
spans — set `session.id`, `user.id`, and a `bryel.interaction.id` you mint. See
|
|
99
|
+
the [docs](https://docs.bryel.ai).
|
|
100
|
+
|
|
101
|
+
MIT © bryel
|
bryel-0.1.0/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# bryel (Python)
|
|
2
|
+
|
|
3
|
+
OpenTelemetry-native tracing for LLM apps and agents. One call ships your model
|
|
4
|
+
calls, tool calls, tokens, and cost to [bryel](https://docs.bryel.ai) as
|
|
5
|
+
OpenInference traces — from any framework.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv add bryel # or: pip install bryel
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import bryel
|
|
15
|
+
bryel.init(api_key="bk_…", service_name="my-app")
|
|
16
|
+
|
|
17
|
+
# Trace OpenAI (any OpenInference instrumentor works the same way):
|
|
18
|
+
from openinference.instrumentation.openai import OpenAIInstrumentor
|
|
19
|
+
OpenAIInstrumentor().instrument()
|
|
20
|
+
|
|
21
|
+
from openai import OpenAI
|
|
22
|
+
OpenAI().chat.completions.create(
|
|
23
|
+
model="gpt-4o-mini",
|
|
24
|
+
messages=[{"role": "user", "content": "hello"}],
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Prefer bryel to wire up everything it can find? Let it instrument every installed
|
|
29
|
+
OpenInference library for you:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import bryel
|
|
33
|
+
bryel.init(api_key="bk_…", instrument=True) # instruments openai, anthropic, langchain, …
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## How it works
|
|
37
|
+
|
|
38
|
+
bryel is just the exporter. It configures an OpenTelemetry `TracerProvider` with
|
|
39
|
+
an OTLP/HTTP exporter pointed at the bryel ingest, authenticated with your API
|
|
40
|
+
key. The [OpenInference instrumentors](https://github.com/Arize-ai/openinference)
|
|
41
|
+
emit the spans; bryel's ingest normalizes both the OpenInference (`llm.*`) and
|
|
42
|
+
OpenTelemetry GenAI (`gen_ai.*`) conventions — so there's nothing to map.
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### `bryel.init(api_key=None, *, service_name=None, endpoint=None, headers=None, set_global=True, instrument=False)`
|
|
47
|
+
|
|
48
|
+
Configures the exporter and returns the `TracerProvider`.
|
|
49
|
+
|
|
50
|
+
- `api_key` — your project API key (`bk_…`). Falls back to `$BRYEL_KEY`.
|
|
51
|
+
- `service_name` — OpenTelemetry `service.name`.
|
|
52
|
+
- `endpoint` — OTLP/HTTP traces URL. Defaults to the bryel ingest, or
|
|
53
|
+
`$BRYEL_INGEST_URL` for self-hosting.
|
|
54
|
+
- `headers` — extra headers, merged after auth.
|
|
55
|
+
- `set_global` — register as the global OTel provider (default `True`).
|
|
56
|
+
- `instrument` — also auto-instrument installed OpenInference libraries.
|
|
57
|
+
|
|
58
|
+
### `bryel.auto_instrument(tracer_provider=None) -> list[str]`
|
|
59
|
+
|
|
60
|
+
Best-effort instrument every installed OpenInference library. Returns the ones it
|
|
61
|
+
wired up. Safe to call with none installed.
|
|
62
|
+
|
|
63
|
+
### `bryel.shutdown()`
|
|
64
|
+
|
|
65
|
+
Flush pending spans and shut down. Call on process exit.
|
|
66
|
+
|
|
67
|
+
## Feedback and grouping
|
|
68
|
+
|
|
69
|
+
Group turns into a conversation and attach 👍/👎 by stamping metadata on your
|
|
70
|
+
spans — set `session.id`, `user.id`, and a `bryel.interaction.id` you mint. See
|
|
71
|
+
the [docs](https://docs.bryel.ai).
|
|
72
|
+
|
|
73
|
+
MIT © bryel
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Minimal example: trace an OpenAI call to bryel.
|
|
2
|
+
|
|
3
|
+
export BRYEL_KEY=bk_... # your bryel project key
|
|
4
|
+
export OPENAI_API_KEY=sk-... # your OpenAI key
|
|
5
|
+
uv run --with bryel[openai] --with openai examples/openai_chat.py
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import bryel
|
|
9
|
+
|
|
10
|
+
# One call sets up the exporter (reads BRYEL_KEY) + instruments installed
|
|
11
|
+
# OpenInference libraries (openai here).
|
|
12
|
+
bryel.init(service_name="bryel-python-example", instrument=True)
|
|
13
|
+
|
|
14
|
+
from openai import OpenAI # noqa: E402
|
|
15
|
+
|
|
16
|
+
resp = OpenAI().chat.completions.create(
|
|
17
|
+
model="gpt-4o-mini",
|
|
18
|
+
messages=[{"role": "user", "content": "In one sentence, what is an OpenTelemetry span?"}],
|
|
19
|
+
)
|
|
20
|
+
print(resp.choices[0].message.content)
|
|
21
|
+
|
|
22
|
+
# Flush before exit so the span is delivered.
|
|
23
|
+
bryel.shutdown()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bryel"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "OpenTelemetry-native tracing for LLM apps and agents — ships OpenInference/OTLP traces to bryel."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "bryel" }]
|
|
13
|
+
keywords = ["bryel", "llm", "tracing", "observability", "opentelemetry", "openinference", "genai"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"opentelemetry-api>=1.20.0",
|
|
21
|
+
"opentelemetry-sdk>=1.20.0",
|
|
22
|
+
"opentelemetry-exporter-otlp-proto-http>=1.20.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://docs.bryel.ai"
|
|
27
|
+
Documentation = "https://docs.bryel.ai"
|
|
28
|
+
|
|
29
|
+
# Convenience extras: pull in an OpenInference instrumentor for your stack.
|
|
30
|
+
# You can also install any instrumentor yourself; bryel just exports the spans.
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
openai = ["openinference-instrumentation-openai"]
|
|
33
|
+
anthropic = ["openinference-instrumentation-anthropic"]
|
|
34
|
+
langchain = ["openinference-instrumentation-langchain"]
|
|
35
|
+
llama-index = ["openinference-instrumentation-llama-index"]
|
|
36
|
+
bedrock = ["openinference-instrumentation-bedrock"]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/bryel"]
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""bryel — OpenTelemetry-native tracing for LLM apps and agents.
|
|
2
|
+
|
|
3
|
+
One call configures OpenTelemetry to ship traces to bryel; then any
|
|
4
|
+
OpenInference instrumentor (OpenAI, Anthropic, LangChain, LlamaIndex, …) flows
|
|
5
|
+
through automatically:
|
|
6
|
+
|
|
7
|
+
import bryel
|
|
8
|
+
bryel.init(api_key="bk_…", service_name="my-app")
|
|
9
|
+
|
|
10
|
+
from openinference.instrumentation.openai import OpenAIInstrumentor
|
|
11
|
+
OpenAIInstrumentor().instrument()
|
|
12
|
+
|
|
13
|
+
or let bryel instrument every installed OpenInference library for you:
|
|
14
|
+
|
|
15
|
+
bryel.init(api_key="bk_…", instrument=True)
|
|
16
|
+
|
|
17
|
+
bryel's ingest normalizes both the OpenInference (`llm.*`, `openinference.*`) and
|
|
18
|
+
OpenTelemetry GenAI (`gen_ai.*`) conventions, so there is nothing to map
|
|
19
|
+
client-side — the SDK is just the exporter.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import os
|
|
25
|
+
from typing import List, Mapping, Optional
|
|
26
|
+
|
|
27
|
+
from opentelemetry import trace
|
|
28
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
29
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
30
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
31
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
32
|
+
|
|
33
|
+
__all__ = ["init", "auto_instrument", "shutdown", "DEFAULT_INGEST_ENDPOINT", "__version__"]
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
36
|
+
|
|
37
|
+
#: OTLP/HTTP traces endpoint for the bryel SaaS ingest. The receiver stamps
|
|
38
|
+
#: tenant/project from the API key server-side — the SDK never sends them.
|
|
39
|
+
DEFAULT_INGEST_ENDPOINT = "https://ingest.eu.bryel.ai/v1/traces"
|
|
40
|
+
|
|
41
|
+
# Common OpenInference instrumentors, tried in order by auto_instrument(). Each
|
|
42
|
+
# is optional — missing ones are skipped, nothing is a hard dependency.
|
|
43
|
+
_INSTRUMENTORS = [
|
|
44
|
+
("openinference.instrumentation.openai", "OpenAIInstrumentor"),
|
|
45
|
+
("openinference.instrumentation.anthropic", "AnthropicInstrumentor"),
|
|
46
|
+
("openinference.instrumentation.langchain", "LangChainInstrumentor"),
|
|
47
|
+
("openinference.instrumentation.llama_index", "LlamaIndexInstrumentor"),
|
|
48
|
+
("openinference.instrumentation.bedrock", "BedrockInstrumentor"),
|
|
49
|
+
("openinference.instrumentation.groq", "GroqInstrumentor"),
|
|
50
|
+
("openinference.instrumentation.mistralai", "MistralAIInstrumentor"),
|
|
51
|
+
("openinference.instrumentation.vertexai", "VertexAIInstrumentor"),
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
_provider: Optional[TracerProvider] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def init(
|
|
58
|
+
api_key: Optional[str] = None,
|
|
59
|
+
*,
|
|
60
|
+
service_name: Optional[str] = None,
|
|
61
|
+
endpoint: Optional[str] = None,
|
|
62
|
+
headers: Optional[Mapping[str, str]] = None,
|
|
63
|
+
set_global: bool = True,
|
|
64
|
+
instrument: bool = False,
|
|
65
|
+
) -> TracerProvider:
|
|
66
|
+
"""Configure OpenTelemetry to ship traces to bryel and return the provider.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
api_key: your bryel project API key (``bk_…``). Falls back to the
|
|
70
|
+
``BRYEL_KEY`` environment variable.
|
|
71
|
+
service_name: OpenTelemetry ``service.name`` for this app. Optional.
|
|
72
|
+
endpoint: OTLP/HTTP traces URL. Defaults to the bryel ingest, or the
|
|
73
|
+
``BRYEL_INGEST_URL`` env var if set (for self-hosting).
|
|
74
|
+
headers: extra headers, merged after the auth header.
|
|
75
|
+
set_global: register the provider as the global OpenTelemetry provider
|
|
76
|
+
(so instrumentors pick it up automatically). Default True.
|
|
77
|
+
instrument: also auto-instrument every installed OpenInference library
|
|
78
|
+
(see :func:`auto_instrument`). Default False.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The configured ``TracerProvider`` (keep it to call ``.shutdown()``).
|
|
82
|
+
"""
|
|
83
|
+
global _provider
|
|
84
|
+
|
|
85
|
+
key = api_key or os.environ.get("BRYEL_KEY")
|
|
86
|
+
if not key:
|
|
87
|
+
raise ValueError("bryel.init: api_key is required (pass api_key=… or set BRYEL_KEY)")
|
|
88
|
+
|
|
89
|
+
resource = Resource.create({SERVICE_NAME: service_name} if service_name else {})
|
|
90
|
+
exporter = OTLPSpanExporter(
|
|
91
|
+
endpoint=endpoint or os.environ.get("BRYEL_INGEST_URL") or DEFAULT_INGEST_ENDPOINT,
|
|
92
|
+
headers={"Authorization": f"Bearer {key}", **(dict(headers) if headers else {})},
|
|
93
|
+
)
|
|
94
|
+
provider = TracerProvider(resource=resource)
|
|
95
|
+
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
96
|
+
|
|
97
|
+
if set_global:
|
|
98
|
+
trace.set_tracer_provider(provider)
|
|
99
|
+
_provider = provider
|
|
100
|
+
|
|
101
|
+
if instrument:
|
|
102
|
+
auto_instrument(tracer_provider=provider)
|
|
103
|
+
|
|
104
|
+
return provider
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def auto_instrument(tracer_provider: Optional[TracerProvider] = None) -> List[str]:
|
|
108
|
+
"""Best-effort: instrument every installed OpenInference library.
|
|
109
|
+
|
|
110
|
+
Safe to call whether or not any instrumentors are installed — missing ones
|
|
111
|
+
are skipped. Returns the names of the libraries that were instrumented.
|
|
112
|
+
"""
|
|
113
|
+
instrumented: List[str] = []
|
|
114
|
+
kwargs = {"tracer_provider": tracer_provider} if tracer_provider is not None else {}
|
|
115
|
+
for module_name, class_name in _INSTRUMENTORS:
|
|
116
|
+
try:
|
|
117
|
+
module = __import__(module_name, fromlist=[class_name])
|
|
118
|
+
getattr(module, class_name)().instrument(**kwargs)
|
|
119
|
+
instrumented.append(class_name)
|
|
120
|
+
except Exception:
|
|
121
|
+
# not installed, or already instrumented — skip it
|
|
122
|
+
continue
|
|
123
|
+
return instrumented
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def shutdown() -> None:
|
|
127
|
+
"""Flush pending spans and shut down the provider. Call on process exit."""
|
|
128
|
+
global _provider
|
|
129
|
+
if _provider is not None:
|
|
130
|
+
_provider.shutdown()
|
|
131
|
+
_provider = None
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import bryel
|
|
4
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_default_endpoint_matches_ingest():
|
|
8
|
+
assert bryel.DEFAULT_INGEST_ENDPOINT == "https://ingest.eu.bryel.ai/v1/traces"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_init_requires_key(monkeypatch):
|
|
12
|
+
monkeypatch.delenv("BRYEL_KEY", raising=False)
|
|
13
|
+
with pytest.raises(ValueError):
|
|
14
|
+
bryel.init()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_init_returns_provider_and_traces(monkeypatch):
|
|
18
|
+
monkeypatch.delenv("BRYEL_KEY", raising=False)
|
|
19
|
+
provider = bryel.init(
|
|
20
|
+
api_key="bk_test",
|
|
21
|
+
service_name="unit-test",
|
|
22
|
+
endpoint="http://localhost:0/v1/traces", # never actually flushed here
|
|
23
|
+
set_global=False,
|
|
24
|
+
)
|
|
25
|
+
assert isinstance(provider, TracerProvider)
|
|
26
|
+
# a tracer from the provider can start/end a span without error
|
|
27
|
+
span = provider.get_tracer("test").start_span("ping")
|
|
28
|
+
span.set_attribute("llm.model_name", "gpt-4o-mini")
|
|
29
|
+
span.end()
|
|
30
|
+
bryel.shutdown()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_api_key_from_env(monkeypatch):
|
|
34
|
+
monkeypatch.setenv("BRYEL_KEY", "bk_env")
|
|
35
|
+
provider = bryel.init(set_global=False, endpoint="http://localhost:0/v1/traces")
|
|
36
|
+
assert isinstance(provider, TracerProvider)
|
|
37
|
+
bryel.shutdown()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_auto_instrument_safe_with_none_installed():
|
|
41
|
+
# returns a list; skips libraries that aren't installed instead of raising
|
|
42
|
+
result = bryel.auto_instrument()
|
|
43
|
+
assert isinstance(result, list)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_shutdown_is_idempotent():
|
|
47
|
+
bryel.shutdown()
|
|
48
|
+
bryel.shutdown() # no-op, must not raise
|