Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tasty-bobcats-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'ai': patch
---

StreamData: add `annotations` and `appendMessageAnnotation` support
14 changes: 14 additions & 0 deletions packages/core/shared/parse-complex-response.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,13 @@ describe('parseComplexResponse function', () => {
// Execute the parser function
const result = await parseComplexResponse({
reader: createTestReader([

'0:"Sample text message."\n',
'8:[{"key":"value"}, 2]\n',

'8:[{"key":"value"}, 2]\n',
'0:"Sample text message."\n',

]),
abortControllerRef: { current: new AbortController() },
update: mockUpdate,
Expand All @@ -243,8 +248,14 @@ describe('parseComplexResponse function', () => {
// check the mockUpdate call:
expect(mockUpdate).toHaveBeenCalledTimes(2);


expect(mockUpdate.mock.calls[0][0]).toEqual([
assistantTextMessage('Sample text message.'),
]);

expect(mockUpdate.mock.calls[0][0]).toEqual([]);


expect(mockUpdate.mock.calls[1][0]).toEqual([
{
...assistantTextMessage('Sample text message.'),
Expand All @@ -264,6 +275,8 @@ describe('parseComplexResponse function', () => {
});
});



it('should parse a combination of a function_call and message annotations', async () => {
const mockUpdate = vi.fn();

Expand Down Expand Up @@ -350,4 +363,5 @@ describe('parseComplexResponse function', () => {
data: [],
});
});

});
36 changes: 35 additions & 1 deletion packages/core/shared/parse-complex-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,29 @@ type PrefixMap = {
data: JSONValue[];
};


function initializeMessage({
generateId,
...rest
}: {
generateId: () => string;
content: string;
createdAt: Date;
annotations?: JSONValue[];
}): Message {
return {
id: generateId(),
role: 'assistant',
...rest
};

function assignAnnotationsToMessage<T extends Message | null | undefined>(
message: T,
annotations: JSONValue[] | undefined,
): T {
if (!message || !annotations || !annotations.length) return message;
return { ...message, annotations: [...annotations] } as T;

}

export async function parseComplexResponse({
Expand Down Expand Up @@ -63,7 +80,24 @@ export async function parseComplexResponse({
id: generateId(),
role: 'assistant',
content: value,
createdAt,
createdAt
};
}
}

if (type == 'message_annotations') {
if (prefixMap['text']) {
prefixMap['text'] = {
...prefixMap['text'],
annotations: [...prefixMap['text'].annotations || [], ...value],
};
} else {
prefixMap['text'] = {
id: generateId(),
role: 'assistant',
content: '',
annotations: [...value],
createdAt
};
}
}
Expand Down
9 changes: 6 additions & 3 deletions packages/core/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ export interface Message {
tool_calls?: string | ToolCall[];

/**
* Additional message-specific information added on the server via StreamData

* Additional message-specific information added on the server via StreamData
* Additional message-specific information added on the server via StreamData

*/
annotations?: JSONValue[] | undefined;
}
Expand Down Expand Up @@ -315,7 +318,7 @@ export type UseCompletionOptions = {

body?: object
}
=======

body?: object;
};

Expand All @@ -328,7 +331,7 @@ export type JSONValue =
| { [x: string]: JSONValue }

| Array<JSONValue>
=======

| Array<JSONValue>;

export type AssistantMessage = {
Expand Down
11 changes: 10 additions & 1 deletion packages/core/streams/ai-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface AIStreamCallbacks {

export interface AIStreamCallbacksAndOptions extends AIStreamCallbacks {
streamData?: Data
=======

export interface AIStreamCallbacksAndOptions {
/** `onStart`: Called once when the stream is initialized. */
onStart?: () => Promise<void> | void;
Expand Down Expand Up @@ -81,10 +81,14 @@ export interface AIStreamParserOptions {
* @returns {string | void} The parsed data or void.
*/
export interface AIStreamParser {

(data: string, options: AIStreamParserOptions): string | void;

(data: string, options: AIStreamParserOptions):
| string
| void
| { isText: false; content: string };

}

/**
Expand Down Expand Up @@ -124,8 +128,13 @@ export function createEventStreamTransformer(

const parsedMessage = customParser
? customParser(event.data, {

event: event.event
})

event: event.event,
})

: event.data;
if (parsedMessage) controller.enqueue(parsedMessage);

Expand Down
7 changes: 7 additions & 0 deletions packages/core/streams/stream-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,18 @@ export class experimental_StreamData {
}

if (self.messageAnnotations.length) {

const encodedmessageAnnotations = self.encoder.encode(
formatStreamPart('message_annotations', self.messageAnnotations),
);
controller.enqueue(encodedmessageAnnotations);

const encodedMessageAnnotations = self.encoder.encode(
formatStreamPart('message_annotations', self.messageAnnotations),
);
self.messageAnnotations = [];
controller.enqueue(encodedMessageAnnotations);

}

controller.enqueue(chunk);
Expand Down
Loading