@dremio/js-sdk 0.45.2 → 0.45.4
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 +208 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -360,6 +360,43 @@ const history = await conversation.history();
|
|
|
360
360
|
console.log(`Conversation has ${history.length} events`);
|
|
361
361
|
```
|
|
362
362
|
|
|
363
|
+
#### Stop a Run
|
|
364
|
+
|
|
365
|
+
Stop the currently active run for a conversation:
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
await conversation.stopRun(runId);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Note: Only one run can be active per conversation at a time.
|
|
372
|
+
|
|
373
|
+
#### Clone a Conversation
|
|
374
|
+
|
|
375
|
+
Create a copy of the conversation instance:
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
const conversationCopy = conversation.clone();
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### Serialize Conversation
|
|
382
|
+
|
|
383
|
+
Convert the conversation to a plain JSON object:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
const json = conversation.toJSON();
|
|
387
|
+
console.log(json);
|
|
388
|
+
// {
|
|
389
|
+
// id: "...",
|
|
390
|
+
// title: "...",
|
|
391
|
+
// createdAt: Temporal.Instant,
|
|
392
|
+
// modifiedAt: Temporal.Instant,
|
|
393
|
+
// currentRunId: "..." | null,
|
|
394
|
+
// modelName: "..." | null,
|
|
395
|
+
// modelProviderId: "..." | null,
|
|
396
|
+
// tag: "..."
|
|
397
|
+
// }
|
|
398
|
+
```
|
|
399
|
+
|
|
363
400
|
### Working with Chat Events
|
|
364
401
|
|
|
365
402
|
The state of a conversation is represented as a stream of `ChatEvent` objects. These events include user messages, tool call requests and results, and Agent replies. While the event stream enables incremental UI updates, it contains implicit relationships that need to be made explicit for easier consumption.
|
|
@@ -371,16 +408,36 @@ The state of a conversation is represented as a stream of `ChatEvent` objects. T
|
|
|
371
408
|
- `toolRequest` - A request to execute a tool (tool calls can run in parallel)
|
|
372
409
|
- `toolResponse` - The result of a tool execution
|
|
373
410
|
- `error` - An error message
|
|
411
|
+
- `conversationUpdate` - Updates to conversation metadata (title, summary)
|
|
412
|
+
- `endOfStream` - Marks the end of a run's event stream
|
|
374
413
|
|
|
375
414
|
#### Building a Conversation Snapshot
|
|
376
415
|
|
|
377
|
-
The `AgentConversation.reduceChatEvents()` method transforms a stream of chat events into a structured snapshot that makes implicit relationships explicit
|
|
416
|
+
The `AgentConversation.reduceChatEvents()` method is a pure reducer function that transforms a stream of chat events into a structured snapshot that makes implicit relationships explicit.
|
|
417
|
+
|
|
418
|
+
**Function Signature:**
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
function reduceChatEvents(
|
|
422
|
+
state: ConversationExchange[],
|
|
423
|
+
chatEvents: ChatEvent[],
|
|
424
|
+
): ConversationExchange[];
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**What it does:**
|
|
378
428
|
|
|
379
429
|
- **Groups events by exchange** - Each user-agent interaction (identified by `runId`) becomes a separate exchange
|
|
380
430
|
- **Pairs tool calls** - Tool requests and responses are matched by `callId` and combined into a single object
|
|
381
|
-
- **Derives tool state** - Automatically determines if a tool call is `pending`, `success`, or `
|
|
382
|
-
-
|
|
383
|
-
-
|
|
431
|
+
- **Derives tool state** - Automatically determines if a tool call is `pending`, `success`, `error`, or `canceled`
|
|
432
|
+
- `pending` - Tool request received but no response yet
|
|
433
|
+
- `success` - Tool response received without error
|
|
434
|
+
- `error` - Tool response received with error message
|
|
435
|
+
- `canceled` - End of stream reached without receiving a response
|
|
436
|
+
- **Handles duplicates** - Efficiently filters duplicate events when merging history and live streams (events with the same `id` are deduplicated)
|
|
437
|
+
- **Preserves order** - Maintains insertion order for messages and tool calls using `Map` data structures
|
|
438
|
+
- **Immutable updates** - Uses structural sharing to efficiently create new snapshots without mutating the original state
|
|
439
|
+
|
|
440
|
+
**Basic Usage:**
|
|
384
441
|
|
|
385
442
|
```typescript
|
|
386
443
|
import { AgentConversation } from "@dremio/js-sdk/enterprise/ai";
|
|
@@ -404,12 +461,110 @@ type ConversationExchange = {
|
|
|
404
461
|
submittedUserMessage?: UserChatMessage;
|
|
405
462
|
};
|
|
406
463
|
|
|
464
|
+
type ConversationExchangeMessage =
|
|
465
|
+
| ChatEventWithChunkType<"error">
|
|
466
|
+
| ChatEventWithChunkType<"model">
|
|
467
|
+
| ChatEventWithChunkType<"userMessage">;
|
|
468
|
+
|
|
407
469
|
type AgentToolCall = {
|
|
408
470
|
id: string;
|
|
409
|
-
state: "error" | "pending" | "success";
|
|
471
|
+
state: "canceled" | "error" | "pending" | "success";
|
|
410
472
|
request: ChatEventWithChunkType<"toolRequest"> | undefined;
|
|
411
473
|
result: ChatEventWithChunkType<"toolResponse"> | undefined;
|
|
412
474
|
};
|
|
475
|
+
|
|
476
|
+
// ChatEvent is the base type for all events in the stream
|
|
477
|
+
type ChatEvent = {
|
|
478
|
+
id: string;
|
|
479
|
+
conversationId: string;
|
|
480
|
+
runId: string;
|
|
481
|
+
createdAt: Temporal.Instant;
|
|
482
|
+
modelName: string;
|
|
483
|
+
modelProviderId: string;
|
|
484
|
+
role: "agent" | "user";
|
|
485
|
+
content:
|
|
486
|
+
| { chunkType: "conversationUpdate"; title?: string; summary?: string }
|
|
487
|
+
| { chunkType: "endOfStream" }
|
|
488
|
+
| { chunkType: "error"; message: string }
|
|
489
|
+
| { chunkType: "model"; name: string; result: Record<string, unknown> }
|
|
490
|
+
| {
|
|
491
|
+
chunkType: "toolRequest";
|
|
492
|
+
callId: string;
|
|
493
|
+
name: string;
|
|
494
|
+
arguments: Record<string, unknown>;
|
|
495
|
+
commentary?: string;
|
|
496
|
+
summarizedTitle?: string;
|
|
497
|
+
}
|
|
498
|
+
| {
|
|
499
|
+
chunkType: "toolResponse";
|
|
500
|
+
callId: string;
|
|
501
|
+
name: string;
|
|
502
|
+
result: Record<string, unknown>;
|
|
503
|
+
commentary?: string;
|
|
504
|
+
}
|
|
505
|
+
| { chunkType: "userMessage"; text: string };
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// ChatEventWithChunkType is a helper type to extract events by their chunk type
|
|
509
|
+
type ChatEventWithChunkType<T extends ChatEvent["content"]["chunkType"]> =
|
|
510
|
+
Extract<ChatEvent, { content: { chunkType: T } }>;
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### UI Rendering and React Reconciliation
|
|
514
|
+
|
|
515
|
+
The conversation snapshot produced by the reducer (and automatically managed by the `AgentConversationMachine`) is designed specifically for modern declarative UIs like React, Vue, Svelte, or Ink.
|
|
516
|
+
|
|
517
|
+
**You do not need to manually track message IDs, buffer stream chunks, or manage parallel tool call states.**
|
|
518
|
+
|
|
519
|
+
The reducer performs highly optimized, **immutable updates** as new events arrive. Because the top-level references change only when the underlying data changes, you can directly map over the snapshot in your render function. It is safe to pass these objects into memoized components (e.g., `React.memo`) without writing complex deep-equality checks or manual diffing logic.
|
|
520
|
+
|
|
521
|
+
**React Example:**
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { createActor } from "xstate";
|
|
525
|
+
import { UserChatMessage } from "@dremio/js-sdk/enterprise/ai";
|
|
526
|
+
|
|
527
|
+
const actor = createActor(conversationMachine, {
|
|
528
|
+
input: { conversationId: null },
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Subscribe to state changes and update React state
|
|
532
|
+
actor.subscribe((snapshot) => {
|
|
533
|
+
setExchanges(snapshot.context.conversationSnapshot ?? []);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
actor.start();
|
|
537
|
+
|
|
538
|
+
// In your render function - direct mapping, no manual diffing needed
|
|
539
|
+
return (
|
|
540
|
+
<div>
|
|
541
|
+
{exchanges.map((exchange) => (
|
|
542
|
+
<Exchange key={exchange.id} exchange={exchange} />
|
|
543
|
+
))}
|
|
544
|
+
</div>
|
|
545
|
+
);
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Why this works:**
|
|
549
|
+
|
|
550
|
+
- The reducer uses **structural sharing** via the `mutative` library, creating new object references only for changed data
|
|
551
|
+
- Unchanged exchanges and messages retain their original references, enabling efficient React reconciliation
|
|
552
|
+
- Maps preserve insertion order and provide O(1) lookups by ID
|
|
553
|
+
- Tool calls are automatically paired and their state is derived, eliminating manual tracking logic
|
|
554
|
+
|
|
555
|
+
**Vue/Svelte Example:**
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
// The same principle applies - just bind to the snapshot
|
|
559
|
+
actor.subscribe((snapshot) => {
|
|
560
|
+
conversationSnapshot = snapshot.context.conversationSnapshot ?? [];
|
|
561
|
+
});
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
```svelte
|
|
565
|
+
{#each conversationSnapshot as exchange (exchange.id)}
|
|
566
|
+
<Exchange {exchange} />
|
|
567
|
+
{/each}
|
|
413
568
|
```
|
|
414
569
|
|
|
415
570
|
#### Incremental Updates
|
|
@@ -504,16 +659,60 @@ actor.send({
|
|
|
504
659
|
- `uninitialized` - Initial state before conversation is created
|
|
505
660
|
- `creating_conversation` - Creating a new conversation
|
|
506
661
|
- `retrieving_history` - Fetching conversation history
|
|
662
|
+
- `retrieve_history_failed` - Error state when history retrieval fails
|
|
507
663
|
- `idle` - Ready to accept new messages
|
|
508
664
|
- `submitting_message` - Sending a user message
|
|
509
|
-
- `
|
|
510
|
-
- `
|
|
665
|
+
- `submitting_message_failed` - Error state when message submission fails
|
|
666
|
+
- `streaming` - Receiving agent responses (with substates: `monitoring_replies`, `stopping`)
|
|
511
667
|
|
|
512
668
|
#### Machine Events
|
|
513
669
|
|
|
514
|
-
- `SUBMIT_USER_MESSAGE` - Send a new message
|
|
670
|
+
- `SUBMIT_USER_MESSAGE` - Send a new message with a `UserChatMessage`
|
|
671
|
+
- `RETRY_MESSAGE` - Retry sending the last failed message
|
|
515
672
|
- `REFRESH_HISTORY` - Reload conversation history
|
|
516
|
-
- `
|
|
673
|
+
- `STOP_RUN` - Stop the current run
|
|
674
|
+
|
|
675
|
+
#### Machine Context
|
|
676
|
+
|
|
677
|
+
The machine maintains the following context:
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
type ConversationMachineContext = {
|
|
681
|
+
conversationId: string | null;
|
|
682
|
+
conversationSnapshot?: ConversationExchange[];
|
|
683
|
+
currentRunId?: string | null;
|
|
684
|
+
lastError?: unknown;
|
|
685
|
+
messageAttempt?: UserChatMessage;
|
|
686
|
+
};
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
#### Machine Emitted Events
|
|
690
|
+
|
|
691
|
+
The machine emits the following events that can be subscribed to:
|
|
692
|
+
|
|
693
|
+
```typescript
|
|
694
|
+
type ConversationMachineEmitted =
|
|
695
|
+
| { type: "apiError"; error: HttpError }
|
|
696
|
+
| { type: "chatEventReceived"; chatEvent: ChatEvent }
|
|
697
|
+
| { type: "conversationCreated"; id: string };
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Subscribe to emitted events:
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
actor.subscribe((snapshot) => {
|
|
704
|
+
// Access emitted events through snapshot
|
|
705
|
+
console.log("State:", snapshot.value);
|
|
706
|
+
console.log("Context:", snapshot.context);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Or use the system to listen for specific events
|
|
710
|
+
actor.system.subscribe((event) => {
|
|
711
|
+
if (event.type === "chatEventReceived") {
|
|
712
|
+
console.log("New chat event:", event.chatEvent);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
```
|
|
517
716
|
|
|
518
717
|
## Catalog
|
|
519
718
|
|