feat(opencode): remplace le provider Ollama par llama.cpp
Le tool-calling local ne fonctionnait jamais via Ollama. Refonte du support local d'OpenCode autour de llama.cpp: profil, catalogue, matérialisation de la config OpenCode et surface first-run alignés sur llama-server (backend + frontend). QA vert (commandes réelles): domain 244, application 81+64, infra 263 (10 échecs = bind-port sandbox identiques sur develop, non-régression), frontend 574, tsc propre. Réserve E2E live non bloquante faute de llama-server joignable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -9,6 +9,7 @@ import type { AgentProfile } from "@/domain";
|
||||
import {
|
||||
defaultHttpChatConfig,
|
||||
defaultInjection,
|
||||
defaultOpenCodeConfig,
|
||||
emptyCustomProfile,
|
||||
isProfileValid,
|
||||
isRelativeSafe,
|
||||
@ -16,6 +17,7 @@ import {
|
||||
isValidHttpUrl,
|
||||
parseArgs,
|
||||
validateHttpChatConfig,
|
||||
validateOpenCodeConfig,
|
||||
validateProfile,
|
||||
} from "./profile";
|
||||
|
||||
@ -189,6 +191,58 @@ describe("validateProfile for openAiCompatible profiles", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateOpenCodeConfig", () => {
|
||||
it("a well-formed llama.cpp config is valid", () => {
|
||||
expect(validateOpenCodeConfig(defaultOpenCodeConfig())).toEqual({});
|
||||
});
|
||||
it("reports a bad base URL scheme and an empty model", () => {
|
||||
const errors = validateOpenCodeConfig({ baseURL: "ftp://x", model: " " });
|
||||
expect(errors.baseURL).toBeDefined();
|
||||
expect(errors.model).toBeDefined();
|
||||
});
|
||||
it("an optional API key is free-form (no env-var-name constraint)", () => {
|
||||
// Unlike the openAiCompatible adapter, OpenCode carries the key itself.
|
||||
expect(
|
||||
validateOpenCodeConfig({
|
||||
baseURL: "http://localhost:8080/v1",
|
||||
apiKey: "sk-secret-123",
|
||||
model: "qwen3-coder-30b",
|
||||
}),
|
||||
).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateProfile for openCode profiles", () => {
|
||||
function opencode(overrides: Partial<AgentProfile> = {}): AgentProfile {
|
||||
return base({
|
||||
name: "OpenCode + llama.cpp",
|
||||
command: "opencode",
|
||||
structuredAdapter: "openCode",
|
||||
opencode: defaultOpenCodeConfig(),
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
it("the reference llama.cpp profile is valid", () => {
|
||||
expect(validateProfile(opencode())).toEqual({});
|
||||
expect(isProfileValid(opencode())).toBe(true);
|
||||
});
|
||||
it("surfaces opencode field errors through the profile validator", () => {
|
||||
const errors = validateProfile(
|
||||
opencode({ opencode: { baseURL: "nope", model: "" } }),
|
||||
);
|
||||
expect(errors.baseURL).toBeDefined();
|
||||
expect(errors.model).toBeDefined();
|
||||
expect(
|
||||
isProfileValid(opencode({ opencode: { baseURL: "nope", model: "m" } })),
|
||||
).toBe(false);
|
||||
});
|
||||
it("a missing opencode config on an openCode profile is invalid", () => {
|
||||
const errors = validateProfile(opencode({ opencode: undefined }));
|
||||
expect(errors.baseURL).toBeDefined();
|
||||
expect(errors.model).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("defaultInjection", () => {
|
||||
it("produces a sensible default per strategy", () => {
|
||||
expect(defaultInjection("conventionFile")).toEqual({
|
||||
|
||||
Reference in New Issue
Block a user