Python SDK
weharness-tracing sends your agent’s traces to WeHarness. It is plain OpenTelemetry with the GenAI semantic conventions, sent over OTLP/HTTP with your ingest key.
| Package | weharness-tracing on PyPI |
| Version | 0.1.2 (changelog) |
| Python | 3.10 or later |
| Licence | Apache-2.0 |
| Depends on | opentelemetry-api, opentelemetry-sdk and opentelemetry-exporter-otlp-proto-http, version 1.45.1 |
| Import | import weharness_tracing as wh |
pip install weharness-tracingExample
Section titled “Example”import weharness_tracing as wh
wh.init(service="refund-agent") # the key comes from WEHARNESS_INGEST_KEY
with wh.agent_run("refunds"): with wh.model_call("gemini-3.8-flash", provider="gcp.gemini") as call: ... # call your model call.usage(input_tokens=120, output_tokens=40) with wh.tool_call("lookup_order"): ... # run the tool
wh.shutdown() # send what is queued before the process exitsinit()
Section titled “init()”wh.init(key=None, service=None, version=None, revision=None, endpoint=None, capture_content=False) -> TracerProviderStarts sending traces and returns the tracer provider spans go through.
| Parameter | Default | What it does |
|---|---|---|
key |
WEHARNESS_INGEST_KEY |
The ingest key. Without one, init() raises ValueError. |
service |
OTEL_SERVICE_NAME, else unknown_service |
The resource’s service.name |
version |
None | The resource’s service.version |
revision |
WEHARNESS_REVISION or a CI variable |
The resource’s vcs.ref.head.revision: the commit your application runs. See Environment variables. |
endpoint |
WEHARNESS_OTLP_ENDPOINT, else https://api.weharness.io/otlp/v1/traces |
Where spans are sent |
capture_content |
False |
Send prompts, outputs and tool payloads too. See Content capture. |
- An existing OpenTelemetry setup keeps working. When the application already registered an SDK
TracerProvider,init()adds WeHarness’s exporter to it instead of replacing it, so spans from your other instrumentation are sent too.service,versionandrevisionthen come from your provider’s resource. - Batches. Spans go in background batches (up to 512 spans, every 2 seconds, at most 2,048 queued), with a 10-second request timeout. A slow or unreachable WeHarness never blocks your agent; spans over the queue limit are dropped.
agent_run()
Section titled “agent_run()”with wh.agent_run(name, **attributes) as run: ...One run of your agent: the root of a trace. The span is named invoke_agent <name>, with gen_ai.operation.name=invoke_agent and gen_ai.agent.name=<name>. Extra keyword arguments become span attributes.
model_call()
Section titled “model_call()”with wh.model_call(model, provider=None, operation="chat", **attributes) as call: call.usage(input_tokens=120, output_tokens=40)A call to a model. The span is named <operation> <model>, of kind client, with gen_ai.operation.name, gen_ai.request.model and, when given, gen_ai.provider.name.
tool_call()
Section titled “tool_call()”with wh.tool_call(name, call_id=None, **attributes) as call: ...A tool your agent calls. The span is named execute_tool <name>, with gen_ai.operation.name=execute_tool, gen_ai.tool.name and, when given, gen_ai.tool.call.id. It also records where your code called it: code.file.path (relative to the working directory), code.line.number and code.function.name. A caller outside the working directory, or inside installed packages, gives its function name only. This is what Find in code opens.
The span helper
Section titled “The span helper”agent_run, model_call and tool_call each yield a helper:
| Method | What it does |
|---|---|
usage(input_tokens=None, output_tokens=None) |
Sets gen_ai.usage.input_tokens and gen_ai.usage.output_tokens |
set(key, value) |
Sets any attribute on the span |
span |
The OpenTelemetry span itself |
An exception raised inside the with block is recorded on the span, and the span is marked as an error.
shutdown()
Section titled “shutdown()”wh.shutdown(timeout_millis=10000)Sends what is still queued. Call it before your process exits, for example at the end of a script or in your server’s shutdown hook.
Content capture
Section titled “Content capture”By default the SDK removes content before spans leave your process: prompts, model outputs, system instructions, tool arguments and results, request and response bodies, database statements, message bodies and stack traces. wh.CONTENT lists the attribute names (and prefixes) it removes; it is the same list WeHarness’s gateway uses.
To send content, pass capture_content=True to init(), and make the ingest key with Keep prompts and outputs on. If the key does not keep content, the gateway removes it anyway. See Content capture.
The filter works on attribute names. It is not a secret detector: do not put secrets or personal data in span names, error messages or your own attributes.
Using it with frameworks
Section titled “Using it with frameworks”Pass the provider init() returns to an OpenTelemetry instrumentation:
from openinference.instrumentation.langchain import LangChainInstrumentor
provider = wh.init(service="refund-agent")LangChainInstrumentor().instrument(tracer_provider=provider)See Framework recipes.