diff --git a/src/lib/git-analyzer.ts b/src/lib/git-analyzer.ts index e506c90..f565701 100644 --- a/src/lib/git-analyzer.ts +++ b/src/lib/git-analyzer.ts @@ -622,45 +622,61 @@ export function exportToCSV(stats: RepoStats, filename: string = "git-stats"): s export async function generateAISummary(stats: RepoStats, model?: string): Promise { const selectedModel = model || (await detectOllamaModel()); - if (!selectedModel) return "Aucun modèle Ollama disponible. Lancez `ollama pull llama3.2`."; + if (!selectedModel) return "Aucun modèle Ollama disponible. Lancez `ollama pull gemma4`."; try { - const openai = new OpenAI({ baseURL: OLLAMA_BASE_URL, apiKey: "ollama" }); - - // Build context from recent commits - const commitSamples = (stats.recentCommits || []).slice(0, 30) + // Build context + const commitSamples = (stats.recentCommits || []).slice(0, 15) .map((c) => `[${c.author}] ${c.message}`) .join("\n"); - const prompt = `Tu es un expert en analyse de projets Git. Analyse ce dépôt et donne un résumé en français (5-8 phrases max) : + const systemPrompt = `Tu es un expert en analyse de projets Git. Réponds TOUJOURS en français, de façon concise. Format demandé : +📊 Activité : [1 phrase] +👥 Équipe : [1 phrase] +🔧 Code : [1 phrase] +💡 Conseil : [1 phrase]`; -**Projet** : ${stats.repoName} -**Période** : ${stats.activeDays} jours actifs, ${stats.totalCommits} commits + const userPrompt = `Projet : ${stats.repoName} +Période : ${stats.activeDays}j actifs, ${stats.totalCommits} commits ++${stats.totalLinesAdded} / −${stats.totalLinesDeleted} lignes +${stats.contributors.length} contributeurs : ${stats.contributors.slice(0, 5).map(c => c.name).join(", ")} +Types : ${stats.commitTypes.filter(t => t.count > 0).map(t => `${t.type} ${t.percentage}%`).join(", ")} +Fichiers modifiés : ${stats.totalFilesChanged} -**Statistiques clés** : -- ${stats.totalLinesAdded} lignes ajoutées / ${stats.totalLinesDeleted} supprimées -- ${stats.contributors.length} contributeur(s) : ${stats.contributors.slice(0, 5).map((c) => c.name).join(", ")} -- Types de commits : ${stats.commitTypes.filter((t) => t.count > 0).map((t) => `${t.type} (${t.percentage}%)`).join(", ")} -- Fichiers modifiés : ${stats.totalFilesChanged} +Commits récents : +${commitSamples}`; -**Derniers commits** : -${commitSamples} - -Réponds avec ce format : -1. 📊 **Activité** : tendance générale -2. 👥 **Équipe** : qui fait quoi -3. 🔧 **Code** : types de changements principaux -4. 💡 **Conseil** : une suggestion pertinente`; - - const response = await openai.chat.completions.create({ - model: selectedModel, - messages: [{ role: "user" as const, content: prompt }], - max_tokens: 500, - temperature: 0.7, + // Use Ollama native API (more reliable than OpenAI compat for thinking models) + const ollamaUrl = OLLAMA_BASE_URL.replace(/\/v1$/, ""); + const res = await fetch(`${ollamaUrl}/api/chat`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + model: selectedModel, + messages: [ + { role: "system", content: systemPrompt }, + { role: "user", content: userPrompt }, + ], + stream: false, + options: { temperature: 0.7, num_predict: 400 }, + }), }); - return response.choices[0]?.message?.content?.trim() || "Pas de résumé généré."; + if (!res.ok) { + const err = await res.text(); + throw new Error(`HTTP ${res.status}: ${err.slice(0, 200)}`); + } + + const data = await res.json(); + const content = data.message?.content?.trim(); + + if (!content || content.length < 10) { + // Maybe the model returned thinking output instead of content + return `Erreur : réponse vide du modèle ${selectedModel}. Le modèle peut nécessiter un format différent. Essayez \`ollama pull llama3.2\`.`; + } + + return content; } catch (error: any) { - return `Erreur Ollama (${selectedModel}) : ${error?.message || "inconnue"}. Vérifiez \`ollama pull ${selectedModel}\`.`; + return `Erreur Ollama (${selectedModel}) : ${error?.message || "inconnue"}. Vérifiez \`ollama serve\` et \`ollama pull ${selectedModel}\`.`; } }