overhaul provider inventory and agent/model selection (#8652)

Signed-off-by: Bradley Axen <baxen@squareup.com>
This commit is contained in:
Bradley Axen
2026-04-20 15:00:17 -07:00
committed by GitHub
parent 3d582943fd
commit 8eda6fdabc
70 changed files with 5321 additions and 2123 deletions
@@ -16,7 +16,7 @@ vi.mock("../acpConnection", () => ({
}));
describe("dictation SDK wiring", () => {
let client: any;
let client: { goose: Record<string, ReturnType<typeof vi.fn>> };
beforeEach(() => {
client = {
goose: {
@@ -33,7 +33,9 @@ describe("dictation SDK wiring", () => {
GooseDictationTranscribe: vi.fn().mockResolvedValue({ text: "hello" }),
},
};
vi.mocked(getClient).mockResolvedValue(client);
vi.mocked(getClient).mockResolvedValue(
client as unknown as Awaited<ReturnType<typeof getClient>>,
);
});
it("getDictationConfig calls GooseDictationConfig and returns providers map", async () => {
@@ -46,7 +48,7 @@ describe("dictation SDK wiring", () => {
const result = await transcribeDictation({
audio: "base64==",
mimeType: "audio/webm",
provider: "openai" as any,
provider: "openai",
});
expect(client.goose.GooseDictationTranscribe).toHaveBeenCalledWith({
audio: "base64==",
@@ -58,7 +60,7 @@ describe("dictation SDK wiring", () => {
it("saveDictationModelSelection calls GooseDictationModelSelect", async () => {
client.goose.GooseDictationModelSelect = vi.fn().mockResolvedValue({});
await saveDictationModelSelection("local" as any, "tiny");
await saveDictationModelSelection("local", "tiny");
expect(client.goose.GooseDictationModelSelect).toHaveBeenCalledWith({
provider: "local",
modelId: "tiny",
+54 -3
View File
@@ -1,6 +1,10 @@
import type { ContentBlock } from "@agentclientprotocol/sdk";
import * as directAcp from "./acpApi";
import * as sessionTracker from "./acpSessionTracker";
import {
getCatalogEntry,
resolveAgentProviderCatalogId,
} from "@/features/providers/providerCatalog";
import {
setActiveMessageId,
clearActiveMessageId,
@@ -25,9 +29,31 @@ export interface AcpPrepareSessionOptions {
personaId?: string;
}
export interface AcpCreateSessionOptions extends AcpPrepareSessionOptions {
modelId?: string | null;
}
/** Discover ACP providers installed on the system. */
export async function discoverAcpProviders(): Promise<AcpProvider[]> {
return directAcp.listProviders();
const providers = await directAcp.listProviders();
const seen = new Set<string>();
return providers
.map((provider) => {
const catalogId = resolveAgentProviderCatalogId(
provider.id,
provider.label,
);
if (!catalogId || seen.has(catalogId)) {
return null;
}
seen.add(catalogId);
return {
id: catalogId,
label: getCatalogEntry(catalogId)?.displayName ?? provider.label,
};
})
.filter((provider): provider is AcpProvider => provider !== null);
}
/** Send a message to an ACP agent. Response streams via Tauri events. */
@@ -79,13 +105,13 @@ export async function acpPrepareSession(
providerId: string,
workingDir: string,
options: AcpPrepareSessionOptions = {},
): Promise<void> {
): Promise<string> {
const sid = sessionId.slice(0, 8);
const t0 = performance.now();
perfLog(
`[perf:prepare] ${sid} acpPrepareSession start (provider=${providerId})`,
);
await sessionTracker.prepareSession(
const gooseSessionId = await sessionTracker.prepareSession(
sessionId,
providerId,
workingDir,
@@ -94,6 +120,31 @@ export async function acpPrepareSession(
perfLog(
`[perf:prepare] ${sid} acpPrepareSession done in ${(performance.now() - t0).toFixed(1)}ms`,
);
return gooseSessionId;
}
export async function acpCreateSession(
providerId: string,
workingDir: string,
options: AcpCreateSessionOptions = {},
): Promise<{ sessionId: string }> {
const localSessionId = crypto.randomUUID();
const gooseSessionId = await acpPrepareSession(
localSessionId,
providerId,
workingDir,
options,
);
sessionTracker.registerSession(
gooseSessionId,
gooseSessionId,
providerId,
workingDir,
);
if (options.modelId) {
await directAcp.setModel(gooseSessionId, options.modelId);
}
return { sessionId: gooseSessionId };
}
export async function acpSetModel(
@@ -455,7 +455,6 @@ function handleShared(sessionId: string, update: SessionUpdate): void {
currentModelId;
const sessionStore = useChatSessionStore.getState();
sessionStore.setSessionModels(sessionId, availableModels);
sessionStore.updateSession(
sessionId,
{ modelId: currentModelId, modelName: currentModelName },
@@ -115,8 +115,10 @@ export async function prepareSession(
`[perf:prepare] ${sid} tracker setProvider(${providerId}) in ${(performance.now() - tProv).toFixed(1)}ms (goose_sid=${gooseSid})`,
);
prepared.set(key, { gooseSessionId, providerId, workingDir });
prepared.set(sessionId, { gooseSessionId, providerId, workingDir });
const entry = { gooseSessionId, providerId, workingDir };
prepared.set(key, entry);
prepared.set(sessionId, entry);
prepared.set(gooseSessionId, entry);
gooseToLocal.set(gooseSessionId, sessionId);
notifySessionRegistered(sessionId, gooseSessionId);
@@ -161,6 +163,7 @@ export function registerSession(
}
prepared.set(sessionId, entry);
prepared.set(gooseSessionId, entry);
gooseToLocal.set(gooseSessionId, sessionId);
notifySessionRegistered(sessionId, gooseSessionId);
@@ -163,7 +163,14 @@
"createProject": "Create project",
"generalChatWithoutProject": "General chat without project context",
"loading": "Loading...",
"loadingModels": "Loading models...",
"model": "Model",
"allModels": "All models",
"searchModels": "Search models...",
"recommended": "Recommended",
"noModelsAvailable": "No models available",
"noSearchResults": "No matching models",
"showAllModels": "Browse all models",
"noProject": "No project",
"selectModel": "Select model",
"selectProject": "Select project",
@@ -163,7 +163,14 @@
"createProject": "Crear proyecto",
"generalChatWithoutProject": "Chat general sin contexto de proyecto",
"loading": "Cargando...",
"loadingModels": "Cargando modelos...",
"model": "Modelo",
"allModels": "Todos los modelos",
"searchModels": "Buscar modelos...",
"recommended": "Recomendados",
"noModelsAvailable": "No hay modelos disponibles",
"noSearchResults": "Sin resultados",
"showAllModels": "Ver todos los modelos",
"noProject": "Sin proyecto",
"selectModel": "Seleccionar modelo",
"selectProject": "Seleccionar proyecto",