mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 19:46:32 +08:00
feat(core): add stream object api (#12841)
Close [AI-193](https://linear.app/affine-design/issue/AI-193) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for streaming structured AI chat responses as objects, enabling richer and more interactive chat experiences. - Chat messages now include a new field displaying structured stream objects, such as reasoning steps, text deltas, tool calls, and tool results. - GraphQL APIs and queries updated to expose these structured streaming objects in chat histories. - Introduced a new streaming chat endpoint for object-based responses. - **Bug Fixes** - Improved error handling for streaming responses to ensure more robust and informative error reporting. - **Refactor** - Centralized and streamlined session preparation and streaming logic for AI chat providers. - Unified streaming setup across multiple AI model providers. - **Tests** - Extended test coverage for streaming object responses to ensure reliability and correctness. - **Documentation** - Updated type definitions and schemas to reflect new streaming object capabilities in both backend and frontend code. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
||||
createExaCrawlTool,
|
||||
createExaSearchTool,
|
||||
} from '../tools';
|
||||
import { PromptMessage } from './types';
|
||||
import { PromptMessage, StreamObject } from './types';
|
||||
|
||||
type ChatMessage = CoreUserMessage | CoreAssistantMessage;
|
||||
|
||||
@@ -387,6 +387,22 @@ export interface CustomAITools extends ToolSet {
|
||||
|
||||
type ChunkType = TextStreamPart<CustomAITools>['type'];
|
||||
|
||||
export function parseUnknownError(error: unknown) {
|
||||
if (typeof error === 'string') {
|
||||
throw new Error(error);
|
||||
} else if (error instanceof Error) {
|
||||
throw error;
|
||||
} else if (
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'message' in error
|
||||
) {
|
||||
throw new Error(String(error.message));
|
||||
} else {
|
||||
throw new Error(JSON.stringify(error));
|
||||
}
|
||||
}
|
||||
|
||||
export class TextStreamParser {
|
||||
private readonly CALLOUT_PREFIX = '\n[!]\n';
|
||||
|
||||
@@ -446,8 +462,8 @@ export class TextStreamParser {
|
||||
break;
|
||||
}
|
||||
case 'error': {
|
||||
const error = chunk.error as { type: string; message: string };
|
||||
throw new Error(error.message);
|
||||
parseUnknownError(chunk.error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.lastType = chunk.type;
|
||||
@@ -490,3 +506,54 @@ export class TextStreamParser {
|
||||
return links;
|
||||
}
|
||||
}
|
||||
|
||||
export class StreamObjectParser {
|
||||
public parse(chunk: TextStreamPart<CustomAITools>) {
|
||||
switch (chunk.type) {
|
||||
case 'reasoning':
|
||||
case 'text-delta':
|
||||
case 'tool-call':
|
||||
case 'tool-result': {
|
||||
return chunk;
|
||||
}
|
||||
case 'error': {
|
||||
parseUnknownError(chunk.error);
|
||||
return null;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public mergeTextDelta(chunks: StreamObject[]): StreamObject[] {
|
||||
return chunks.reduce((acc, curr) => {
|
||||
const prev = acc.at(-1);
|
||||
switch (curr.type) {
|
||||
case 'reasoning':
|
||||
case 'text-delta': {
|
||||
if (prev && prev.type === curr.type) {
|
||||
prev.textDelta += curr.textDelta;
|
||||
} else {
|
||||
acc.push(curr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
acc.push(curr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, [] as StreamObject[]);
|
||||
}
|
||||
|
||||
public mergeContent(chunks: StreamObject[]): string {
|
||||
return chunks.reduce((acc, curr) => {
|
||||
if (curr.type === 'text-delta') {
|
||||
acc += curr.textDelta;
|
||||
}
|
||||
return acc;
|
||||
}, '');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user