hue-run 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.
- hue_run-0.1.0/.gitignore +6 -0
- hue_run-0.1.0/EVALUATIONS.md +59 -0
- hue_run-0.1.0/LICENSE +18 -0
- hue_run-0.1.0/PKG-INFO +108 -0
- hue_run-0.1.0/README.md +92 -0
- hue_run-0.1.0/pyproject.toml +44 -0
- hue_run-0.1.0/src/hue_sdk/__init__.py +7 -0
- hue_run-0.1.0/src/hue_sdk/client.py +434 -0
- hue_run-0.1.0/src/hue_sdk/evals/__init__.py +33 -0
- hue_run-0.1.0/src/hue_sdk/evals/_checkpoint.py +86 -0
- hue_run-0.1.0/src/hue_sdk/evals/_json.py +90 -0
- hue_run-0.1.0/src/hue_sdk/evals/_schema_worker.py +35 -0
- hue_run-0.1.0/src/hue_sdk/evals/client.py +331 -0
- hue_run-0.1.0/src/hue_sdk/evals/runner.py +478 -0
- hue_run-0.1.0/src/hue_sdk/evals/scorers.py +298 -0
- hue_run-0.1.0/src/hue_sdk/evals/types.py +57 -0
- hue_run-0.1.0/src/hue_sdk/py.typed +0 -0
- hue_run-0.1.0/src/hue_sdk/transport.py +210 -0
- hue_run-0.1.0/tests/conftest.py +106 -0
- hue_run-0.1.0/tests/test_configuration.py +96 -0
- hue_run-0.1.0/tests/test_evaluations.py +762 -0
- hue_run-0.1.0/tests/test_openinference.py +91 -0
- hue_run-0.1.0/tests/test_sdk.py +322 -0
- hue_run-0.1.0/tests/test_wheel.py +74 -0
- hue_run-0.1.0/uv.lock +1452 -0
hue_run-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Local evaluations
|
|
2
|
+
|
|
3
|
+
`hue_sdk.evals` adds a project-key HTTP client, local experiment runner, built-in scorers, explicit Python scorer declarations, and historical rescoring. Dataset versions and scorer versions must be frozen/published before execution. The runner reads their pinned IDs and digests from Hue; it never resolves mutable “latest” definitions while running.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import os
|
|
7
|
+
from hue_sdk import Hue
|
|
8
|
+
from hue_sdk.evals import EvaluationClient, TraceEvidence, run_experiment
|
|
9
|
+
|
|
10
|
+
client = EvaluationClient(api_key=os.environ["HUE_API_KEY"])
|
|
11
|
+
|
|
12
|
+
def target(inputs, context):
|
|
13
|
+
# Call your application here. context.config is the frozen experiment config;
|
|
14
|
+
# context.item contains the frozen case and context.span is an ordinary Hue span helper.
|
|
15
|
+
return inputs["question"].upper()
|
|
16
|
+
|
|
17
|
+
with Hue(api_key=os.environ["HUE_API_KEY"], capture_content=False) as hue:
|
|
18
|
+
report = run_experiment(
|
|
19
|
+
client=client, hue=hue, experiment_id=os.environ["HUE_EXPERIMENT_ID"],
|
|
20
|
+
target=target, checkpoint_directory=".local/my-evaluation",
|
|
21
|
+
persist_result_content=True,
|
|
22
|
+
trace_evidence=TraceEvidence("required"),
|
|
23
|
+
)
|
|
24
|
+
print(report.run_id)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The [evaluation guide](https://docs.hue.run/evaluations/first-evaluation) covers creating datasets and scorers before running an experiment.
|
|
28
|
+
|
|
29
|
+
## Client and definitions
|
|
30
|
+
|
|
31
|
+
`EvaluationClient(api_key=..., timeout_seconds=10)` uses the same project service key as telemetry and defaults to `https://app.hue.run`. Set `base_url` to override the origin for another Hue deployment; `EvaluationClient(base_url, api_key)` remains supported. Its methods cover dataset creation/versioning/cases/freezing, scorer creation/publication, experiment creation/start/completion/finish, evaluation runs/subjects/results, and hosted judge job submission/list/get/cancel/budget reads. Python method arguments use snake_case; response dictionaries and `complete_execution` payloads retain the documented HTTP camelCase fields. Reads are paged with `after`/`limit`. Mutations never retry implicitly: retain their `idempotency_key` when retrying an experiment or result write. HTTP failures expose only status, with no server body, key or content in the error.
|
|
32
|
+
|
|
33
|
+
`builtins.exact_match()`, `builtins.includes(case_sensitive=True)` and `builtins.json_schema(schema)` return publishable declarations. Exact match preserves JSON types (`False` differs from `0`), object key order is irrelevant, and equivalent JSON numbers compare equally. Missing output/reference produces a skipped score, never zero. `None` is present JSON null; the exported `MISSING` sentinel represents intentional absence.
|
|
34
|
+
|
|
35
|
+
`define_local_scorer(source=..., entrypoint=..., metrics=..., score=...)` hashes explicitly supplied source text or bytes. The binding must match the pinned language, source digest, entry point and complete metric definition. This is a caller declaration, not independent attestation of closures or installed dependencies. Callbacks receive a private `ScoreContext` copy with `inputs`, optional `output`/`expected`, `has_output`/`has_expected`, `metadata`, and `execution_state`. They return `state` (`scored`, `error`, `skipped`), typed `metrics` and meaningful explanation/evidence. A false quality verdict remains a scored result; invalid callback results and exceptions become typed scorer errors.
|
|
36
|
+
|
|
37
|
+
Manual and `llm_judge` pins are deferred to their owning service and returned in `report.deferred_scorer_version_ids`. The local runner writes no synthetic skipped result into those slots. Use the explicit judge-job client methods to request hosted execution; submitting jobs can consume the project's configured allowance.
|
|
38
|
+
|
|
39
|
+
JSON Schema uses pinned `jsonschema` 4.26.0 with Draft 2020-12, a non-fetching registry, no format checker, no coercion/default insertion, and a fresh subprocess that is terminated on timeout. The default timeout is 2 seconds; `schema_timeout_millis` supports 100–60000. Python regular expressions follow Python's regex engine; use portable expressions for comparisons with other runtime implementations. [Official reference resolution documentation](https://python-jsonschema.readthedocs.io/en/stable/referencing/) explains the explicit registry model. Trusted custom target/scorer callbacks have no claimed timeout or cancellation sandbox.
|
|
40
|
+
|
|
41
|
+
JSON values are bounded to 200 KB, depth 32 and 20,000 nodes; request bodies are at most 1 MiB and responses at most 4 MiB. Invalid Unicode/NUL, non-finite numbers, non-string object keys, cycles and non-JSON objects are rejected before writes. Shared object references are serialized at each occurrence and count toward expansion limits. Python integers outside the JavaScript safe integer range are rejected rather than silently rounded by the API; encode larger exact integers as strings. Connection/read timeouts bound HTTP I/O; the client does not follow redirects.
|
|
42
|
+
|
|
43
|
+
## Content and durable recovery
|
|
44
|
+
|
|
45
|
+
`persist_result_content` is required independently of telemetry `capture_content`. When false, HTTP completions and local checkpoints omit raw target output, target exception messages, scorer evidence and arbitrary explanations. Typed metrics and caller-owned identity/configuration/policy metadata remain. Locally computed scores can still be uploaded. Historical scorers skip missing stored output without invoking a local callback. Telemetry content follows the separately chosen helper setting and redactor.
|
|
46
|
+
|
|
47
|
+
`TraceEvidence("required")` ends the root span, flushes both OTLP signals, records acknowledgement in the checkpoint, then completes the execution. A failed flush leaves the known target outcome saved and raises `TelemetryExportError`. A fresh exporter cannot acknowledge a prior failed export. `TraceEvidence("omit", "reason")` is an explicit policy that omits a copied trace snapshot even if export succeeds; it permits completion when export fails and retains the declared trace identity and reason. There is no automatic fallback or target rerun.
|
|
48
|
+
|
|
49
|
+
The checkpoint directory has one exclusive owner, mode 0700, atomic fsynced files with mode 0600, integrity digests, and project/run/version/config/content-policy identity. These durability guarantees require a POSIX filesystem supporting private permissions and no-follow opens. A crash leaves `.lock`; verify that its process stopped before explicitly removing it. A lock is never automatically considered stale based on elapsed time.
|
|
50
|
+
|
|
51
|
+
The runner saves a starting marker before requesting an execution and a running marker before invoking a target. Missing saved outcomes raise `UncertainExecutionError`. A successfully returned but non-serializable output raises `OutcomeSerializationError`; it is not recorded as target failure. Inspect the exposed execution ID and use the explicit client recovery API when necessary. A new uncertain attempt requires both the latest `previous_execution_id` and `allow_uncertain_retry=True`; restarting the runner never grants this permission. An explicit new attempt should use a new checkpoint directory after the previous owner is stopped.
|
|
52
|
+
|
|
53
|
+
Prepared completion/results and their stable request keys are saved before upload. A dropped acknowledgement can replay the same request without repeating the target or acknowledged scoring. A crash before the prepared checkpoint exists is uncertain; custom scorers may recompute after a crash before their results are saved. The runner stops starting new cases after a failure and lets already-running callbacks finish safely.
|
|
54
|
+
|
|
55
|
+
`run_experiment` and `rescore` are synchronous entry points with bounded worker concurrency (1–16). Sync and async callbacks are supported inside their workers. From an async application, call the runner through `asyncio.to_thread`.
|
|
56
|
+
|
|
57
|
+
## Historical rescoring
|
|
58
|
+
|
|
59
|
+
Create a fresh run with `client.create_evaluation_run(idempotency_key=..., name=..., subject_ids=..., scorer_version_ids=...)`, then call `rescore(client=..., run_id=..., checkpoint_directory=..., persist_result_content=...)`. It reads existing immutable subjects and pins new scorer versions. It has no target callback and never re-executes an agent. Existing scores and execution states remain unchanged.
|
hue_run-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hue contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
hue_run-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: hue-run
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OpenTelemetry helpers for sending agent traces and correlated logs to Hue
|
|
5
|
+
Project-URL: Homepage, https://docs.hue.run
|
|
6
|
+
Project-URL: Documentation, https://docs.hue.run
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: jsonschema==4.26.0
|
|
11
|
+
Requires-Dist: opentelemetry-api==1.44.0
|
|
12
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.44.0
|
|
13
|
+
Requires-Dist: opentelemetry-sdk==1.44.0
|
|
14
|
+
Requires-Dist: requests<3,>=2.32.5
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Hue Python SDK
|
|
18
|
+
|
|
19
|
+
For frozen datasets, local experiments, custom scorers, durable retries and historical rescoring, see [Local evaluations](https://docs.hue.run/evaluations/first-evaluation).
|
|
20
|
+
|
|
21
|
+
Python helpers around official OpenTelemetry **1.44.0** trace and log SDKs and OTLP HTTP/protobuf exporters. Provider requests run in your application. This package does not proxy model calls or configure global OTel providers.
|
|
22
|
+
|
|
23
|
+
The distribution is named `hue-run` (`import hue_sdk`). Python 3.10+ is supported by the package contract; recorded validation below identifies the tested runtime.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install hue-run
|
|
29
|
+
# Or, in a uv project:
|
|
30
|
+
uv add hue-run
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Send a trace
|
|
34
|
+
|
|
35
|
+
In the consuming application, use only public imports:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import os
|
|
39
|
+
from hue_sdk import Hue
|
|
40
|
+
|
|
41
|
+
with Hue(
|
|
42
|
+
api_key=os.environ["HUE_API_KEY"],
|
|
43
|
+
capture_content=False, # Required: choose explicitly.
|
|
44
|
+
) as hue:
|
|
45
|
+
project = hue.validate_project()
|
|
46
|
+
with hue.context(session_id="conversation-42", user_id="observed-user-7"):
|
|
47
|
+
with hue.span("agent.run") as run:
|
|
48
|
+
run.set_input({"question": "What is 2 + 2?"})
|
|
49
|
+
with hue.tool("add") as tool:
|
|
50
|
+
tool.set_input({"a": 2, "b": 2})
|
|
51
|
+
tool.set_output(4)
|
|
52
|
+
run.set_output({"answer": 4})
|
|
53
|
+
if not hue.force_flush():
|
|
54
|
+
raise RuntimeError("Telemetry export failed; inspect Hue export_status.")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The SDK uses `https://app.hue.run` by default. Set `base_url` only for a different Hue deployment or a local receiver, using an origin without an API suffix. Existing `Hue(base_url, api_key, ...)` calls remain supported. The project service key determines the project. The SDK validates the project at `GET /api/v1/projects/current` when explicitly requested; construction itself does not perform a request. HTTP is permitted only for `localhost` and loopback IPs. Userinfo, query strings, fragments, paths and redirects are rejected. The key is sent only as `Authorization: Bearer …`; `repr(hue)`, SDK errors and status counters omit it.
|
|
58
|
+
|
|
59
|
+
## Content and semantic fields
|
|
60
|
+
|
|
61
|
+
`capture_content` has no default. `False` makes `set_input`, `set_output` and inference-log bodies omit content before it reaches an OTel queue. Explicit JSON null, empty strings and absent content stay distinct when capture is enabled. Exception recording includes the exception type and ERROR status; exception messages and stacks are always excluded by these helpers.
|
|
62
|
+
|
|
63
|
+
This setting is **not a blanket PII filter**. Custom attributes, span names, session/user identifiers, resource attributes, third-party instrumentors and other exporters remain under your control. The server stores received content; there is no automatic telemetry expiry. Delete scoped data explicitly when required by your retention policy.
|
|
64
|
+
|
|
65
|
+
Use `redactor=lambda field, value: ...` to transform content in supported helpers. It runs synchronously before serialization and export. Return a redacted JSON value; failures raise a generic `ValueError` and the field is not recorded. It does not inspect arbitrary OTel attributes or logs:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
def redact(field, value):
|
|
69
|
+
if isinstance(value, dict):
|
|
70
|
+
return {key: "[redacted]" if key == "email" else item for key, item in value.items()}
|
|
71
|
+
return value
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The callback should cover your actual nested input format; this small example is only a top-level dictionary transformation.
|
|
75
|
+
|
|
76
|
+
| Helper | Attributes / behavior |
|
|
77
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| `span(name)` | Generic `input.value` / `output.value`, optional OTel attributes and kind |
|
|
79
|
+
| `model(model, provider=...)` | `gen_ai.operation.name`, `gen_ai.request.model`, `gen_ai.provider.name`; message content in `gen_ai.input.messages` / `gen_ai.output.messages` |
|
|
80
|
+
| `tool(name, call_id=...)` | `gen_ai.operation.name=execute_tool`, `gen_ai.tool.name`, call ID, arguments and result |
|
|
81
|
+
| `context(session_id=..., user_id=...)` | Task-local `gen_ai.conversation.id` / `user.id` on nested Hue helpers; observed users are not Hue account identities |
|
|
82
|
+
| `span.set_usage(...)` | Nonnegative reported `gen_ai.usage.input_tokens` / `output_tokens`; `None` leaves a field absent |
|
|
83
|
+
| `span.log_inference(input=..., output=...)` | Correlated `gen_ai.client.inference.operation.details` log, explicitly linked to that span |
|
|
84
|
+
| `span.record_error(error)` | Exception type event and ERROR status; context managers also record escaping errors/cancellation |
|
|
85
|
+
| `Hue.inject(headers)` / `Hue.extract(headers)` | W3C trace context propagation; pass extracted context to `span(parent_context=...)` |
|
|
86
|
+
|
|
87
|
+
For model helpers, pass the message representation produced by your integration. Prefer current [GenAI message conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) when authoring your own messages. Use either span content or correlated logs for a given input/output, avoiding duplicate copies. Structured log fields contain JSON strings so null remains distinguishable from protobuf's absent value.
|
|
88
|
+
|
|
89
|
+
## Existing instrumentation
|
|
90
|
+
|
|
91
|
+
Pass an existing `opentelemetry.sdk.trace.TracerProvider` through `tracer_provider=provider` to add Hue's exporter. Hue does not call `set_tracer_provider`. It exposes `hue.tracer_provider`, `hue.tracer` and `hue.logger_provider` for explicit integration. `shutdown()` closes Hue's processors; a borrowed tracer provider and its other processors stay usable. Do not repeatedly attach Hue clients to one long-lived provider: OTel has no public processor-removal API. Create one client per provider lifecycle.
|
|
92
|
+
|
|
93
|
+
An instrumentor that accepts `tracer_provider` can receive `hue.tracer_provider`; follow that instrumentor's own capture/redaction configuration. OpenInference and other OTel instrumentors are optional dependencies, not implicitly enabled. They can emit content even when Hue helper capture is disabled. The optional compatibility group pins **OpenAI 3.14.0**, **OpenInference OpenAI 0.1.60** and its resolved **OpenInference instrumentation 0.1.63**. A synthetic HTTP streaming response verifies parentage, canonical model/usage attributes and enabled/disabled message capture with `TraceConfig(enable_genai_semconv=True, hide_inputs=..., hide_outputs=..., hide_input_messages=..., hide_output_messages=...)`. This is a tested adapter combination, not a claim about all OpenAI APIs or live-provider compatibility. See the [instrumentor's official source](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai). See the [Python integration guide](https://docs.hue.run/sdks/python) for application setup.
|
|
94
|
+
|
|
95
|
+
## Export behavior and limits
|
|
96
|
+
|
|
97
|
+
- Traces go to `/api/v1/otlp/v1/traces`; correlated logs go to `/api/v1/otlp/v1/logs`. Both use the official OTLP HTTP/protobuf exporter, uncompressed. The official retry policy remains active for retryable network/service failures. There is no proprietary provider transport.
|
|
98
|
+
- Batches initially contain at most 64 records and are split by encoded protobuf size to fit **1 MiB**, both on wire and after decoding. A single oversized record fails visibly through export status. Helper content exceeding **256 KiB** UTF-8 JSON raises before enqueue; it is never silently truncated by Hue. Third-party record validation remains the receiver's responsibility. OTel's own attribute/count/environment limits can still affect externally configured providers.
|
|
99
|
+
- Hue accepts at most **2,000 distinct spans per trace**. This is enforced by the receiver across distributed producers; the client cannot guarantee a global count.
|
|
100
|
+
- HTTP errors, malformed/non-200 success responses and OTLP `partial_success` rejected counts cause a failed export status. Partial rejection is not retried wholesale. Receiver error text is not echoed. A warning-only partial-success response with zero rejected records remains successful.
|
|
101
|
+
- `force_flush(timeout_millis=30000)` drains both processors and returns `False` for a timeout or any recorded failed export batch since this client was created. Because OTel 1.44 ignores its processor timeout, Hue serializes flushes in one background worker and bounds the caller's wait. Pending exports continue after timeout. `export_status` exposes cumulative failure counters. `shutdown()` stops new helpers, drains and closes owned exporters within the caller's wait budget; repeated calls wait for the same shutdown. After a timeout, keep the process alive and call shutdown again to confirm completion. Context-manager exit calls shutdown; check flush explicitly when an exit code must reflect delivery failure.
|
|
102
|
+
- Standard OTel batch queues hold 2,048 records per signal and are in-memory. Queue overflow, process termination and sampling can lose telemetry. Flush success reports observed exporter outcomes, not durable local delivery or proof that every application operation was instrumented. The exporter timeout controls individual export/retry operations. A caller timeout does not cancel an HTTP request already in flight; background workers continue until the operation completes.
|
|
103
|
+
|
|
104
|
+
## Supported runtimes and verification
|
|
105
|
+
|
|
106
|
+
Python 3.10+ is supported. CI tests Python 3.10 and 3.14, source imports and an independently installed wheel. Tests use synthetic loopback HTTP receivers and decode official OTLP protobuf messages to verify trace/log correlation, metadata-only capture, redaction, propagation, existing-provider ownership, authentication failures, redirects, partial rejection, retries and encoded request limits. Compatibility tests also exercise local evaluations and the optional OpenInference adapter. No live model provider is required for these checks.
|
|
107
|
+
|
|
108
|
+
See the [documentation](https://docs.hue.run/sdks/python) for integration guidance and [troubleshooting](https://docs.hue.run/guides/troubleshooting) for export failures.
|
hue_run-0.1.0/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Hue Python SDK
|
|
2
|
+
|
|
3
|
+
For frozen datasets, local experiments, custom scorers, durable retries and historical rescoring, see [Local evaluations](https://docs.hue.run/evaluations/first-evaluation).
|
|
4
|
+
|
|
5
|
+
Python helpers around official OpenTelemetry **1.44.0** trace and log SDKs and OTLP HTTP/protobuf exporters. Provider requests run in your application. This package does not proxy model calls or configure global OTel providers.
|
|
6
|
+
|
|
7
|
+
The distribution is named `hue-run` (`import hue_sdk`). Python 3.10+ is supported by the package contract; recorded validation below identifies the tested runtime.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install hue-run
|
|
13
|
+
# Or, in a uv project:
|
|
14
|
+
uv add hue-run
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Send a trace
|
|
18
|
+
|
|
19
|
+
In the consuming application, use only public imports:
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import os
|
|
23
|
+
from hue_sdk import Hue
|
|
24
|
+
|
|
25
|
+
with Hue(
|
|
26
|
+
api_key=os.environ["HUE_API_KEY"],
|
|
27
|
+
capture_content=False, # Required: choose explicitly.
|
|
28
|
+
) as hue:
|
|
29
|
+
project = hue.validate_project()
|
|
30
|
+
with hue.context(session_id="conversation-42", user_id="observed-user-7"):
|
|
31
|
+
with hue.span("agent.run") as run:
|
|
32
|
+
run.set_input({"question": "What is 2 + 2?"})
|
|
33
|
+
with hue.tool("add") as tool:
|
|
34
|
+
tool.set_input({"a": 2, "b": 2})
|
|
35
|
+
tool.set_output(4)
|
|
36
|
+
run.set_output({"answer": 4})
|
|
37
|
+
if not hue.force_flush():
|
|
38
|
+
raise RuntimeError("Telemetry export failed; inspect Hue export_status.")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The SDK uses `https://app.hue.run` by default. Set `base_url` only for a different Hue deployment or a local receiver, using an origin without an API suffix. Existing `Hue(base_url, api_key, ...)` calls remain supported. The project service key determines the project. The SDK validates the project at `GET /api/v1/projects/current` when explicitly requested; construction itself does not perform a request. HTTP is permitted only for `localhost` and loopback IPs. Userinfo, query strings, fragments, paths and redirects are rejected. The key is sent only as `Authorization: Bearer …`; `repr(hue)`, SDK errors and status counters omit it.
|
|
42
|
+
|
|
43
|
+
## Content and semantic fields
|
|
44
|
+
|
|
45
|
+
`capture_content` has no default. `False` makes `set_input`, `set_output` and inference-log bodies omit content before it reaches an OTel queue. Explicit JSON null, empty strings and absent content stay distinct when capture is enabled. Exception recording includes the exception type and ERROR status; exception messages and stacks are always excluded by these helpers.
|
|
46
|
+
|
|
47
|
+
This setting is **not a blanket PII filter**. Custom attributes, span names, session/user identifiers, resource attributes, third-party instrumentors and other exporters remain under your control. The server stores received content; there is no automatic telemetry expiry. Delete scoped data explicitly when required by your retention policy.
|
|
48
|
+
|
|
49
|
+
Use `redactor=lambda field, value: ...` to transform content in supported helpers. It runs synchronously before serialization and export. Return a redacted JSON value; failures raise a generic `ValueError` and the field is not recorded. It does not inspect arbitrary OTel attributes or logs:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
def redact(field, value):
|
|
53
|
+
if isinstance(value, dict):
|
|
54
|
+
return {key: "[redacted]" if key == "email" else item for key, item in value.items()}
|
|
55
|
+
return value
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The callback should cover your actual nested input format; this small example is only a top-level dictionary transformation.
|
|
59
|
+
|
|
60
|
+
| Helper | Attributes / behavior |
|
|
61
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `span(name)` | Generic `input.value` / `output.value`, optional OTel attributes and kind |
|
|
63
|
+
| `model(model, provider=...)` | `gen_ai.operation.name`, `gen_ai.request.model`, `gen_ai.provider.name`; message content in `gen_ai.input.messages` / `gen_ai.output.messages` |
|
|
64
|
+
| `tool(name, call_id=...)` | `gen_ai.operation.name=execute_tool`, `gen_ai.tool.name`, call ID, arguments and result |
|
|
65
|
+
| `context(session_id=..., user_id=...)` | Task-local `gen_ai.conversation.id` / `user.id` on nested Hue helpers; observed users are not Hue account identities |
|
|
66
|
+
| `span.set_usage(...)` | Nonnegative reported `gen_ai.usage.input_tokens` / `output_tokens`; `None` leaves a field absent |
|
|
67
|
+
| `span.log_inference(input=..., output=...)` | Correlated `gen_ai.client.inference.operation.details` log, explicitly linked to that span |
|
|
68
|
+
| `span.record_error(error)` | Exception type event and ERROR status; context managers also record escaping errors/cancellation |
|
|
69
|
+
| `Hue.inject(headers)` / `Hue.extract(headers)` | W3C trace context propagation; pass extracted context to `span(parent_context=...)` |
|
|
70
|
+
|
|
71
|
+
For model helpers, pass the message representation produced by your integration. Prefer current [GenAI message conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) when authoring your own messages. Use either span content or correlated logs for a given input/output, avoiding duplicate copies. Structured log fields contain JSON strings so null remains distinguishable from protobuf's absent value.
|
|
72
|
+
|
|
73
|
+
## Existing instrumentation
|
|
74
|
+
|
|
75
|
+
Pass an existing `opentelemetry.sdk.trace.TracerProvider` through `tracer_provider=provider` to add Hue's exporter. Hue does not call `set_tracer_provider`. It exposes `hue.tracer_provider`, `hue.tracer` and `hue.logger_provider` for explicit integration. `shutdown()` closes Hue's processors; a borrowed tracer provider and its other processors stay usable. Do not repeatedly attach Hue clients to one long-lived provider: OTel has no public processor-removal API. Create one client per provider lifecycle.
|
|
76
|
+
|
|
77
|
+
An instrumentor that accepts `tracer_provider` can receive `hue.tracer_provider`; follow that instrumentor's own capture/redaction configuration. OpenInference and other OTel instrumentors are optional dependencies, not implicitly enabled. They can emit content even when Hue helper capture is disabled. The optional compatibility group pins **OpenAI 3.14.0**, **OpenInference OpenAI 0.1.60** and its resolved **OpenInference instrumentation 0.1.63**. A synthetic HTTP streaming response verifies parentage, canonical model/usage attributes and enabled/disabled message capture with `TraceConfig(enable_genai_semconv=True, hide_inputs=..., hide_outputs=..., hide_input_messages=..., hide_output_messages=...)`. This is a tested adapter combination, not a claim about all OpenAI APIs or live-provider compatibility. See the [instrumentor's official source](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai). See the [Python integration guide](https://docs.hue.run/sdks/python) for application setup.
|
|
78
|
+
|
|
79
|
+
## Export behavior and limits
|
|
80
|
+
|
|
81
|
+
- Traces go to `/api/v1/otlp/v1/traces`; correlated logs go to `/api/v1/otlp/v1/logs`. Both use the official OTLP HTTP/protobuf exporter, uncompressed. The official retry policy remains active for retryable network/service failures. There is no proprietary provider transport.
|
|
82
|
+
- Batches initially contain at most 64 records and are split by encoded protobuf size to fit **1 MiB**, both on wire and after decoding. A single oversized record fails visibly through export status. Helper content exceeding **256 KiB** UTF-8 JSON raises before enqueue; it is never silently truncated by Hue. Third-party record validation remains the receiver's responsibility. OTel's own attribute/count/environment limits can still affect externally configured providers.
|
|
83
|
+
- Hue accepts at most **2,000 distinct spans per trace**. This is enforced by the receiver across distributed producers; the client cannot guarantee a global count.
|
|
84
|
+
- HTTP errors, malformed/non-200 success responses and OTLP `partial_success` rejected counts cause a failed export status. Partial rejection is not retried wholesale. Receiver error text is not echoed. A warning-only partial-success response with zero rejected records remains successful.
|
|
85
|
+
- `force_flush(timeout_millis=30000)` drains both processors and returns `False` for a timeout or any recorded failed export batch since this client was created. Because OTel 1.44 ignores its processor timeout, Hue serializes flushes in one background worker and bounds the caller's wait. Pending exports continue after timeout. `export_status` exposes cumulative failure counters. `shutdown()` stops new helpers, drains and closes owned exporters within the caller's wait budget; repeated calls wait for the same shutdown. After a timeout, keep the process alive and call shutdown again to confirm completion. Context-manager exit calls shutdown; check flush explicitly when an exit code must reflect delivery failure.
|
|
86
|
+
- Standard OTel batch queues hold 2,048 records per signal and are in-memory. Queue overflow, process termination and sampling can lose telemetry. Flush success reports observed exporter outcomes, not durable local delivery or proof that every application operation was instrumented. The exporter timeout controls individual export/retry operations. A caller timeout does not cancel an HTTP request already in flight; background workers continue until the operation completes.
|
|
87
|
+
|
|
88
|
+
## Supported runtimes and verification
|
|
89
|
+
|
|
90
|
+
Python 3.10+ is supported. CI tests Python 3.10 and 3.14, source imports and an independently installed wheel. Tests use synthetic loopback HTTP receivers and decode official OTLP protobuf messages to verify trace/log correlation, metadata-only capture, redaction, propagation, existing-provider ownership, authentication failures, redirects, partial rejection, retries and encoded request limits. Compatibility tests also exercise local evaluations and the optional OpenInference adapter. No live model provider is required for these checks.
|
|
91
|
+
|
|
92
|
+
See the [documentation](https://docs.hue.run/sdks/python) for integration guidance and [troubleshooting](https://docs.hue.run/guides/troubleshooting) for export failures.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hue-run"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "OpenTelemetry helpers for sending agent traces and correlated logs to Hue"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
dependencies = [
|
|
10
|
+
"opentelemetry-api==1.44.0",
|
|
11
|
+
"opentelemetry-sdk==1.44.0",
|
|
12
|
+
"opentelemetry-exporter-otlp-proto-http==1.44.0",
|
|
13
|
+
"requests>=2.32.5,<3",
|
|
14
|
+
"jsonschema==4.26.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://docs.hue.run"
|
|
19
|
+
Documentation = "https://docs.hue.run"
|
|
20
|
+
|
|
21
|
+
[dependency-groups]
|
|
22
|
+
dev = ["pytest>=8.3,<10", "build>=1.2,<2", "ruff>=0.15,<1", "hatchling>=1.27,<2"]
|
|
23
|
+
compatibility = ["openai==3.14.0", "openinference-instrumentation-openai==0.1.60"]
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["hatchling>=1.27,<2"]
|
|
27
|
+
build-backend = "hatchling.build"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/hue_sdk"]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
34
|
+
addopts = "-q"
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
target-version = "py310"
|
|
38
|
+
line-length = 100
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
42
|
+
|
|
43
|
+
[tool.ruff.lint.isort]
|
|
44
|
+
known-first-party = ["hue_sdk"]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Hue's public Python telemetry API. Provider requests stay in your application."""
|
|
2
|
+
|
|
3
|
+
from .client import Hue, HueSpan, Project, ProjectValidationError, Redactor
|
|
4
|
+
from .transport import ExportStatus
|
|
5
|
+
|
|
6
|
+
__all__ = ["ExportStatus", "Hue", "HueSpan", "Project", "ProjectValidationError", "Redactor"]
|
|
7
|
+
__version__ = "0.1.0"
|