feat(ollama): enhance model pull with improved error handling, persistence, and UI
- Add catalog metadata lookup for better error messages with hints (e.g., for missing manifests or tags) - Persist downloaded model references in saved remote models for future reuse - Replace searchRemoteModels import with getSavedRemoteModels in search route - Enhance pull progress UI to display download bytes, digest, and fallback message when data is unavailable - Improve error payload with model details, URL, and tags count to guide users toward valid tags or models
This commit is contained in:
1 parent
1545020afe
commit
a8bb7cc22d
33 files changed
+1298
-235
No files matched your search
@@ -1,5 +1,7 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { pullModel, ensureOllamaAvailable } from "@/lib/ollama";
|
||||
import { getCatalogModels } from "@/lib/catalog";
|
||||
import { addSavedRemoteModel } from "@/lib/user-remote-models";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -26,6 +28,11 @@ export async function POST(req: NextRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Buscar metadados do catálogo para melhorar mensagens de erro
|
||||
const baseName = String(model).split(":")[0];
|
||||
const catalog = await getCatalogModels();
|
||||
const catalogEntry = catalog.find((m) => m.name === baseName);
|
||||
|
||||
const response = await pullModel(model);
|
||||
|
||||
for await (const chunk of response) {
|
||||
@@ -34,18 +41,59 @@ export async function POST(req: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Persistir referência do modelo baixado para reutilização futura
|
||||
try {
|
||||
await addSavedRemoteModel({ name: String(model), installed: true });
|
||||
} catch {}
|
||||
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(
|
||||
JSON.stringify({ status: "success" }) + "\n",
|
||||
),
|
||||
);
|
||||
controller.close();
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error("Pull streaming error:", error);
|
||||
const message = typeof error?.message === "string" ? error.message : "Failed to pull model";
|
||||
const baseName = String(model).split(":")[0];
|
||||
let hint: string | undefined;
|
||||
|
||||
// Recarregar entrada de catálogo para enriquecer erro
|
||||
let catalogEntry: { url?: string; tags_count?: number } | undefined;
|
||||
try {
|
||||
const catalog = await getCatalogModels();
|
||||
catalogEntry = catalog.find((m) => m.name === baseName);
|
||||
} catch {}
|
||||
|
||||
// Heurísticas: manifest não encontrado → provavelmente falta tag válida
|
||||
if (/manifest/i.test(message) || /does not exist/i.test(message)) {
|
||||
hint = "Modelo/tag não encontrado no registro. Selecione uma tag válida ou verifique a página do modelo.";
|
||||
}
|
||||
|
||||
const payload: Record<string, unknown> = {
|
||||
error: "Failed to pull model",
|
||||
detail: message,
|
||||
model,
|
||||
name: baseName,
|
||||
};
|
||||
|
||||
// Anexar URL do catálogo quando disponível
|
||||
if (catalogEntry?.url) {
|
||||
payload.url = catalogEntry.url;
|
||||
}
|
||||
|
||||
// Se usuário não especificou tag (sem ":") e sabemos que há múltiplas tags
|
||||
if (!String(model).includes(":") && typeof catalogEntry?.tags_count === "number") {
|
||||
payload.tags_count = catalogEntry.tags_count;
|
||||
if (!hint && catalogEntry.tags_count > 0) {
|
||||
hint = "Este modelo pode não ter a tag 'latest'. Escolha uma tag específica.";
|
||||
}
|
||||
}
|
||||
|
||||
if (hint) payload.hint = hint;
|
||||
|
||||
controller.enqueue(
|
||||
new TextEncoder().encode(
|
||||
JSON.stringify({ error: "Failed to pull model" }) + "\n",
|
||||
),
|
||||
new TextEncoder().encode(JSON.stringify(payload) + "\n"),
|
||||
);
|
||||
controller.close();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user