feat(model-server): wizard V2 config modèles locaux — source, options structurées & preview

Refonte UX du wizard de configuration des modèles locaux du serveur
llama.cpp intégré, alignée sur la source Hugging Face et les options backend.

- choix de source sans jargon (chemin local .gguf vs dépôt Hugging Face) ;
- champs structurés -ngl (gpu_layers), -c (context_size), --jinja, --host ;
- zone d'arguments libres avec alerte sur les flags réservés ;
- preview de la commande via le backend (previewModelServerCommand, debounced) ;
- gateway/ports et mock adaptés au contrat V2.

QA : Vitest 22 verts, tsc/build OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:32:04 +02:00
parent 38aecf2cac
commit efa081b074
10 changed files with 794 additions and 143 deletions

View File

@ -39,6 +39,17 @@ export type ModelServerStatus =
*/
export type StopPolicy = "keepAlive" | "stopOnAppExit" | "stopWhenUnused";
/**
* Where the served `.gguf` model comes from (F35 V2, mirror of the backend
* `ModelSourceDto`, camelCase wire, tagged on `type`):
* - `localPath`: an absolute `.gguf` path already present on disk (`--model`),
* - `huggingFace`: a `namespace/repo[:quant]` reference llama.cpp downloads and
* caches automatically (`-hf`).
*/
export type ModelSource =
| { type: "localPath"; path: string }
| { type: "huggingFace"; repo: string };
/**
* A declared local model server (F35, mirror of the backend flat
* `LocalModelServerConfigDto`, camelCase wire). Global to IdeA (not project
@ -50,7 +61,8 @@ export type StopPolicy = "keepAlive" | "stopOnAppExit" | "stopWhenUnused";
* UUID; it does not generate the server id, only an internal model id it hides).
* - `servedModelName` is what the user configures and what is sent to OpenCode as
* the `model` — the backend's internal `model.id`/`model.label` are not exposed.
* - `modelPath` is required by the backend when `autoStart` is `true`.
* - `modelSource` (V2) replaces the former `modelPath`; it is required by the
* backend when `autoStart` is `true`.
*/
export interface LocalModelServerConfig {
id: string;
@ -60,17 +72,39 @@ export interface LocalModelServerConfig {
/** OpenAI-compatible base URL served by `llama-server` (normalised to `/v1`). */
baseURL: string;
port: number;
/** Absolute `.gguf` path. Required when `autoStart` is `true`. */
modelPath?: string;
/** Explicit model source (`.gguf` path or Hugging Face ref). Required for auto-start. */
modelSource?: ModelSource;
/** Model name exposed by the server and sent to OpenCode as `model`. */
servedModelName: string;
/** Explicit `llama-server` executable path/command (optional). */
binaryPath?: string;
/** llama.cpp `--host` (default `127.0.0.1`). */
host: string;
/** llama.cpp `-ngl` GPU layers (optional; `0` = CPU only). */
gpuLayers?: number;
/** llama.cpp `-c` context size (optional). */
contextSize?: number;
/** Whether to pass `--jinja` (Jinja chat template). */
jinja: boolean;
args: string[];
autoStart: boolean;
stopPolicy: StopPolicy;
}
/**
* A previewed `llama-server` command line (F35 V2, mirror of the backend
* `PreviewModelServerCommandDto`). Built by the backend from a draft config —
* never reconstructed client-side.
*/
export interface ModelServerCommandPreview {
/** Resolved executable (e.g. `llama-server` or an absolute path). */
command: string;
/** Argument vector, unescaped. */
args: string[];
/** Human-readable, shell-escaped command line. */
display: string;
}
/** A domain event relayed from the backend (tagged union on `type`). */
export type DomainEvent =
| { type: "projectCreated"; projectId: string }