Ajout d'une partie mise à jour automatique : plus besoin de docker pull en ligne de commande ; on peut passer par l'interface Refactoring partie Java pour respecter d'avantage le DDD : plus de jackson dans la partie domain Passage version 0.6.6
202 lines
7.9 KiB
HTML
202 lines
7.9 KiB
HTML
<div class="settings-page">
|
|
|
|
<header class="page-header">
|
|
<button class="btn-back" (click)="goBack()">
|
|
<lucide-icon [img]="ArrowLeft" [size]="16"></lucide-icon>
|
|
<span>Retour</span>
|
|
</button>
|
|
<h1>Parametres</h1>
|
|
</header>
|
|
|
|
<div *ngIf="errorMessage" class="alert alert-error">
|
|
<lucide-icon [img]="AlertCircle" [size]="16"></lucide-icon>
|
|
<span>{{ errorMessage }}</span>
|
|
</div>
|
|
<div *ngIf="successMessage" class="alert alert-success">
|
|
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
|
|
<span>{{ successMessage }}</span>
|
|
</div>
|
|
|
|
<section class="card" *ngIf="settings">
|
|
<h2>Moteur IA</h2>
|
|
<p class="hint">Choix du fournisseur de modele de langage utilise par le chat et la generation de pages.</p>
|
|
|
|
<div class="form-row">
|
|
<label>Fournisseur</label>
|
|
<div class="radio-group">
|
|
<label class="radio">
|
|
<input type="radio" name="provider" value="ollama" [(ngModel)]="settings.llm_provider">
|
|
<span>Ollama (local)</span>
|
|
</label>
|
|
<label class="radio">
|
|
<input type="radio" name="provider" value="onemin" [(ngModel)]="settings.llm_provider">
|
|
<span>1min.ai (cloud)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Bloc Ollama -->
|
|
<section class="card" *ngIf="settings && settings.llm_provider === 'ollama'">
|
|
<h2>Configuration Ollama</h2>
|
|
|
|
<div class="form-row">
|
|
<label for="ollama-url">URL du serveur Ollama</label>
|
|
<input id="ollama-url" type="text" [(ngModel)]="settings.ollama_base_url" placeholder="http://localhost:11434">
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="ollama-model">Modele</label>
|
|
<div class="inline-select">
|
|
<select id="ollama-model" [(ngModel)]="settings.llm_model" (ngModelChange)="fetchOllamaModelInfo()">
|
|
<option *ngIf="ollamaModels.length === 0" [value]="settings.llm_model">{{ settings.llm_model }}</option>
|
|
<option *ngFor="let m of ollamaModels" [value]="m">{{ m }}</option>
|
|
</select>
|
|
<button type="button" class="btn-secondary" (click)="refreshModels()" [disabled]="loadingModels">
|
|
<lucide-icon [img]="RefreshCw" [size]="14"></lucide-icon>
|
|
<span>{{ loadingModels ? 'Chargement...' : 'Actualiser' }}</span>
|
|
</button>
|
|
</div>
|
|
<p class="hint" *ngIf="ollamaModels.length === 0">Aucun modele detecte. Verifie que Ollama tourne et que l'URL est correcte.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Bloc 1min.ai -->
|
|
<section class="card" *ngIf="settings && settings.llm_provider === 'onemin'">
|
|
<h2>Configuration 1min.ai</h2>
|
|
|
|
<div class="form-row">
|
|
<label for="onemin-key">Cle API</label>
|
|
<input
|
|
id="onemin-key"
|
|
type="password"
|
|
[(ngModel)]="oneminApiKeyInput"
|
|
[placeholder]="settings.onemin_api_key_set ? 'Cle configuree (laisser vide pour ne pas changer)' : 'Saisir votre cle API'">
|
|
<label class="checkbox" *ngIf="settings.onemin_api_key_set">
|
|
<input type="checkbox" [(ngModel)]="clearApiKey">
|
|
<span>Effacer la cle enregistree</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="onemin-provider">Fournisseur</label>
|
|
<select id="onemin-provider" [(ngModel)]="oneminProvider" (ngModelChange)="onProviderChange()">
|
|
<option *ngFor="let g of oneminGroups" [value]="g.provider">{{ g.provider }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<label for="onemin-model">Modele</label>
|
|
<select id="onemin-model" [(ngModel)]="settings.onemin_model">
|
|
<option *ngFor="let m of currentProviderModels" [value]="m">{{ m }}</option>
|
|
</select>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Bloc Fenetre de contexte -->
|
|
<section class="card" *ngIf="settings">
|
|
<h2>Fenetre de contexte</h2>
|
|
|
|
<!-- Ollama : slider borne par le max du modele -->
|
|
<div class="form-row" *ngIf="settings.llm_provider === 'ollama'">
|
|
<label for="llm-num-ctx">
|
|
Tokens alloues au modele
|
|
<span class="ctx-value">{{ settings.llm_num_ctx | number }}</span>
|
|
<span class="ctx-max" *ngIf="ollamaModelMaxContext > 0">
|
|
/ {{ ollamaModelMaxContext | number }} max
|
|
</span>
|
|
</label>
|
|
<input
|
|
id="llm-num-ctx"
|
|
type="range"
|
|
[min]="CTX_MIN"
|
|
[max]="effectiveMaxContext"
|
|
step="1024"
|
|
[(ngModel)]="settings.llm_num_ctx"
|
|
class="ctx-slider">
|
|
<p class="hint" *ngIf="ollamaModelMaxContext > 0">
|
|
Le modele <strong>{{ settings.llm_model }}</strong> accepte jusqu'a
|
|
{{ ollamaModelMaxContext | number }} tokens. Plus la valeur est elevee, plus
|
|
l'IA peut tenir d'historique et de contexte — au prix de VRAM et de latence.
|
|
</p>
|
|
<p class="hint" *ngIf="ollamaModelMaxContext === 0">
|
|
Impossible de determiner la fenetre max du modele (Ollama injoignable ou modele
|
|
inconnu). Slider borne a {{ CTX_FALLBACK_MAX | number }} par securite.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 1min.ai : saisie libre (pas d'introspection possible) -->
|
|
<div class="form-row" *ngIf="settings.llm_provider === 'onemin'">
|
|
<label for="llm-num-ctx-onemin">Fenetre de contexte (tokens)</label>
|
|
<input
|
|
id="llm-num-ctx-onemin"
|
|
type="number"
|
|
min="2048"
|
|
step="1024"
|
|
[(ngModel)]="settings.llm_num_ctx">
|
|
<p class="hint">
|
|
A regler selon la capacite du modele 1min.ai choisi (ex: 128 000 pour gpt-4o,
|
|
200 000 pour claude-sonnet). Sert de plafond a la jauge de contexte du chat.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="actions" *ngIf="settings">
|
|
<button class="btn-primary" (click)="save()" [disabled]="saving">
|
|
<lucide-icon [img]="Save" [size]="16"></lucide-icon>
|
|
<span>{{ saving ? 'Sauvegarde...' : 'Sauvegarder' }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Bloc Mises a jour -->
|
|
<section class="card" *ngIf="config.updateCheckEnabled">
|
|
<h2>Mises a jour</h2>
|
|
<p class="hint">Verifie aupres du registry Docker si une nouvelle version
|
|
des conteneurs (core, brain, web) est disponible. Postgres et MinIO sont
|
|
exclus — ils sont mis a jour manuellement.</p>
|
|
|
|
<div class="form-row">
|
|
<button type="button" class="btn-secondary" (click)="checkUpdates()" [disabled]="updateChecking">
|
|
<lucide-icon [img]="RefreshCw" [size]="14"></lucide-icon>
|
|
<span>{{ updateChecking ? 'Verification...' : 'Verifier maintenant' }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div *ngIf="updateStatus && !updateStatus.enabled" class="hint">
|
|
Feature non configuree (WATCHTOWER_TOKEN absent).
|
|
</div>
|
|
|
|
<div *ngIf="updateStatus?.enabled">
|
|
<div *ngIf="updateStatus?.updateAvailable" class="alert alert-success">
|
|
<lucide-icon [img]="Download" [size]="16"></lucide-icon>
|
|
<span>Une mise a jour est disponible.</span>
|
|
</div>
|
|
<div *ngIf="!updateStatus?.updateAvailable" class="hint">
|
|
Tout est a jour (verifie le {{ updateStatus?.checkedAt | date:'short' }}).
|
|
</div>
|
|
|
|
<ul class="update-images" *ngIf="updateStatus?.images?.length">
|
|
<li *ngFor="let img of updateStatus?.images">
|
|
<strong>{{ img.image }}</strong>
|
|
<span *ngIf="img.updateAvailable" class="badge-update">MAJ dispo</span>
|
|
<span *ngIf="!img.updateAvailable && img.remoteDigest" class="badge-ok">a jour</span>
|
|
<span *ngIf="!img.remoteDigest" class="badge-warn">indisponible</span>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="form-row" *ngIf="updateStatus?.updateAvailable">
|
|
<button type="button" class="btn-primary" (click)="applyUpdate()" [disabled]="updateApplying">
|
|
<lucide-icon [img]="Download" [size]="16"></lucide-icon>
|
|
<span>{{ updateApplying ? 'Mise a jour en cours...' : 'Mettre a jour maintenant' }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div *ngIf="updateMessage" class="alert alert-success">
|
|
<lucide-icon [img]="Check" [size]="16"></lucide-icon>
|
|
<span>{{ updateMessage }}</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</div>
|