mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-23 06:58:10 +02:00
feat: implement admin AI chat route with SQL execution tools and multi-provider support
This commit is contained in:
1 parent
ade120a9da
commit
4994d55ffc
1 file changed
+48
-32
@@ -245,46 +245,61 @@ async function handleGoogleAi(messages: Array<{ role: string; content: string }>
|
|||||||
try {
|
try {
|
||||||
const ai = new GoogleGenAI({ apiKey: config.googleAiKey! });
|
const ai = new GoogleGenAI({ apiKey: config.googleAiKey! });
|
||||||
|
|
||||||
// Convert messages to Google format
|
// Build full contents array for generateContent
|
||||||
const history = messages.slice(0, -1).map((m) => ({
|
const contents: Array<{ role: string; parts: Array<{ text: string }> }> = [
|
||||||
role: m.role === "assistant" ? "model" : "user",
|
...messages.map((m) => ({
|
||||||
parts: [{ text: m.content }],
|
role: m.role === "assistant" ? "model" : "user",
|
||||||
}));
|
parts: [{ text: m.content }],
|
||||||
const lastMessage = messages[messages.length - 1];
|
})),
|
||||||
|
];
|
||||||
const chat = ai.chats.create({
|
|
||||||
model,
|
|
||||||
config: { systemInstruction: SYSTEM_PROMPT, tools: googleTools as any },
|
|
||||||
history,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Agentic loop
|
|
||||||
let currentParts: Array<{ text?: string; functionCall?: { name: string; args: Record<string, unknown> } }> = [{ text: lastMessage.content }];
|
|
||||||
|
|
||||||
|
// Agentic loop — non-streaming tool phase, streaming final answer
|
||||||
for (let round = 0; round < 8; round++) {
|
for (let round = 0; round < 8; round++) {
|
||||||
const response = await chat.sendMessage({ message: currentParts as any });
|
const response = await ai.models.generateContent({
|
||||||
const candidate = response.candidates?.[0];
|
model,
|
||||||
if (!candidate) break;
|
contents: contents as any,
|
||||||
|
config: {
|
||||||
|
systemInstruction: SYSTEM_PROMPT,
|
||||||
|
tools: googleTools as any,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const fnCalls = candidate.content?.parts?.filter((p: any) => p.functionCall) ?? [];
|
const fnCalls = response.functionCalls;
|
||||||
const textParts = candidate.content?.parts?.filter((p: any) => p.text) ?? [];
|
|
||||||
|
|
||||||
if (fnCalls.length === 0) {
|
if (!fnCalls || fnCalls.length === 0) {
|
||||||
// Final answer
|
// No tool calls — stream the final text
|
||||||
const text = textParts.map((p: any) => p.text).join("");
|
const streamResponse = await ai.models.generateContentStream({
|
||||||
send({ type: "text", content: text });
|
model,
|
||||||
|
contents: contents as any,
|
||||||
|
config: {
|
||||||
|
systemInstruction: SYSTEM_PROMPT,
|
||||||
|
tools: googleTools as any,
|
||||||
|
maxOutputTokens: 8192,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for await (const chunk of streamResponse) {
|
||||||
|
const text = chunk.text;
|
||||||
|
if (text) send({ type: "text", content: text });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute tools
|
// Add model response with function calls to history
|
||||||
const toolResults: Array<{ functionResponse: { name: string; response: { output: string } } }> = [];
|
contents.push({
|
||||||
for (const part of fnCalls) {
|
role: "model",
|
||||||
const fn = (part as any).functionCall as { name: string; args: Record<string, unknown> };
|
parts: response.candidates![0].content!.parts as any,
|
||||||
send({ type: "tool_start", name: fn.name, args: fn.args });
|
});
|
||||||
|
|
||||||
|
// Execute tool calls
|
||||||
|
const toolResultParts: Array<{ functionResponse: { name: string; response: { output: string } } }> = [];
|
||||||
|
for (const fn of fnCalls) {
|
||||||
|
const args = (fn.args ?? {}) as Record<string, unknown>;
|
||||||
|
send({ type: "tool_start", name: fn.name, args });
|
||||||
|
|
||||||
let result = "";
|
let result = "";
|
||||||
if (fn.name === "execute_sql") {
|
if (fn.name === "execute_sql") {
|
||||||
result = await executeSql((fn.args as any).query as string);
|
result = await executeSql(args.query as string);
|
||||||
} else if (fn.name === "get_db_schema") {
|
} else if (fn.name === "get_db_schema") {
|
||||||
result = DB_SCHEMA;
|
result = DB_SCHEMA;
|
||||||
} else {
|
} else {
|
||||||
@@ -292,10 +307,11 @@ async function handleGoogleAi(messages: Array<{ role: string; content: string }>
|
|||||||
}
|
}
|
||||||
|
|
||||||
send({ type: "tool_result", name: fn.name, result: result.substring(0, 500) + (result.length > 500 ? "..." : "") });
|
send({ type: "tool_result", name: fn.name, result: result.substring(0, 500) + (result.length > 500 ? "..." : "") });
|
||||||
toolResults.push({ functionResponse: { name: fn.name, response: { output: result } } });
|
toolResultParts.push({ functionResponse: { name: fn.name!, response: { output: result } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
currentParts = toolResults as any;
|
// Add tool results to history
|
||||||
|
contents.push({ role: "user", parts: toolResultParts as any });
|
||||||
}
|
}
|
||||||
|
|
||||||
send({ type: "usage", prompt_tokens: 0, completion_tokens: 0 });
|
send({ type: "usage", prompt_tokens: 0, completion_tokens: 0 });
|
||||||
|
|||||||
Reference in new issue
Block a user