@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
48 lines (47 loc) • 2.09 kB
JavaScript
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
import { safeValidateUIMessages } from 'ai';
import { fromZodError } from 'zod-validation-error';
import { createUiStream } from '../lib/create-ui-stream.js';
import { ChatRequest } from '../models/chat-request.js';
import { chatRequestToolToAiSdkTool } from '../utils/chat-request-tool-to-ai-sdk-tool.js';
import { fixErrorToolCalls } from '../utils/fix-error-tool-calls.js';
export const aiChatPostHandler = async (req, res, _next) => {
if (!req.accountability?.app) {
throw new ForbiddenError();
}
const parseResult = ChatRequest.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidPayloadError({ reason: fromZodError(parseResult.error).message });
}
const { provider, model, messages: rawMessages, tools: requestedTools, toolApprovals } = parseResult.data;
if (rawMessages.length === 0) {
throw new InvalidPayloadError({ reason: `"messages" must not be empty` });
}
const tools = requestedTools.reduce((acc, t) => {
const name = typeof t === 'string' ? t : t.name;
const aiTool = chatRequestToolToAiSdkTool({
chatRequestTool: t,
accountability: req.accountability,
schema: req.schema,
...(toolApprovals && { toolApprovals }),
});
acc[name] = aiTool;
return acc;
}, {});
const fixedMessages = fixErrorToolCalls(rawMessages);
const validationResult = await safeValidateUIMessages({ messages: fixedMessages });
if (validationResult.success === false) {
throw new InvalidPayloadError({ reason: validationResult.error.message });
}
const stream = createUiStream(validationResult.data, {
provider,
model,
tools: tools,
apiKeys: res.locals['ai'].apiKeys,
systemPrompt: res.locals['ai'].systemPrompt,
onUsage: (usage) => {
res.write(`data: ${JSON.stringify({ type: 'data-usage', data: usage })}\n\n`);
},
});
stream.pipeUIMessageStreamToResponse(res);
};