Mise en place v0.6.8
Some checks failed
E2E Tests / e2e (push) Failing after 19s
Build & Push Images / build (brain) (push) Successful in 52s
Build & Push Images / build (core) (push) Successful in 1m20s
Build & Push Images / build (web) (push) Successful in 1m30s

Amélioration de l'installation automatique
Ajout de la possibilité de télécharger le llm que l'on veut à l'interieur de l'application en communicant avec ollama
This commit is contained in:
2026-04-26 01:11:04 +02:00
parent 5e04e84ee4
commit addf78f01d
12 changed files with 754 additions and 18 deletions

View File

@@ -65,6 +65,86 @@ export class SettingsService {
listOneMinModels(): Observable<{ groups: OneMinModelGroup[] }> {
return this.http.get<{ groups: OneMinModelGroup[] }>(`${this.apiUrl}/models/onemin`, this.authOptions);
}
/**
* Telecharge un modele Ollama et streame la progression au client.
*
* Le backend renvoie du NDJSON (un objet JSON par ligne) avec le format
* Ollama natif : `{status, digest?, total?, completed?}`. On parse chaque
* ligne au fur et a mesure et on emet via un Observable que le composant
* peut consommer pour mettre a jour une barre de progression.
*
* On utilise `fetch` directement plutot que `HttpClient` car Angular
* bufferise les reponses XHR, ce qui empeche le streaming en temps reel.
*/
pullOllamaModel(name: string): Observable<OllamaPullEvent> {
return new Observable<OllamaPullEvent>((subscriber) => {
const controller = new AbortController();
(async () => {
try {
const response = await fetch(`${this.apiUrl}/models/ollama/pull`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ name }),
signal: controller.signal,
});
if (!response.ok || !response.body) {
subscriber.error(new Error(`HTTP ${response.status}`));
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Decoupage NDJSON : chaque ligne est un objet JSON complet.
let nl: number;
while ((nl = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, nl).trim();
buffer = buffer.slice(nl + 1);
if (!line) continue;
try {
subscriber.next(JSON.parse(line) as OllamaPullEvent);
} catch {
// Ligne non JSON (rare) : on l'ignore.
}
}
}
subscriber.complete();
} catch (err) {
if ((err as Error).name !== 'AbortError') subscriber.error(err);
}
})();
return () => controller.abort();
});
}
deleteOllamaModel(name: string): Observable<{ status: string; name: string }> {
return this.http.delete<{ status: string; name: string }>(
`${this.apiUrl}/models/ollama/${encodeURIComponent(name)}`, this.authOptions);
}
}
/**
* Format des evenements emis par Ollama pendant un pull. Les champs sont
* optionnels car le serveur emet differents types de messages selon l'etape :
* - `{status: "pulling manifest"}`
* - `{status: "downloading", digest, total, completed}`
* - `{status: "verifying sha256 digest"}`
* - `{status: "writing manifest"}`
* - `{status: "removing any unused layers"}`
* - `{status: "success"}`
* - `{error: "..."}` en cas d'erreur (modele inexistant, reseau, etc.)
*/
export interface OllamaPullEvent {
status?: string;
digest?: string;
total?: number;
completed?: number;
error?: string;
}
/** Un groupe de modeles 1min.ai regroupes par fournisseur (Anthropic, OpenAI, ...). */