feat: Introduce new core components for conversation and speech management

- Added ConversationManager for managing conversation history with configurable limits.
- Implemented InputQueue for serial processing of input items.
- Created SpeechManager for handling text-to-speech generation and streaming.
- Developed StreamProcessor for processing LLM streams and forwarding events.
- Added TranscriptionManager for audio transcription using AI SDK.
- Introduced WebSocketManager for managing WebSocket connections and messaging.
- Updated VoiceAgent to support new architecture and improved socket handling.
- Refactored index files to export new core components.
This commit is contained in:
Bijit Mondal
2026-02-23 16:15:49 +05:30
parent 4dd30b89c0
commit 5e7eb469ae
71 changed files with 5175 additions and 19 deletions

228
dist/core/StreamProcessor.js vendored Normal file
View File

@@ -0,0 +1,228 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processFullStream = processFullStream;
exports.handleStreamChunk = handleStreamChunk;
/**
* Processes the fullStream from an AI SDK `streamText` call,
* forwarding events to WebSocket clients and collecting the complete response.
*
* This is a standalone function (not a class) because it has no persistent state.
*/
async function processFullStream(result, callbacks, extraResponseFields) {
const { onTextDelta, onTextEnd, sendMessage, emitEvent } = callbacks;
let fullText = "";
let fullReasoning = "";
const allToolCalls = [];
const allToolResults = [];
const allSources = [];
const allFiles = [];
for await (const part of result.fullStream) {
switch (part.type) {
// ── Stream lifecycle ──────────────────────────────
case "start":
sendMessage({ type: "stream_start" });
break;
case "finish":
emitEvent("text", { role: "assistant", text: fullText });
sendMessage({
type: "stream_finish",
finishReason: part.finishReason,
usage: part.totalUsage,
});
break;
case "error":
emitEvent("error", part.error);
sendMessage({
type: "stream_error",
error: String(part.error),
});
break;
case "abort":
emitEvent("abort", { reason: part.reason });
sendMessage({
type: "stream_abort",
reason: part.reason,
});
break;
// ── Step lifecycle ────────────────────────────────
case "start-step":
sendMessage({
type: "step_start",
warnings: part.warnings,
});
break;
case "finish-step":
sendMessage({
type: "step_finish",
finishReason: part.finishReason,
usage: part.usage,
});
break;
// ── Text streaming ────────────────────────────────
case "text-start":
sendMessage({ type: "text_start", id: part.id });
break;
case "text-delta":
fullText += part.text;
onTextDelta?.(part.text);
sendMessage({
type: "text_delta",
id: part.id,
text: part.text,
});
break;
case "text-end":
onTextEnd?.();
sendMessage({ type: "text_end", id: part.id });
break;
// ── Reasoning streaming ───────────────────────────
case "reasoning-start":
sendMessage({ type: "reasoning_start", id: part.id });
break;
case "reasoning-delta":
fullReasoning += part.text;
sendMessage({
type: "reasoning_delta",
id: part.id,
text: part.text,
});
break;
case "reasoning-end":
sendMessage({ type: "reasoning_end", id: part.id });
break;
// ── Tool input streaming ──────────────────────────
case "tool-input-start":
sendMessage({
type: "tool_input_start",
id: part.id,
toolName: part.toolName,
});
break;
case "tool-input-delta":
sendMessage({
type: "tool_input_delta",
id: part.id,
delta: part.delta,
});
break;
case "tool-input-end":
sendMessage({ type: "tool_input_end", id: part.id });
break;
// ── Tool execution ────────────────────────────────
case "tool-call":
allToolCalls.push({
toolName: part.toolName,
toolCallId: part.toolCallId,
input: part.input,
});
sendMessage({
type: "tool_call",
toolName: part.toolName,
toolCallId: part.toolCallId,
input: part.input,
});
break;
case "tool-result":
allToolResults.push({
toolName: part.toolName,
toolCallId: part.toolCallId,
output: part.output,
});
sendMessage({
type: "tool_result",
toolName: part.toolName,
toolCallId: part.toolCallId,
result: part.output,
});
break;
case "tool-error":
sendMessage({
type: "tool_error",
toolName: part.toolName,
toolCallId: part.toolCallId,
error: String(part.error),
});
break;
// ── Sources and files ─────────────────────────────
case "source":
allSources.push(part);
sendMessage({
type: "source",
source: part,
});
break;
case "file":
allFiles.push(part.file);
sendMessage({
type: "file",
file: part.file,
});
break;
}
}
// Send the complete response
sendMessage({
type: "response_complete",
text: fullText,
reasoning: fullReasoning || undefined,
toolCalls: allToolCalls,
toolResults: allToolResults,
sources: allSources.length > 0 ? allSources : undefined,
files: allFiles.length > 0 ? allFiles : undefined,
...extraResponseFields,
});
return {
fullText,
fullReasoning,
allToolCalls,
allToolResults,
allSources,
allFiles,
};
}
/**
* Handle onChunk callback events and emit them.
*/
function handleStreamChunk(chunk, emitEvent) {
switch (chunk.type) {
case "text-delta":
emitEvent("chunk:text_delta", { id: chunk.id, text: chunk.text });
break;
case "reasoning-delta":
emitEvent("chunk:reasoning_delta", {
id: chunk.id,
text: chunk.text,
});
break;
case "tool-call":
emitEvent("chunk:tool_call", {
toolName: chunk.toolName,
toolCallId: chunk.toolCallId,
input: chunk.input,
});
break;
case "tool-result":
emitEvent("chunk:tool_result", {
toolName: chunk.toolName,
toolCallId: chunk.toolCallId,
result: chunk.output,
});
break;
case "tool-input-start":
emitEvent("chunk:tool_input_start", {
id: chunk.id,
toolName: chunk.toolName,
});
break;
case "tool-input-delta":
emitEvent("chunk:tool_input_delta", {
id: chunk.id,
delta: chunk.delta,
});
break;
case "source":
emitEvent("chunk:source", chunk);
break;
}
}
//# sourceMappingURL=StreamProcessor.js.map