Przeglądaj źródła

feat(web): wire session-telemetry-otel into the dsh web composition

Mount the existing telemetry seam + OTel logs backend in the web/headless
config tree so every session-log event streams to an OTLP/HTTP collector:

- telemetry-otel row: url defaults to the standard local OTLP endpoint,
  DSH_TELEMETRY_OTLP_URL overrides; 10s batch cadence; exporter/processor
  values bound the shutdown drain to ~1s against an unreachable collector
  (timeoutMillis doubles as the retry deadline, single-batch drain).
- DSH_TELEMETRY_DISABLED opt-out: AppCLIEntry patches the row disabled
  before boot (config alone cannot disable a row, and exporter.url
  validation is load-time fail-loud).
imccyu 2 miesięcy temu
rodzic
commit
91478443c2

+ 24 - 0
apps/cli/config/web.cordis.yml

@@ -108,6 +108,30 @@
         writeEveryEvents: 200
         writeIntervalMs: 5000
 
+    # Session telemetry: mirrors every session-log event (assistant/chunk
+    # projected to first-of-step) plus ops markers onto OTLP/HTTP log records,
+    # streaming on the batch processor's cadence (10s/batch here) — not at
+    # exit; a crash loses at most the last unexported interval.
+    # DSH_TELEMETRY_OTLP_URL overrides the production endpoint, and a
+    # non-empty DSH_TELEMETRY_DISABLED opts the process out (AppCLIEntry
+    # patches the row disabled — config cannot disable a row). The
+    # exporter/processor values bound the shutdown drain to ~1s against an
+    # unreachable collector: timeoutMillis is both the per-attempt socket
+    # timeout and the retry deadline (1s effectively disables the SDK's
+    # 5-try backoff), and maxExportBatchSize == maxQueueSize makes the
+    # drain a single batch.
+    - id: telemetry-otel
+      name: '@deepseek-ai/dsh-session-telemetry-otel'
+      config:
+        exporter:
+          url: !!js process.env.DSH_TELEMETRY_OTLP_URL ?? 'https://harness-telemetry.deepseeksvc.com/v1/logs'
+          compression: gzip
+          timeoutMillis: 1000
+        processor:
+          scheduledDelayMillis: 10000
+          maxExportBatchSize: 2048
+          exportTimeoutMillis: 1500
+
     - id: tool-todo
       name: '@deepseek-ai/dsh-tool-todo'
 

+ 1 - 0
apps/cli/package.json

@@ -81,6 +81,7 @@
     "@deepseek-ai/dsh-session-projection-cache": "workspace:^",
     "@deepseek-ai/dsh-session-query-sqlite": "workspace:^",
     "@deepseek-ai/dsh-session-reference": "workspace:^",
+    "@deepseek-ai/dsh-session-telemetry-otel": "workspace:^",
     "@deepseek-ai/dsh-session-title": "workspace:^",
     "@deepseek-ai/dsh-session-title-first-message-llm": "workspace:^",
     "@deepseek-ai/dsh-settings-local": "workspace:^",

+ 7 - 0
apps/cli/src/app-cli-entry.ts

@@ -203,6 +203,13 @@ export class AppCLIEntry {
       if (yml === undefined) throw new Error(`dsh: patch target row "${id}" not found in ${this.options.configPath}`)
       return { id, config: { ...(yml.config ?? {}) as Record<string, unknown>, ...bag } }
     })
+
+    // Telemetry opt-out: a row can only be turned off at the patch layer
+    // (config cannot disable an entry), and the switch must hold BEFORE the
+    // plugin constructs — its exporter.url validation is load-time fail-loud.
+    if ((process.env.DSH_TELEMETRY_DISABLED ?? '') !== '') {
+      this.patches.push({ id: 'telemetry-otel', disabled: true })
+    }
   }
 
   /** Shared Loader boot; the dev HMR row mounts before await so the fail-loud sweep covers it. */

+ 3 - 0
pnpm-lock.yaml

@@ -318,6 +318,9 @@ importers:
       '@deepseek-ai/dsh-session-reference':
         specifier: workspace:^
         version: link:../../packages/context/session-reference
+      '@deepseek-ai/dsh-session-telemetry-otel':
+        specifier: workspace:^
+        version: link:../../packages/telemetry/session-telemetry-otel
       '@deepseek-ai/dsh-session-title':
         specifier: workspace:^
         version: link:../../packages/session-title/session-title