dsh-tracing
Verifieddsh-tracing · v0.1.1 · MIT
OpenTelemetry (OTLP/HTTP) + Langfuse trace exporter for DeepSeek Harness — GenAI semconv, sanitized before send, batched with retry, off by default.
Install
dsh plugin add dsh-tracing Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Readme
dsh-tracing
npm package name is
dsh-tracing(dsh-traceis blocked by npm name-similarity rules); the harness plugin id staysdsh-trace.
OpenTelemetry-compatible tracing for DeepSeek Harness sessions. Projects session events into a span tree and exports them over OTLP/HTTP (JSON) or the Langfuse v3 ingestion API — no vendor lock, no built-in panel, your existing observability stack stays the source of truth.
What you get
- Span tree per session turn:
invoke_agent turn N(root) →step N→execute_tool {name}/chat {model}, with deterministic trace IDs (sha1(sessionId:turn)) so replays and cross-references stay stable. - GenAI semantic conventions:
gen_ai.operation.name,gen_ai.request.model,gen_ai.usage.input_tokens/output_tokens, plus cache-read and reasoning token details. Token invariants are respected: cache-read counts inside input, reasoning inside output — details are exported as attributes, never re-summed. - Two backends, fan-out enabled: OTLP/HTTP (Jaeger, Tempo, Alloy, …) and
Langfuse (traces map to
trace-create, tools/steps tospan-create, LLM calls togeneration-createwithusagein Langfuse token units). - Sanitize before send: built-in key redaction (
key,token,secret,password,authorization, …), credential patterns (sk-…,ghp_…,AKIA…,Bearer …, PEM blocks), per-field truncation budgets, and an attribute-length cap. Custom keys/regexes merge in. - Batching with bounded memory: records queue up to
batch.maxQueueRecords, flush everybatch.flushIntervalMsorbatch.maxRecords, retried with exponential backoff up toretry.maxAttempts, then dropped with a warning — tracing never blocks or crashes a session. - Fail-safe lifecycle: flushes on
session/flushandsession/disposed; missing starts still emit best-effort spans (zero-duration, status derived).
Install & enable
dsh plugin --profile web add dsh-tracing
The bundled row ships enabled: false. Enable it in your profile patch:
- insert:
- id: dsh-trace
name: dsh-tracing
config:
enabled: true
otlp:
endpoint: http://localhost:4318 # /v1/traces is appended
serviceName: deepseek-harness
# langfuse:
# publicKey: pk-lf-...
# secretKey: sk-lf-...
# baseUrl: https://cloud.langfuse.com
A profile-level patch targeting this id replaces the whole row config (no deep merge) — restate every key you want to keep.
Configuration
| Key | Type | Default | Notes |
|---|---|---|---|
enabled |
boolean |
false |
No-op when false (logs and exits). |
otlp |
object? |
– | OTLP/HTTP backend. endpoint required; /v1/traces appended automatically. Optional serviceName (deepseek-harness), serviceVersion, headers. |
langfuse |
object? |
– | Langfuse backend. publicKey/secretKey required (Basic auth); optional baseUrl (cloud default), release, tags. |
capture.turns / capture.steps / capture.tools / capture.llm |
boolean |
true |
Per-kind capture switches. |
capture.llm.prompt / capture.llm.completion |
boolean |
true |
Include prompt/completion text (subject to truncation + sanitization). |
sanitize.enabled |
boolean |
true |
Master switch. |
sanitize.redactKeys |
string[] |
[] |
Extra key names to redact (case-insensitive substring match, merged with builtins). |
sanitize.redactPatterns |
string[] |
[] |
Extra regexes for values (invalid entries are skipped, not thrown). |
sanitize.truncatePromptChars / …CompletionChars / …ToolInputChars / …ToolOutputChars |
number |
4000/4000/2000/2000 | Per-field truncation budgets (marker appended: …[truncated N chars]). |
sanitize.truncateAttributeChars |
number |
1000 | Cap for any single span attribute string. |
metadata |
object |
{} |
Static key/values copied onto every span (dsh.trace.* prefix is reserved). |
batch.maxRecords / batch.flushIntervalMs / batch.maxQueueRecords |
number |
256 / 5000 / 2000 | Batching and queue bound. |
retry.maxAttempts / retry.baseDelayMs / retry.factor / retry.maxDelayMs |
number |
5 / 1000 / 2 / 60000 | Export retry backoff. |
Event mapping
The collector consumes harness session/event broadcasts and makes a
best-effort projection (snake_case/camelCase, type/event/kind, nested or
flat payloads all tolerated):
| Event shape | Projection |
|---|---|
turn/start, turn/end |
invoke_agent turn N span (root of the turn's trace) |
step/start, step/end |
step N child span |
tool/start + tool/end (matched by callId) |
execute_tool {name} span |
| assistant/model/chat/message/attempt events | chat {model} span; usage fields (input_tokens, output_tokens, cache_read…, reasoning…) accepted in both cases |
Events this plugin does not model are silently skipped. Unpaired ends still emit
a best-effort span; the first end for a session closes any dangling parents.
Known limitations (v0.1)
- Session events are shaped best-effort; harness payloads may evolve and the projector will miss fields it does not recognize (never crash).
- OTel
SpanKindis alwaysINTERNAL(1). - The Langfuse mapping identifies observations by OTel span IDs — queries that join both views should use trace IDs.
Compatibility
Targets @deepseek-ai/cordis ^4.0.1. See the repo-level
compatibility policy and the monthly CI job.
License
MIT