elasticdash-test 0.1.24 → 0.1.25-alpha-2

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.
@@ -0,0 +1,195 @@
1
+ # Observability Mode
2
+
3
+ Observability mode turns the ElasticDash SDK into an always-on tracing instrument. When enabled, every `wrapTool` and `wrapAI` call automatically records and streams trace events to your ElasticDash backend — no test runner required.
4
+
5
+ ## Quick Start
6
+
7
+ ### Option 1: Programmatic (recommended)
8
+
9
+ Add a single call at your app's entry point:
10
+
11
+ ```typescript
12
+ // instrumentation.ts (Next.js) or server entry point
13
+ import { initObservability } from 'elasticdash-test/http'
14
+
15
+ const obs = initObservability({
16
+ serverUrl: 'https://server.elasticdash.com',
17
+ })
18
+
19
+ // On shutdown (optional — auto-registered on process exit)
20
+ // await obs.shutdown()
21
+ ```
22
+
23
+ ### Option 2: Environment Variables Only
24
+
25
+ If your app already uses `wrapTool` / `wrapAI`, just set the env vars:
26
+
27
+ ```bash
28
+ ELASTICDASH_API_URL=https://server.elasticdash.com \
29
+ ELASTICDASH_API_KEY=ed_key_xxx \
30
+ node server.js
31
+ ```
32
+
33
+ ### Option 3: CLI
34
+
35
+ ```bash
36
+ elasticdash observe --server https://server.elasticdash.com
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ ### `initObservability(options?)`
42
+
43
+ | Option | Env Variable | Default | Description |
44
+ |--------|-------------|---------|-------------|
45
+ | `serverUrl` | `ELASTICDASH_API_URL` | *required* | ElasticDash backend URL |
46
+ | `apiKey` | `ELASTICDASH_API_KEY` | — | Project authentication token |
47
+ | `sessionId` | `ELASTICDASH_SESSION_ID` | auto-generated UUID | Session identifier |
48
+ | `batchIntervalMs` | — | `2000` | How often to flush events (ms) |
49
+ | `maxBatchSize` | — | `50` | Max events per batch before auto-flush |
50
+ | `heartbeatIntervalMs` | — | `30000` | Heartbeat interval (ms) |
51
+ | `sampleRate` | — | `1.0` | Fraction of events to send (0.0–1.0) |
52
+ | `redactKeys` | — | `[]` | Object keys to redact from input/output |
53
+
54
+ ### Return Value
55
+
56
+ ```typescript
57
+ interface ObservabilityHandle {
58
+ sessionId: string // The active session ID
59
+ shutdown: () => Promise<void> // Graceful shutdown
60
+ }
61
+ ```
62
+
63
+ ## Grouping Events by Workflow with `startTrace()`
64
+
65
+ By default, the SDK discovers workflow names from `ed_workflows.ts`. If exactly one workflow is exported, its name is used as the traceId prefix automatically (e.g. `chatStreamHandler::1712851200000::a1b2c3d4`). If multiple workflows are exported, the traceId defaults to `unknown-workflow` until you call `startTrace()`.
66
+
67
+ To explicitly group events under a specific workflow, call `startTrace(workflowName)` at the start of each request handler:
68
+
69
+ ```typescript
70
+ import { startTrace } from 'elasticdash-test/http'
71
+
72
+ // In your route handler, before any wrapTool/wrapAI calls:
73
+ startTrace('chatStreamHandler')
74
+ ```
75
+
76
+ This sets the traceId to `chatStreamHandler::1712851200000::a1b2c3d4`, so all subsequent `wrapTool` / `wrapAI` / `wrapDB` / fetch calls in that request are grouped under the `chatStreamHandler` workflow in the dashboard.
77
+
78
+ **Important:** Call `startTrace()` before any tool/AI calls execute. If you're using a streaming `ReadableStream`, place it inside the `start()` callback:
79
+
80
+ ```typescript
81
+ const stream = new ReadableStream({
82
+ async start(controller) {
83
+ startTrace('chatStreamHandler')
84
+ // ... workflow logic with wrapTool/wrapAI calls ...
85
+ },
86
+ })
87
+ ```
88
+
89
+ ### Alternative: `wrapWorkflow()`
90
+
91
+ If you control the workflow function directly, you can use `wrapWorkflow()` instead. It calls `startTrace()` automatically before each invocation:
92
+
93
+ ```typescript
94
+ import { wrapWorkflow } from 'elasticdash-test/http'
95
+
96
+ export const chatStreamHandler = wrapWorkflow('chatStreamHandler', async (input) => {
97
+ // All tool/AI calls here are automatically grouped under 'chatStreamHandler'
98
+ const result = await fetchUser(input.userId)
99
+ return generateReply(result)
100
+ })
101
+ ```
102
+
103
+ ## How It Works
104
+
105
+ 1. **`initObservability()`** creates an `ObservabilityContext` and installs the AI interceptor
106
+ 2. Every `wrapTool(name, fn)` and `wrapAI(model, fn)` call checks for this context
107
+ 3. When active, the wrapper executes the real function, captures timing/input/output, and enqueues the event
108
+ 4. Events are batched and flushed to `POST /api/observability/events` on the backend
109
+ 5. A heartbeat event is sent every 30 seconds (configurable) so the backend knows the service is alive
110
+ 6. On process exit, remaining events are flushed and a `session_end` event is sent
111
+
112
+ ### Event Flow
113
+
114
+ ```
115
+ wrapTool("searchDB", fn) called
116
+ → fn(...args) executes normally
117
+ → WorkflowEvent created: { type: 'tool', name: 'searchDB', input, output, durationMs }
118
+ → pushTelemetryEvent(event)
119
+ → TelemetryBatcher.enqueue(event)
120
+ → Batch flushed every 2s → POST /api/observability/events
121
+ ```
122
+
123
+ ## Sampling & Redaction
124
+
125
+ ### Sampling
126
+
127
+ For high-throughput services, use `sampleRate` to reduce event volume:
128
+
129
+ ```typescript
130
+ initObservability({
131
+ serverUrl: 'https://server.elasticdash.com',
132
+ sampleRate: 0.1, // Send only 10% of events
133
+ })
134
+ ```
135
+
136
+ ### Redaction
137
+
138
+ Strip sensitive fields from input/output before sending:
139
+
140
+ ```typescript
141
+ initObservability({
142
+ serverUrl: 'https://server.elasticdash.com',
143
+ redactKeys: ['apiKey', 'password', 'ssn', 'credit_card'],
144
+ })
145
+ ```
146
+
147
+ This deep-clones and replaces matching keys (case-insensitive) with `"[REDACTED]"` before serialization.
148
+
149
+ ## Batching & Reliability
150
+
151
+ Events are not sent individually — they are buffered and flushed in batches:
152
+
153
+ - **Interval flush**: every `batchIntervalMs` (default 2 seconds)
154
+ - **Size flush**: when the buffer reaches `maxBatchSize` (default 50 events)
155
+ - **Exit flush**: on `beforeExit`, `SIGTERM`, or `SIGINT`
156
+
157
+ Failed flushes are retried with exponential backoff (1s, 2s, 4s) up to 3 times. After max retries, events are dropped to prevent memory leaks.
158
+
159
+ ## Graceful Shutdown
160
+
161
+ Shutdown happens automatically on process exit signals. For manual control (e.g., serverless functions):
162
+
163
+ ```typescript
164
+ import { shutdownObservability } from 'elasticdash-test/http'
165
+
166
+ // In your cleanup handler
167
+ await shutdownObservability()
168
+ ```
169
+
170
+ This flushes all buffered events, sends a `session_end` marker, and clears the context.
171
+
172
+ ## Debug Logging
173
+
174
+ To see SDK internal logs (telemetry push status, batch flush counts, etc.), set:
175
+
176
+ ```bash
177
+ ELASTICDASH_DEBUG=1 node server.js
178
+ ```
179
+
180
+ All internal `console.log` calls are gated behind this flag and produce no output by default.
181
+
182
+ ## Comparison with Test Mode
183
+
184
+ | Feature | Test Mode | Observability Mode |
185
+ |---------|-----------|-------------------|
186
+ | Requires test runner | Yes | No |
187
+ | Mocking support | Yes | No |
188
+ | Step replay/freezing | Yes | No |
189
+ | Event delivery | Fire-and-forget per event | Batched with retry |
190
+ | Sampling | No | Yes |
191
+ | Redaction | No | Yes |
192
+ | Heartbeat | No | Yes |
193
+ | Graceful shutdown | No | Yes |
194
+
195
+ Both modes use the same `wrapTool` / `wrapAI` wrappers — the SDK detects which context is active and routes events accordingly.