elasticdash-test 0.1.25 → 0.1.26-alpha-1
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/docs/agent-integration-guide.md +200 -211
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
> **How to use**: Read the full guide first to understand the project's structure, then execute each step. Use the decision trees section to choose the right pattern for the user's project (framework type, module system, tool architecture).
|
|
8
8
|
>
|
|
9
9
|
> **Important**: Do NOT leave `YOUR_TOOL_NAME` / `YOUR_WORKFLOW_NAME` placeholders in generated files. Before creating `ed_tools.ts` and `ed_workflows.ts`, scan the user's codebase to identify their actual tool functions (functions that call external APIs, databases, or services) and workflow functions (async functions that orchestrate LLM calls and tool calls). Create the files with the real function names, imports, and types found in the codebase. If no tools or workflows exist yet, ask the user which functions to instrument rather than generating placeholder files.
|
|
10
|
+
>
|
|
11
|
+
> **Automation level**: Automatically implement all steps unless you are not confident about a specific change (e.g., unclear which function is a tool vs. a utility, or ambiguous workflow boundaries). In those cases, ask the user before proceeding. For straightforward tool/workflow functions, proceed without asking.
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
@@ -39,6 +41,26 @@ Check these before choosing patterns:
|
|
|
39
41
|
|
|
40
42
|
---
|
|
41
43
|
|
|
44
|
+
## Step 0: Check environment variables (do this FIRST)
|
|
45
|
+
|
|
46
|
+
Before making any code changes, check the project's `.env` file for the required ElasticDash variables.
|
|
47
|
+
|
|
48
|
+
### 1. Check for `ELASTICDASH_API_URL`
|
|
49
|
+
|
|
50
|
+
- If present: use the existing value.
|
|
51
|
+
- If missing: add `ELASTICDASH_API_URL=https://server.elasticdash.com` to `.env`.
|
|
52
|
+
|
|
53
|
+
### 2. Check for `ELASTICDASH_API_KEY`
|
|
54
|
+
|
|
55
|
+
- If present: proceed to Step 1.
|
|
56
|
+
- If missing: **STOP and ask the user**:
|
|
57
|
+
|
|
58
|
+
> "Your project needs an ElasticDash API key. Please complete the onboarding at https://app.elasticdash.com to get your project API key, then add `ELASTICDASH_API_KEY=ed_your_key_here` to your `.env` file. Let me know when it's ready."
|
|
59
|
+
|
|
60
|
+
**Do not continue until `ELASTICDASH_API_KEY` is set.** The SDK cannot connect to the backend without it.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
42
64
|
## Step 1: Install
|
|
43
65
|
|
|
44
66
|
```bash
|
|
@@ -56,163 +78,63 @@ Add to `.gitignore`:
|
|
|
56
78
|
|
|
57
79
|
## Step 2: Create `ed_tools.ts`
|
|
58
80
|
|
|
59
|
-
Create `ed_tools.ts` in the project root. This file wraps each tool function with
|
|
81
|
+
Create `ed_tools.ts` in the project root. This file wraps each tool function with `wrapTool()` for automatic tracing, mocking, and telemetry.
|
|
60
82
|
|
|
61
83
|
**Before writing this file**: Search the codebase for functions that call external services (APIs, databases, SDKs). Each one becomes a wrapped export in `ed_tools.ts`. Replace all `YOUR_TOOL_*` and `YOUR_SOURCE_PATH_*` placeholders below with the actual function names and import paths found in the project.
|
|
62
84
|
|
|
63
|
-
###
|
|
64
|
-
|
|
65
|
-
- **Pattern B (recommended for most projects)**: inline async with mock support. Use when tools are imported individually, no central dispatcher exists, or you want dashboard mock support.
|
|
66
|
-
- **Pattern A**: `withTrace` HOF with central dispatcher. Use when tools are pure functions called through a single `dispatchTool(name, args)` dispatcher.
|
|
67
|
-
|
|
68
|
-
### Pattern B template (recommended)
|
|
85
|
+
### Template
|
|
69
86
|
|
|
70
87
|
```ts
|
|
71
88
|
// ed_tools.ts
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
import { setElasticDashModule } from './ed_workflows'
|
|
90
|
+
|
|
91
|
+
// Import original tool implementations from the actual source files
|
|
92
|
+
import { originalTool1 } from './services/YOUR_SOURCE_1'
|
|
93
|
+
import { originalTool2 } from './utils/YOUR_SOURCE_2'
|
|
75
94
|
|
|
76
95
|
// ---------------------------------------------------------------------------
|
|
77
|
-
//
|
|
96
|
+
// Load elasticdash-test and share the module with ed_workflows.ts
|
|
78
97
|
// ---------------------------------------------------------------------------
|
|
79
98
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (entry.mode === 'mock-all') {
|
|
94
|
-
const data = entry.mockData ?? {}
|
|
95
|
-
const result = data[callNumber] !== undefined ? data[callNumber] : data[0]
|
|
96
|
-
return { mocked: true, result }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (entry.mode === 'mock-specific') {
|
|
100
|
-
const indices = entry.callIndices ?? []
|
|
101
|
-
if (indices.includes(callNumber)) {
|
|
102
|
-
return { mocked: true, result: (entry.mockData ?? {})[callNumber] }
|
|
103
|
-
}
|
|
104
|
-
return { mocked: false }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return { mocked: false }
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function safeRecordToolCall(tool: string, input: any, result: any) {
|
|
111
|
-
if (!(globalThis as any).__ELASTICDASH_WORKER__) return
|
|
112
|
-
try {
|
|
113
|
-
const { recordToolCall } = await import('elasticdash-test')
|
|
114
|
-
recordToolCall(tool, input, result)
|
|
115
|
-
} catch { /* tracing must never block business logic */ }
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
type WrapToolFn = <T extends (...args: any[]) => any>(name: string, fn: T) => T
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
102
|
+
let wrapTool: WrapToolFn = (_name: string, fn: any) => fn
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
106
|
+
const _edModule = (eval('require') as (id: string) => any)('elasticdash-test')
|
|
107
|
+
wrapTool = _edModule.wrapTool ?? wrapTool
|
|
108
|
+
// Share the module instance with ed_workflows.ts so trace hooks use the same context
|
|
109
|
+
setElasticDashModule(_edModule)
|
|
110
|
+
} catch {
|
|
111
|
+
// elasticdash-test not available — all wrappers pass through to original functions
|
|
116
112
|
}
|
|
117
113
|
|
|
118
114
|
// ---------------------------------------------------------------------------
|
|
119
|
-
//
|
|
115
|
+
// Wrapped tools — one export per tool
|
|
120
116
|
// ---------------------------------------------------------------------------
|
|
121
117
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (mock.mocked) {
|
|
126
|
-
await safeRecordToolCall('YOUR_TOOL_1', input, mock.result)
|
|
127
|
-
return mock.result
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return await YOUR_TOOL_1_impl(input)
|
|
131
|
-
.then(async (res: any) => {
|
|
132
|
-
await safeRecordToolCall('YOUR_TOOL_1', input, res)
|
|
133
|
-
return res
|
|
134
|
-
})
|
|
135
|
-
.catch(async (err: any) => {
|
|
136
|
-
await safeRecordToolCall('YOUR_TOOL_1', input, err)
|
|
137
|
-
throw err
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export const YOUR_TOOL_2 = async (input: any) => {
|
|
142
|
-
const mock = resolveMock('YOUR_TOOL_2')
|
|
143
|
-
if (mock.mocked) {
|
|
144
|
-
await safeRecordToolCall('YOUR_TOOL_2', input, mock.result)
|
|
145
|
-
return mock.result
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return await YOUR_TOOL_2_impl(input)
|
|
149
|
-
.then(async (res: any) => {
|
|
150
|
-
await safeRecordToolCall('YOUR_TOOL_2', input, res)
|
|
151
|
-
return res
|
|
152
|
-
})
|
|
153
|
-
.catch(async (err: any) => {
|
|
154
|
-
await safeRecordToolCall('YOUR_TOOL_2', input, err)
|
|
155
|
-
throw err
|
|
156
|
-
})
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Pattern A template (dispatcher-based)
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
// ed_tools.ts
|
|
164
|
-
// Replace imports with your actual tool functions and source paths
|
|
165
|
-
import {
|
|
166
|
-
YOUR_TOOL_1 as _YOUR_TOOL_1,
|
|
167
|
-
YOUR_TOOL_2 as _YOUR_TOOL_2,
|
|
168
|
-
dispatchTool as _dispatchTool,
|
|
169
|
-
} from './YOUR_TOOLS_SOURCE'
|
|
170
|
-
|
|
171
|
-
async function withTrace<I, O>(
|
|
172
|
-
toolName: string,
|
|
173
|
-
input: I,
|
|
174
|
-
fn: (input: I) => Promise<O>,
|
|
175
|
-
): Promise<O> {
|
|
176
|
-
const result = await fn(input)
|
|
177
|
-
try {
|
|
178
|
-
const { recordToolCall } = await import('elasticdash-test')
|
|
179
|
-
recordToolCall(toolName, input, result)
|
|
180
|
-
} catch { /* tracing must never block business logic */ }
|
|
181
|
-
return result
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function YOUR_TOOL_1(input: Parameters<typeof _YOUR_TOOL_1>[0]) {
|
|
185
|
-
return withTrace('YOUR_TOOL_1', input, _YOUR_TOOL_1)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function YOUR_TOOL_2(input: Parameters<typeof _YOUR_TOOL_2>[0]) {
|
|
189
|
-
return withTrace('YOUR_TOOL_2', input, _YOUR_TOOL_2)
|
|
190
|
-
}
|
|
118
|
+
export const myTool1 = wrapTool('myTool1', async (input: any) => {
|
|
119
|
+
return await originalTool1(input)
|
|
120
|
+
})
|
|
191
121
|
|
|
192
|
-
export
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
default: return _dispatchTool(name, args)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
122
|
+
export const myTool2 = wrapTool('myTool2', async (input: any) => {
|
|
123
|
+
const { someField } = input as { someField: string }
|
|
124
|
+
return await originalTool2(someField)
|
|
125
|
+
})
|
|
199
126
|
```
|
|
200
127
|
|
|
201
|
-
###
|
|
202
|
-
|
|
203
|
-
For simpler setups where you don't need mock support or dashboard replays:
|
|
128
|
+
### Key patterns
|
|
204
129
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
export const YOUR_TOOL_1 = wrapTool('YOUR_TOOL_1', YOUR_TOOL_1_impl)
|
|
211
|
-
```
|
|
130
|
+
- **`wrapTool(name, fn)`** wraps the function with automatic tracing, mocking, and telemetry. Falls back to a passthrough if `elasticdash-test` is not installed.
|
|
131
|
+
- **`eval('require')`** is used instead of `import()` to share the same module instance across `ed_tools.ts` and `ed_workflows.ts`. This avoids ESM/CJS dual-instance issues.
|
|
132
|
+
- **`setElasticDashModule`** shares the loaded module with `ed_workflows.ts` so `edStartTrace`/`edEndTrace` use the same tracing context as `wrapTool`.
|
|
133
|
+
- The exported name (e.g., `myTool1`) can differ from the original function name (e.g., `originalTool1`). The call sites in existing source files will be updated to use the new name in Step 4.
|
|
212
134
|
|
|
213
135
|
### Important rules
|
|
214
136
|
|
|
215
|
-
- The string name passed to `
|
|
137
|
+
- The string name passed to `wrapTool()` **must match** the exported function name exactly.
|
|
216
138
|
- Each tool function must accept a single input object and return a plain value (JSON-serializable).
|
|
217
139
|
- Tool functions must not close over HTTP context, framework state, or database clients — extract pure logic first.
|
|
218
140
|
|
|
@@ -232,128 +154,173 @@ export default nextConfig
|
|
|
232
154
|
|
|
233
155
|
## Step 3: Create `ed_workflows.ts`
|
|
234
156
|
|
|
235
|
-
Create `ed_workflows.ts` in the project root. This file
|
|
157
|
+
Create `ed_workflows.ts` in the project root. This file serves two purposes:
|
|
158
|
+
|
|
159
|
+
1. **Trace lifecycle hooks** (`edStartTrace`, `edEndTrace`) — called from route handlers to mark workflow boundaries
|
|
160
|
+
2. **Workflow exports** — callable functions for the ElasticDash dashboard and test runner
|
|
236
161
|
|
|
237
162
|
**Before writing this file**: Search the codebase for the main AI workflow functions — async functions that orchestrate LLM calls and tool calls (e.g., a chat handler, an agent loop, a pipeline function). Replace all `YOUR_WORKFLOW` and `YOUR_SOURCE_PATH` placeholders below with the actual function names and import paths.
|
|
238
163
|
|
|
239
|
-
###
|
|
164
|
+
### Trace lifecycle hooks
|
|
165
|
+
|
|
166
|
+
Every `ed_workflows.ts` should export `edStartTrace` and `edEndTrace`. These are called from route handlers (Step 4) to mark when a workflow starts and ends:
|
|
240
167
|
|
|
241
168
|
```ts
|
|
242
|
-
// ed_workflows.ts
|
|
243
|
-
|
|
244
|
-
|
|
169
|
+
// ed_workflows.ts — trace hooks (copy as-is)
|
|
170
|
+
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
+
let _ed: any = null
|
|
173
|
+
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
|
+
export function setElasticDashModule(mod: any): void {
|
|
176
|
+
_ed = mod
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const edStartTrace = async (workflowName: string): Promise<void> => {
|
|
180
|
+
if (!_ed) return
|
|
181
|
+
try {
|
|
182
|
+
await _ed.tryAutoInitHttpContext()
|
|
183
|
+
_ed.startTrace(workflowName)
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.error('[ed_workflows] edStartTrace error:', err)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export const edEndTrace = (): void => {
|
|
190
|
+
if (!_ed) return
|
|
191
|
+
try {
|
|
192
|
+
_ed.endTrace()
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error('[ed_workflows] edEndTrace error:', err)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
245
197
|
```
|
|
246
198
|
|
|
247
|
-
###
|
|
199
|
+
### Workflow exports — simple case
|
|
248
200
|
|
|
249
|
-
|
|
201
|
+
For non-framework projects where the workflow can be imported directly:
|
|
250
202
|
|
|
251
203
|
```ts
|
|
252
204
|
// ed_workflows.ts
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
export async function YOUR_WORKFLOW(input: { message: string; sessionId: string }) {
|
|
256
|
-
return _YOUR_HANDLER(input)
|
|
257
|
-
}
|
|
205
|
+
export { YOUR_WORKFLOW } from './YOUR_SOURCE_PATH'
|
|
258
206
|
```
|
|
259
207
|
|
|
260
|
-
###
|
|
208
|
+
### Workflow exports — HTTP mode (Next.js / Remix / framework projects)
|
|
261
209
|
|
|
262
|
-
|
|
210
|
+
For framework projects, the workflow calls the running dev server instead of importing the route handler:
|
|
263
211
|
|
|
264
212
|
```ts
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
import type { VercelAIStreamResult } from 'elasticdash-test'
|
|
269
|
-
import { POST } from './route'
|
|
213
|
+
// ed_workflows.ts
|
|
214
|
+
|
|
215
|
+
const APP_URL = process.env.APP_URL ?? 'http://localhost:3000'
|
|
270
216
|
|
|
271
|
-
export async
|
|
217
|
+
export const YOUR_WORKFLOW = async (input: {
|
|
272
218
|
messages: Array<{ role: string; content: string }>
|
|
273
219
|
sessionId?: string
|
|
274
|
-
}): Promise<
|
|
275
|
-
const
|
|
220
|
+
}): Promise<unknown> => {
|
|
221
|
+
const response = await fetch(`${APP_URL}/api/YOUR_ENDPOINT`, {
|
|
276
222
|
method: 'POST',
|
|
277
223
|
headers: { 'Content-Type': 'application/json' },
|
|
278
|
-
body: JSON.stringify(
|
|
224
|
+
body: JSON.stringify(input),
|
|
279
225
|
})
|
|
280
226
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const errorMessage = await response.text().catch(() => `HTTP ${response.status}`)
|
|
285
|
-
return { message: errorMessage, type: 'error', error: errorMessage }
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
const text = await response.text()
|
|
229
|
+
throw new Error(`HTTP ${response.status}: ${text}`)
|
|
286
230
|
}
|
|
287
231
|
|
|
288
|
-
|
|
289
|
-
recordToolCall('chatStream', args, result)
|
|
290
|
-
return result
|
|
232
|
+
return response.json()
|
|
291
233
|
}
|
|
292
234
|
```
|
|
293
235
|
|
|
294
|
-
Then re-export from `ed_workflows.ts`:
|
|
295
|
-
|
|
296
|
-
```ts
|
|
297
|
-
// ed_workflows.ts
|
|
298
|
-
export { chatStreamHandler } from './app/api/chat-stream/chatStreamHandler'
|
|
299
|
-
```
|
|
300
|
-
|
|
301
236
|
### Requirements for all workflow exports
|
|
302
237
|
|
|
303
238
|
- Accept only JSON-serializable inputs (strings, numbers, arrays, plain objects)
|
|
304
239
|
- Return only JSON-serializable outputs
|
|
305
240
|
- Must not depend on framework runtime APIs, HTTP request context, or live service clients
|
|
306
|
-
- If a dependency is non-serializable (e.g., database client), instantiate it inside `ed_workflows.ts`, not passed as a parameter
|
|
307
241
|
|
|
308
242
|
---
|
|
309
243
|
|
|
310
|
-
## Step 4:
|
|
244
|
+
## Step 4: Update existing source files to use `ed_tools`
|
|
311
245
|
|
|
312
|
-
> **
|
|
246
|
+
> **This step modifies the user's existing source files.** The files that call tool functions must import from `ed_tools` instead of the original source, so that all tool calls go through the ElasticDash tracing layer. Similarly, workflow entry points (route handlers, etc.) must call `edStartTrace` / `edEndTrace` from `ed_workflows`.
|
|
313
247
|
|
|
314
|
-
|
|
248
|
+
### Architecture after integration
|
|
315
249
|
|
|
316
250
|
```
|
|
317
|
-
|
|
318
|
-
|
|
251
|
+
ed_tools.ts
|
|
252
|
+
├── imports original functions from services/utils
|
|
253
|
+
├── wraps each with wrapTool() for tracing
|
|
254
|
+
└── exports wrapped versions with the SAME or similar names
|
|
255
|
+
|
|
256
|
+
ed_workflows.ts
|
|
257
|
+
├── exports edStartTrace / edEndTrace for workflow-level tracing
|
|
258
|
+
└── exports workflow functions (for dashboard/test runner)
|
|
319
259
|
|
|
320
|
-
|
|
321
|
-
|
|
260
|
+
Existing source files (MODIFIED):
|
|
261
|
+
app/api/chat/route.ts
|
|
262
|
+
├── BEFORE: import { myTool } from '@/services/myService'
|
|
263
|
+
├── AFTER: import { myTool } from '@/ed_tools'
|
|
264
|
+
├── ADDED: import { edStartTrace, edEndTrace } from '@/ed_workflows'
|
|
265
|
+
└── ADDED: await edStartTrace('workflowName') at handler entry
|
|
266
|
+
edEndTrace() at handler exit
|
|
322
267
|
```
|
|
323
268
|
|
|
324
269
|
### What to do
|
|
325
270
|
|
|
326
|
-
|
|
271
|
+
**1. Find every file that calls a tool function and update its imports:**
|
|
327
272
|
|
|
328
|
-
|
|
273
|
+
For each tool exported from `ed_tools.ts`, search the codebase for files that import the original function. Update the import to come from `ed_tools` instead.
|
|
329
274
|
|
|
330
275
|
```ts
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
import {
|
|
334
|
-
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const payment = await chargeCard({ amount: order.total })
|
|
339
|
-
return { orderId: order.orderId, paymentId: payment.id }
|
|
340
|
-
}
|
|
276
|
+
// BEFORE — app/api/chat/route.ts
|
|
277
|
+
import { clarifyAndRefineUserInput } from '@/utils/queryRefinement'
|
|
278
|
+
import { dynamicApiRequest } from '@/services/apiService'
|
|
279
|
+
|
|
280
|
+
// AFTER — app/api/chat/route.ts
|
|
281
|
+
import { queryRefinement } from '@/ed_tools'
|
|
282
|
+
import { apiService } from '@/ed_tools'
|
|
341
283
|
```
|
|
342
284
|
|
|
343
285
|
```ts
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
import { chargeCard as chargeCardImpl } from './src/services/payments'
|
|
347
|
-
import { getOrderDetails as getOrderDetailsImpl } from './src/services/orders'
|
|
286
|
+
// BEFORE — app/api/chat/executor.ts
|
|
287
|
+
import { dynamicApiRequest } from '@/services/apiService'
|
|
348
288
|
|
|
349
|
-
//
|
|
289
|
+
// AFTER — app/api/chat/executor.ts
|
|
290
|
+
import { apiService } from '@/ed_tools'
|
|
350
291
|
```
|
|
351
292
|
|
|
352
|
-
|
|
293
|
+
**2. Add trace hooks to route handlers / workflow entry points:**
|
|
353
294
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
295
|
+
At the top of each route handler or workflow entry function, add `edStartTrace` and `edEndTrace`:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
// app/api/chat/route.ts
|
|
299
|
+
import { edStartTrace, edEndTrace } from '@/ed_workflows'
|
|
300
|
+
|
|
301
|
+
export async function POST(req: Request) {
|
|
302
|
+
await edStartTrace('chatHandler')
|
|
303
|
+
try {
|
|
304
|
+
// ... existing handler logic, now using tools from ed_tools
|
|
305
|
+
} finally {
|
|
306
|
+
edEndTrace()
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**3. Update call sites if the wrapped function has a different name:**
|
|
312
|
+
|
|
313
|
+
If `ed_tools.ts` exports a tool under a different name than the original (e.g., `queryRefinement` wrapping `clarifyAndRefineUserInput`), update the call sites to use the new name.
|
|
314
|
+
|
|
315
|
+
### What NOT to modify
|
|
316
|
+
|
|
317
|
+
- **Do not modify `ed_tools.ts` imports** — it must still import from the original source files.
|
|
318
|
+
- **Do not modify the original tool implementation files** (e.g., `services/apiService.ts`, `utils/queryRefinement.ts`) — they stay as-is.
|
|
319
|
+
- **Do not modify files that don't call tool functions** — only update files that directly import and call the wrapped tools.
|
|
320
|
+
|
|
321
|
+
### When to ask the user
|
|
322
|
+
|
|
323
|
+
If you are not confident about a change — e.g., a function might be a utility rather than a tool, or it's unclear whether a file should use the wrapped version — **ask the user** before modifying that file. For clear tool calls (API services, database queries, LLM calls), proceed automatically.
|
|
357
324
|
|
|
358
325
|
---
|
|
359
326
|
|
|
@@ -474,7 +441,30 @@ npx ed ed-test --no-upload
|
|
|
474
441
|
|
|
475
442
|
---
|
|
476
443
|
|
|
477
|
-
## Step 8:
|
|
444
|
+
## Step 8: Validate the integration
|
|
445
|
+
|
|
446
|
+
After all files are created, validate the setup by establishing a socket.io connection to the ElasticDash backend. Run the `observe` command — it connects to the server, authenticates with the API key, and registers the project's tools and workflows:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
npx elasticdash observe
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Expected output on success:**
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
[elasticdash] Observability active
|
|
456
|
+
Session ID : <uuid>
|
|
457
|
+
Server : https://server.elasticdash.com
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
This confirms:
|
|
461
|
+
- `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` are valid
|
|
462
|
+
- The socket.io connection to the backend is established
|
|
463
|
+
- The SDK can discover `ed_tools.ts` and `ed_workflows.ts`
|
|
464
|
+
|
|
465
|
+
**If it fails:** Check that `.env` has valid `ELASTICDASH_API_URL` and `ELASTICDASH_API_KEY` values. If the API key is rejected, the user needs to get a new one from https://app.elasticdash.com.
|
|
466
|
+
|
|
467
|
+
After validation, stop the observe process (Ctrl+C) and inform the user that ElasticDash is integrated. Provide these commands for ongoing use:
|
|
478
468
|
|
|
479
469
|
```bash
|
|
480
470
|
# Run aiTest tests
|
|
@@ -486,6 +476,9 @@ npx ed ed-test --no-upload
|
|
|
486
476
|
# Open the dashboard
|
|
487
477
|
npx elasticdash dashboard
|
|
488
478
|
|
|
479
|
+
# Start observability (connect to backend for live tracing)
|
|
480
|
+
npx elasticdash observe
|
|
481
|
+
|
|
489
482
|
# Record a trace fixture
|
|
490
483
|
ELASTICDASH_CAPTURE_TRACE=1 tsx your-workflow.ts
|
|
491
484
|
```
|
|
@@ -591,16 +584,12 @@ After integration, verify these files exist:
|
|
|
591
584
|
|
|
592
585
|
```
|
|
593
586
|
your-project/
|
|
594
|
-
|
|
595
|
-
|
|
587
|
+
.env # ELASTICDASH_API_URL and ELASTICDASH_API_KEY set
|
|
588
|
+
ed_tools.ts # Instrumented tool wrappers (real function names, not placeholders)
|
|
589
|
+
ed_workflows.ts # Workflow exports using tools from ed_tools.ts
|
|
596
590
|
elasticdash.config.ts # Test runner config
|
|
597
591
|
package.json # dashboard:ai and test:ai scripts added
|
|
598
592
|
.gitignore # .temp/ and .ed_traces/ added
|
|
599
593
|
```
|
|
600
594
|
|
|
601
|
-
Verify
|
|
602
|
-
|
|
603
|
-
```bash
|
|
604
|
-
npx elasticdash test # Should discover and run *.ai.test.ts files
|
|
605
|
-
npx elasticdash dashboard # Should open the dashboard UI
|
|
606
|
-
```
|
|
595
|
+
Verify by running `npx elasticdash observe` — a successful socket.io connection confirms the integration is complete.
|