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.
- package/README.md +22 -0
- package/dist/cli.js +53 -6
- package/dist/cli.js.map +1 -1
- package/docs/agent-integration-guide.md +557 -0
- package/docs/agents.md +140 -0
- package/docs/backend_rerun_alignment.md +291 -0
- package/docs/backend_traceid_update.md +141 -0
- package/docs/dashboard.md +394 -0
- package/docs/deno.md +69 -0
- package/docs/instrumentation.md +424 -0
- package/docs/langfuse-trace-structure.md +145 -0
- package/docs/matchers.md +173 -0
- package/docs/observability_backend_contract.md +577 -0
- package/docs/observability_mode.md +195 -0
- package/docs/observability_rerun_backend_plan.md +596 -0
- package/docs/quickstart.md +621 -0
- package/docs/security-compliance.md +566 -0
- package/docs/test-writing-guidelines.md +444 -0
- package/docs/tools.md +165 -0
- package/docs/workflow-modes.md +253 -0
- package/package.json +2 -1
- package/src/cli.ts +60 -7
|
@@ -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.
|