@raindrop-ai/wizard 0.0.1 → 0.0.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.
Files changed (71) hide show
  1. package/dist/src/docs/claude-agent-sdk.mdx +382 -0
  2. package/dist/src/docs/{typescript.md → typescript.mdx} +8 -4
  3. package/dist/src/docs/vercel-ai-sdk.mdx +769 -0
  4. package/dist/src/lib/agent-interface.d.ts +4 -3
  5. package/dist/src/lib/agent-interface.js +290 -197
  6. package/dist/src/lib/agent-interface.js.map +1 -1
  7. package/dist/src/lib/constants.d.ts +1 -0
  8. package/dist/src/lib/constants.js +1 -0
  9. package/dist/src/lib/constants.js.map +1 -1
  10. package/dist/src/lib/handlers.d.ts +16 -8
  11. package/dist/src/lib/handlers.js +232 -118
  12. package/dist/src/lib/handlers.js.map +1 -1
  13. package/dist/src/lib/integration-testing.d.ts +5 -5
  14. package/dist/src/lib/integration-testing.js +28 -12
  15. package/dist/src/lib/integration-testing.js.map +1 -1
  16. package/dist/src/lib/mcp.d.ts +1 -1
  17. package/dist/src/lib/mcp.js +88 -49
  18. package/dist/src/lib/mcp.js.map +1 -1
  19. package/dist/src/lib/sdk-messages.d.ts +8 -1
  20. package/dist/src/lib/sdk-messages.js +83 -27
  21. package/dist/src/lib/sdk-messages.js.map +1 -1
  22. package/dist/src/lib/wizard.js +16 -20
  23. package/dist/src/lib/wizard.js.map +1 -1
  24. package/dist/src/ui/App.d.ts +5 -4
  25. package/dist/src/ui/App.js +12 -12
  26. package/dist/src/ui/App.js.map +1 -1
  27. package/dist/src/ui/components/ClarifyingQuestionsPrompt.js +4 -2
  28. package/dist/src/ui/components/ClarifyingQuestionsPrompt.js.map +1 -1
  29. package/dist/src/ui/components/ContinuePrompt.d.ts +3 -2
  30. package/dist/src/ui/components/ContinuePrompt.js +4 -4
  31. package/dist/src/ui/components/ContinuePrompt.js.map +1 -1
  32. package/dist/src/ui/components/DiffDisplay.js +16 -6
  33. package/dist/src/ui/components/DiffDisplay.js.map +1 -1
  34. package/dist/src/ui/components/FeedbackSelectPrompt.js +6 -3
  35. package/dist/src/ui/components/FeedbackSelectPrompt.js.map +1 -1
  36. package/dist/src/ui/components/HistoryItemDisplay.d.ts +5 -3
  37. package/dist/src/ui/components/HistoryItemDisplay.js +10 -9
  38. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  39. package/dist/src/ui/components/Logo.js +19 -34
  40. package/dist/src/ui/components/Logo.js.map +1 -1
  41. package/dist/src/ui/components/OrgInfoBox.d.ts +2 -1
  42. package/dist/src/ui/components/OrgInfoBox.js +2 -4
  43. package/dist/src/ui/components/OrgInfoBox.js.map +1 -1
  44. package/dist/src/ui/components/PersistentTextInput.js +13 -11
  45. package/dist/src/ui/components/PersistentTextInput.js.map +1 -1
  46. package/dist/src/ui/components/PromptContainer.js +4 -8
  47. package/dist/src/ui/components/PromptContainer.js.map +1 -1
  48. package/dist/src/ui/components/ToolApprovalPrompt.js +4 -4
  49. package/dist/src/ui/components/ToolApprovalPrompt.js.map +1 -1
  50. package/dist/src/ui/components/WriteKeyDisplay.d.ts +1 -1
  51. package/dist/src/ui/components/WriteKeyDisplay.js +1 -1
  52. package/dist/src/ui/components/WriteKeyDisplay.js.map +1 -1
  53. package/dist/src/ui/contexts/WizardContext.d.ts +13 -5
  54. package/dist/src/ui/contexts/WizardContext.js +60 -20
  55. package/dist/src/ui/contexts/WizardContext.js.map +1 -1
  56. package/dist/src/ui/render.js +49 -5
  57. package/dist/src/ui/render.js.map +1 -1
  58. package/dist/src/ui/types.d.ts +4 -2
  59. package/dist/src/ui/types.js.map +1 -1
  60. package/dist/src/utils/oauth.js +0 -4
  61. package/dist/src/utils/oauth.js.map +1 -1
  62. package/dist/src/utils/session.d.ts +1 -0
  63. package/dist/src/utils/session.js +40 -1
  64. package/dist/src/utils/session.js.map +1 -1
  65. package/dist/src/utils/ui.d.ts +7 -2
  66. package/dist/src/utils/ui.js +9 -2
  67. package/dist/src/utils/ui.js.map +1 -1
  68. package/package.json +2 -1
  69. package/dist/src/docs/vercel-ai-sdk.md +0 -304
  70. /package/dist/src/docs/{browser.md → browser.mdx} +0 -0
  71. /package/dist/src/docs/{python.md → python.mdx} +0 -0
@@ -0,0 +1,769 @@
1
+ ---
2
+ title: Vercel AI SDK
3
+ description: >-
4
+ Reference for integrating Raindrop with the Vercel AI SDK.
5
+ ---
6
+
7
+ The `@raindrop-ai/ai-sdk` package instruments the
8
+ [Vercel AI SDK](https://sdk.vercel.ai/) so wrapped `generateText`, `streamText`,
9
+ `generateObject`, and `streamObject` calls can be tracked in Raindrop.
10
+
11
+ ## Installation
12
+
13
+ <CodeGroup>
14
+ ```bash npm
15
+ npm install @raindrop-ai/ai-sdk
16
+ ```
17
+
18
+ ```bash yarn
19
+ yarn add @raindrop-ai/ai-sdk
20
+ ```
21
+
22
+ ```bash pnpm
23
+ pnpm add @raindrop-ai/ai-sdk
24
+ ```
25
+
26
+ ```bash bun
27
+ bun add @raindrop-ai/ai-sdk
28
+ ```
29
+
30
+ </CodeGroup>
31
+
32
+ ## Quick Start
33
+
34
+ ```typescript
35
+ import * as ai from 'ai';
36
+ import { openai } from '@ai-sdk/openai';
37
+ import { createRaindropAISDK, eventMetadata } from '@raindrop-ai/ai-sdk';
38
+
39
+ // 1. Create the Raindrop client
40
+ const raindrop = createRaindropAISDK({
41
+ writeKey: process.env.RAINDROP_WRITE_KEY!,
42
+ });
43
+
44
+ // 2. Wrap the AI SDK with defaults
45
+ const { generateText, streamText } = raindrop.wrap(ai, {
46
+ context: {
47
+ userId, // REQUIRED: authenticated user id from your app
48
+ eventName, // Optional: e.g. 'chat_message'
49
+ properties: {
50
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
51
+ },
52
+ },
53
+ });
54
+
55
+ // 3. Use the wrapped methods
56
+ const result = await generateText({
57
+ model: openai('gpt-4o'),
58
+ prompt: 'Hello, world!',
59
+ // Optional: override wrap defaults for this call
60
+ experimental_telemetry: {
61
+ isEnabled: true,
62
+ metadata: eventMetadata({
63
+ userId, // REQUIRED
64
+ convoId, // Optional: conversation/thread id from your app
65
+ eventName, // Optional override for this call
66
+ properties: { source: requestSource },
67
+ }),
68
+ },
69
+ });
70
+
71
+ // Optional: identify user traits
72
+ await raindrop.users.identify({
73
+ userId,
74
+ traits: { plan: userPlan },
75
+ });
76
+
77
+ // 4. Flush before shutdown (serverless/scripts)
78
+ await raindrop.flush();
79
+ ```
80
+
81
+ Wrapped `generateText`, `streamText`, `generateObject`, and `streamObject` calls
82
+ are now tracked.
83
+
84
+ ## Runtime support
85
+
86
+ ### Node.js
87
+
88
+ Use the default import:
89
+
90
+ ```typescript
91
+ import { createRaindropAISDK } from '@raindrop-ai/ai-sdk';
92
+ ```
93
+
94
+ ### Cloudflare Workers
95
+
96
+ Cloudflare Workers supports a subset of Node’s `AsyncLocalStorage` when you
97
+ enable `nodejs_compat`
98
+ ([docs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/asynclocalstorage/)).
99
+
100
+ Import the Workers entrypoint:
101
+
102
+ ```typescript
103
+ import { createRaindropAISDK } from '@raindrop-ai/ai-sdk/workers';
104
+ ```
105
+
106
+ If `nodejs_compat` is not enabled, `node:async_hooks` is not available and
107
+ AsyncLocalStorage-based context propagation cannot work.
108
+
109
+ ### Edge / browser runtimes
110
+
111
+ Edge/browser runtimes don’t provide Node `AsyncLocalStorage`. Event/traces
112
+ shipping still works, but context propagation across async boundaries is
113
+ disabled. If you need correlation across nested calls, pass an explicit
114
+ `eventId` with `eventMetadata()`.
115
+
116
+ ---
117
+
118
+ ## Configuration
119
+
120
+ ### Client Options
121
+
122
+ ```typescript
123
+ const raindrop = createRaindropAISDK({
124
+ writeKey: process.env.RAINDROP_WRITE_KEY!, // REQUIRED
125
+ endpoint: 'https://api.raindrop.ai/v1', // Optional, defaults to production
126
+
127
+ traces: {
128
+ enabled: true, // Default: true
129
+ flushIntervalMs: 1000, // Default: 1000
130
+ maxBatchSize: 50, // Default: 50
131
+ debug: false, // Logs trace shipping
132
+ debugSpans: false, // Logs span parent relationships
133
+ },
134
+
135
+ events: {
136
+ enabled: true, // Default: true
137
+ partialFlushMs: 1000, // Debounce for streaming updates
138
+ debug: false, // Logs event shipping
139
+ },
140
+ });
141
+ ```
142
+
143
+ ### Wrap Options
144
+
145
+ ```typescript
146
+ const { generateText } = raindrop.wrap(ai, {
147
+ // Set defaults at wrap-time. Override per call with eventMetadata().
148
+ context: {
149
+ userId, // REQUIRED when setting user context here
150
+ eventId, // Optional - use your own ID for correlation
151
+ eventName, // Optional - categorize events
152
+ convoId, // Optional - group events into conversations
153
+ properties: {
154
+ ...defaultProperties,
155
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
156
+ }, // Optional - custom metadata
157
+ attachments: [], // Optional - input/output attachments
158
+ },
159
+
160
+ autoAttachment: true, // Default: true. Set false to disable automatic parsing
161
+
162
+ // Optional: customize event payload from messages
163
+ buildEvent: (messages) => ({
164
+ input: messages
165
+ .filter((m) => m.role === 'user')
166
+ .map((m) => m.content)
167
+ .join('\n'),
168
+ output: messages.filter((m) => m.role === 'assistant').pop()?.content,
169
+ properties: { messageCount: messages.length },
170
+ }),
171
+
172
+ // Optional: control what gets sent
173
+ send: {
174
+ events: true, // Default: true
175
+ traces: true, // Default: true
176
+ },
177
+ });
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Context & Metadata
183
+
184
+ ### Per-Call Context Override (Recommended)
185
+
186
+ Set stable defaults in `wrap()` and override them per call with
187
+ `eventMetadata()` when needed:
188
+
189
+ ```typescript
190
+ import { createRaindropAISDK, eventMetadata } from '@raindrop-ai/ai-sdk';
191
+
192
+ // Wrap once with defaults
193
+ const { generateText } = raindrop.wrap(ai, {
194
+ context: {
195
+ userId,
196
+ eventName,
197
+ properties: {
198
+ app: appName,
199
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
200
+ },
201
+ },
202
+ });
203
+
204
+ // Override defaults for this call
205
+ const result = await generateText({
206
+ model: openai('gpt-4o'),
207
+ prompt: 'Hello!',
208
+ experimental_telemetry: {
209
+ isEnabled: true,
210
+ metadata: eventMetadata({
211
+ userId, // REQUIRED per call when explicitly tracking
212
+ convoId, // Conversation routing
213
+ eventName, // Event categorization
214
+ properties: { source: requestSource }, // Additional metadata
215
+ // eventId, // Optional explicit eventId for correlation
216
+ }),
217
+ },
218
+ });
219
+ ```
220
+
221
+ This keeps common values in one place while allowing each request to set or
222
+ override `userId`, `convoId`, `eventName`, `properties`, or `eventId`.
223
+
224
+ **Merge behavior:**
225
+
226
+ - Call-time values override wrap-time defaults
227
+ - `properties` are merged (call-time wins on conflicts)
228
+ - `eventId` is auto-generated if neither wrap-time nor call-time sets it
229
+
230
+ If you need full manual control over attachments, set `autoAttachment: false` in
231
+ `wrap()`.
232
+
233
+ <Info>
234
+ `context` in `wrap()` is optional and useful for defaults. If both are
235
+ present, call-time `eventMetadata()` wins.
236
+
237
+ ```typescript
238
+ const { generateText } = raindrop.wrap(ai, {
239
+ context: {
240
+ userId, // Fallback defaults
241
+ properties: {
242
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
243
+ },
244
+ },
245
+ });
246
+
247
+ await generateText({
248
+ model,
249
+ prompt,
250
+ experimental_telemetry: {
251
+ isEnabled: true,
252
+ metadata: eventMetadata({ userId: actualUserId }),
253
+ },
254
+ });
255
+ ```
256
+ </Info>
257
+
258
+ ---
259
+
260
+ ## Identifying Users
261
+
262
+ Use `users.identify` to associate traits with a user.
263
+
264
+ ```typescript
265
+ await raindrop.users.identify({
266
+ userId,
267
+ traits: {
268
+ email: userEmail,
269
+ plan: userPlan,
270
+ orgId,
271
+ },
272
+ });
273
+ ```
274
+
275
+ ---
276
+
277
+ ## Tools
278
+
279
+ Tools are automatically wrapped and traced. No additional configuration needed:
280
+
281
+ ```typescript
282
+ import { tool } from 'ai';
283
+ import { z } from 'zod';
284
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
285
+
286
+ const weatherTool = tool({
287
+ description: 'Get current weather',
288
+ parameters: z.object({ city: z.string() }),
289
+ execute: async ({ city }) => {
290
+ const data = await fetchWeather(city);
291
+ return { temperature: data.temp, conditions: data.conditions };
292
+ },
293
+ });
294
+
295
+ const { generateText } = raindrop.wrap(ai, {
296
+ context: {
297
+ properties: {
298
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
299
+ },
300
+ },
301
+ });
302
+
303
+ // Tool calls are traced with args and results
304
+ const result = await generateText({
305
+ model: openai('gpt-4o'),
306
+ prompt: "What's the weather in Tokyo?",
307
+ tools: { weather: weatherTool },
308
+ maxSteps: 3,
309
+ experimental_telemetry: {
310
+ isEnabled: true,
311
+ metadata: eventMetadata({
312
+ userId,
313
+ eventName,
314
+ }),
315
+ },
316
+ });
317
+ ```
318
+
319
+ ### Nested LLM Calls in Tools
320
+
321
+ If your tool makes additional LLM calls, wrap them too for full trace
322
+ visibility:
323
+
324
+ ```typescript
325
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
326
+
327
+ const summarizeTool = tool({
328
+ description: 'Summarize text using AI',
329
+ parameters: z.object({ text: z.string() }),
330
+ execute: async ({ text }) => {
331
+ // Inner wrapper - traces only, no separate event
332
+ const { generateText: innerGenerate } = raindrop.wrap(ai, {
333
+ context: {
334
+ properties: {
335
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
336
+ },
337
+ },
338
+ send: { events: false, traces: true },
339
+ });
340
+
341
+ const summary = await innerGenerate({
342
+ model: openai('gpt-4o-mini'),
343
+ prompt: `Summarize: ${text}`,
344
+ experimental_telemetry: {
345
+ isEnabled: true,
346
+ metadata: eventMetadata({ userId, eventId }), // Same eventId links traces
347
+ },
348
+ });
349
+
350
+ return summary.text;
351
+ },
352
+ });
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Streaming
358
+
359
+ Streaming methods (`streamText`, `streamObject`) work identically:
360
+
361
+ ```typescript
362
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
363
+
364
+ const { streamText } = raindrop.wrap(ai, {
365
+ context: {
366
+ properties: {
367
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
368
+ },
369
+ },
370
+ });
371
+
372
+ const result = await streamText({
373
+ model: openai('gpt-4o'),
374
+ prompt: 'Write a haiku about coding',
375
+ experimental_telemetry: {
376
+ isEnabled: true,
377
+ metadata: eventMetadata({
378
+ userId,
379
+ eventName,
380
+ }),
381
+ },
382
+ });
383
+
384
+ // Consume the stream
385
+ for await (const chunk of result.textStream) {
386
+ process.stdout.write(chunk);
387
+ }
388
+ ```
389
+
390
+ Raindrop captures streaming-specific metrics:
391
+
392
+ - Time to first chunk (`ai.stream.msToFirstChunk`)
393
+ - Total stream duration (`ai.stream.msToFinish`)
394
+ - Average output tokens per second (`ai.stream.avgOutputTokensPerSecond`)
395
+
396
+ ### streamObject
397
+
398
+ `streamObject` works out of the box. You can await `result.object` directly
399
+ without manually consuming the stream:
400
+
401
+ ```typescript
402
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
403
+
404
+ const { streamObject } = raindrop.wrap(ai, {
405
+ context: {
406
+ properties: {
407
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
408
+ },
409
+ },
410
+ });
411
+
412
+ const result = await streamObject({
413
+ model: openai('gpt-4o'),
414
+ schema: z.object({ name: z.string(), age: z.number() }),
415
+ prompt: 'Generate a person named Alice who is 30',
416
+ experimental_telemetry: {
417
+ isEnabled: true,
418
+ metadata: eventMetadata({
419
+ userId,
420
+ eventName,
421
+ }),
422
+ },
423
+ });
424
+
425
+ // This works without consuming partialObjectStream
426
+ const person = await result.object;
427
+ ```
428
+
429
+ ---
430
+
431
+ ## ToolLoopAgent
432
+
433
+ <Info>Requires AI SDK v6 or later.</Info>
434
+
435
+ ToolLoopAgent is wrapped automatically. Both `generate()` and `stream()` trace
436
+ tool calls with their arguments and results.
437
+
438
+ ### generate
439
+
440
+ ```typescript
441
+ import { ToolLoopAgent, tool } from 'ai';
442
+ import { openai } from '@ai-sdk/openai';
443
+ import { z } from 'zod';
444
+ import { createRaindropAISDK, eventMetadata } from '@raindrop-ai/ai-sdk';
445
+
446
+ const raindrop = createRaindropAISDK({
447
+ writeKey: process.env.RAINDROP_WRITE_KEY!,
448
+ });
449
+
450
+ const { ToolLoopAgent } = raindrop.wrap(ai, {
451
+ context: {
452
+ properties: {
453
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
454
+ },
455
+ },
456
+ });
457
+
458
+ const weatherTool = tool({
459
+ description: 'Get current weather',
460
+ parameters: z.object({ city: z.string() }),
461
+ execute: async ({ city }) => {
462
+ return { temperature: 72, conditions: 'sunny' };
463
+ },
464
+ });
465
+
466
+ const agent = new ToolLoopAgent({
467
+ model: openai('gpt-4o'),
468
+ tools: { weather: weatherTool },
469
+ maxSteps: 5,
470
+ });
471
+
472
+ const result = await agent.generate({
473
+ prompt: "What's the weather in Tokyo?",
474
+ experimental_telemetry: {
475
+ isEnabled: true,
476
+ metadata: eventMetadata({
477
+ userId,
478
+ eventName,
479
+ }),
480
+ },
481
+ });
482
+ ```
483
+
484
+ ### stream
485
+
486
+ ```typescript
487
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
488
+
489
+ const result = await agent.stream({
490
+ prompt: "What's the weather in Tokyo?",
491
+ experimental_telemetry: {
492
+ isEnabled: true,
493
+ metadata: eventMetadata({
494
+ userId,
495
+ eventName,
496
+ }),
497
+ },
498
+ });
499
+
500
+ for await (const chunk of result.textStream) {
501
+ process.stdout.write(chunk);
502
+ }
503
+ ```
504
+
505
+ ---
506
+
507
+ ## Signals (Feedback)
508
+
509
+ Track user feedback on AI responses using the same `eventId`:
510
+
511
+ ```typescript
512
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
513
+
514
+ const eventId = crypto.randomUUID();
515
+
516
+ const { generateText } = raindrop.wrap(ai, {
517
+ context: {
518
+ properties: {
519
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
520
+ },
521
+ },
522
+ });
523
+
524
+ const result = await generateText({
525
+ model: openai('gpt-4o'),
526
+ prompt: userMessage,
527
+ experimental_telemetry: {
528
+ isEnabled: true,
529
+ metadata: eventMetadata({
530
+ userId,
531
+ eventId,
532
+ eventName,
533
+ }),
534
+ },
535
+ });
536
+
537
+ // Later, when user gives feedback
538
+ await raindrop.signals.track({
539
+ eventId,
540
+ name: 'thumbs_down',
541
+ comment: 'Answer was incorrect',
542
+ });
543
+ ```
544
+
545
+ ### Signal Types
546
+
547
+ | Type | Use Case |
548
+ | ------------ | -------------------------------- |
549
+ | `"default"` | Generic signals (thumbs up/down) |
550
+ | `"feedback"` | User comments about quality |
551
+ | `"edit"` | User corrected the output |
552
+
553
+ ```typescript
554
+ // Thumbs up
555
+ await raindrop.signals.track({
556
+ eventId,
557
+ name: 'thumbs_up',
558
+ sentiment: 'POSITIVE',
559
+ });
560
+
561
+ // User edit
562
+ await raindrop.signals.track({
563
+ eventId,
564
+ name: 'user_edit',
565
+ type: 'edit',
566
+ after: 'The corrected response text',
567
+ });
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Manual Event Updates
573
+
574
+ Update events after they're created:
575
+
576
+ ```typescript
577
+ // Add properties
578
+ await raindrop.events.setProperties(eventId, {
579
+ latencyMs: 1234,
580
+ cached: true,
581
+ });
582
+
583
+ // Add attachments
584
+ await raindrop.events.addAttachments(eventId, [
585
+ {
586
+ type: 'code',
587
+ name: 'generated.py',
588
+ value: "print('hello')",
589
+ role: 'output',
590
+ language: 'python',
591
+ },
592
+ ]);
593
+
594
+ // Mark as complete with final output
595
+ await raindrop.events.finish(eventId, {
596
+ output: 'Final response',
597
+ model: 'gpt-4o',
598
+ });
599
+ ```
600
+
601
+ ---
602
+
603
+ ## Custom Event Builder
604
+
605
+ Use `buildEvent` to customize the event payload from the conversation messages:
606
+
607
+ ```typescript
608
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
609
+
610
+ const { generateText } = raindrop.wrap(ai, {
611
+ context: {
612
+ properties: {
613
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
614
+ },
615
+ },
616
+ buildEvent: (messages) => {
617
+ const userMessages = messages.filter((m) => m.role === 'user');
618
+ const assistantMessages = messages.filter((m) => m.role === 'assistant');
619
+
620
+ return {
621
+ input: userMessages.map((m) => m.content).join('\n'),
622
+ output: assistantMessages.pop()?.content,
623
+ eventName: customEventName,
624
+ properties: {
625
+ turnCount: userMessages.length,
626
+ hasToolCalls: messages.some((m) => m.role === 'tool'),
627
+ },
628
+ };
629
+ },
630
+ });
631
+
632
+ await generateText({
633
+ model: openai('gpt-4o'),
634
+ prompt: 'Summarize this conversation',
635
+ experimental_telemetry: {
636
+ isEnabled: true,
637
+ metadata: eventMetadata({ userId, eventName }),
638
+ },
639
+ });
640
+ ```
641
+
642
+ ---
643
+
644
+ ## Flush & Shutdown
645
+
646
+ Always flush before your process exits to ensure all data is sent:
647
+
648
+ ```typescript
649
+ // Serverless: flush at end of request
650
+ export async function POST(request: Request) {
651
+ const result = await generateText({ ... });
652
+ await raindrop.flush();
653
+ return Response.json({ text: result.text });
654
+ }
655
+
656
+ // Long-running: shutdown gracefully
657
+ process.on("SIGTERM", async () => {
658
+ await raindrop.shutdown();
659
+ process.exit(0);
660
+ });
661
+ ```
662
+
663
+ ---
664
+
665
+ ## Debugging
666
+
667
+ Enable debug logging to troubleshoot issues:
668
+
669
+ ```typescript
670
+ const raindrop = createRaindropAISDK({
671
+ writeKey: process.env.RAINDROP_WRITE_KEY!,
672
+ events: { debug: true },
673
+ traces: { debug: true, debugSpans: true },
674
+ });
675
+ ```
676
+
677
+ This logs:
678
+
679
+ - Every event sent to Raindrop
680
+ - Every trace batch shipped
681
+ - Span parent/child relationships (with `debugSpans`)
682
+
683
+ ---
684
+
685
+ ## Advanced: Context Propagation APIs
686
+
687
+ For advanced use cases, access the underlying context propagation utilities:
688
+
689
+ <Info>
690
+ These APIs use AsyncLocalStorage and are only effective in Node.js (and
691
+ Cloudflare Workers with `nodejs_compat`). In Edge/browser runtimes they behave
692
+ like no-ops.
693
+ </Info>
694
+
695
+ ```typescript
696
+ import {
697
+ currentSpan,
698
+ withCurrent,
699
+ getContextManager,
700
+ } from '@raindrop-ai/ai-sdk';
701
+
702
+ // Get the current span (returns NOOP_SPAN if none)
703
+ const span = currentSpan();
704
+ console.log(span.eventId, span.traceIdB64);
705
+
706
+ // Run code within a span context
707
+ withCurrent(span, () => {
708
+ // Nested calls inherit this span as parent
709
+ const nested = currentSpan();
710
+ console.log(nested.spanIdB64 === span.spanIdB64); // true
711
+ });
712
+
713
+ // Access the context manager directly
714
+ const cm = getContextManager();
715
+ const parentIds = cm.getParentSpanIds();
716
+ ```
717
+
718
+ ---
719
+
720
+ ## Troubleshooting
721
+
722
+ ### Events not appearing in dashboard
723
+
724
+ 1. **Check your write key** - Ensure `RAINDROP_WRITE_KEY` is set correctly
725
+ 2. **Flush before exit** - Call `await raindrop.flush()` before your process
726
+ ends
727
+ 3. **Enable debug logging** - Set `events: { debug: true }` to see what's being
728
+ sent
729
+
730
+ ### Traces missing or incomplete
731
+
732
+ 1. **Enable trace debugging** - Set `traces: { debug: true, debugSpans: true }`
733
+ 2. **Check for errors** - Look for `[raindrop-ai/ai-sdk]` prefixed logs
734
+
735
+ ### Context not propagating to nested calls
736
+
737
+ 1. **Check your runtime** - AsyncLocalStorage-based propagation requires Node.js
738
+ (or Cloudflare Workers with `nodejs_compat`)
739
+ 2. **Workers import** - On Cloudflare Workers, import
740
+ `@raindrop-ai/ai-sdk/workers`
741
+
742
+ Ensure nested AI calls use the same `eventId` and set `send: { events: false }`
743
+ to avoid duplicate events:
744
+
745
+ ```typescript
746
+ import { eventMetadata } from '@raindrop-ai/ai-sdk';
747
+
748
+ const { generateText: innerGenerate } = raindrop.wrap(ai, {
749
+ context: {
750
+ properties: {
751
+ wizardSession: '__WIZARD_SESSION_UUID__', // REQUIRED
752
+ },
753
+ },
754
+ send: { events: false, traces: true },
755
+ });
756
+
757
+ await innerGenerate({
758
+ model,
759
+ prompt,
760
+ experimental_telemetry: {
761
+ isEnabled: true,
762
+ metadata: eventMetadata({ userId, eventId }),
763
+ },
764
+ });
765
+ ```
766
+
767
+ ---
768
+
769
+ Use the Raindrop dashboard to verify events and traces after integration.