feat: implement admin AI chat API with SQL execution and schema retrieval tools

This commit is contained in:
Michael committed 2026-04-10 15:23:46 +02:00
1 parent 4994d55ffc
commit 0c1d3c10bd
1 file changed
+32 -27
+32 -27
View File
@@ -253,48 +253,54 @@ async function handleGoogleAi(messages: Array<{ role: string; content: string }>
})), })),
]; ];
// Agentic loop — non-streaming tool phase, streaming final answer // Agentic loop — streaming throughout
for (let round = 0; round < 8; round++) { for (let round = 0; round < 8; round++) {
const response = await ai.models.generateContent({ const streamResponse = await ai.models.generateContentStream({
model, model,
contents: contents as any, contents: contents as any,
config: { config: {
systemInstruction: SYSTEM_PROMPT, systemInstruction: SYSTEM_PROMPT,
tools: googleTools as any, tools: googleTools as any,
maxOutputTokens: 8192,
}, },
}); });
const fnCalls = response.functionCalls; // Collect stream, forwarding text chunks immediately
let collectedText = "";
const collectedParts: any[] = [];
if (!fnCalls || fnCalls.length === 0) { for await (const chunk of streamResponse) {
// No tool calls — stream the final text const text = chunk.text;
const streamResponse = await ai.models.generateContentStream({ if (text) {
model, collectedText += text;
contents: contents as any, send({ type: "text", content: text });
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 });
} }
// Collect parts for function calls
const parts = chunk.candidates?.[0]?.content?.parts;
if (parts) collectedParts.push(...parts);
}
// Check for function calls in collected parts
const fnCallParts = collectedParts.filter((p: any) => p.functionCall);
if (fnCallParts.length === 0) {
// Pure text response — done
break; break;
} }
// Add model response with function calls to history // Had function calls — add model turn to history
contents.push({ if (collectedText) {
role: "model", // Mixed text + function calls
parts: response.candidates![0].content!.parts as any, contents.push({ role: "model", parts: collectedParts });
}); } else {
contents.push({ role: "model", parts: fnCallParts });
}
// Execute tool calls // Execute tool calls
const toolResultParts: Array<{ functionResponse: { name: string; response: { output: string } } }> = []; const toolResultParts: Array<{ functionResponse: { name: string; response: { output: string } } }> = [];
for (const fn of fnCalls) { for (const part of fnCallParts) {
const args = (fn.args ?? {}) as Record<string, unknown>; const fn = part.functionCall as { name: string; args: Record<string, unknown> };
const args = fn.args ?? {};
send({ type: "tool_start", name: fn.name, args }); send({ type: "tool_start", name: fn.name, args });
let result = ""; let result = "";
@@ -307,10 +313,9 @@ 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 ? "..." : "") });
toolResultParts.push({ functionResponse: { name: fn.name!, response: { output: result } } }); toolResultParts.push({ functionResponse: { name: fn.name, response: { output: result } } });
} }
// Add tool results to history
contents.push({ role: "user", parts: toolResultParts as any }); contents.push({ role: "user", parts: toolResultParts as any });
} }