feat(api): add pagination to messages endpoint and improve chat API robustness
- Add pagination support to /api/conversations/[id]/messages with limit and before query params, returning hasMore and cursor for efficient message fetching - Enhance /api/ollama/chat by adding non-blocking Ollama availability check and conditionally disabling debug logging in production环境下Featured
This commit is contained in:
1 parent
30290ae87a
commit
1545020afe
9 files changed
+114
-38
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { chatWithStream } from "@/lib/ollama";
|
||||
import { chatWithStream, ensureOllamaAvailable } from "@/lib/ollama";
|
||||
import { convertToOllamaMessages } from "@/lib/chat";
|
||||
import { detectModelCapabilities } from "@/lib/services/model-capabilities";
|
||||
import { MCPExecutor } from "@/lib/services/mcp-executor";
|
||||
@@ -97,24 +97,30 @@ export async function POST(req: NextRequest) {
|
||||
};
|
||||
|
||||
try {
|
||||
// Garantir disponibilidade com timeout curto (não bloquear)
|
||||
ensureOllamaAvailable({ timeoutMs: 2000 }).catch(() => {});
|
||||
const ollamaMessages = convertToOllamaMessages(messages);
|
||||
console.log("🗣️ [CHAT] Messages ->", ollamaMessages);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log("🗣️ [CHAT] Messages ->", ollamaMessages);
|
||||
}
|
||||
|
||||
// Merge system prompt com tools prompt
|
||||
const finalSystemPrompt = toolsPrompt
|
||||
? `${system || ""}\n\n${toolsPrompt}`
|
||||
: system;
|
||||
if (toolsPrompt) console.log("🧩 [CHAT] Tools prompt injected");
|
||||
if (toolsPrompt && process.env.NODE_ENV !== "production") console.log("🧩 [CHAT] Tools prompt injected");
|
||||
|
||||
// Preparar parâmetros para chat
|
||||
const chatParams = capabilities.supportsNativeTools ? { tools } : {};
|
||||
if (capabilities.supportsNativeTools) {
|
||||
console.log(
|
||||
"🧰 [CHAT] Native tools enabled:",
|
||||
tools?.map((t: any) => t.function?.name)
|
||||
);
|
||||
} else {
|
||||
console.log("🧰 [CHAT] Prompt-engineering tools mode");
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
if (capabilities.supportsNativeTools) {
|
||||
console.log(
|
||||
"🧰 [CHAT] Native tools enabled:",
|
||||
tools?.map((t: any) => t.function?.name)
|
||||
);
|
||||
} else {
|
||||
console.log("🧰 [CHAT] Prompt-engineering tools mode");
|
||||
}
|
||||
}
|
||||
|
||||
const response = await chatWithStream(
|
||||
@@ -124,7 +130,7 @@ export async function POST(req: NextRequest) {
|
||||
finalSystemPrompt,
|
||||
capabilities.supportsNativeTools ? tools : undefined
|
||||
);
|
||||
console.log("🌊 [CHAT] Streaming started for model:", model);
|
||||
if (process.env.NODE_ENV !== "production") console.log("🌊 [CHAT] Streaming started for model:", model);
|
||||
|
||||
const normalizeParameters = (params: any) => {
|
||||
try {
|
||||
@@ -149,7 +155,7 @@ export async function POST(req: NextRequest) {
|
||||
};
|
||||
|
||||
for await (const chunk of response) {
|
||||
console.log("📦 [CHAT stream chunk]", JSON.stringify(chunk));
|
||||
if (process.env.NODE_ENV !== "production") console.log("📦 [CHAT stream chunk]", JSON.stringify(chunk));
|
||||
// Verificar se há tool calls (modelos com suporte nativo)
|
||||
if (
|
||||
(chunk.message?.tool_calls &&
|
||||
@@ -157,19 +163,16 @@ export async function POST(req: NextRequest) {
|
||||
(Array.isArray((chunk as any).tool_calls) &&
|
||||
(chunk as any).tool_calls.length > 0)
|
||||
) {
|
||||
console.log("🔧 Model requested tool calls (native)");
|
||||
if (process.env.NODE_ENV !== "production") console.log("🔧 Model requested tool calls (native)");
|
||||
|
||||
const nativeCalls =
|
||||
chunk.message?.tool_calls || (chunk as any).tool_calls || [];
|
||||
for (const toolCall of nativeCalls) {
|
||||
const toolKey = toolCall.function.name;
|
||||
const toolInfo = mcpToolsMap.get(toolKey);
|
||||
console.log(
|
||||
"🧭 [CHAT] Native tool call:",
|
||||
toolKey,
|
||||
"->",
|
||||
toolInfo
|
||||
);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log("🧭 [CHAT] Native tool call:", toolKey, "->", toolInfo);
|
||||
}
|
||||
|
||||
if (toolInfo) {
|
||||
// Executar tool
|
||||
@@ -185,11 +188,13 @@ export async function POST(req: NextRequest) {
|
||||
|
||||
if (result.success) {
|
||||
toolExecuted = true;
|
||||
console.log("✅ [CHAT] Tool executed successfully", {
|
||||
mcpId: toolInfo.mcpId,
|
||||
toolName: toolInfo.toolName,
|
||||
executionTime: result.executionTime,
|
||||
});
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log("✅ [CHAT] Tool executed successfully", {
|
||||
mcpId: toolInfo.mcpId,
|
||||
toolName: toolInfo.toolName,
|
||||
executionTime: result.executionTime,
|
||||
});
|
||||
}
|
||||
// Enviar indicador de execução de tool
|
||||
safeEnqueue(
|
||||
JSON.stringify({
|
||||
@@ -206,10 +211,12 @@ export async function POST(req: NextRequest) {
|
||||
toolInfo.toolName,
|
||||
result.result
|
||||
);
|
||||
console.log(
|
||||
"🧩 [CHAT] Injecting tool result into context (length)",
|
||||
String(resultMessage).length
|
||||
);
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log(
|
||||
"🧩 [CHAT] Injecting tool result into context (length)",
|
||||
String(resultMessage).length
|
||||
);
|
||||
}
|
||||
|
||||
// Fazer nova chamada ao modelo com o resultado
|
||||
const updatedMessages = [
|
||||
@@ -264,9 +271,9 @@ export async function POST(req: NextRequest) {
|
||||
extractToolCallFromResponse(accumulatedResponse);
|
||||
|
||||
if (extractedToolCall) {
|
||||
console.log("🔧 Model requested tool call (prompt engineering)");
|
||||
if (process.env.NODE_ENV !== "production") console.log("🔧 Model requested tool call (prompt engineering)");
|
||||
lastExtractedToolCall = extractedToolCall;
|
||||
console.log("🧭 [CHAT] Extracted tool call:", extractedToolCall);
|
||||
if (process.env.NODE_ENV !== "production") console.log("🧭 [CHAT] Extracted tool call:", extractedToolCall);
|
||||
|
||||
// Encontrar MCP e tool
|
||||
let foundToolInfo: { mcpId: string; toolName: string } | null =
|
||||
@@ -339,9 +346,8 @@ export async function POST(req: NextRequest) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
"🕵️ [CHAT] No tool call extracted from accumulated response"
|
||||
);
|
||||
if (process.env.NODE_ENV !== "production")
|
||||
console.log("🕵️ [CHAT] No tool call extracted from accumulated response");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
// Cache leve em memória para reduzir chamadas repetidas em curto intervalo
|
||||
let __modelsCache: { ts: number; payload: any } | null = null;
|
||||
const CACHE_TTL_MS = 8000; // 8s
|
||||
|
||||
function normalizeModel(model: any): ModelInfo {
|
||||
const name: string = model?.model || model?.name || "unknown";
|
||||
const sizeBytes: number = model?.size || 0;
|
||||
@@ -30,6 +34,11 @@ function normalizeModel(model: any): ModelInfo {
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Tentar retorno via cache recente
|
||||
if (__modelsCache && Date.now() - __modelsCache.ts < CACHE_TTL_MS) {
|
||||
return NextResponse.json(__modelsCache.payload);
|
||||
}
|
||||
|
||||
let models: any[] = [];
|
||||
// Tentar garantir disponibilidade rapidamente (não bloquear longamente a UI)
|
||||
await ensureOllamaAvailable({ timeoutMs: 3000 }).catch(() => {});
|
||||
@@ -39,8 +48,12 @@ export async function GET() {
|
||||
models = await listModelsViaHttp();
|
||||
}
|
||||
const normalized = models.map((m) => normalizeModel(m));
|
||||
return NextResponse.json({ offline: false, models: normalized });
|
||||
const payload = { offline: false, models: normalized };
|
||||
__modelsCache = { ts: Date.now(), payload };
|
||||
return NextResponse.json(payload);
|
||||
} catch (e) {
|
||||
return NextResponse.json({ offline: true, models: mockModels });
|
||||
const payload = { offline: true, models: mockModels };
|
||||
__modelsCache = { ts: Date.now(), payload };
|
||||
return NextResponse.json(payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { Ollama } from "ollama";
|
||||
import { OLLAMA_HOST } from "@/lib/ollama";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
const model = url.searchParams.get("model");
|
||||
if (!model) {
|
||||
return NextResponse.json({ error: "model required" }, { status: 400 });
|
||||
}
|
||||
|
||||
const client = new Ollama({ host: OLLAMA_HOST });
|
||||
await client.chat({
|
||||
model,
|
||||
messages: [{ role: "system", content: "warm" }],
|
||||
stream: false,
|
||||
options: { keep_alive: "5m" },
|
||||
});
|
||||
|
||||
return NextResponse.json({ warmed: true });
|
||||
} catch (e) {
|
||||
return NextResponse.json({ warmed: false }, { status: 200 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user