✨ (frontend): refactor message processing in ChatPage component to handle function calls from chunks or response_data
♻️ (agent.py): refactor async_response, async_langflow, async_chat, async_langflow_chat, and async_langflow_chat_stream functions to return full response object for function calls 🔧 (chat_service.py): update ChatService to include function call data in message_data if present
This commit is contained in:
parent
7ff3bfd70b
commit
f83851b259
3 changed files with 121 additions and 13 deletions
|
|
@ -454,12 +454,99 @@ function ChatPage() {
|
||||||
content: string;
|
content: string;
|
||||||
timestamp?: string;
|
timestamp?: string;
|
||||||
response_id?: string;
|
response_id?: string;
|
||||||
}) => ({
|
chunks?: any[];
|
||||||
role: msg.role as "user" | "assistant",
|
response_data?: any;
|
||||||
content: msg.content,
|
}) => {
|
||||||
timestamp: new Date(msg.timestamp || new Date()),
|
const message: Message = {
|
||||||
// Add any other necessary properties
|
role: msg.role as "user" | "assistant",
|
||||||
})
|
content: msg.content,
|
||||||
|
timestamp: new Date(msg.timestamp || new Date()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extract function calls from chunks or response_data
|
||||||
|
if (msg.role === "assistant" && (msg.chunks || msg.response_data)) {
|
||||||
|
const functionCalls: FunctionCall[] = [];
|
||||||
|
console.log("Processing assistant message for function calls:", {
|
||||||
|
hasChunks: !!msg.chunks,
|
||||||
|
chunksLength: msg.chunks?.length,
|
||||||
|
hasResponseData: !!msg.response_data,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process chunks (streaming data)
|
||||||
|
if (msg.chunks && Array.isArray(msg.chunks)) {
|
||||||
|
for (const chunk of msg.chunks) {
|
||||||
|
// Handle Langflow format: chunks[].item.tool_call
|
||||||
|
if (chunk.item && chunk.item.type === "tool_call") {
|
||||||
|
const toolCall = chunk.item;
|
||||||
|
console.log("Found Langflow tool call:", toolCall);
|
||||||
|
functionCalls.push({
|
||||||
|
id: toolCall.id,
|
||||||
|
name: toolCall.tool_name,
|
||||||
|
arguments: toolCall.inputs || {},
|
||||||
|
argumentsString: JSON.stringify(toolCall.inputs || {}),
|
||||||
|
result: toolCall.results,
|
||||||
|
status: toolCall.status || "completed",
|
||||||
|
type: "tool_call",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle OpenAI format: chunks[].delta.tool_calls
|
||||||
|
else if (chunk.delta?.tool_calls) {
|
||||||
|
for (const toolCall of chunk.delta.tool_calls) {
|
||||||
|
if (toolCall.function) {
|
||||||
|
functionCalls.push({
|
||||||
|
id: toolCall.id,
|
||||||
|
name: toolCall.function.name,
|
||||||
|
arguments: toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {},
|
||||||
|
argumentsString: toolCall.function.arguments,
|
||||||
|
status: "completed",
|
||||||
|
type: toolCall.type || "function",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process tool call results from chunks
|
||||||
|
if (chunk.type === "response.tool_call.result" || chunk.type === "tool_call_result") {
|
||||||
|
const lastCall = functionCalls[functionCalls.length - 1];
|
||||||
|
if (lastCall) {
|
||||||
|
lastCall.result = chunk.result || chunk;
|
||||||
|
lastCall.status = "completed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process response_data (non-streaming data)
|
||||||
|
if (msg.response_data && typeof msg.response_data === 'object') {
|
||||||
|
// Look for tool_calls in various places in the response data
|
||||||
|
const responseData = typeof msg.response_data === 'string' ? JSON.parse(msg.response_data) : msg.response_data;
|
||||||
|
|
||||||
|
if (responseData.tool_calls && Array.isArray(responseData.tool_calls)) {
|
||||||
|
for (const toolCall of responseData.tool_calls) {
|
||||||
|
functionCalls.push({
|
||||||
|
id: toolCall.id,
|
||||||
|
name: toolCall.function?.name || toolCall.name,
|
||||||
|
arguments: toolCall.function?.arguments || toolCall.arguments,
|
||||||
|
argumentsString: typeof (toolCall.function?.arguments || toolCall.arguments) === 'string'
|
||||||
|
? toolCall.function?.arguments || toolCall.arguments
|
||||||
|
: JSON.stringify(toolCall.function?.arguments || toolCall.arguments),
|
||||||
|
result: toolCall.result,
|
||||||
|
status: "completed",
|
||||||
|
type: toolCall.type || "function",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functionCalls.length > 0) {
|
||||||
|
console.log("Setting functionCalls on message:", functionCalls);
|
||||||
|
message.functionCalls = functionCalls;
|
||||||
|
} else {
|
||||||
|
console.log("No function calls found in message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
setMessages(convertedMessages);
|
setMessages(convertedMessages);
|
||||||
|
|
|
||||||
21
src/agent.py
21
src/agent.py
|
|
@ -180,7 +180,7 @@ async def async_response(
|
||||||
response, "response_id", None
|
response, "response_id", None
|
||||||
)
|
)
|
||||||
|
|
||||||
return response_text, response_id
|
return response_text, response_id, response
|
||||||
|
|
||||||
|
|
||||||
# Unified streaming function for both chat and langflow
|
# Unified streaming function for both chat and langflow
|
||||||
|
|
@ -211,7 +211,7 @@ async def async_langflow(
|
||||||
extra_headers: dict = None,
|
extra_headers: dict = None,
|
||||||
previous_response_id: str = None,
|
previous_response_id: str = None,
|
||||||
):
|
):
|
||||||
response_text, response_id = await async_response(
|
response_text, response_id, response_obj = await async_response(
|
||||||
langflow_client,
|
langflow_client,
|
||||||
prompt,
|
prompt,
|
||||||
flow_id,
|
flow_id,
|
||||||
|
|
@ -281,7 +281,7 @@ async def async_chat(
|
||||||
"Added user message", message_count=len(conversation_state["messages"])
|
"Added user message", message_count=len(conversation_state["messages"])
|
||||||
)
|
)
|
||||||
|
|
||||||
response_text, response_id = await async_response(
|
response_text, response_id, response_obj = await async_response(
|
||||||
async_client,
|
async_client,
|
||||||
prompt,
|
prompt,
|
||||||
model,
|
model,
|
||||||
|
|
@ -292,12 +292,13 @@ async def async_chat(
|
||||||
"Got response", response_preview=response_text[:50], response_id=response_id
|
"Got response", response_preview=response_text[:50], response_id=response_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add assistant response to conversation with response_id and timestamp
|
# Add assistant response to conversation with response_id, timestamp, and full response object
|
||||||
assistant_message = {
|
assistant_message = {
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": response_text,
|
"content": response_text,
|
||||||
"response_id": response_id,
|
"response_id": response_id,
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
|
"response_data": response_obj.model_dump() if hasattr(response_obj, "model_dump") else str(response_obj), # Store complete response for function calls
|
||||||
}
|
}
|
||||||
conversation_state["messages"].append(assistant_message)
|
conversation_state["messages"].append(assistant_message)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|
@ -419,7 +420,7 @@ async def async_langflow_chat(
|
||||||
message_count=len(conversation_state["messages"]),
|
message_count=len(conversation_state["messages"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
response_text, response_id = await async_response(
|
response_text, response_id, response_obj = await async_response(
|
||||||
langflow_client,
|
langflow_client,
|
||||||
prompt,
|
prompt,
|
||||||
flow_id,
|
flow_id,
|
||||||
|
|
@ -433,12 +434,13 @@ async def async_langflow_chat(
|
||||||
response_id=response_id,
|
response_id=response_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add assistant response to conversation with response_id and timestamp
|
# Add assistant response to conversation with response_id, timestamp, and full response object
|
||||||
assistant_message = {
|
assistant_message = {
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": response_text,
|
"content": response_text,
|
||||||
"response_id": response_id,
|
"response_id": response_id,
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
|
"response_data": response_obj.model_dump() if hasattr(response_obj, "model_dump") else str(response_obj), # Store complete response for function calls
|
||||||
}
|
}
|
||||||
conversation_state["messages"].append(assistant_message)
|
conversation_state["messages"].append(assistant_message)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|
@ -504,6 +506,8 @@ async def async_langflow_chat_stream(
|
||||||
|
|
||||||
full_response = ""
|
full_response = ""
|
||||||
response_id = None
|
response_id = None
|
||||||
|
collected_chunks = [] # Store all chunks for function call data
|
||||||
|
|
||||||
async for chunk in async_stream(
|
async for chunk in async_stream(
|
||||||
langflow_client,
|
langflow_client,
|
||||||
prompt,
|
prompt,
|
||||||
|
|
@ -517,6 +521,8 @@ async def async_langflow_chat_stream(
|
||||||
import json
|
import json
|
||||||
|
|
||||||
chunk_data = json.loads(chunk.decode("utf-8"))
|
chunk_data = json.loads(chunk.decode("utf-8"))
|
||||||
|
collected_chunks.append(chunk_data) # Collect all chunk data
|
||||||
|
|
||||||
if "delta" in chunk_data and "content" in chunk_data["delta"]:
|
if "delta" in chunk_data and "content" in chunk_data["delta"]:
|
||||||
full_response += chunk_data["delta"]["content"]
|
full_response += chunk_data["delta"]["content"]
|
||||||
# Extract response_id from chunk
|
# Extract response_id from chunk
|
||||||
|
|
@ -528,13 +534,14 @@ async def async_langflow_chat_stream(
|
||||||
pass
|
pass
|
||||||
yield chunk
|
yield chunk
|
||||||
|
|
||||||
# Add the complete assistant response to message history with response_id and timestamp
|
# Add the complete assistant response to message history with response_id, timestamp, and function call data
|
||||||
if full_response:
|
if full_response:
|
||||||
assistant_message = {
|
assistant_message = {
|
||||||
"role": "assistant",
|
"role": "assistant",
|
||||||
"content": full_response,
|
"content": full_response,
|
||||||
"response_id": response_id,
|
"response_id": response_id,
|
||||||
"timestamp": datetime.now(),
|
"timestamp": datetime.now(),
|
||||||
|
"chunks": collected_chunks, # Store complete chunk data for function calls
|
||||||
}
|
}
|
||||||
conversation_state["messages"].append(assistant_message)
|
conversation_state["messages"].append(assistant_message)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,13 @@ class ChatService:
|
||||||
}
|
}
|
||||||
if msg.get("response_id"):
|
if msg.get("response_id"):
|
||||||
message_data["response_id"] = msg["response_id"]
|
message_data["response_id"] = msg["response_id"]
|
||||||
|
|
||||||
|
# Include function call data if present
|
||||||
|
if msg.get("chunks"):
|
||||||
|
message_data["chunks"] = msg["chunks"]
|
||||||
|
if msg.get("response_data"):
|
||||||
|
message_data["response_data"] = msg["response_data"]
|
||||||
|
|
||||||
messages.append(message_data)
|
messages.append(message_data)
|
||||||
|
|
||||||
if messages: # Only include conversations with actual messages
|
if messages: # Only include conversations with actual messages
|
||||||
|
|
@ -305,6 +312,13 @@ class ChatService:
|
||||||
}
|
}
|
||||||
if msg.get("response_id"):
|
if msg.get("response_id"):
|
||||||
message_data["response_id"] = msg["response_id"]
|
message_data["response_id"] = msg["response_id"]
|
||||||
|
|
||||||
|
# Include function call data if present
|
||||||
|
if msg.get("chunks"):
|
||||||
|
message_data["chunks"] = msg["chunks"]
|
||||||
|
if msg.get("response_data"):
|
||||||
|
message_data["response_data"] = msg["response_data"]
|
||||||
|
|
||||||
messages.append(message_data)
|
messages.append(message_data)
|
||||||
|
|
||||||
if messages: # Only include conversations with actual messages
|
if messages: # Only include conversations with actual messages
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue