PerryLink/dsh-observe
OpenTelemetry and Langfuse observability exporter for DeepSeek Harness: turn/step/tool/LLM spans, token and cost metrics, sanitized prompt/completion capture, async batching, bounded offline buffering, retry with backoff
Listed
0
Dev
Bundle verified
What it does
Exports the session event stream to OpenTelemetry OTLP and Langfuse as sanitized, buffered traces and metrics, off by default.
Best for
- Teams exporting DSH execution traces and metrics to OTLP or Langfuse.
- Operators investigating turn, step, tool, and LLM performance with token and cost metrics.
- Deployments needing sanitized capture, asynchronous batching, and bounded offline buffering.
Not ideal for
- Users without an OTLP or Langfuse backend; exporting requires at least one configured backend.
- Workflows that must remain entirely local and make no telemetry exports.
- Users expecting immediate telemetry after installation; capture and export are off by default and require explicit enablement.
README
๐ dsh-observe
OpenTelemetry and Langfuse observability exporter for DeepSeek Harness.
Turn session events into OTLP traces and Langfuse observations โ sanitized, buffered, off by default.
English ยท ็ฎไฝไธญๆ ยท Espaรฑol ยท Portuguรชs ยท เคนเคฟเคจเฅเคฆเฅ
Compatibility
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness 0.1.0-rc.6
|
| Node | ^22.19.0 \|\| >=24.0.0 |
| Backends | OpenTelemetry OTLP/HTTP (traces + metrics, JSON encoding) and Langfuse (LLM observability) โ either or both |
| Model | Model-agnostic: it exports the session/event stream; no model calls are made |
What you get
dsh-observe turns the harnessโs session/event stream into standard observability protocols:
- Spans โ turn, step, tool-call (duration, status, retry derivation), and LLM generation spans, linked into per-turn traces with deterministic ids.
-
Metrics โ per-provider/model token counters, USD cost counters (configurable pricing table), and the optional context-pressure gauge from
ctx.tokenMeter. - Sanitized capture โ prompt and completion bodies are redacted (structural key names + built-in secret patterns + your patterns) and truncated before anything is queued or sent.
- Reliability โ async batching (size- and timer-triggered), a bounded durable offline buffer (storage-domain) with oldest-first eviction, and deterministic exponential-backoff retries; undeliverable batches survive restarts.
-
Runtime kill switch โ the optional Typert remote (
observe/status,observe/setEnabled) lets a settings page stop and resume exporting without unmounting. -
Off by default โ
enabled: trueplus at least one backend is an explicit opt-in; nothing is captured or exported otherwise.
session/event stream
โ collector (turn/step/tool/llm spans, metrics)
โ sanitize (keys, secrets, budgets)
โโโโถ pipeline "otlp" โโ queue โโ flush โโโถ OTLP /v1/traces + /v1/metrics
โ โโ retry/backoff โโ
โโโโถ pipeline "langfuse" โโ queue โโ flush โโโถ Langfuse ingestion
โ โโ retry/backoff โโค
โโโโโโโโโโโ durable spool (offline buffer, bounded) โโ
Quick start
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-observe#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-observe
# 2. configure a backend in your profile patch (cordis.yml) and restart
dsh --profile web
Minimal OTLP configuration (the row ships commented out in cordis.patch.yml):
- insert:
- id: dsh-observe
name: dsh-observe
config:
enabled: true
otlp:
endpoint: http://localhost:4318
Then verify the row mounts:
dsh --profile web --dump-config | grep -A2 'id: dsh-observe'
Install & uninstall
-
git channel (latest
main):dsh plugin --profile web add "github:PerryLink/dsh-observe#main"โ thepreparescript builds with production dependencies only. -
npm channel (published releases):
dsh plugin --profile web add dsh-observe. -
tarball channel:
pnpm packin this repo, thendsh plugin --profile web add ./dsh-observe-<version>.tgz. -
uninstall:
dsh plugin --profile web remove dsh-observe(or remove the row from the profile patch).
If pnpm reports
ERR_PNPM_IGNORED_BUILDSfor this package (esbuildโs harmless platform-binary validation), addallowBuilds: { esbuild: true }to yourpnpm-workspace.yamlโ thedshCLI prints the exact snippet.
Configuration
All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row โ restate every key you need. cordis.patch.yml documents each key inline.
| Key | Default | Meaning |
|---|---|---|
enabled |
false |
Master switch; true plus at least one backend is the explicit opt-in |
otlp |
null |
OTLP backend config, or null to disable it |
otlp.endpoint |
(required) | OTLP base URL; /v1/traces and /v1/metrics are appended |
otlp.serviceName |
deepseek-harness |
service.name resource attribute |
otlp.serviceVersion |
(none) |
service.version resource attribute |
otlp.headers |
{} |
Extra headers merged into every export request |
otlp.timeoutMs |
10000 |
Per-request timeout |
langfuse |
null |
Langfuse backend config, or null to disable it |
langfuse.baseUrl |
https://cloud.langfuse.com |
Langfuse base URL |
langfuse.publicKey |
(required) | Project public key |
langfuse.secretKey |
(required) | Project secret key |
langfuse.release |
(none) | Release tag stamped onto traces |
langfuse.timeoutMs |
10000 |
Per-request timeout |
capture.turns |
true |
Turn lifecycle spans |
capture.steps |
true |
Step lifecycle spans |
capture.tools |
true |
Tool-call spans with sanitized arguments/results |
capture.llm |
true |
LLM generation spans |
llm.prompt |
true |
Capture the sanitized request prompt (false = sizes only) |
llm.completion |
true |
Capture the sanitized completion (false = sizes only) |
metadata.sessionId |
true |
Session id attribute |
metadata.cwd |
false |
Session working directory (a local path โ off by default) |
metadata.agentPreset |
true |
Agent preset id attribute |
metadata.model |
true |
Provider/model attributes |
metrics.tokens |
true |
Per-provider/model token counters |
metrics.cost |
true |
USD cost counters (need pricing rules to match) |
metrics.contextTokens |
true |
Context-pressure gauge (needs ctx.tokenMeter) |
pricing |
[] |
Pricing table, first match wins: { provider?, model, inputPerToken, outputPerToken, cacheReadPerToken?, cacheWritePerToken? }
|
sanitize.enabled |
true |
Redaction master switch (false disables redaction, never truncation) |
sanitize.redactKeys |
[] |
Extra key-name substrings (key/token/secret/password/authorization/credential/apiKey are always included) |
sanitize.redactPatterns |
[] |
Extra secret regular expressions |
sanitize.truncatePromptChars |
4000 |
Prompt character budget |
sanitize.truncateCompletionChars |
4000 |
Completion character budget |
sanitize.truncateToolInputChars |
2000 |
Tool argument character budget |
sanitize.truncateToolOutputChars |
2000 |
Tool result character budget |
sanitize.truncateAttributeChars |
512 |
Span attribute string budget |
batch.maxRecords |
256 |
Flush once the queue holds this many records |
batch.flushIntervalMs |
5000 |
Timer flush interval |
batch.maxQueueRecords |
2000 |
In-memory queue bound; excess spills to the buffer |
batch.maxBufferRecords |
10000 |
Durable offline buffer bound; oldest records drop first |
batch.bufferRetryIntervalMs |
30000 |
Offline buffer retry interval |
retry.maxAttempts |
5 |
Attempts per batch, including the first try |
retry.baseDelayMs |
1000 |
First backoff delay |
retry.factor |
2 |
Backoff multiplier per consecutive failure |
retry.maxDelayMs |
60000 |
Backoff ceiling |
remote.enabled |
false |
Mount the observe Typert remote (kill switch) |
Tools & surfaces
This plugin registers no model tools โ it is a background exporter. Its surfaces:
-
Consumes
session/event(span/metric collection),session/flush(best-effort export kick โ the durability checkpoint never waits on a remote backend), andsession/disposed. -
Optional remote service
observeโobserve/statusreturns the kill-switch state, configured backends, queue depths, and buffer occupancy;observe/setEnabledstops and resumes exporting at runtime.
Permissions & data
-
Permissions:
network:outboundto the endpoints you configure,session:readfor the event stream,storage:writefor the offline buffer; no native code, no filesystem access. - Data: everything sent is derived from the session log and sanitized (redaction + truncation) before it is queued, buffered, or transmitted. The offline buffer stores only sanitized records, re-validated when read back.
- Credentials: Langfuse public/secret keys travel only to the configured Langfuse endpoint; OTLP headers only to the configured OTLP endpoint. The plugin stores no credentials itself โ keep them in credential references or environment-injected values.
Security boundaries
- Off by default โ nothing is captured or exported unless you opt in explicitly.
- Sanitize before send โ structural key redaction, built-in secret patterns (API keys, GitHub tokens, AWS keys, bearer credentials, private keys), your patterns, and character budgets all apply before any record leaves memory.
- Durable boundary re-validation โ records read back from storage are checked again before a sink can see them.
- Failure loud, failure contained โ export failures warn, count, retry, and finally spool; a failing session handler is caught and logged so observability can never break the harness hot path.
- Model-visible โบ logged โ prompt/completion exports project only the logged header and the session surface; the exporter invents no content.
Known limitations
-
rc.6 only โ the plugin is developed and tested against
@deepseek-ai/dsh@0.1.0-rc.6; newer harness baselines are expected to work but are verified by the monthly compat workflow. - Metrics bypass the retry/spool path โ OTLP metrics are aggregated cumulatively, so a lost flush self-heals on the next one (by design, not a bug).
-
No sampling โ every enabled span family is exported; set
capture.*switches andbatch.maxBufferRecordsfor high-volume sessions.
Development
pnpm install # node ^22.19 || >=24
pnpm run typecheck # tsc: src + tests against the local harness checkout
pnpm run typecheck:ci # tsc against the published 0.1.0-rc.6 types (no paths)
pnpm test # vitest: 95 tests, 13 suites (real Context/Session/storage seam)
pnpm run test:coverage # coverage gate (90/80/90/90)
pnpm run build # tsdown bundle + tsc declarations (lib/)
pnpm run verify:self-contained # dependency specs resolve from the registry
pnpm run verify:artifacts # built ESM face + bundle patch present
node scripts/check-readme-sync.mjs # five-language README sync gate
pnpm pack # the published tarball
Topics
dsh, dsh-plugin, deepseek-harness, deepseek, cordis, observability, opentelemetry, otlp, langfuse, tracing
Contributors
- @PerryLink โ creator and maintainer: collector, pipelines, spool, OTLP/Langfuse sinks, sanitization, and the five-language docs.
PerryLink DSH Plugin Family
This project is one of the 29 DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:
| Plugin | One-liner |
|---|---|
| dsh-auto-review | Second-model auto-review on the approval chain, fail-closed by default |
| dsh-background-agents | Durable background child agents with a Web UI sidebar, messaging and interrupt |
| dsh-budget | Cost governance for DeepSeek Harness: budgets, carbon, and latency in one panel. |
| dsh-checkpoint-rewind | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore |
| dsh-claude-move | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
| dsh-click | Cross-platform native desktop control for DeepSeek Harness โ Windows first. |
| dsh-composer-history | Terminal-style input history for the web composer: arrows, Ctrl+R search |
| dsh-defend | Prompt-injection, jailbreak, and secret-leak defense for DeepSeek Harness. |
| dsh-doublecheck | Engineering-discipline guard: requirements grill, test gates, adversary review |
| dsh-draw | Unified static-image generation routing for DeepSeek Harness. |
| dsh-fast | Read-only performance diagnostics for DeepSeek Harness. |
| dsh-github | GitHub PR/issues integration for DSH, every write gated by approval |
| dsh-library | Local document knowledge base for DeepSeek Harness. |
| dsh-local-ai | Local-model (Ollama) integration for DeepSeek Harness. |
| dsh-lsp-actions | LSP diagnostics, formatting, completion, code actions and rename over language servers |
| dsh-mask | PII masking middleware for DeepSeek Harness โ anonymize personal data before it reaches the model, restore it at the display layer. |
| dsh-mcp-panel | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors |
| dsh-memento | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
| dsh-observe | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. |
| dsh-output-styles | Claude Code outputStyles-equivalent runtime style switching |
| dsh-permission-rules | Claude Code-style declarative allow/deny/ask permission rules with audit |
| dsh-plugin-guide | Plugin-development knowledge base as an on-demand agent skill |
| dsh-score | Multi-dimensional quality scoring for DeepSeek Harness plugins. |
| dsh-session-pin | Pin sessions in the Web sidebar with durable ordering |
| dsh-session-sync | Cross-device session sync for DeepSeek Harness โ a dedicated git mirror of your session store. |
| dsh-skill-pack-security | Security-audit skill pack: secret scan, dependency and supply-chain review |
| dsh-talk | Voice-first session loop for DeepSeek Harness: talk to it, hear it answer. |
| dsh-test-drive | Isolated install-and-smoke test drives for DeepSeek Harness plugins. |
| dsh-translate | Vendor parameter translation and deterministic JSON repair for DeepSeek Harness. |
License
Apache License 2.0 ยฉ 2026 dsh-observe contributors
Frequently Asked QuestionsFAQ
Use the verified command dsh plugin --profile default add github:PerryLink/dsh-observe in a DSH-enabled shell. The command resolves the public package metadata and keeps the plugin attached to the catalog identity shown on this page.
Compatibility follows the bundle and profile status shown above. If a profile is not detected, keep the plugin disabled there and check the repository documentation before enabling it in production.
The GitHub link and activity metadata are the source of truth for releases and maintenance. Revisit this page after a new release to confirm the catalog has observed the latest version.