2025-09-22 20:13:43 +00:00
|
|
|
import { type ReasoningPart } from '@ai-sdk/provider-utils';
|
2025-09-17 09:30:24 +00:00
|
|
|
import { type TextPart } from 'ai';
|
2025-09-22 20:13:43 +00:00
|
|
|
import {
|
|
|
|
|
parseStreamLine,
|
|
|
|
|
splitStreamIntoLines,
|
|
|
|
|
type TextBlock,
|
|
|
|
|
} from 'twenty-shared/ai';
|
2025-09-17 09:30:24 +00:00
|
|
|
|
|
|
|
|
export const constructAssistantMessageContentFromStream = (
|
|
|
|
|
rawContent: string,
|
|
|
|
|
) => {
|
2025-09-22 20:13:43 +00:00
|
|
|
const lines = splitStreamIntoLines(rawContent);
|
2025-09-17 09:30:24 +00:00
|
|
|
|
|
|
|
|
const output: Array<TextPart | ReasoningPart> = [];
|
2025-09-22 20:13:43 +00:00
|
|
|
let currentTextBlock: TextBlock = null;
|
|
|
|
|
|
|
|
|
|
const flushTextBlock = () => {
|
|
|
|
|
if (currentTextBlock) {
|
|
|
|
|
if (currentTextBlock.type === 'reasoning') {
|
|
|
|
|
output.push({
|
|
|
|
|
type: 'reasoning',
|
|
|
|
|
text: currentTextBlock.content,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
output.push({
|
|
|
|
|
type: 'text',
|
|
|
|
|
text: currentTextBlock.content,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
currentTextBlock = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-09-17 09:30:24 +00:00
|
|
|
|
|
|
|
|
for (const line of lines) {
|
2025-09-22 20:13:43 +00:00
|
|
|
const event = parseStreamLine(line);
|
2025-09-17 09:30:24 +00:00
|
|
|
|
2025-09-22 20:13:43 +00:00
|
|
|
if (!event) {
|
2025-09-17 09:30:24 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (event.type) {
|
2025-09-22 20:13:43 +00:00
|
|
|
case 'reasoning-start':
|
|
|
|
|
flushTextBlock();
|
|
|
|
|
currentTextBlock = {
|
|
|
|
|
type: 'reasoning',
|
|
|
|
|
content: '',
|
|
|
|
|
isThinking: true,
|
|
|
|
|
};
|
2025-09-17 09:30:24 +00:00
|
|
|
break;
|
|
|
|
|
|
2025-09-22 20:13:43 +00:00
|
|
|
case 'reasoning-delta':
|
|
|
|
|
if (!currentTextBlock || currentTextBlock.type !== 'reasoning') {
|
|
|
|
|
flushTextBlock();
|
|
|
|
|
currentTextBlock = {
|
2025-09-17 09:30:24 +00:00
|
|
|
type: 'reasoning',
|
2025-09-22 20:13:43 +00:00
|
|
|
content: '',
|
|
|
|
|
isThinking: true,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
currentTextBlock.content += event.text || '';
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'reasoning-end':
|
|
|
|
|
if (currentTextBlock?.type === 'reasoning') {
|
|
|
|
|
currentTextBlock.isThinking = false;
|
2025-09-17 09:30:24 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'text-delta':
|
2025-09-22 20:13:43 +00:00
|
|
|
if (!currentTextBlock || currentTextBlock.type !== 'text') {
|
|
|
|
|
flushTextBlock();
|
|
|
|
|
currentTextBlock = { type: 'text', content: '' };
|
|
|
|
|
}
|
|
|
|
|
currentTextBlock.content += event.text || '';
|
2025-09-17 09:30:24 +00:00
|
|
|
break;
|
|
|
|
|
|
2025-09-22 20:13:43 +00:00
|
|
|
case 'step-finish':
|
|
|
|
|
if (currentTextBlock?.type === 'reasoning') {
|
|
|
|
|
currentTextBlock.isThinking = false;
|
2025-09-17 09:30:24 +00:00
|
|
|
}
|
2025-09-22 20:13:43 +00:00
|
|
|
flushTextBlock();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'error':
|
|
|
|
|
flushTextBlock();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2025-09-17 09:30:24 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-22 20:13:43 +00:00
|
|
|
flushTextBlock();
|
|
|
|
|
|
2025-09-17 09:30:24 +00:00
|
|
|
return output;
|
|
|
|
|
};
|