Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,21 @@ export const useAssistantChat = ({
toolCall.input || toolCall.args,
{} as any,
);
chat.addToolResult({

chat.addToolOutput({
state: 'output-available',
tool: toolCall.toolName,
toolCallId: toolCall.toolCallId,
output: result,
});
}
} catch (error) {
chat.addToolOutput({
state: 'output-error',
tool: toolCall.toolName,
toolCallId: toolCall.toolCallId,
errorText: String(error),
});
console.error('Error executing frontend tool:', error);
}
}
Expand Down
17 changes: 15 additions & 2 deletions packages/server/api/src/app/ai/chat/ai-mcp-chat.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ModelMessage } from 'ai';
import { FastifyReply } from 'fastify';
import { StatusCodes } from 'http-status-codes';
import removeMarkdown from 'markdown-to-text';
import { extractUiToolResultsFromMessage } from '../mcp/tool-utils';
import {
createChatContext,
deleteChatHistory,
Expand All @@ -44,7 +45,10 @@ import {
import { routeChatRequest } from './chat-request-router';
import { streamCode } from './code.service';
import { enrichContext, IncludeOptions } from './context-enrichment.service';
import { parseUserMessage } from './message-parser';
import {
parseUserMessage,
UI_TOOL_RESULT_SUBMISSION_MESSAGE,
} from './message-parser';
import { createUserMessage } from './model-message-factory';
import { getBlockSystemPrompt } from './prompts.service';

Expand Down Expand Up @@ -196,11 +200,18 @@ export const aiMCPChatController: FastifyPluginAsyncTypebox = async (app) => {
const chatId = request.body.chatId;
const userId = request.principal.id;

const frontendToolResults = extractUiToolResultsFromMessage(
request.body.message,
);
const messageContent = await getUserMessage(request.body, reply);
if (messageContent === null) {
return; // Error response already sent
return;
}

const isToolResultOnly =
frontendToolResults.length > 0 &&
messageContent === UI_TOOL_RESULT_SUBMISSION_MESSAGE;

updateActiveObservation({
input: messageContent,
});
Expand All @@ -210,6 +221,8 @@ export const aiMCPChatController: FastifyPluginAsyncTypebox = async (app) => {
app,
request,
newMessage: createUserMessage(messageContent),
isToolResultOnly,
frontendToolResults,
reply,
});
});
Expand Down
23 changes: 20 additions & 3 deletions packages/server/api/src/app/ai/chat/chat-request-router.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { logger } from '@openops/server-shared';
import { CODE_BLOCK_NAME, NewMessageRequest, Principal } from '@openops/shared';
import {
CODE_BLOCK_NAME,
NewMessageRequest,
Principal,
ToolResult,
} from '@openops/shared';
import { ModelMessage } from 'ai';
import { FastifyInstance, FastifyReply } from 'fastify';
import { IncomingMessage, ServerResponse } from 'node:http';
Expand All @@ -24,12 +29,21 @@ export type ChatRequestContext = {
reply: FastifyReply;
app: FastifyInstance;
newMessage: ModelMessage;
isToolResultOnly?: boolean;
frontendToolResults?: ToolResult[];
};

export async function routeChatRequest(
params: ChatRequestContext,
): Promise<void> {
const { app, request, newMessage, reply: fastifyReply } = params;
const {
app,
request,
newMessage,
isToolResultOnly,
frontendToolResults,
reply: fastifyReply,
} = params;
const serverResponse = fastifyReply.raw;

const controller = new AbortController();
Expand Down Expand Up @@ -74,7 +88,9 @@ export async function routeChatRequest(
model: currentModel,
};

Comment thread
alexandrudanpop marked this conversation as resolved.
conversation.chatHistory.push(newMessage);
if (!isToolResultOnly) {
conversation.chatHistory.push(newMessage);
}

const generationRequestParams = {
app,
Expand All @@ -89,6 +105,7 @@ export async function routeChatRequest(
languageModel,
additionalContext: request.body.additionalContext,
frontendTools: request.body.tools || {},
frontendToolResults,
abortSignal,
};

Expand Down
10 changes: 5 additions & 5 deletions packages/server/api/src/app/ai/chat/message-parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNil, NewMessageRequest } from '@openops/shared';
import { AIChatMessage, isNil } from '@openops/shared';

export type ParsedMessage = {
content: string;
Expand All @@ -12,9 +12,9 @@ export type InvalidMessage = {

export type MessageParseResult = ParsedMessage | InvalidMessage;

export function parseUserMessage(
message: NewMessageRequest['message'],
): MessageParseResult {
export const UI_TOOL_RESULT_SUBMISSION_MESSAGE = '[ui-tool-result-submission]';

export function parseUserMessage(message: AIChatMessage): MessageParseResult {
if (typeof message === 'string') {
return { isValid: true, content: message };
}
Expand All @@ -38,7 +38,7 @@ export function parseUserMessage(
const content =
firstContentElement.type !== 'reasoning' && !isNil(firstContentElement.text)
? String(firstContentElement.text)
: 'continue';
: UI_TOOL_RESULT_SUBMISSION_MESSAGE;

return {
isValid: true,
Expand Down
37 changes: 26 additions & 11 deletions packages/server/api/src/app/ai/chat/user-message-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { AppSystemProp, logger, system } from '@openops/server-shared';
import { AiConfigParsed, ChatFlowContext } from '@openops/shared';
import { AiConfigParsed, ChatFlowContext, ToolResult } from '@openops/shared';
import {
AssistantModelMessage,
LanguageModel,
Expand All @@ -14,7 +14,10 @@ import {
sendAiChatAbortedEvent,
sendAiChatFailureEvent,
} from '../../telemetry/event-models';
import { addUiToolResults } from '../mcp/tool-utils';
import {
addMissingUiToolResults,
createToolResultsMap,
} from '../mcp/tool-utils';
import { getMCPToolsContext } from '../mcp/tools-context-builder';
import { AssistantUITools } from '../mcp/types';
import { saveChatHistory } from './ai-chat.service';
Expand All @@ -41,6 +44,7 @@ type UserMessageParams = RequestContext &
abortSignal: AbortSignal;
} & {
frontendTools: AssistantUITools;
frontendToolResults?: ToolResult[];
additionalContext?: ChatFlowContext;
};

Expand Down Expand Up @@ -92,18 +96,26 @@ export async function handleUserMessage(
serverResponse,
conversation: { chatContext, chatHistory },
frontendTools,
frontendToolResults,
additionalContext,
abortSignal,
} = params;

const messageId = generateMessageId();

const toolResultsMap = createToolResultsMap(frontendToolResults);

const updatedChatHistory = addMissingUiToolResults(
chatHistory,
toolResultsMap,
);

const { mcpClients, systemPrompt, filteredTools } = await getMCPToolsContext({
app,
projectId,
authToken,
aiConfig,
messages: chatHistory,
messages: updatedChatHistory,
chatContext,
languageModel,
frontendTools,
Expand All @@ -121,7 +133,7 @@ export async function handleUserMessage(
userId,
projectId,
aiConfig,
chatHistory,
chatHistory: updatedChatHistory,
systemPrompt,
languageModel,
serverResponse,
Expand All @@ -134,10 +146,15 @@ export async function handleUserMessage(
serverResponse.write(finishStepPart);
serverResponse.write(finishMessagePart);

await saveChatHistory(chatId, userId, projectId, [
...chatHistory,
...addUiToolResults(newMessages),
]);
await saveChatHistory(
chatId,
userId,
projectId,
addMissingUiToolResults(
[...updatedChatHistory, ...newMessages],
toolResultsMap,
),
);
} finally {
await closeMCPClients(mcpClients);
serverResponse.write(doneMarker);
Expand Down Expand Up @@ -191,9 +208,7 @@ async function streamLLMResponse(
const partialMessages = steps.at(-1)?.response.messages ?? [];
return saveChatHistory(params.chatId, params.userId, params.projectId, [
...params.chatHistory,
...addUiToolResults(
markToolResultsWithErrors(partialMessages, errorToolCallIds),
),
...markToolResultsWithErrors(partialMessages, errorToolCallIds),
]);
},
abortSignal: params.abortSignal,
Expand Down
Loading
Loading