changed useChatStreaming to work with the chat page
This commit is contained in:
parent
f3911afe92
commit
d336baba32
1 changed files with 293 additions and 8 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import { useRef, useState } from "react";
|
||||
import type { FunctionCall, Message } from "@/app/chat/types";
|
||||
import type { FunctionCall, Message, SelectedFilters } from "@/app/chat/types";
|
||||
|
||||
interface UseChatStreamingOptions {
|
||||
endpoint?: string;
|
||||
|
|
@ -7,6 +7,14 @@ interface UseChatStreamingOptions {
|
|||
onError?: (error: Error) => void;
|
||||
}
|
||||
|
||||
interface SendMessageOptions {
|
||||
prompt: string;
|
||||
previousResponseId?: string;
|
||||
filters?: SelectedFilters;
|
||||
limit?: number;
|
||||
scoreThreshold?: number;
|
||||
}
|
||||
|
||||
export function useChatStreaming({
|
||||
endpoint = "/api/chat",
|
||||
onComplete,
|
||||
|
|
@ -19,7 +27,13 @@ export function useChatStreaming({
|
|||
const streamAbortRef = useRef<AbortController | null>(null);
|
||||
const streamIdRef = useRef(0);
|
||||
|
||||
const sendMessage = async (prompt: string, previousResponseId?: string) => {
|
||||
const sendMessage = async ({
|
||||
prompt,
|
||||
previousResponseId,
|
||||
filters,
|
||||
limit = 10,
|
||||
scoreThreshold = 0,
|
||||
}: SendMessageOptions) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
|
|
@ -36,19 +50,28 @@ export function useChatStreaming({
|
|||
prompt: string;
|
||||
stream: boolean;
|
||||
previous_response_id?: string;
|
||||
filters?: SelectedFilters;
|
||||
limit?: number;
|
||||
scoreThreshold?: number;
|
||||
} = {
|
||||
prompt,
|
||||
stream: true,
|
||||
limit,
|
||||
scoreThreshold,
|
||||
};
|
||||
|
||||
if (previousResponseId) {
|
||||
requestBody.previous_response_id = previousResponseId;
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
requestBody.filters = filters;
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: controller.signal,
|
||||
|
|
@ -104,13 +127,275 @@ export function useChatStreaming({
|
|||
newResponseId = chunk.response_id;
|
||||
}
|
||||
|
||||
// Handle OpenRAG backend format (from agent.py async_response_stream)
|
||||
// The chunk is serialized via chunk.model_dump() and contains output_text or delta
|
||||
if (chunk.output_text) {
|
||||
// Direct output text from chunk
|
||||
// Handle OpenAI Chat Completions streaming format
|
||||
if (chunk.object === "response.chunk" && chunk.delta) {
|
||||
// Handle function calls in delta
|
||||
if (chunk.delta.function_call) {
|
||||
if (chunk.delta.function_call.name) {
|
||||
const functionCall: FunctionCall = {
|
||||
name: chunk.delta.function_call.name,
|
||||
arguments: undefined,
|
||||
status: "pending",
|
||||
argumentsString:
|
||||
chunk.delta.function_call.arguments || "",
|
||||
};
|
||||
currentFunctionCalls.push(functionCall);
|
||||
} else if (chunk.delta.function_call.arguments) {
|
||||
const lastFunctionCall =
|
||||
currentFunctionCalls[currentFunctionCalls.length - 1];
|
||||
if (lastFunctionCall) {
|
||||
if (!lastFunctionCall.argumentsString) {
|
||||
lastFunctionCall.argumentsString = "";
|
||||
}
|
||||
lastFunctionCall.argumentsString +=
|
||||
chunk.delta.function_call.arguments;
|
||||
|
||||
if (lastFunctionCall.argumentsString.includes("}")) {
|
||||
try {
|
||||
const parsed = JSON.parse(
|
||||
lastFunctionCall.argumentsString
|
||||
);
|
||||
lastFunctionCall.arguments = parsed;
|
||||
lastFunctionCall.status = "completed";
|
||||
} catch (e) {
|
||||
// Arguments not yet complete
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle tool calls in delta
|
||||
else if (
|
||||
chunk.delta.tool_calls &&
|
||||
Array.isArray(chunk.delta.tool_calls)
|
||||
) {
|
||||
for (const toolCall of chunk.delta.tool_calls) {
|
||||
if (toolCall.function) {
|
||||
if (toolCall.function.name) {
|
||||
const functionCall: FunctionCall = {
|
||||
name: toolCall.function.name,
|
||||
arguments: undefined,
|
||||
status: "pending",
|
||||
argumentsString: toolCall.function.arguments || "",
|
||||
};
|
||||
currentFunctionCalls.push(functionCall);
|
||||
} else if (toolCall.function.arguments) {
|
||||
const lastFunctionCall =
|
||||
currentFunctionCalls[
|
||||
currentFunctionCalls.length - 1
|
||||
];
|
||||
if (lastFunctionCall) {
|
||||
if (!lastFunctionCall.argumentsString) {
|
||||
lastFunctionCall.argumentsString = "";
|
||||
}
|
||||
lastFunctionCall.argumentsString +=
|
||||
toolCall.function.arguments;
|
||||
|
||||
if (
|
||||
lastFunctionCall.argumentsString.includes("}")
|
||||
) {
|
||||
try {
|
||||
const parsed = JSON.parse(
|
||||
lastFunctionCall.argumentsString
|
||||
);
|
||||
lastFunctionCall.arguments = parsed;
|
||||
lastFunctionCall.status = "completed";
|
||||
} catch (e) {
|
||||
// Arguments not yet complete
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle content/text in delta
|
||||
else if (chunk.delta.content) {
|
||||
currentContent += chunk.delta.content;
|
||||
}
|
||||
|
||||
// Handle finish reason
|
||||
if (chunk.delta.finish_reason) {
|
||||
currentFunctionCalls.forEach((fc) => {
|
||||
if (fc.status === "pending" && fc.argumentsString) {
|
||||
try {
|
||||
fc.arguments = JSON.parse(fc.argumentsString);
|
||||
fc.status = "completed";
|
||||
} catch (e) {
|
||||
fc.arguments = { raw: fc.argumentsString };
|
||||
fc.status = "error";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Handle Realtime API format - function call added
|
||||
else if (
|
||||
chunk.type === "response.output_item.added" &&
|
||||
chunk.item?.type === "function_call"
|
||||
) {
|
||||
let existing = currentFunctionCalls.find(
|
||||
(fc) => fc.id === chunk.item.id
|
||||
);
|
||||
if (!existing) {
|
||||
existing = [...currentFunctionCalls]
|
||||
.reverse()
|
||||
.find(
|
||||
(fc) =>
|
||||
fc.status === "pending" &&
|
||||
!fc.id &&
|
||||
fc.name === (chunk.item.tool_name || chunk.item.name)
|
||||
);
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
existing.id = chunk.item.id;
|
||||
existing.type = chunk.item.type;
|
||||
existing.name =
|
||||
chunk.item.tool_name || chunk.item.name || existing.name;
|
||||
existing.arguments =
|
||||
chunk.item.inputs || existing.arguments;
|
||||
} else {
|
||||
const functionCall: FunctionCall = {
|
||||
name:
|
||||
chunk.item.tool_name || chunk.item.name || "unknown",
|
||||
arguments: chunk.item.inputs || undefined,
|
||||
status: "pending",
|
||||
argumentsString: "",
|
||||
id: chunk.item.id,
|
||||
type: chunk.item.type,
|
||||
};
|
||||
currentFunctionCalls.push(functionCall);
|
||||
}
|
||||
}
|
||||
// Handle Realtime API format - tool call added
|
||||
else if (
|
||||
chunk.type === "response.output_item.added" &&
|
||||
chunk.item?.type?.includes("_call") &&
|
||||
chunk.item?.type !== "function_call"
|
||||
) {
|
||||
let existing = currentFunctionCalls.find(
|
||||
(fc) => fc.id === chunk.item.id
|
||||
);
|
||||
if (!existing) {
|
||||
existing = [...currentFunctionCalls]
|
||||
.reverse()
|
||||
.find(
|
||||
(fc) =>
|
||||
fc.status === "pending" &&
|
||||
!fc.id &&
|
||||
fc.name ===
|
||||
(chunk.item.tool_name ||
|
||||
chunk.item.name ||
|
||||
chunk.item.type)
|
||||
);
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
existing.id = chunk.item.id;
|
||||
existing.type = chunk.item.type;
|
||||
existing.name =
|
||||
chunk.item.tool_name ||
|
||||
chunk.item.name ||
|
||||
chunk.item.type ||
|
||||
existing.name;
|
||||
existing.arguments =
|
||||
chunk.item.inputs || existing.arguments;
|
||||
} else {
|
||||
const functionCall = {
|
||||
name:
|
||||
chunk.item.tool_name ||
|
||||
chunk.item.name ||
|
||||
chunk.item.type ||
|
||||
"unknown",
|
||||
arguments: chunk.item.inputs || {},
|
||||
status: "pending" as const,
|
||||
id: chunk.item.id,
|
||||
type: chunk.item.type,
|
||||
};
|
||||
currentFunctionCalls.push(functionCall);
|
||||
}
|
||||
}
|
||||
// Handle function call done
|
||||
else if (
|
||||
chunk.type === "response.output_item.done" &&
|
||||
chunk.item?.type === "function_call"
|
||||
) {
|
||||
const functionCall = currentFunctionCalls.find(
|
||||
(fc) =>
|
||||
fc.id === chunk.item.id ||
|
||||
fc.name === chunk.item.tool_name ||
|
||||
fc.name === chunk.item.name
|
||||
);
|
||||
|
||||
if (functionCall) {
|
||||
functionCall.status =
|
||||
chunk.item.status === "completed" ? "completed" : "error";
|
||||
functionCall.id = chunk.item.id;
|
||||
functionCall.type = chunk.item.type;
|
||||
functionCall.name =
|
||||
chunk.item.tool_name ||
|
||||
chunk.item.name ||
|
||||
functionCall.name;
|
||||
functionCall.arguments =
|
||||
chunk.item.inputs || functionCall.arguments;
|
||||
|
||||
if (chunk.item.results) {
|
||||
functionCall.result = chunk.item.results;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle tool call done with results
|
||||
else if (
|
||||
chunk.type === "response.output_item.done" &&
|
||||
chunk.item?.type?.includes("_call") &&
|
||||
chunk.item?.type !== "function_call"
|
||||
) {
|
||||
const functionCall = currentFunctionCalls.find(
|
||||
(fc) =>
|
||||
fc.id === chunk.item.id ||
|
||||
fc.name === chunk.item.tool_name ||
|
||||
fc.name === chunk.item.name ||
|
||||
fc.name === chunk.item.type ||
|
||||
fc.name.includes(chunk.item.type.replace("_call", "")) ||
|
||||
chunk.item.type.includes(fc.name)
|
||||
);
|
||||
|
||||
if (functionCall) {
|
||||
functionCall.arguments =
|
||||
chunk.item.inputs || functionCall.arguments;
|
||||
functionCall.status =
|
||||
chunk.item.status === "completed" ? "completed" : "error";
|
||||
functionCall.id = chunk.item.id;
|
||||
functionCall.type = chunk.item.type;
|
||||
|
||||
if (chunk.item.results) {
|
||||
functionCall.result = chunk.item.results;
|
||||
}
|
||||
} else {
|
||||
const newFunctionCall = {
|
||||
name:
|
||||
chunk.item.tool_name ||
|
||||
chunk.item.name ||
|
||||
chunk.item.type ||
|
||||
"unknown",
|
||||
arguments: chunk.item.inputs || {},
|
||||
status: "completed" as const,
|
||||
id: chunk.item.id,
|
||||
type: chunk.item.type,
|
||||
result: chunk.item.results,
|
||||
};
|
||||
currentFunctionCalls.push(newFunctionCall);
|
||||
}
|
||||
}
|
||||
// Handle text output streaming (Realtime API)
|
||||
else if (chunk.type === "response.output_text.delta") {
|
||||
currentContent += chunk.delta || "";
|
||||
}
|
||||
// Handle OpenRAG backend format
|
||||
else if (chunk.output_text) {
|
||||
currentContent += chunk.output_text;
|
||||
} else if (chunk.delta) {
|
||||
// Handle delta - could be string, dict, or have content/text properties
|
||||
if (typeof chunk.delta === "string") {
|
||||
currentContent += chunk.delta;
|
||||
} else if (typeof chunk.delta === "object") {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue