xray-sdk 0.1.0
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 +426 -0
- package/dist/XRay.d.ts +30 -0
- package/dist/XRay.js +95 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +21 -0
- package/dist/reasoning/config.d.ts +8 -0
- package/dist/reasoning/config.js +16 -0
- package/dist/reasoning/generator.d.ts +12 -0
- package/dist/reasoning/generator.js +119 -0
- package/dist/reasoning/queue.d.ts +57 -0
- package/dist/reasoning/queue.js +309 -0
- package/dist/storage/DatabaseStorage.d.ts +21 -0
- package/dist/storage/DatabaseStorage.js +177 -0
- package/dist/storage/MemoryStorage.d.ts +9 -0
- package/dist/storage/MemoryStorage.js +35 -0
- package/dist/types/index.d.ts +59 -0
- package/dist/types/index.js +3 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# X-Ray SDK
|
|
2
|
+
|
|
3
|
+
**AI-powered observability for multi-step pipelines**
|
|
4
|
+
|
|
5
|
+
X-Ray is a lightweight SDK that automatically tracks pipeline executions and generates human-readable reasoning for each step using LLMs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
✨ **Automatic Step Tracking** - Track pipeline execution with simple `startStep()` / `endStep()` calls
|
|
10
|
+
🤖 **AI-Powered Reasoning** - Generate natural language explanations for each step using OpenAI
|
|
11
|
+
💾 **Flexible Storage** - In-memory or database-backed (Prisma + PostgreSQL)
|
|
12
|
+
⚡ **Async Processing** - Non-blocking reasoning generation with retry logic
|
|
13
|
+
🔄 **On-Demand Generation** - Only generate reasoning when you need it (cost savings)
|
|
14
|
+
📊 **Job Queue** - Built-in queue for managing concurrent LLM calls
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install xray-sdk p-queue
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Optional Dependencies
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# For database storage
|
|
26
|
+
npm install @prisma/client
|
|
27
|
+
|
|
28
|
+
# For OpenAI reasoning
|
|
29
|
+
npm install openai
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Basic Usage (In-Memory)
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { XRay, MemoryStorage } from 'xray-sdk'
|
|
38
|
+
|
|
39
|
+
const storage = new MemoryStorage()
|
|
40
|
+
const xray = new XRay('my-execution-1', { projectId: 'demo' }, storage)
|
|
41
|
+
|
|
42
|
+
// Track a step
|
|
43
|
+
xray.startStep('fetch_data', { query: 'shoes' })
|
|
44
|
+
const data = await fetchData('shoes')
|
|
45
|
+
xray.endStep('fetch_data', { results: data.length })
|
|
46
|
+
|
|
47
|
+
// End execution
|
|
48
|
+
const execution = xray.end({ success: true })
|
|
49
|
+
await xray.save()
|
|
50
|
+
|
|
51
|
+
console.log('Execution saved:', execution.executionId)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### With Database Storage (Prisma)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { XRay, DatabaseStorage } from '@xray/sdk'
|
|
58
|
+
import { PrismaClient } from '@prisma/client'
|
|
59
|
+
|
|
60
|
+
const prisma = new PrismaClient()
|
|
61
|
+
const storage = new DatabaseStorage(prisma)
|
|
62
|
+
|
|
63
|
+
const xray = new XRay('my-execution-2', { projectId: 'demo' }, storage)
|
|
64
|
+
|
|
65
|
+
// Track steps
|
|
66
|
+
xray.startStep('step1', { input: 'data' })
|
|
67
|
+
xray.endStep('step1', { output: 'result' })
|
|
68
|
+
|
|
69
|
+
// Save to database
|
|
70
|
+
const execution = xray.end({ success: true })
|
|
71
|
+
await xray.save()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With AI Reasoning (OpenAI)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import {
|
|
78
|
+
XRay,
|
|
79
|
+
DatabaseStorage,
|
|
80
|
+
ReasoningQueue,
|
|
81
|
+
createOpenAIGenerator
|
|
82
|
+
} from '@xray/sdk'
|
|
83
|
+
import { PrismaClient } from '@prisma/client'
|
|
84
|
+
import OpenAI from 'openai'
|
|
85
|
+
|
|
86
|
+
const prisma = new PrismaClient()
|
|
87
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
|
|
88
|
+
|
|
89
|
+
// Setup storage and reasoning
|
|
90
|
+
const storage = new DatabaseStorage(prisma)
|
|
91
|
+
const generator = createOpenAIGenerator(openai)
|
|
92
|
+
const queue = new ReasoningQueue(
|
|
93
|
+
storage,
|
|
94
|
+
generator,
|
|
95
|
+
{ concurrency: 3, debug: true },
|
|
96
|
+
prisma
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Run pipeline
|
|
100
|
+
const xray = new XRay('my-execution-3', { projectId: 'demo' }, storage)
|
|
101
|
+
|
|
102
|
+
xray.startStep('search', { query: 'laptops' })
|
|
103
|
+
const results = await search('laptops')
|
|
104
|
+
xray.endStep('search', { count: results.length })
|
|
105
|
+
|
|
106
|
+
xray.startStep('filter', { threshold: 4.5 })
|
|
107
|
+
const filtered = results.filter(r => r.rating >= 4.5)
|
|
108
|
+
xray.endStep('filter', { remaining: filtered.length })
|
|
109
|
+
|
|
110
|
+
// Save execution (without reasoning)
|
|
111
|
+
const execution = xray.end({ success: true })
|
|
112
|
+
await xray.save()
|
|
113
|
+
|
|
114
|
+
// Enqueue reasoning generation (async)
|
|
115
|
+
await xray.enqueueReasoning(queue)
|
|
116
|
+
|
|
117
|
+
console.log('✅ Execution saved, reasoning generating in background')
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### On-Demand Reasoning (Recommended)
|
|
121
|
+
|
|
122
|
+
Instead of auto-generating reasoning, trigger it only when viewing an execution:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Pipeline API - just save execution
|
|
126
|
+
const execution = xray.end({ success: true })
|
|
127
|
+
await xray.save()
|
|
128
|
+
return { executionId: execution.executionId } // Returns instantly
|
|
129
|
+
|
|
130
|
+
// Later, when user views execution detail page
|
|
131
|
+
await queue.processExecution(executionId) // Generate reasoning now
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This approach:
|
|
135
|
+
- ✅ API responds instantly (~150ms)
|
|
136
|
+
- ✅ Saves LLM costs (only generate for executions users actually view)
|
|
137
|
+
- ✅ Better user experience
|
|
138
|
+
|
|
139
|
+
## API Reference
|
|
140
|
+
|
|
141
|
+
### XRay
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
class XRay {
|
|
145
|
+
constructor(executionId: string, metadata?: Record<string, any>, storage?: StorageProvider)
|
|
146
|
+
|
|
147
|
+
// V1 API (backward compatible)
|
|
148
|
+
logStep(step: { name: string, input: any, output: any, metadata?: any }): void
|
|
149
|
+
|
|
150
|
+
// V2 API (recommended)
|
|
151
|
+
startStep(name: string, input: any, metadata?: any): void
|
|
152
|
+
endStep(name: string, output: any): void
|
|
153
|
+
errorStep(name: string, error: Error): void
|
|
154
|
+
|
|
155
|
+
end(finalOutcome: any): Execution
|
|
156
|
+
save(): Promise<void>
|
|
157
|
+
enqueueReasoning(queue: ReasoningQueue): Promise<void>
|
|
158
|
+
getExecution(): Execution
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### StorageProvider
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface StorageProvider {
|
|
166
|
+
saveExecution(execution: Execution): Promise<void>
|
|
167
|
+
getExecutionById(executionId: string): Promise<Execution | undefined>
|
|
168
|
+
getAllExecutions(): Promise<Execution[]>
|
|
169
|
+
updateStepReasoning(executionId: string, stepName: string, reasoning: string): Promise<void>
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Implementations:**
|
|
174
|
+
- `MemoryStorage` - In-memory storage (testing)
|
|
175
|
+
- `DatabaseStorage` - Prisma + PostgreSQL (production)
|
|
176
|
+
|
|
177
|
+
### ReasoningQueue
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
class ReasoningQueue {
|
|
181
|
+
constructor(
|
|
182
|
+
storage: StorageProvider,
|
|
183
|
+
generator: ReasoningGenerator,
|
|
184
|
+
config?: Partial<ReasoningConfig>,
|
|
185
|
+
prismaClient?: any
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
enqueue(executionId: string, stepName: string): Promise<string>
|
|
189
|
+
enqueueExecution(executionId: string): Promise<string[]>
|
|
190
|
+
processExecution(executionId: string): Promise<void>
|
|
191
|
+
getStats(): QueueStats
|
|
192
|
+
getJob(jobId: string): ReasoningJob | undefined
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Reasoning Generators
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// OpenAI-powered reasoning
|
|
200
|
+
const generator = createOpenAIGenerator(openaiClient)
|
|
201
|
+
|
|
202
|
+
// Simple reasoning (no LLM)
|
|
203
|
+
const generator = createSimpleGenerator()
|
|
204
|
+
|
|
205
|
+
// Custom reasoning
|
|
206
|
+
const generator: ReasoningGenerator = async (step: Step) => {
|
|
207
|
+
return `My custom reasoning for ${step.name}`
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Configuration
|
|
212
|
+
|
|
213
|
+
### Reasoning Config
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
interface ReasoningConfig {
|
|
217
|
+
concurrency: number // Parallel LLM calls (default: 3)
|
|
218
|
+
maxRetries: number // Max retries per job (default: 4)
|
|
219
|
+
retryDelays: number[] // Backoff delays in ms (default: [1000, 2000, 4000, 8000])
|
|
220
|
+
debug: boolean // Enable logging (default: false)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const config = createReasoningConfig({
|
|
224
|
+
concurrency: 5,
|
|
225
|
+
maxRetries: 3,
|
|
226
|
+
debug: true
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const queue = new ReasoningQueue(storage, generator, config)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Prisma Schema
|
|
233
|
+
|
|
234
|
+
If using `DatabaseStorage`, add this to your Prisma schema:
|
|
235
|
+
|
|
236
|
+
```prisma
|
|
237
|
+
model Execution {
|
|
238
|
+
id String @id @default(cuid())
|
|
239
|
+
executionId String @unique
|
|
240
|
+
projectId String @default("default")
|
|
241
|
+
metadata Json?
|
|
242
|
+
finalOutcome Json?
|
|
243
|
+
startedAt DateTime @default(now())
|
|
244
|
+
completedAt DateTime?
|
|
245
|
+
steps Step[]
|
|
246
|
+
reasoningJobs ReasoningJob[]
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
model Step {
|
|
250
|
+
id String @id @default(cuid())
|
|
251
|
+
executionId String
|
|
252
|
+
execution Execution @relation(fields: [executionId], references: [id], onDelete: Cascade)
|
|
253
|
+
name String
|
|
254
|
+
input Json?
|
|
255
|
+
output Json?
|
|
256
|
+
error String?
|
|
257
|
+
durationMs Int?
|
|
258
|
+
reasoning String?
|
|
259
|
+
createdAt DateTime @default(now())
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
model ReasoningJob {
|
|
263
|
+
id String @id @default(cuid())
|
|
264
|
+
executionId String
|
|
265
|
+
execution Execution @relation(fields: [executionId], references: [id], onDelete: Cascade)
|
|
266
|
+
stepName String
|
|
267
|
+
status String
|
|
268
|
+
reasoning String?
|
|
269
|
+
error String?
|
|
270
|
+
attempts Int @default(0)
|
|
271
|
+
createdAt DateTime @default(now())
|
|
272
|
+
completedAt DateTime?
|
|
273
|
+
|
|
274
|
+
@@unique([executionId, stepName])
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Then run:
|
|
279
|
+
```bash
|
|
280
|
+
npx prisma migrate dev --name add_xray
|
|
281
|
+
npx prisma generate
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Examples
|
|
285
|
+
|
|
286
|
+
### Example 1: Simple Pipeline
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { XRay, MemoryStorage } from '@xray/sdk'
|
|
290
|
+
|
|
291
|
+
async function runPipeline() {
|
|
292
|
+
const storage = new MemoryStorage()
|
|
293
|
+
const xray = new XRay('exec-1', {}, storage)
|
|
294
|
+
|
|
295
|
+
xray.startStep('fetch', { url: 'https://api.example.com' })
|
|
296
|
+
const data = await fetch('https://api.example.com').then(r => r.json())
|
|
297
|
+
xray.endStep('fetch', { count: data.length })
|
|
298
|
+
|
|
299
|
+
xray.startStep('process', { data })
|
|
300
|
+
const processed = data.map(d => d.value * 2)
|
|
301
|
+
xray.endStep('process', { result: processed })
|
|
302
|
+
|
|
303
|
+
const execution = xray.end({ total: processed.length })
|
|
304
|
+
await xray.save()
|
|
305
|
+
|
|
306
|
+
return execution
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Example 2: With Error Handling
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
xray.startStep('risky_operation', { input: 'data' })
|
|
314
|
+
try {
|
|
315
|
+
const result = await riskyOperation()
|
|
316
|
+
xray.endStep('risky_operation', { result })
|
|
317
|
+
} catch (error) {
|
|
318
|
+
xray.errorStep('risky_operation', error as Error)
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Example 3: Custom Storage
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { StorageProvider, Execution } from '@xray/sdk'
|
|
326
|
+
|
|
327
|
+
class S3Storage implements StorageProvider {
|
|
328
|
+
async saveExecution(execution: Execution): Promise<void> {
|
|
329
|
+
// Upload to S3
|
|
330
|
+
await s3.putObject({
|
|
331
|
+
Bucket: 'my-bucket',
|
|
332
|
+
Key: `executions/${execution.executionId}.json`,
|
|
333
|
+
Body: JSON.stringify(execution)
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async getExecutionById(id: string): Promise<Execution | undefined> {
|
|
338
|
+
// Download from S3
|
|
339
|
+
const obj = await s3.getObject({
|
|
340
|
+
Bucket: 'my-bucket',
|
|
341
|
+
Key: `executions/${id}.json`
|
|
342
|
+
})
|
|
343
|
+
return JSON.parse(obj.Body.toString())
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ... implement other methods
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Best Practices
|
|
351
|
+
|
|
352
|
+
### 1. Use On-Demand Reasoning
|
|
353
|
+
|
|
354
|
+
Don't generate reasoning on every pipeline run - only when users view executions:
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// ❌ Bad: Auto-generate reasoning (slow, expensive)
|
|
358
|
+
await xray.save()
|
|
359
|
+
await xray.enqueueReasoning(queue) // Blocks API response
|
|
360
|
+
return { executionId }
|
|
361
|
+
|
|
362
|
+
// ✅ Good: Generate on-demand (fast, cost-effective)
|
|
363
|
+
await xray.save()
|
|
364
|
+
return { executionId } // Returns instantly
|
|
365
|
+
|
|
366
|
+
// Later, when viewing execution:
|
|
367
|
+
await queue.processExecution(executionId)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 2. Use Database Storage in Production
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
// ❌ Bad: In-memory (data lost on restart)
|
|
374
|
+
const storage = new MemoryStorage()
|
|
375
|
+
|
|
376
|
+
// ✅ Good: Database-backed (persistent)
|
|
377
|
+
const storage = new DatabaseStorage(prisma)
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 3. Configure Concurrency
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Balance API costs vs speed
|
|
384
|
+
const queue = new ReasoningQueue(storage, generator, {
|
|
385
|
+
concurrency: 3, // 3 parallel LLM calls
|
|
386
|
+
maxRetries: 4, // Retry failed jobs
|
|
387
|
+
debug: true // Enable logging
|
|
388
|
+
})
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 4. Handle Errors Gracefully
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
xray.startStep('step', { input })
|
|
395
|
+
try {
|
|
396
|
+
const result = await operation()
|
|
397
|
+
xray.endStep('step', { result })
|
|
398
|
+
} catch (error) {
|
|
399
|
+
xray.errorStep('step', error as Error)
|
|
400
|
+
// Continue pipeline or throw
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## TypeScript
|
|
405
|
+
|
|
406
|
+
Full TypeScript support with type definitions:
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { Execution, Step, ReasoningJob, QueueStats } from '@xray/sdk'
|
|
410
|
+
|
|
411
|
+
const execution: Execution = xray.getExecution()
|
|
412
|
+
const stats: QueueStats = queue.getStats()
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
MIT
|
|
418
|
+
|
|
419
|
+
## Contributing
|
|
420
|
+
|
|
421
|
+
Contributions welcome! Please open an issue or PR.
|
|
422
|
+
|
|
423
|
+
## Support
|
|
424
|
+
|
|
425
|
+
- GitHub Issues: https://github.com/yourusername/xray-sdk/issues
|
|
426
|
+
- Documentation: https://xray-sdk.dev
|
package/dist/XRay.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Execution, StorageProvider } from "./types";
|
|
2
|
+
import { ReasoningQueue } from "./reasoning/queue";
|
|
3
|
+
export declare class XRay {
|
|
4
|
+
private execution;
|
|
5
|
+
private activeSteps;
|
|
6
|
+
private pendingReasoningSteps;
|
|
7
|
+
private storage?;
|
|
8
|
+
constructor(executionId: string, metadata?: Record<string, any>, storage?: StorageProvider);
|
|
9
|
+
/** ✅ BACKWARD-COMPATIBLE (v1) – no auto reasoning */
|
|
10
|
+
logStep(step: {
|
|
11
|
+
name: string;
|
|
12
|
+
input: any;
|
|
13
|
+
output: any;
|
|
14
|
+
metadata?: Record<string, any>;
|
|
15
|
+
}): void;
|
|
16
|
+
/** Start a step (v2) */
|
|
17
|
+
startStep(name: string, input: any, metadata?: Record<string, any>): void;
|
|
18
|
+
/** Finish a step (v3) – stores without reasoning (populated asynchronously) */
|
|
19
|
+
endStep(name: string, output: any): void;
|
|
20
|
+
/** Capture error inside a step (v2) – stores without reasoning (populated asynchronously) */
|
|
21
|
+
errorStep(name: string, error: Error): void;
|
|
22
|
+
/** End execution - returns execution without enqueueing reasoning */
|
|
23
|
+
end(finalOutcome: any): Execution;
|
|
24
|
+
/** Save execution to storage (if configured) */
|
|
25
|
+
save(): Promise<void>;
|
|
26
|
+
/** Enqueue reasoning jobs - call AFTER saveExecution() */
|
|
27
|
+
enqueueReasoning(queue: ReasoningQueue): Promise<void>;
|
|
28
|
+
/** Get the current execution */
|
|
29
|
+
getExecution(): Execution;
|
|
30
|
+
}
|
package/dist/XRay.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.XRay = void 0;
|
|
4
|
+
class XRay {
|
|
5
|
+
constructor(executionId, metadata, storage) {
|
|
6
|
+
this.activeSteps = new Map();
|
|
7
|
+
this.pendingReasoningSteps = [];
|
|
8
|
+
this.execution = {
|
|
9
|
+
executionId,
|
|
10
|
+
startedAt: new Date().toISOString(),
|
|
11
|
+
steps: [],
|
|
12
|
+
metadata
|
|
13
|
+
};
|
|
14
|
+
this.storage = storage;
|
|
15
|
+
}
|
|
16
|
+
/** ✅ BACKWARD-COMPATIBLE (v1) – no auto reasoning */
|
|
17
|
+
logStep(step) {
|
|
18
|
+
this.execution.steps.push({
|
|
19
|
+
name: step.name,
|
|
20
|
+
input: step.input,
|
|
21
|
+
output: step.output,
|
|
22
|
+
timestamp: new Date().toISOString(),
|
|
23
|
+
metadata: step.metadata
|
|
24
|
+
// reasoning can be added manually via metadata.reasoning if needed
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Start a step (v2) */
|
|
28
|
+
startStep(name, input, metadata) {
|
|
29
|
+
const step = {
|
|
30
|
+
name,
|
|
31
|
+
input,
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
startedAt: new Date().toISOString(),
|
|
34
|
+
metadata
|
|
35
|
+
};
|
|
36
|
+
this.activeSteps.set(name, step);
|
|
37
|
+
}
|
|
38
|
+
/** Finish a step (v3) – stores without reasoning (populated asynchronously) */
|
|
39
|
+
endStep(name, output) {
|
|
40
|
+
const step = this.activeSteps.get(name);
|
|
41
|
+
if (!step)
|
|
42
|
+
return;
|
|
43
|
+
step.output = output;
|
|
44
|
+
step.endedAt = new Date().toISOString();
|
|
45
|
+
step.durationMs =
|
|
46
|
+
new Date(step.endedAt).getTime() -
|
|
47
|
+
new Date(step.startedAt).getTime();
|
|
48
|
+
// Store without reasoning (will be populated asynchronously)
|
|
49
|
+
step.reasoning = undefined;
|
|
50
|
+
this.execution.steps.push(step);
|
|
51
|
+
this.activeSteps.delete(name);
|
|
52
|
+
// Track step for later processing
|
|
53
|
+
this.pendingReasoningSteps.push(step.name);
|
|
54
|
+
}
|
|
55
|
+
/** Capture error inside a step (v2) – stores without reasoning (populated asynchronously) */
|
|
56
|
+
errorStep(name, error) {
|
|
57
|
+
const step = this.activeSteps.get(name);
|
|
58
|
+
if (!step)
|
|
59
|
+
return;
|
|
60
|
+
step.error = error.message;
|
|
61
|
+
step.endedAt = new Date().toISOString();
|
|
62
|
+
step.durationMs =
|
|
63
|
+
new Date(step.endedAt).getTime() -
|
|
64
|
+
new Date(step.startedAt).getTime();
|
|
65
|
+
// Store without reasoning (will be populated asynchronously)
|
|
66
|
+
step.reasoning = undefined;
|
|
67
|
+
this.execution.steps.push(step);
|
|
68
|
+
this.activeSteps.delete(name);
|
|
69
|
+
// Track step for later processing
|
|
70
|
+
this.pendingReasoningSteps.push(step.name);
|
|
71
|
+
}
|
|
72
|
+
/** End execution - returns execution without enqueueing reasoning */
|
|
73
|
+
end(finalOutcome) {
|
|
74
|
+
this.execution.endedAt = new Date().toISOString();
|
|
75
|
+
this.execution.finalOutcome = finalOutcome;
|
|
76
|
+
return this.execution;
|
|
77
|
+
}
|
|
78
|
+
/** Save execution to storage (if configured) */
|
|
79
|
+
async save() {
|
|
80
|
+
if (this.storage) {
|
|
81
|
+
await this.storage.saveExecution(this.execution);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Enqueue reasoning jobs - call AFTER saveExecution() */
|
|
85
|
+
async enqueueReasoning(queue) {
|
|
86
|
+
for (const stepName of this.pendingReasoningSteps) {
|
|
87
|
+
await queue.enqueue(this.execution.executionId, stepName);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Get the current execution */
|
|
91
|
+
getExecution() {
|
|
92
|
+
return this.execution;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.XRay = XRay;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { XRay } from './XRay';
|
|
2
|
+
export { DatabaseStorage } from './storage/DatabaseStorage';
|
|
3
|
+
export { MemoryStorage } from './storage/MemoryStorage';
|
|
4
|
+
export { ReasoningQueue } from './reasoning/queue';
|
|
5
|
+
export { createOpenAIGenerator, createSimpleGenerator } from './reasoning/generator';
|
|
6
|
+
export { createReasoningConfig, DEFAULT_REASONING_CONFIG } from './reasoning/config';
|
|
7
|
+
export type { Execution, Step, ReasoningJob, QueueStats, XRayConfig, ReasoningConfig, StorageProvider } from './types';
|
|
8
|
+
export type { ReasoningGenerator } from './reasoning/generator';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Main entry point for X-Ray SDK
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.DEFAULT_REASONING_CONFIG = exports.createReasoningConfig = exports.createSimpleGenerator = exports.createOpenAIGenerator = exports.ReasoningQueue = exports.MemoryStorage = exports.DatabaseStorage = exports.XRay = void 0;
|
|
5
|
+
// Core classes
|
|
6
|
+
var XRay_1 = require("./XRay");
|
|
7
|
+
Object.defineProperty(exports, "XRay", { enumerable: true, get: function () { return XRay_1.XRay; } });
|
|
8
|
+
// Storage implementations
|
|
9
|
+
var DatabaseStorage_1 = require("./storage/DatabaseStorage");
|
|
10
|
+
Object.defineProperty(exports, "DatabaseStorage", { enumerable: true, get: function () { return DatabaseStorage_1.DatabaseStorage; } });
|
|
11
|
+
var MemoryStorage_1 = require("./storage/MemoryStorage");
|
|
12
|
+
Object.defineProperty(exports, "MemoryStorage", { enumerable: true, get: function () { return MemoryStorage_1.MemoryStorage; } });
|
|
13
|
+
// Reasoning
|
|
14
|
+
var queue_1 = require("./reasoning/queue");
|
|
15
|
+
Object.defineProperty(exports, "ReasoningQueue", { enumerable: true, get: function () { return queue_1.ReasoningQueue; } });
|
|
16
|
+
var generator_1 = require("./reasoning/generator");
|
|
17
|
+
Object.defineProperty(exports, "createOpenAIGenerator", { enumerable: true, get: function () { return generator_1.createOpenAIGenerator; } });
|
|
18
|
+
Object.defineProperty(exports, "createSimpleGenerator", { enumerable: true, get: function () { return generator_1.createSimpleGenerator; } });
|
|
19
|
+
var config_1 = require("./reasoning/config");
|
|
20
|
+
Object.defineProperty(exports, "createReasoningConfig", { enumerable: true, get: function () { return config_1.createReasoningConfig; } });
|
|
21
|
+
Object.defineProperty(exports, "DEFAULT_REASONING_CONFIG", { enumerable: true, get: function () { return config_1.DEFAULT_REASONING_CONFIG; } });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ReasoningConfig {
|
|
2
|
+
concurrency: number;
|
|
3
|
+
maxRetries: number;
|
|
4
|
+
retryDelays: number[];
|
|
5
|
+
debug: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_REASONING_CONFIG: ReasoningConfig;
|
|
8
|
+
export declare function createReasoningConfig(overrides?: Partial<ReasoningConfig>): ReasoningConfig;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_REASONING_CONFIG = void 0;
|
|
4
|
+
exports.createReasoningConfig = createReasoningConfig;
|
|
5
|
+
exports.DEFAULT_REASONING_CONFIG = {
|
|
6
|
+
concurrency: 3,
|
|
7
|
+
maxRetries: 4,
|
|
8
|
+
retryDelays: [1000, 2000, 4000, 8000], // 1s, 2s, 4s, 8s
|
|
9
|
+
debug: false
|
|
10
|
+
};
|
|
11
|
+
function createReasoningConfig(overrides) {
|
|
12
|
+
return {
|
|
13
|
+
...exports.DEFAULT_REASONING_CONFIG,
|
|
14
|
+
...overrides
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Step } from "../types";
|
|
2
|
+
export type ReasoningGenerator = (step: Step) => Promise<string>;
|
|
3
|
+
/**
|
|
4
|
+
* Default reasoning generator using OpenAI
|
|
5
|
+
* Users can provide their own OpenAI client instance
|
|
6
|
+
*/
|
|
7
|
+
export declare function createOpenAIGenerator(openaiClient: any): ReasoningGenerator;
|
|
8
|
+
/**
|
|
9
|
+
* Simple reasoning generator (no LLM)
|
|
10
|
+
* Returns numeric summaries based on step data
|
|
11
|
+
*/
|
|
12
|
+
export declare function createSimpleGenerator(): ReasoningGenerator;
|