Migrate local inference model management to ACP (#10124)

This commit is contained in:
Jasper
2026-07-01 00:40:04 +02:00
committed by GitHub
parent 159fb4d53e
commit d1cc1439ec
26 changed files with 3517 additions and 2579 deletions
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
import type {
LocalInferenceDownloadProgressDto,
LocalInferenceHfModelInfoDto,
LocalInferenceHfModelVariantDto,
LocalInferenceModelDownloadRequest_unstable,
LocalInferenceModelDto,
LocalInferenceModelSettingsDto,
} from '@aaif/goose-sdk';
import { getAcpClient } from './acpConnection';
export type LocalModelResponse = LocalInferenceModelDto;
export type DownloadProgress = LocalInferenceDownloadProgressDto;
export type DownloadModelRequest = LocalInferenceModelDownloadRequest_unstable;
export type HfModelInfo = LocalInferenceHfModelInfoDto;
export type HfModelVariant = LocalInferenceHfModelVariantDto;
export type ModelSettings = LocalInferenceModelSettingsDto;
export type SamplingConfig = NonNullable<LocalInferenceModelSettingsDto['sampling']>;
export type ToolCallingMode = NonNullable<LocalInferenceModelSettingsDto['toolCalling']>;
export type ChatTemplate = NonNullable<LocalInferenceModelSettingsDto['chatTemplate']>;
export type RepoVariantsResponse = {
variants: HfModelVariant[];
recommendedIndex: number | null;
availableMemoryBytes: number;
downloadedQuants: string[];
downloadedVariants: string[];
};
export async function listLocalModels(): Promise<LocalModelResponse[]> {
const client = await getAcpClient();
const response = await client.goose.localInferenceModelsList_unstable({});
return response.models;
}
export async function downloadHfModel(request: DownloadModelRequest): Promise<string> {
const client = await getAcpClient();
const response = await client.goose.localInferenceModelsDownload_unstable(request);
return response.modelId;
}
export async function getLocalModelDownloadProgress(
modelId: string
): Promise<DownloadProgress | null> {
const client = await getAcpClient();
const response = await client.goose.localInferenceModelsDownloadProgress_unstable({ modelId });
return response.progress ?? null;
}
export async function cancelLocalModelDownload(modelId: string): Promise<void> {
const client = await getAcpClient();
await client.goose.localInferenceModelsDownloadCancel_unstable({ modelId });
}
export async function deleteLocalModel(modelId: string): Promise<void> {
const client = await getAcpClient();
await client.goose.localInferenceModelsDelete_unstable({ modelId });
}
export async function getModelSettings(modelId: string): Promise<ModelSettings> {
const client = await getAcpClient();
const response = await client.goose.localInferenceModelsSettingsRead_unstable({ modelId });
return response.settings;
}
export async function updateModelSettings(
modelId: string,
settings: ModelSettings
): Promise<ModelSettings> {
const client = await getAcpClient();
const response = await client.goose.localInferenceModelsSettingsUpdate_unstable({
modelId,
settings,
});
return response.settings;
}
export async function searchHfModels(query: string, limit?: number): Promise<HfModelInfo[]> {
const client = await getAcpClient();
const response = await client.goose.localInferenceHuggingfaceSearch_unstable({ query, limit });
return response.models;
}
export async function getRepoFiles(repoId: string): Promise<RepoVariantsResponse> {
const client = await getAcpClient();
const response = await client.goose.localInferenceHuggingfaceRepoVariants_unstable({ repoId });
return {
variants: response.variants,
recommendedIndex: response.recommendedIndex ?? null,
availableMemoryBytes: response.availableMemoryBytes,
downloadedQuants: response.downloadedQuants,
downloadedVariants: response.downloadedVariants,
};
}
export async function listBuiltinChatTemplates(): Promise<string[]> {
const client = await getAcpClient();
const response = await client.goose.localInferenceChatTemplatesBuiltinList_unstable({});
return response.templates;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-407
View File
@@ -59,16 +59,6 @@ export type ChatRequest = {
user_message: Message;
};
export type ChatTemplate = {
type: 'embedded';
} | {
name: string;
type: 'builtin';
} | {
template: string;
type: 'custom_inline';
};
export type CheckProviderRequest = {
provider: string;
};
@@ -316,21 +306,6 @@ export type DictationProviderStatus = {
uses_provider_config: boolean;
};
export type DownloadModelRequest = {
/**
* Optional backend id for callers selecting a concrete variant row.
*/
backend_id?: string | null;
/**
* Model spec/download id like "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M" or "google/gemma-4-31B-it"
*/
spec: string;
/**
* Optional backend-specific variant id, such as a GGUF quantization or MLX dtype.
*/
variant_id?: string | null;
};
export type DownloadProgress = {
/**
* Bytes downloaded so far
@@ -568,55 +543,6 @@ export type GooseApp = McpAppResource & (WindowProps | null) & {
export type GooseMode = 'auto' | 'approve' | 'smart_approve' | 'chat';
/**
* A single downloadable GGUF file (used internally and for downloads).
*/
export type HfGgufFile = {
download_url: string;
filename: string;
quantization: string;
size_bytes: number;
};
export type HfModelInfo = {
author: string;
downloads: number;
gguf_files: Array<HfGgufFile>;
model_name: string;
repo_id: string;
variants?: Array<HfModelVariant>;
};
export type HfModelVariant = {
backend_id: string;
description: string;
download_id: string;
download_url?: string | null;
filename?: string | null;
format: string;
label: string;
model_id: string;
quality_rank: number;
sharded?: boolean;
size_bytes: number;
supported?: boolean;
unsupported_reason?: string | null;
variant_id: string;
};
/**
* A quantization variant — groups sharded files into one logical entry.
*/
export type HfQuantVariant = {
description: string;
download_url: string;
filename: string;
quality_rank: number;
quantization: string;
sharded?: boolean;
size_bytes: number;
};
export type Icon = {
mimeType?: string;
sizes?: Array<string>;
@@ -672,19 +598,6 @@ export type LoadedProvider = {
is_editable: boolean;
};
export type LocalModelResponse = {
filename: string;
id: string;
mmproj_status?: ModelDownloadStatus | null;
quantization: string;
recommended: boolean;
repo_id: string;
settings: ModelSettings;
size_bytes: number;
status: ModelDownloadStatus;
vision_capable: boolean;
};
/**
* MCP App Resource
* Represents a UI resource that can be rendered in an MCP App
@@ -824,18 +737,6 @@ export type ModelConfig = {
toolshim_model?: string | null;
};
export type ModelDownloadStatus = {
state: 'NotDownloaded';
} | {
bytes_downloaded: number;
progress_percent: number;
speed_bps?: number | null;
state: 'Downloading';
total_bytes: number;
} | {
state: 'Downloaded';
};
/**
* Information about a model's capabilities
*/
@@ -897,43 +798,6 @@ export type ModelInfoResponse = {
source: string;
};
export type ModelSettings = {
/**
* Backend implementation to use for this model. Defaults to llama.cpp.
*/
backend_id?: string | null;
chat_template?: ChatTemplate;
context_size?: number | null;
draft_model?: string | null;
enable_thinking?: boolean;
flash_attention?: boolean | null;
frequency_penalty?: number;
/**
* Estimated tokens per image for budget planning before mtmd tokenization.
* The actual count is determined after tokenization via `chunks.total_tokens()`.
*/
image_token_estimate?: number;
max_output_tokens?: number | null;
/**
* Size of the mmproj file in bytes, used for memory accounting.
*/
mmproj_size_bytes?: number;
n_batch?: number | null;
n_gpu_layers?: number | null;
n_threads?: number | null;
presence_penalty?: number;
repeat_last_n?: number;
repeat_penalty?: number;
sampling?: SamplingConfig;
tool_calling?: ToolCallingMode;
use_mlock?: boolean;
/**
* Whether this model architecture supports vision input.
* Derived from associated mmproj metadata, not user-configurable.
*/
vision_capable?: boolean;
};
export type ModelTemplate = {
capabilities: ModelCapabilities;
context_limit: number;
@@ -1200,14 +1064,6 @@ export type RemoveExtensionRequest = {
session_id: string;
};
export type RepoVariantsResponse = {
available_memory_bytes: number;
downloaded_quants: Array<string>;
downloaded_variants: Array<string>;
recommended_index?: number | null;
variants: Array<HfModelVariant>;
};
export type ResourceContents = {
_meta?: {
[key: string]: unknown;
@@ -1285,22 +1141,6 @@ export type RunNowResponse = {
session_id: string;
};
export type SamplingConfig = {
type: 'Greedy';
} | {
min_p: number;
seed?: number | null;
temperature: number;
top_k: number;
top_p: number;
type: 'Temperature';
} | {
eta: number;
seed?: number | null;
tau: number;
type: 'MirostatV2';
};
export type SavePromptRequest = {
content: string;
};
@@ -1572,8 +1412,6 @@ export type ToolAnnotations = {
title?: string;
};
export type ToolCallingMode = 'auto' | 'force_native' | 'force_emulated';
export type ToolConfirmationRequest = {
arguments: JsonObject;
id: string;
@@ -3054,251 +2892,6 @@ export type TranscribeDictationResponses = {
export type TranscribeDictationResponse = TranscribeDictationResponses[keyof TranscribeDictationResponses];
export type ListBuiltinChatTemplatesData = {
body?: never;
path?: never;
query?: never;
url: '/local-inference/chat-templates/builtin';
};
export type ListBuiltinChatTemplatesResponses = {
/**
* llama.cpp built-in chat template names
*/
200: Array<string>;
};
export type ListBuiltinChatTemplatesResponse = ListBuiltinChatTemplatesResponses[keyof ListBuiltinChatTemplatesResponses];
export type DownloadHfModelData = {
body: DownloadModelRequest;
path?: never;
query?: never;
url: '/local-inference/download';
};
export type DownloadHfModelErrors = {
/**
* Invalid request
*/
400: unknown;
};
export type DownloadHfModelResponses = {
/**
* Download started
*/
202: string;
};
export type DownloadHfModelResponse = DownloadHfModelResponses[keyof DownloadHfModelResponses];
export type ListLocalModelsData = {
body?: never;
path?: never;
query?: never;
url: '/local-inference/models';
};
export type ListLocalModelsResponses = {
/**
* List of available local LLM models
*/
200: Array<LocalModelResponse>;
};
export type ListLocalModelsResponse = ListLocalModelsResponses[keyof ListLocalModelsResponses];
export type DeleteLocalModelData = {
body?: never;
path: {
model_id: string;
};
query?: never;
url: '/local-inference/models/{model_id}';
};
export type DeleteLocalModelErrors = {
/**
* Model not found
*/
404: unknown;
};
export type DeleteLocalModelResponses = {
/**
* Model deleted
*/
200: unknown;
};
export type CancelLocalModelDownloadData = {
body?: never;
path: {
model_id: string;
};
query?: never;
url: '/local-inference/models/{model_id}/download';
};
export type CancelLocalModelDownloadErrors = {
/**
* No active download
*/
404: unknown;
};
export type CancelLocalModelDownloadResponses = {
/**
* Download cancelled
*/
200: unknown;
};
export type GetLocalModelDownloadProgressData = {
body?: never;
path: {
model_id: string;
};
query?: never;
url: '/local-inference/models/{model_id}/download';
};
export type GetLocalModelDownloadProgressErrors = {
/**
* No active download
*/
404: unknown;
};
export type GetLocalModelDownloadProgressResponses = {
/**
* Download progress
*/
200: DownloadProgress;
};
export type GetLocalModelDownloadProgressResponse = GetLocalModelDownloadProgressResponses[keyof GetLocalModelDownloadProgressResponses];
export type GetModelSettingsData = {
body?: never;
path: {
model_id: string;
};
query?: never;
url: '/local-inference/models/{model_id}/settings';
};
export type GetModelSettingsErrors = {
/**
* Model not found
*/
404: unknown;
};
export type GetModelSettingsResponses = {
/**
* Model settings
*/
200: ModelSettings;
};
export type GetModelSettingsResponse = GetModelSettingsResponses[keyof GetModelSettingsResponses];
export type UpdateModelSettingsData = {
body: ModelSettings;
path: {
model_id: string;
};
query?: never;
url: '/local-inference/models/{model_id}/settings';
};
export type UpdateModelSettingsErrors = {
/**
* Model not found
*/
404: unknown;
/**
* Failed to save settings
*/
500: unknown;
};
export type UpdateModelSettingsResponses = {
/**
* Settings updated
*/
200: ModelSettings;
};
export type UpdateModelSettingsResponse = UpdateModelSettingsResponses[keyof UpdateModelSettingsResponses];
export type GetRepoFilesData = {
body?: never;
path: {
author: string;
repo: string;
};
query?: never;
url: '/local-inference/repo/{author}/{repo}/files';
};
export type GetRepoFilesResponses = {
/**
* GGUF files in the repo
*/
200: RepoVariantsResponse;
};
export type GetRepoFilesResponse = GetRepoFilesResponses[keyof GetRepoFilesResponses];
export type SearchHfModelsData = {
body?: never;
path?: never;
query: {
/**
* Search query
*/
q: string;
/**
* Max results
*/
limit?: number | null;
};
url: '/local-inference/search';
};
export type SearchHfModelsErrors = {
/**
* Search failed
*/
500: unknown;
};
export type SearchHfModelsResponses = {
/**
* Search results
*/
200: Array<HfModelInfo>;
};
export type SearchHfModelsResponse = SearchHfModelsResponses[keyof SearchHfModelsResponses];
export type SyncFeaturedModelsData = {
body?: never;
path?: never;
query?: never;
url: '/local-inference/sync-featured';
};
export type SyncFeaturedModelsResponses = {
/**
* Featured models synced to registry
*/
200: unknown;
};
export type DecodeRecipeData = {
body: DecodeRecipeRequest;
path?: never;
@@ -1,13 +1,12 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import {
listLocalModels,
syncFeaturedModels,
downloadHfModel,
getLocalModelDownloadProgress,
cancelLocalModelDownload,
type DownloadProgress,
type LocalModelResponse,
} from '../../api';
} from '../../acp/local-inference';
import { trackOnboardingSetupFailed } from '../../utils/analytics';
import { defineMessages, useIntl } from '../../i18n';
@@ -124,16 +123,15 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
useEffect(() => {
const load = async () => {
try {
await syncFeaturedModels();
const response = await listLocalModels({ throwOnError: true });
if (response.data) {
setModels(response.data);
const models = await listLocalModels();
if (models) {
setModels(models);
const alreadyDownloaded = response.data.find((m) => m.status.state === 'Downloaded');
const alreadyDownloaded = models.find((m) => m.status.state === 'Downloaded');
if (alreadyDownloaded) {
setSelectedModelId(alreadyDownloaded.id);
} else {
const recommended = response.data.find((m: LocalModelResponse) => m.recommended);
const recommended = models.find((m: LocalModelResponse) => m.recommended);
if (recommended) setSelectedModelId(recommended.id);
}
}
@@ -165,7 +163,7 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
}
try {
await downloadHfModel({ body: { spec: model.id }, throwOnError: true });
await downloadHfModel({ spec: model.id });
} catch (error) {
console.error('Failed to start download:', error);
setErrorMessage(intl.formatMessage(i18n.failedToStartDownload));
@@ -176,24 +174,27 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
pollRef.current = setInterval(async () => {
try {
const response = await getLocalModelDownloadProgress({
path: { model_id: modelId },
throwOnError: true,
});
if (response.data) {
setDownloadProgress(response.data);
if (response.data.status === 'completed') {
cleanup();
finishSetup(modelId);
} else if (response.data.status === 'failed') {
cleanup();
setErrorMessage(response.data.error || 'Download failed.');
trackOnboardingSetupFailed(LOCAL_PROVIDER, response.data.error || 'download_failed');
setPhase('error');
} else if (response.data.status === 'cancelled') {
cleanup();
setPhase('select');
}
const progress = await getLocalModelDownloadProgress(modelId);
if (!progress) {
cleanup();
setErrorMessage(intl.formatMessage(i18n.lostConnection));
trackOnboardingSetupFailed(LOCAL_PROVIDER, 'progress_missing');
setPhase('error');
return;
}
setDownloadProgress(progress);
if (progress.status === 'completed') {
cleanup();
finishSetup(modelId);
} else if (progress.status === 'failed') {
cleanup();
setErrorMessage(progress.error || 'Download failed.');
trackOnboardingSetupFailed(LOCAL_PROVIDER, progress.error || 'download_failed');
setPhase('error');
} else if (progress.status === 'cancelled') {
cleanup();
setPhase('select');
}
} catch {
cleanup();
@@ -208,7 +209,7 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
if (phase === 'downloading' && selectedModelId) {
cleanup();
try {
await cancelLocalModelDownload({ path: { model_id: selectedModelId } });
await cancelLocalModelDownload(selectedModelId);
} catch {
// best-effort
}
@@ -296,7 +297,7 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
)}
</div>
<p className="text-text-muted text-xs mt-1">
{formatSize(recommended.size_bytes)}
{formatSize(recommended.sizeBytes)}
</p>
</div>
</div>
@@ -350,7 +351,7 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
{model.id}
</span>
<span className="text-xs text-text-muted">
{formatSize(model.size_bytes)}
{formatSize(model.sizeBytes)}
</span>
{model.status.state === 'Downloaded' && (
<span className="text-xs bg-green-600 text-white px-2 py-0.5 rounded-full">
@@ -375,7 +376,7 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
{selectedModel?.status.state === 'Downloaded'
? intl.formatMessage(i18n.useModel, { modelId: selectedModel.id })
: selectedModel
? intl.formatMessage(i18n.downloadModel, { modelId: selectedModel.id, size: formatSize(selectedModel.size_bytes) })
? intl.formatMessage(i18n.downloadModel, { modelId: selectedModel.id, size: formatSize(selectedModel.sizeBytes) })
: intl.formatMessage(i18n.selectModel)}
</button>
@@ -394,30 +395,30 @@ export default function LocalModelPicker({ onConfigured }: LocalModelPickerProps
<div className="w-full bg-background-subtle rounded-full h-2 overflow-hidden">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-500 ease-out"
style={{ width: `${downloadProgress.progress_percent}%` }}
style={{ width: `${downloadProgress.progressPercent}%` }}
/>
</div>
<div className="flex justify-between text-xs text-text-muted">
<span>
{formatBytes(downloadProgress.bytes_downloaded)} of{' '}
{formatBytes(downloadProgress.total_bytes)}
{formatBytes(downloadProgress.bytesDownloaded)} of{' '}
{formatBytes(downloadProgress.totalBytes)}
</span>
<span>{downloadProgress.progress_percent.toFixed(0)}%</span>
<span>{downloadProgress.progressPercent.toFixed(0)}%</span>
</div>
<div className="flex justify-between text-xs text-text-muted">
{downloadProgress.speed_bps ? (
<span>{formatBytes(downloadProgress.speed_bps)}/s</span>
{downloadProgress.speedBps ? (
<span>{formatBytes(downloadProgress.speedBps)}/s</span>
) : (
<span />
)}
{downloadProgress.eta_seconds != null && downloadProgress.eta_seconds > 0 && (
{downloadProgress.etaSeconds != null && downloadProgress.etaSeconds > 0 && (
<span>
~
{downloadProgress.eta_seconds < 60
? `${Math.round(downloadProgress.eta_seconds)}s`
: `${Math.round(downloadProgress.eta_seconds / 60)}m`}{' '}
{downloadProgress.etaSeconds < 60
? `${Math.round(downloadProgress.etaSeconds)}s`
: `${Math.round(downloadProgress.etaSeconds / 60)}m`}{' '}
remaining
</span>
)}
@@ -17,7 +17,8 @@ import {
type DownloadModelRequest,
type HfModelInfo,
type HfModelVariant,
} from '../../../api';
type RepoVariantsResponse,
} from '../../../acp/local-inference';
import { defineMessages, useIntl } from '../../../i18n';
const i18n = defineMessages({
@@ -127,61 +128,43 @@ export const HuggingFaceModelSearch = ({
setSearching(true);
setError(null);
try {
const response = await searchHfModels({
query: { q, limit: 20 },
});
if (response.data) {
// Pre-fetch variants for all results and filter out repos with no compatible local variants
const modelsWithVariants = await Promise.all(
response.data.map(async (model) => {
try {
const [author, repo] = model.repo_id.split('/');
const filesResponse = await getRepoFiles({ path: { author, repo } });
if (filesResponse.data && filesResponse.data.variants.length > 0) {
return { model, data: filesResponse.data };
}
} catch {
// Skip repos we can't fetch
const models = await searchHfModels(q, 20);
const modelsWithVariants = await Promise.all(
models.map(async (model) => {
try {
const repoData = await getRepoFiles(model.repoId);
if (repoData.variants.length > 0) {
return { model, data: repoData };
}
return null;
})
);
const validResults = modelsWithVariants.filter(Boolean) as {
model: HfModelInfo;
data: {
variants: HfModelVariant[];
recommended_index?: number | null;
available_memory_bytes: number;
downloaded_quants: string[];
downloaded_variants: string[];
};
}[];
setResults(validResults.map((r) => r.model));
setRepoData((prev) => {
const next = { ...prev };
for (const r of validResults) {
next[r.model.repo_id] = {
variants: r.data.variants,
recommendedIndex: r.data.recommended_index ?? null,
availableMemoryBytes: r.data.available_memory_bytes,
downloadedQuants: new Set(r.data.downloaded_quants),
downloadedVariants: new Set(r.data.downloaded_variants),
};
} catch {
// Skip repos we can't fetch
}
return next;
});
return null;
})
);
if (validResults.length === 0) {
setError(intl.formatMessage(i18n.noGgufModels));
const validResults = modelsWithVariants.filter(Boolean) as {
model: HfModelInfo;
data: RepoVariantsResponse;
}[];
setResults(validResults.map((r) => r.model));
setRepoData((prev) => {
const next = { ...prev };
for (const r of validResults) {
next[r.model.repoId] = {
variants: r.data.variants,
recommendedIndex: r.data.recommendedIndex ?? null,
availableMemoryBytes: r.data.availableMemoryBytes,
downloadedQuants: new Set(r.data.downloadedQuants),
downloadedVariants: new Set(r.data.downloadedVariants),
};
}
} else {
console.error('Search response:', response);
const errMsg = response.error
? intl.formatMessage(i18n.searchError, { details: JSON.stringify(response.error) })
: intl.formatMessage(i18n.searchNoData);
setError(errMsg);
return next;
});
if (validResults.length === 0) {
setError(intl.formatMessage(i18n.noGgufModels));
}
} catch (e) {
console.error('Search failed:', e);
@@ -209,22 +192,17 @@ export const HuggingFaceModelSearch = ({
if (!repoData[repoId]?.variants.length) {
setLoadingFiles((prev) => new Set(prev).add(repoId));
try {
const [author, repo] = repoId.split('/');
const response = await getRepoFiles({
path: { author, repo },
});
if (response.data) {
setRepoData((prev) => ({
...prev,
[repoId]: {
variants: response.data!.variants,
recommendedIndex: response.data!.recommended_index ?? null,
availableMemoryBytes: response.data!.available_memory_bytes,
downloadedQuants: new Set(response.data!.downloaded_quants),
downloadedVariants: new Set(response.data!.downloaded_variants),
},
}));
}
const response = await getRepoFiles(repoId);
setRepoData((prev) => ({
...prev,
[repoId]: {
variants: response.variants,
recommendedIndex: response.recommendedIndex ?? null,
availableMemoryBytes: response.availableMemoryBytes,
downloadedQuants: new Set(response.downloadedQuants),
downloadedVariants: new Set(response.downloadedVariants),
},
}));
} catch (e) {
console.error('Failed to fetch repo files:', e);
} finally {
@@ -238,20 +216,16 @@ export const HuggingFaceModelSearch = ({
};
const startDownload = async (repoId: string, variant: HfModelVariant) => {
const downloadKey = variant.download_id;
const downloadKey = variant.downloadId;
const request: DownloadModelRequest = {
spec: repoId,
backend_id: variant.backend_id,
variant_id: variant.variant_id,
backendId: variant.backendId,
variantId: variant.variantId,
};
setDownloading((prev) => new Set(prev).add(downloadKey));
try {
const response = await downloadHfModel({
body: request,
});
if (response.data) {
onDownloadStarted(response.data, request);
}
const modelId = await downloadHfModel(request);
onDownloadStarted(modelId, request);
} catch (e) {
console.error('Download failed:', e);
} finally {
@@ -289,8 +263,8 @@ export const HuggingFaceModelSearch = ({
{results.length > 0 && (
<div className="space-y-1">
{results.map((model) => {
const isExpanded = expandedRepo === model.repo_id;
const data = repoData[model.repo_id];
const isExpanded = expandedRepo === model.repoId;
const data = repoData[model.repoId];
const variants = data?.variants || [];
const recommendedIndex = data?.recommendedIndex ?? null;
const availableMemory = data?.availableMemoryBytes ?? 0;
@@ -298,15 +272,15 @@ export const HuggingFaceModelSearch = ({
const downloadedVariants = data?.downloadedVariants ?? new Set<string>();
return (
<div key={model.repo_id} className="border border-border-subtle rounded-lg">
<div key={model.repoId} className="border border-border-subtle rounded-lg">
<button
onClick={() => toggleRepo(model.repo_id)}
onClick={() => toggleRepo(model.repoId)}
className="w-full flex items-center justify-between p-3 text-left hover:bg-background-subtle rounded-lg"
>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-text-default truncate">
{model.repo_id}
{model.repoId}
</span>
</div>
<div className="flex items-center gap-3 mt-0.5">
@@ -324,28 +298,28 @@ export const HuggingFaceModelSearch = ({
{isExpanded && (
<div className="border-t border-border-subtle px-3 pb-3 space-y-1">
{loadingFiles.has(model.repo_id) && (
{loadingFiles.has(model.repoId) && (
<div className="flex items-center gap-2 py-2 text-xs text-text-muted">
<Loader2 className="w-3 h-3 animate-spin" />
{intl.formatMessage(i18n.loadingVariants)}
</div>
)}
{variants.map((variant, idx) => {
const isStarting = downloading.has(variant.download_id);
const isStarting = downloading.has(variant.downloadId);
const isRecommended = idx === recommendedIndex;
const modelId = variant.model_id;
const modelId = variant.modelId;
const isActiveDownload = activeDownloadIds?.has(modelId) ?? false;
const isDownloaded = downloadedModelIds
? downloadedModelIds.has(modelId)
: downloadedVariants.has(modelId) ||
downloadedQuants.has(variant.variant_id);
downloadedQuants.has(variant.variantId);
const tooLarge =
availableMemory > 0 && variant.size_bytes > availableMemory * 0.85;
availableMemory > 0 && variant.sizeBytes > availableMemory * 0.85;
const isSupported = variant.supported ?? true;
return (
<div
key={variant.download_id}
key={variant.downloadId}
className={`flex items-center justify-between py-2 px-2 rounded ${
isDownloaded
? 'bg-green-500/5 border border-green-500/20'
@@ -363,7 +337,7 @@ export const HuggingFaceModelSearch = ({
{variant.label}
</span>
<span className="text-xs text-text-muted">
{formatBytes(variant.size_bytes)}
{formatBytes(variant.sizeBytes)}
</span>
{isRecommended && !isDownloaded && (
<span className="inline-flex items-center gap-1 text-xs bg-blue-500 text-white px-1.5 py-0.5 rounded">
@@ -375,17 +349,17 @@ export const HuggingFaceModelSearch = ({
{variant.description && (
<span className="text-xs text-text-muted">{variant.description}</span>
)}
{!isSupported && variant.unsupported_reason && (
{!isSupported && variant.unsupportedReason && (
<span className="inline-flex items-center gap-1 text-xs text-amber-500">
<AlertTriangle className="w-3 h-3" />
{variant.unsupported_reason}
{variant.unsupportedReason}
</span>
)}
{tooLarge && !isDownloaded && (
<span className="inline-flex items-center gap-1 text-xs text-amber-500">
<AlertTriangle className="w-3 h-3" />
{intl.formatMessage(i18n.tooLarge, {
size: formatBytes(variant.size_bytes),
size: formatBytes(variant.sizeBytes),
available: formatBytes(availableMemory),
})}
</span>
@@ -406,7 +380,7 @@ export const HuggingFaceModelSearch = ({
variant="outline"
size="sm"
disabled={isStarting || !isSupported}
onClick={() => startDownload(model.repo_id, variant)}
onClick={() => startDownload(model.repoId, variant)}
>
{isStarting ? (
<Loader2 className="w-3 h-3 animate-spin" />
@@ -14,7 +14,6 @@ import { useModelAndProvider } from '../../ModelAndProviderContext';
import { defineMessages, useIntl } from '../../../i18n';
import {
listLocalModels,
syncFeaturedModels,
downloadHfModel,
getLocalModelDownloadProgress,
cancelLocalModelDownload,
@@ -22,7 +21,7 @@ import {
type DownloadProgress,
type DownloadModelRequest,
type LocalModelResponse,
} from '../../../api';
} from '../../../acp/local-inference';
import { HuggingFaceModelSearch } from './HuggingFaceModelSearch';
import { ModelSettingsPanel } from './ModelSettingsPanel';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog';
@@ -133,9 +132,9 @@ const VisionBadge = ({
model: LocalModelResponse;
intl: ReturnType<typeof useIntl>;
}) => {
if (!model.vision_capable) return null;
if (!model.visionCapable) return null;
const mmproj = model.mmproj_status;
const mmproj = model.mmprojStatus;
const isDownloaded = mmproj?.state === 'Downloaded';
const isDownloading = mmproj?.state === 'Downloading';
@@ -150,7 +149,7 @@ const VisionBadge = ({
if (isDownloading) {
const percent =
mmproj && 'progress_percent' in mmproj ? Math.round(mmproj.progress_percent) : null;
mmproj && mmproj.progressPercent != null ? Math.round(mmproj.progressPercent) : null;
return (
<span className="inline-flex items-center gap-1 text-xs text-yellow-400 bg-yellow-500/10 px-2 py-0.5 rounded">
<Eye className="w-3 h-3" />
@@ -191,12 +190,11 @@ export const LocalInferenceSettings = () => {
const loadModels = useCallback(async (): Promise<LocalModelResponse[] | undefined> => {
try {
await syncFeaturedModels();
const response = await listLocalModels();
if (response.data) {
setModels(response.data);
const models = await listLocalModels();
if (models) {
setModels(models);
const downloadedIds = new Set(
response.data
models
.filter((model) => model.status.state === 'Downloaded')
.map((model) => model.id)
);
@@ -212,13 +210,13 @@ export const LocalInferenceSettings = () => {
return next;
});
}
response.data.forEach((model) => {
models.forEach((model) => {
if (model.status.state === 'Downloading') {
pollDownloadProgress(model.id);
}
});
return response.data;
return models;
}
} catch (error) {
console.error('Failed to load models:', error);
@@ -235,7 +233,7 @@ export const LocalInferenceSettings = () => {
// Poll model list while any vision encoder is downloading
useEffect(() => {
const hasDownloadingMmproj = models.some(
(m) => m.vision_capable && m.mmproj_status?.state === 'Downloading'
(m) => m.visionCapable && m.mmprojStatus?.state === 'Downloading'
);
if (!hasDownloadingMmproj) return;
@@ -259,7 +257,7 @@ export const LocalInferenceSettings = () => {
if (!model) return;
const request = { spec: model.id };
try {
await downloadHfModel({ body: request });
await downloadHfModel(request);
setDownloadRequests((prev) => new Map(prev).set(modelId, request));
pollDownloadProgress(modelId);
scrollToDownloads();
@@ -285,9 +283,8 @@ export const LocalInferenceSettings = () => {
const interval = setInterval(async () => {
try {
const response = await getLocalModelDownloadProgress({ path: { model_id: modelId } });
if (response.data) {
const progress = response.data;
const progress = await getLocalModelDownloadProgress(modelId);
if (progress) {
setDownloads((prev) => new Map(prev).set(modelId, progress));
if (progress.status === 'completed') {
@@ -314,7 +311,7 @@ export const LocalInferenceSettings = () => {
const cancelDownload = async (modelId: string) => {
try {
await cancelLocalModelDownload({ path: { model_id: modelId } });
await cancelLocalModelDownload(modelId);
setDownloads((prev) => {
const next = new Map(prev);
const progress = next.get(modelId);
@@ -346,8 +343,7 @@ export const LocalInferenceSettings = () => {
const request = downloadRequests.get(modelId) ?? { spec: modelId };
dismissDownload(modelId);
try {
const response = await downloadHfModel({ body: request });
const nextModelId = response.data ?? modelId;
const nextModelId = await downloadHfModel(request);
setDownloadRequests((prev) => new Map(prev).set(nextModelId, request));
pollDownloadProgress(nextModelId);
scrollToDownloads();
@@ -359,7 +355,7 @@ export const LocalInferenceSettings = () => {
const handleDeleteModel = async (modelId: string) => {
if (!window.confirm(intl.formatMessage(i18n.deleteConfirm))) return;
try {
await deleteLocalModel({ path: { model_id: modelId } });
await deleteLocalModel(modelId);
const updatedModels = await loadModels();
if (selectedModelId === modelId && updatedModels) {
@@ -442,30 +438,30 @@ export const LocalInferenceSettings = () => {
<div className="w-full bg-gray-700 rounded-full h-2">
<div
className="bg-blue-500 h-2 rounded-full transition-all duration-300"
style={{ width: `${progress.progress_percent}%` }}
style={{ width: `${progress.progressPercent}%` }}
/>
</div>
<div className="flex justify-between text-xs text-text-muted">
<span>
{intl.formatMessage(i18n.downloadProgress, {
downloaded: formatBytes(progress.bytes_downloaded),
total: formatBytes(progress.total_bytes),
percent: progress.progress_percent.toFixed(0),
downloaded: formatBytes(progress.bytesDownloaded),
total: formatBytes(progress.totalBytes),
percent: progress.progressPercent.toFixed(0),
})}
</span>
<span className="flex gap-2">
{progress.eta_seconds != null && progress.eta_seconds > 0 && (
{progress.etaSeconds != null && progress.etaSeconds > 0 && (
<span>
{intl.formatMessage(i18n.remaining, {
time:
progress.eta_seconds < 60
? `${Math.round(progress.eta_seconds)}s`
: `${Math.round(progress.eta_seconds / 60)}m`,
progress.etaSeconds < 60
? `${Math.round(progress.etaSeconds)}s`
: `${Math.round(progress.etaSeconds / 60)}m`,
})}
</span>
)}
{progress.speed_bps != null && progress.speed_bps > 0 && (
<span>{formatBytes(progress.speed_bps)}/s</span>
{progress.speedBps != null && progress.speedBps > 0 && (
<span>{formatBytes(progress.speedBps)}/s</span>
)}
</span>
</div>
@@ -540,7 +536,7 @@ export const LocalInferenceSettings = () => {
/>
<span className="text-sm font-medium text-text-default">{model.id}</span>
<span className="text-xs text-text-muted">
{formatBytes(model.size_bytes)}
{formatBytes(model.sizeBytes)}
</span>
{model.recommended && (
<span className="text-xs bg-blue-500 text-white px-2 py-0.5 rounded">
@@ -592,7 +588,7 @@ export const LocalInferenceSettings = () => {
<div className="flex items-center gap-2 flex-wrap">
<h4 className="text-sm font-medium text-text-default">{model.id}</h4>
<span className="text-xs text-text-muted">
{formatBytes(model.size_bytes)}
{formatBytes(model.sizeBytes)}
</span>
{model.recommended && (
<span className="text-xs bg-blue-500 text-white px-2 py-0.5 rounded">
@@ -10,7 +10,7 @@ import {
type ModelSettings,
type SamplingConfig,
type ToolCallingMode,
} from '../../../api';
} from '../../../acp/local-inference';
import { defineMessages, useIntl } from '../../../i18n';
const i18n = defineMessages({
@@ -221,27 +221,33 @@ const i18n = defineMessages({
});
const DEFAULT_SETTINGS: ModelSettings = {
context_size: null,
max_output_tokens: null,
backendId: null,
contextSize: null,
maxOutputTokens: null,
draftModel: null,
sampling: {
type: 'Temperature',
temperature: 0.8,
top_k: 40,
top_p: 0.95,
min_p: 0.05,
topK: 40,
topP: 0.95,
minP: 0.05,
seed: null,
},
repeat_penalty: 1.0,
repeat_last_n: 64,
frequency_penalty: 0.0,
presence_penalty: 0.0,
n_batch: null,
n_gpu_layers: null,
use_mlock: false,
flash_attention: null,
n_threads: null,
tool_calling: 'auto',
chat_template: { type: 'embedded' },
repeatPenalty: 1.0,
repeatLastN: 64,
frequencyPenalty: 0.0,
presencePenalty: 0.0,
nBatch: null,
nGpuLayers: null,
useMlock: false,
flashAttention: null,
nThreads: null,
toolCalling: 'auto',
chatTemplate: { type: 'embedded' },
enableThinking: true,
visionCapable: false,
imageTokenEstimate: 256,
mmprojSizeBytes: 0,
};
type SamplingType = SamplingConfig['type'];
@@ -390,17 +396,17 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
const load = useCallback(async () => {
try {
const [settingsResult, builtinsResult] = await Promise.allSettled([
getModelSettings({ path: { model_id: modelId } }),
getModelSettings(modelId),
listBuiltinChatTemplates(),
]);
if (builtinsResult.status === 'fulfilled' && builtinsResult.value.data?.length) {
setBuiltinTemplateOptions(builtinsResult.value.data);
if (builtinsResult.status === 'fulfilled' && builtinsResult.value.length) {
setBuiltinTemplateOptions(builtinsResult.value);
}
if (settingsResult.status === 'fulfilled' && settingsResult.value.data) {
if (settingsResult.status === 'fulfilled') {
setSettings({
...settingsResult.value.data,
tool_calling: settingsResult.value.data.tool_calling ?? 'auto',
chat_template: settingsResult.value.data.chat_template ?? { type: 'embedded' },
...settingsResult.value,
toolCalling: settingsResult.value.toolCalling ?? 'auto',
chatTemplate: settingsResult.value.chatTemplate ?? { type: 'embedded' },
});
}
} catch {
@@ -415,7 +421,7 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
}, [load]);
useEffect(() => {
const chatTemplate = settings.chat_template;
const chatTemplate = settings.chatTemplate;
if (chatTemplate?.type === 'custom_inline') {
setChatTemplateDraft(chatTemplate.template ?? '');
} else {
@@ -424,13 +430,13 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
if (chatTemplate?.type === 'builtin') {
setBuiltinTemplateDraft(chatTemplate.name ?? 'chatml');
}
}, [settings.chat_template]);
}, [settings.chatTemplate]);
const save = async (updated: ModelSettings) => {
setSettings(updated);
setSaving(true);
try {
await updateModelSettings({ path: { model_id: modelId }, body: updated });
await updateModelSettings(modelId, updated);
} catch (e) {
console.error('Failed to save settings:', e);
} finally {
@@ -445,7 +451,7 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
};
const samplingType: SamplingType = settings.sampling?.type ?? 'Temperature';
const chatTemplate = settings.chat_template ?? { type: 'embedded' };
const chatTemplate = settings.chatTemplate ?? { type: 'embedded' };
const chatTemplateMode: ChatTemplateMode =
chatTemplate.type === 'custom_inline'
? 'custom_inline'
@@ -458,23 +464,26 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
if (mode === 'custom_inline') {
next = { type: 'custom_inline', template: chatTemplateDraft };
} else if (mode === 'builtin') {
next = { type: 'builtin', name: builtinTemplateDraft.trim() || builtinTemplateOptions[0] || 'chatml' };
next = {
type: 'builtin',
name: builtinTemplateDraft.trim() || builtinTemplateOptions[0] || 'chatml',
};
} else {
next = { type: 'embedded' };
}
updateField('chat_template', next);
updateField('chatTemplate', next);
};
const setBuiltinTemplateName = (name: string) => {
setBuiltinTemplateDraft(name);
if (chatTemplateMode === 'builtin') {
updateField('chat_template', { type: 'builtin', name });
updateField('chatTemplate', { type: 'builtin', name });
}
};
const saveChatTemplateDraft = () => {
if (chatTemplateMode === 'custom_inline') {
updateField('chat_template', { type: 'custom_inline', template: chatTemplateDraft });
updateField('chatTemplate', { type: 'custom_inline', template: chatTemplateDraft });
}
};
@@ -488,9 +497,9 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
sampling = {
type: 'Temperature',
temperature: 0.8,
top_k: 40,
top_p: 0.95,
min_p: 0.05,
topK: 40,
topP: 0.95,
minP: 0.05,
seed: null,
};
}
@@ -526,8 +535,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.contextSize)}
description={intl.formatMessage(i18n.contextSizeDescription)}
value={settings.context_size}
onChange={(v) => updateField('context_size', v)}
value={settings.contextSize}
onChange={(v) => updateField('contextSize', v)}
placeholder="Auto"
min={0}
allowNull
@@ -535,8 +544,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.maxOutputTokens)}
description={intl.formatMessage(i18n.maxOutputTokensDescription)}
value={settings.max_output_tokens}
onChange={(v) => updateField('max_output_tokens', v)}
value={settings.maxOutputTokens}
onChange={(v) => updateField('maxOutputTokens', v)}
placeholder="No limit"
min={1}
allowNull
@@ -569,22 +578,22 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
/>
<NumberField
label={intl.formatMessage(i18n.topK)}
value={settings.sampling.top_k}
onChange={(v) => updateSampling({ top_k: v ?? 40 })}
value={settings.sampling.topK}
onChange={(v) => updateSampling({ topK: v ?? 40 })}
min={0}
/>
<NumberField
label={intl.formatMessage(i18n.topP)}
value={settings.sampling.top_p}
onChange={(v) => updateSampling({ top_p: v ?? 0.95 })}
value={settings.sampling.topP}
onChange={(v) => updateSampling({ topP: v ?? 0.95 })}
min={0}
max={1}
step={0.01}
/>
<NumberField
label={intl.formatMessage(i18n.minP)}
value={settings.sampling.min_p}
onChange={(v) => updateSampling({ min_p: v ?? 0.05 })}
value={settings.sampling.minP}
onChange={(v) => updateSampling({ minP: v ?? 0.05 })}
min={0}
max={1}
step={0.01}
@@ -636,23 +645,23 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.repeatPenalty)}
description={intl.formatMessage(i18n.repeatPenaltyDescription)}
value={settings.repeat_penalty}
onChange={(v) => updateField('repeat_penalty', v ?? 1.0)}
value={settings.repeatPenalty}
onChange={(v) => updateField('repeatPenalty', v ?? 1.0)}
min={0}
step={0.05}
/>
<NumberField
label={intl.formatMessage(i18n.repeatWindow)}
description={intl.formatMessage(i18n.repeatWindowDescription)}
value={settings.repeat_last_n}
onChange={(v) => updateField('repeat_last_n', v ?? 64)}
value={settings.repeatLastN}
onChange={(v) => updateField('repeatLastN', v ?? 64)}
min={0}
/>
<NumberField
label={intl.formatMessage(i18n.frequencyPenalty)}
description={intl.formatMessage(i18n.frequencyPenaltyDescription)}
value={settings.frequency_penalty}
onChange={(v) => updateField('frequency_penalty', v ?? 0.0)}
value={settings.frequencyPenalty}
onChange={(v) => updateField('frequencyPenalty', v ?? 0.0)}
min={0}
max={2}
step={0.05}
@@ -660,8 +669,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.presencePenalty)}
description={intl.formatMessage(i18n.presencePenaltyDescription)}
value={settings.presence_penalty}
onChange={(v) => updateField('presence_penalty', v ?? 0.0)}
value={settings.presencePenalty}
onChange={(v) => updateField('presencePenalty', v ?? 0.0)}
min={0}
max={2}
step={0.05}
@@ -676,8 +685,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.batchSize)}
description={intl.formatMessage(i18n.batchSizeDescription)}
value={settings.n_batch}
onChange={(v) => updateField('n_batch', v)}
value={settings.nBatch}
onChange={(v) => updateField('nBatch', v)}
placeholder="Auto"
min={1}
allowNull
@@ -685,8 +694,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.gpuLayers)}
description={intl.formatMessage(i18n.gpuLayersDescription)}
value={settings.n_gpu_layers}
onChange={(v) => updateField('n_gpu_layers', v)}
value={settings.nGpuLayers}
onChange={(v) => updateField('nGpuLayers', v)}
placeholder="All"
min={0}
allowNull
@@ -694,8 +703,8 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<NumberField
label={intl.formatMessage(i18n.threads)}
description={intl.formatMessage(i18n.threadsDescription)}
value={settings.n_threads}
onChange={(v) => updateField('n_threads', v)}
value={settings.nThreads}
onChange={(v) => updateField('nThreads', v)}
placeholder="Auto"
min={1}
allowNull
@@ -704,16 +713,16 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
<ToggleField
label={intl.formatMessage(i18n.lockModelInRam)}
description={intl.formatMessage(i18n.lockModelInRamDescription)}
value={settings.use_mlock ?? false}
onChange={(v) => updateField('use_mlock', v)}
value={settings.useMlock ?? false}
onChange={(v) => updateField('useMlock', v)}
/>
<SelectField
label={intl.formatMessage(i18n.flashAttention)}
description={intl.formatMessage(i18n.flashAttentionDescription)}
value={
settings.flash_attention === null || settings.flash_attention === undefined
settings.flashAttention === null || settings.flashAttention === undefined
? 'auto'
: settings.flash_attention
: settings.flashAttention
? 'on'
: 'off'
}
@@ -722,18 +731,18 @@ export const ModelSettingsPanel = ({ modelId }: { modelId: string }) => {
{ value: 'on', label: 'On' },
{ value: 'off', label: 'Off' },
]}
onChange={(v) => updateField('flash_attention', v === 'auto' ? null : v === 'on')}
onChange={(v) => updateField('flashAttention', v === 'auto' ? null : v === 'on')}
/>
<SelectField<ToolCallingMode>
label={intl.formatMessage(i18n.toolCalling)}
description={intl.formatMessage(i18n.toolCallingDescription)}
value={settings.tool_calling ?? 'auto'}
value={settings.toolCalling ?? 'auto'}
options={[
{ value: 'auto', label: intl.formatMessage(i18n.toolCallingAuto) },
{ value: 'force_native', label: intl.formatMessage(i18n.toolCallingForceNative) },
{ value: 'force_emulated', label: intl.formatMessage(i18n.toolCallingForceEmulated) },
]}
onChange={(v) => updateField('tool_calling', v)}
onChange={(v) => updateField('toolCalling', v)}
/>
<SelectField<ChatTemplateMode>
label={intl.formatMessage(i18n.chatTemplate)}
@@ -1,4 +1,5 @@
import { ProviderDetails, ThinkingEffort, listLocalModels } from '../../../api';
import { ProviderDetails, ThinkingEffort } from '../../../api';
import { listLocalModels } from '../../../acp/local-inference';
import { acpListProviderDetails, acpListProviderModels } from '../../../acp/providers';
import { errorMessage as getErrorMessage } from '../../../utils/conversionUtils';
@@ -56,8 +57,7 @@ export async function fetchModelsForProviders(
try {
// For local provider, use listLocalModels and filter to only downloaded models
if (p.name === 'local') {
const response = await listLocalModels();
const allModels = response.data || [];
const allModels = await listLocalModels();
const downloadedModels = allModels
.filter((m) => m.status.state === 'Downloaded')
.map((m) => ({ name: m.id, provider: p.name }) as Model);
+140
View File
@@ -109,6 +109,24 @@ import type {
ListSlashCommandsResponse_unstable,
ListSourcesRequest_unstable,
ListSourcesResponse_unstable,
LocalInferenceBuiltinChatTemplatesListRequest_unstable,
LocalInferenceBuiltinChatTemplatesListResponse_unstable,
LocalInferenceHuggingFaceRepoVariantsRequest_unstable,
LocalInferenceHuggingFaceRepoVariantsResponse_unstable,
LocalInferenceHuggingFaceSearchRequest_unstable,
LocalInferenceHuggingFaceSearchResponse_unstable,
LocalInferenceModelDeleteRequest_unstable,
LocalInferenceModelDownloadCancelRequest_unstable,
LocalInferenceModelDownloadProgressRequest_unstable,
LocalInferenceModelDownloadProgressResponse_unstable,
LocalInferenceModelDownloadRequest_unstable,
LocalInferenceModelDownloadResponse_unstable,
LocalInferenceModelSettingsReadRequest_unstable,
LocalInferenceModelSettingsReadResponse_unstable,
LocalInferenceModelSettingsUpdateRequest_unstable,
LocalInferenceModelSettingsUpdateResponse_unstable,
LocalInferenceModelsListRequest_unstable,
LocalInferenceModelsListResponse_unstable,
OnboardingImportApplyRequest_unstable,
OnboardingImportApplyResponse_unstable,
OnboardingImportScanRequest_unstable,
@@ -222,6 +240,14 @@ import {
zListSchedulesResponse_unstable,
zListSlashCommandsResponse_unstable,
zListSourcesResponse_unstable,
zLocalInferenceBuiltinChatTemplatesListResponse_unstable,
zLocalInferenceHuggingFaceRepoVariantsResponse_unstable,
zLocalInferenceHuggingFaceSearchResponse_unstable,
zLocalInferenceModelDownloadProgressResponse_unstable,
zLocalInferenceModelDownloadResponse_unstable,
zLocalInferenceModelSettingsReadResponse_unstable,
zLocalInferenceModelSettingsUpdateResponse_unstable,
zLocalInferenceModelsListResponse_unstable,
zOnboardingImportApplyResponse_unstable,
zOnboardingImportScanResponse_unstable,
zParseRecipeResponse_unstable,
@@ -1296,6 +1322,120 @@ export class GooseExtClient {
params,
);
}
async localInferenceModelsList_unstable(
params: LocalInferenceModelsListRequest_unstable,
): Promise<LocalInferenceModelsListResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/models/list",
params,
);
return zLocalInferenceModelsListResponse_unstable.parse(
raw,
) as LocalInferenceModelsListResponse_unstable;
}
async localInferenceModelsDownload_unstable(
params: LocalInferenceModelDownloadRequest_unstable,
): Promise<LocalInferenceModelDownloadResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/models/download",
params,
);
return zLocalInferenceModelDownloadResponse_unstable.parse(
raw,
) as LocalInferenceModelDownloadResponse_unstable;
}
async localInferenceModelsDownloadProgress_unstable(
params: LocalInferenceModelDownloadProgressRequest_unstable,
): Promise<LocalInferenceModelDownloadProgressResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/models/download/progress",
params,
);
return zLocalInferenceModelDownloadProgressResponse_unstable.parse(
raw,
) as LocalInferenceModelDownloadProgressResponse_unstable;
}
async localInferenceModelsDownloadCancel_unstable(
params: LocalInferenceModelDownloadCancelRequest_unstable,
): Promise<void> {
await this.conn.extMethod(
"_goose/unstable/local-inference/models/download/cancel",
params,
);
}
async localInferenceModelsDelete_unstable(
params: LocalInferenceModelDeleteRequest_unstable,
): Promise<void> {
await this.conn.extMethod(
"_goose/unstable/local-inference/models/delete",
params,
);
}
async localInferenceModelsSettingsRead_unstable(
params: LocalInferenceModelSettingsReadRequest_unstable,
): Promise<LocalInferenceModelSettingsReadResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/models/settings/read",
params,
);
return zLocalInferenceModelSettingsReadResponse_unstable.parse(
raw,
) as LocalInferenceModelSettingsReadResponse_unstable;
}
async localInferenceModelsSettingsUpdate_unstable(
params: LocalInferenceModelSettingsUpdateRequest_unstable,
): Promise<LocalInferenceModelSettingsUpdateResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/models/settings/update",
params,
);
return zLocalInferenceModelSettingsUpdateResponse_unstable.parse(
raw,
) as LocalInferenceModelSettingsUpdateResponse_unstable;
}
async localInferenceHuggingfaceSearch_unstable(
params: LocalInferenceHuggingFaceSearchRequest_unstable,
): Promise<LocalInferenceHuggingFaceSearchResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/huggingface/search",
params,
);
return zLocalInferenceHuggingFaceSearchResponse_unstable.parse(
raw,
) as LocalInferenceHuggingFaceSearchResponse_unstable;
}
async localInferenceHuggingfaceRepoVariants_unstable(
params: LocalInferenceHuggingFaceRepoVariantsRequest_unstable,
): Promise<LocalInferenceHuggingFaceRepoVariantsResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/huggingface/repo/variants",
params,
);
return zLocalInferenceHuggingFaceRepoVariantsResponse_unstable.parse(
raw,
) as LocalInferenceHuggingFaceRepoVariantsResponse_unstable;
}
async localInferenceChatTemplatesBuiltinList_unstable(
params: LocalInferenceBuiltinChatTemplatesListRequest_unstable,
): Promise<LocalInferenceBuiltinChatTemplatesListResponse_unstable> {
const raw = await this.conn.extMethod(
"_goose/unstable/local-inference/chat-templates/builtin/list",
params,
);
return zLocalInferenceBuiltinChatTemplatesListResponse_unstable.parse(
raw,
) as LocalInferenceBuiltinChatTemplatesListResponse_unstable;
}
}
export interface GooseExtNotifications {
File diff suppressed because one or more lines are too long
+201 -2
View File
@@ -2312,6 +2312,205 @@ export type DictationModelSelectRequest_unstable = {
modelId: string;
};
export type LocalInferenceModelsListRequest_unstable = {
[key: string]: unknown;
};
export type LocalInferenceModelsListResponse_unstable = {
models: Array<LocalInferenceModelDto>;
};
export type LocalInferenceModelDto = {
id: string;
repoId: string;
filename: string;
quantization: string;
sizeBytes: number;
status: LocalInferenceModelDownloadStatusDto;
recommended: boolean;
settings: LocalInferenceModelSettingsDto;
visionCapable: boolean;
mmprojStatus?: LocalInferenceModelDownloadStatusDto | null;
};
export type LocalInferenceModelDownloadStatusDto = {
state: LocalInferenceDownloadState;
progressPercent?: number | null;
bytesDownloaded?: number | null;
totalBytes?: number | null;
speedBps?: number | null;
};
export type LocalInferenceDownloadState = 'NotDownloaded' | 'Downloading' | 'Downloaded';
export type LocalInferenceModelSettingsDto = {
backendId?: string | null;
contextSize?: number | null;
maxOutputTokens?: number | null;
draftModel?: string | null;
sampling?: LocalInferenceSamplingConfig;
repeatPenalty: number;
repeatLastN: number;
frequencyPenalty: number;
presencePenalty: number;
nBatch?: number | null;
nGpuLayers?: number | null;
useMlock: boolean;
flashAttention?: boolean | null;
nThreads?: number | null;
toolCalling?: LocalInferenceToolCallingMode;
chatTemplate?: LocalInferenceChatTemplate;
enableThinking: boolean;
visionCapable: boolean;
imageTokenEstimate: number;
mmprojSizeBytes: number;
};
export type LocalInferenceSamplingConfig = {
type: 'Greedy';
} | {
temperature: number;
topK: number;
topP: number;
minP: number;
seed?: number | null;
type: 'Temperature';
} | {
tau: number;
eta: number;
seed?: number | null;
type: 'MirostatV2';
};
export type LocalInferenceToolCallingMode = 'auto' | 'force_native' | 'force_emulated';
export type LocalInferenceChatTemplate = {
type: 'embedded';
} | {
name: string;
type: 'builtin';
} | {
template: string;
type: 'custom_inline';
};
export type LocalInferenceModelDownloadRequest_unstable = {
spec: string;
backendId?: string | null;
variantId?: string | null;
};
export type LocalInferenceModelDownloadResponse_unstable = {
modelId: string;
};
export type LocalInferenceModelDownloadProgressRequest_unstable = {
modelId: string;
};
export type LocalInferenceModelDownloadProgressResponse_unstable = {
progress?: LocalInferenceDownloadProgressDto | null;
};
export type LocalInferenceDownloadProgressDto = {
modelId: string;
status: string;
bytesDownloaded: number;
totalBytes: number;
progressPercent: number;
speedBps?: number | null;
etaSeconds?: number | null;
error?: string | null;
taskExited: boolean;
};
export type LocalInferenceModelDownloadCancelRequest_unstable = {
modelId: string;
};
export type LocalInferenceModelDeleteRequest_unstable = {
modelId: string;
};
export type LocalInferenceModelSettingsReadRequest_unstable = {
modelId: string;
};
export type LocalInferenceModelSettingsReadResponse_unstable = {
settings: LocalInferenceModelSettingsDto;
};
export type LocalInferenceModelSettingsUpdateRequest_unstable = {
modelId: string;
settings: LocalInferenceModelSettingsDto;
};
export type LocalInferenceModelSettingsUpdateResponse_unstable = {
settings: LocalInferenceModelSettingsDto;
};
export type LocalInferenceHuggingFaceSearchRequest_unstable = {
query: string;
limit?: number | null;
};
export type LocalInferenceHuggingFaceSearchResponse_unstable = {
models: Array<LocalInferenceHfModelInfoDto>;
};
export type LocalInferenceHfModelInfoDto = {
repoId: string;
author: string;
modelName: string;
downloads: number;
ggufFiles?: Array<LocalInferenceHfGgufFileDto>;
variants?: Array<LocalInferenceHfModelVariantDto>;
};
export type LocalInferenceHfGgufFileDto = {
filename: string;
sizeBytes: number;
quantization: string;
downloadUrl: string;
};
export type LocalInferenceHfModelVariantDto = {
variantId: string;
label: string;
backendId: string;
format: string;
modelId: string;
downloadId: string;
sizeBytes: number;
filename?: string | null;
downloadUrl?: string | null;
description: string;
qualityRank: number;
sharded: boolean;
supported: boolean;
unsupportedReason?: string | null;
};
export type LocalInferenceHuggingFaceRepoVariantsRequest_unstable = {
repoId: string;
};
export type LocalInferenceHuggingFaceRepoVariantsResponse_unstable = {
variants: Array<LocalInferenceHfModelVariantDto>;
recommendedIndex?: number | null;
availableMemoryBytes: number;
downloadedQuants: Array<string>;
downloadedVariants: Array<string>;
};
export type LocalInferenceBuiltinChatTemplatesListRequest_unstable = {
[key: string]: unknown;
};
export type LocalInferenceBuiltinChatTemplatesListResponse_unstable = {
templates: Array<string>;
};
/**
* Goose-custom session update notification — a parallel to ACP's
* `session/update` carrying goose-specific update variants.
@@ -2379,14 +2578,14 @@ export type RecipeParamsAction = 'submit' | 'cancel';
export type ExtRequest = {
id: string;
method: string;
params?: AddSessionExtensionRequest_unstable | RemoveSessionExtensionRequest_unstable | GetToolsRequest_unstable | SetToolPermissionsRequest_unstable | GooseToolCallRequest_unstable | ReadResourceRequest_unstable | AppsListRequest_unstable | AppsExportRequest_unstable | AppsImportRequest_unstable | UpdateWorkingDirRequest_unstable | SetSessionSystemPromptRequest_unstable | SteerSessionRequest_unstable | DiagnosticsGetRequest_unstable | ListPromptsRequest_unstable | GetPromptRequest_unstable | SavePromptRequest_unstable | ResetPromptRequest_unstable | DeleteSessionRequest | GetConfigExtensionsRequest_unstable | GetAvailableExtensionsRequest_unstable | AddConfigExtensionRequest_unstable | RemoveConfigExtensionRequest_unstable | SetConfigExtensionEnabledRequest_unstable | GetSessionExtensionsRequest_unstable | ListProvidersRequest_unstable | ProviderSupportedModelsListRequest_unstable | ProviderCatalogListRequest_unstable | ProviderSetupCatalogListRequest_unstable | ProviderCatalogTemplateRequest_unstable | CustomProviderCreateRequest_unstable | CustomProviderReadRequest_unstable | CustomProviderUpdateRequest_unstable | CustomProviderDeleteRequest_unstable | RefreshProviderInventoryRequest_unstable | ProviderConfigReadRequest_unstable | ProviderConfigStatusRequest_unstable | ProviderConfigSaveRequest_unstable | ProviderConfigDeleteRequest_unstable | ProviderConfigAuthenticateRequest_unstable | ProviderSecretsListRequest_unstable | ProviderSecretDeleteRequest_unstable | CanonicalModelInfoRequest_unstable | PreferencesReadRequest_unstable | PreferencesSaveRequest_unstable | PreferencesRemoveRequest_unstable | ConfigReadRequest_unstable | ConfigUpsertRequest_unstable | ConfigRemoveRequest_unstable | ConfigReadAllRequest_unstable | DefaultsReadRequest_unstable | DefaultsSaveRequest_unstable | DefaultsClearRequest_unstable | OnboardingImportScanRequest_unstable | OnboardingImportApplyRequest_unstable | ExportSessionRequest_unstable | ImportSessionRequest_unstable | ShareSessionNostrRequest_unstable | EncodeRecipeRequest_unstable | DecodeRecipeRequest_unstable | ScanRecipeRequest_unstable | ListRecipesRequest_unstable | DeleteRecipeRequest_unstable | ScheduleRecipeRequest_unstable | SetRecipeSlashCommandRequest_unstable | SaveRecipeRequest_unstable | ParseRecipeRequest_unstable | RecipeToYamlRequest_unstable | ListSchedulesRequest_unstable | ListScheduleSessionsRequest_unstable | CreateScheduleRequest_unstable | DeleteScheduleRequest_unstable | PauseScheduleRequest_unstable | UnpauseScheduleRequest_unstable | UpdateScheduleRequest_unstable | RunScheduleNowRequest_unstable | KillRunningJobRequest_unstable | InspectRunningJobRequest_unstable | GetSessionInfoRequest_unstable | TruncateSessionConversationRequest_unstable | UpdateSessionProjectRequest_unstable | RenameSessionRequest_unstable | ArchiveSessionRequest_unstable | UnarchiveSessionRequest_unstable | CreateSourceRequest_unstable | ListSourcesRequest_unstable | ListAgentMentionsRequest_unstable | ListSlashCommandsRequest_unstable | UpdateSourceRequest_unstable | DeleteSourceRequest_unstable | ExportSourceRequest_unstable | ImportSourcesRequest_unstable | DictationTranscribeRequest_unstable | DictationConfigRequest_unstable | DictationSecretSaveRequest_unstable | DictationSecretDeleteRequest_unstable | DictationModelsListRequest_unstable | DictationModelDownloadRequest_unstable | DictationModelDownloadProgressRequest_unstable | DictationModelCancelRequest_unstable | DictationModelDeleteRequest_unstable | DictationModelSelectRequest_unstable | {
params?: AddSessionExtensionRequest_unstable | RemoveSessionExtensionRequest_unstable | GetToolsRequest_unstable | SetToolPermissionsRequest_unstable | GooseToolCallRequest_unstable | ReadResourceRequest_unstable | AppsListRequest_unstable | AppsExportRequest_unstable | AppsImportRequest_unstable | UpdateWorkingDirRequest_unstable | SetSessionSystemPromptRequest_unstable | SteerSessionRequest_unstable | DiagnosticsGetRequest_unstable | ListPromptsRequest_unstable | GetPromptRequest_unstable | SavePromptRequest_unstable | ResetPromptRequest_unstable | DeleteSessionRequest | GetConfigExtensionsRequest_unstable | GetAvailableExtensionsRequest_unstable | AddConfigExtensionRequest_unstable | RemoveConfigExtensionRequest_unstable | SetConfigExtensionEnabledRequest_unstable | GetSessionExtensionsRequest_unstable | ListProvidersRequest_unstable | ProviderSupportedModelsListRequest_unstable | ProviderCatalogListRequest_unstable | ProviderSetupCatalogListRequest_unstable | ProviderCatalogTemplateRequest_unstable | CustomProviderCreateRequest_unstable | CustomProviderReadRequest_unstable | CustomProviderUpdateRequest_unstable | CustomProviderDeleteRequest_unstable | RefreshProviderInventoryRequest_unstable | ProviderConfigReadRequest_unstable | ProviderConfigStatusRequest_unstable | ProviderConfigSaveRequest_unstable | ProviderConfigDeleteRequest_unstable | ProviderConfigAuthenticateRequest_unstable | ProviderSecretsListRequest_unstable | ProviderSecretDeleteRequest_unstable | CanonicalModelInfoRequest_unstable | PreferencesReadRequest_unstable | PreferencesSaveRequest_unstable | PreferencesRemoveRequest_unstable | ConfigReadRequest_unstable | ConfigUpsertRequest_unstable | ConfigRemoveRequest_unstable | ConfigReadAllRequest_unstable | DefaultsReadRequest_unstable | DefaultsSaveRequest_unstable | DefaultsClearRequest_unstable | OnboardingImportScanRequest_unstable | OnboardingImportApplyRequest_unstable | ExportSessionRequest_unstable | ImportSessionRequest_unstable | ShareSessionNostrRequest_unstable | EncodeRecipeRequest_unstable | DecodeRecipeRequest_unstable | ScanRecipeRequest_unstable | ListRecipesRequest_unstable | DeleteRecipeRequest_unstable | ScheduleRecipeRequest_unstable | SetRecipeSlashCommandRequest_unstable | SaveRecipeRequest_unstable | ParseRecipeRequest_unstable | RecipeToYamlRequest_unstable | ListSchedulesRequest_unstable | ListScheduleSessionsRequest_unstable | CreateScheduleRequest_unstable | DeleteScheduleRequest_unstable | PauseScheduleRequest_unstable | UnpauseScheduleRequest_unstable | UpdateScheduleRequest_unstable | RunScheduleNowRequest_unstable | KillRunningJobRequest_unstable | InspectRunningJobRequest_unstable | GetSessionInfoRequest_unstable | TruncateSessionConversationRequest_unstable | UpdateSessionProjectRequest_unstable | RenameSessionRequest_unstable | ArchiveSessionRequest_unstable | UnarchiveSessionRequest_unstable | CreateSourceRequest_unstable | ListSourcesRequest_unstable | ListAgentMentionsRequest_unstable | ListSlashCommandsRequest_unstable | UpdateSourceRequest_unstable | DeleteSourceRequest_unstable | ExportSourceRequest_unstable | ImportSourcesRequest_unstable | DictationTranscribeRequest_unstable | DictationConfigRequest_unstable | DictationSecretSaveRequest_unstable | DictationSecretDeleteRequest_unstable | DictationModelsListRequest_unstable | DictationModelDownloadRequest_unstable | DictationModelDownloadProgressRequest_unstable | DictationModelCancelRequest_unstable | DictationModelDeleteRequest_unstable | DictationModelSelectRequest_unstable | LocalInferenceModelsListRequest_unstable | LocalInferenceModelDownloadRequest_unstable | LocalInferenceModelDownloadProgressRequest_unstable | LocalInferenceModelDownloadCancelRequest_unstable | LocalInferenceModelDeleteRequest_unstable | LocalInferenceModelSettingsReadRequest_unstable | LocalInferenceModelSettingsUpdateRequest_unstable | LocalInferenceHuggingFaceSearchRequest_unstable | LocalInferenceHuggingFaceRepoVariantsRequest_unstable | LocalInferenceBuiltinChatTemplatesListRequest_unstable | {
[key: string]: unknown;
} | null;
};
export type ExtResponse = {
id: string;
result?: EmptyResponse | GetToolsResponse_unstable | SetToolPermissionsResponse_unstable | GooseToolCallResponse_unstable | ReadResourceResponse_unstable | AppsListResponse_unstable | AppsExportResponse_unstable | AppsImportResponse_unstable | SteerSessionResponse_unstable | DiagnosticsGetResponse_unstable | ListPromptsResponse_unstable | GetPromptResponse_unstable | PromptOperationResponse_unstable | GetConfigExtensionsResponse_unstable | GetAvailableExtensionsResponse_unstable | GetSessionExtensionsResponse_unstable | ListProvidersResponse_unstable | ProviderSupportedModelsListResponse_unstable | ProviderCatalogListResponse_unstable | ProviderSetupCatalogListResponse_unstable | ProviderCatalogTemplateResponse_unstable | CustomProviderCreateResponse_unstable | CustomProviderReadResponse_unstable | CustomProviderUpdateResponse_unstable | CustomProviderDeleteResponse_unstable | RefreshProviderInventoryResponse_unstable | ProviderConfigReadResponse_unstable | ProviderConfigStatusResponse_unstable | ProviderConfigChangeResponse_unstable | ProviderSecretsListResponse_unstable | CanonicalModelInfoResponse_unstable | PreferencesReadResponse_unstable | ConfigReadResponse_unstable | ConfigReadAllResponse_unstable | DefaultsReadResponse_unstable | OnboardingImportScanResponse_unstable | OnboardingImportApplyResponse_unstable | ExportSessionResponse_unstable | ImportSessionResponse_unstable | ShareSessionNostrResponse_unstable | EncodeRecipeResponse_unstable | DecodeRecipeResponse_unstable | ScanRecipeResponse_unstable | ListRecipesResponse_unstable | SaveRecipeResponse_unstable | ParseRecipeResponse_unstable | RecipeToYamlResponse_unstable | ListSchedulesResponse_unstable | ListScheduleSessionsResponse_unstable | CreateScheduleResponse_unstable | UpdateScheduleResponse_unstable | RunScheduleNowResponse_unstable | KillRunningJobResponse_unstable | InspectRunningJobResponse_unstable | GetSessionInfoResponse_unstable | CreateSourceResponse_unstable | ListSourcesResponse_unstable | ListAgentMentionsResponse_unstable | ListSlashCommandsResponse_unstable | UpdateSourceResponse_unstable | ExportSourceResponse_unstable | ImportSourcesResponse_unstable | DictationTranscribeResponse_unstable | DictationConfigResponse_unstable | DictationModelsListResponse_unstable | DictationModelDownloadProgressResponse_unstable | unknown;
result?: EmptyResponse | GetToolsResponse_unstable | SetToolPermissionsResponse_unstable | GooseToolCallResponse_unstable | ReadResourceResponse_unstable | AppsListResponse_unstable | AppsExportResponse_unstable | AppsImportResponse_unstable | SteerSessionResponse_unstable | DiagnosticsGetResponse_unstable | ListPromptsResponse_unstable | GetPromptResponse_unstable | PromptOperationResponse_unstable | GetConfigExtensionsResponse_unstable | GetAvailableExtensionsResponse_unstable | GetSessionExtensionsResponse_unstable | ListProvidersResponse_unstable | ProviderSupportedModelsListResponse_unstable | ProviderCatalogListResponse_unstable | ProviderSetupCatalogListResponse_unstable | ProviderCatalogTemplateResponse_unstable | CustomProviderCreateResponse_unstable | CustomProviderReadResponse_unstable | CustomProviderUpdateResponse_unstable | CustomProviderDeleteResponse_unstable | RefreshProviderInventoryResponse_unstable | ProviderConfigReadResponse_unstable | ProviderConfigStatusResponse_unstable | ProviderConfigChangeResponse_unstable | ProviderSecretsListResponse_unstable | CanonicalModelInfoResponse_unstable | PreferencesReadResponse_unstable | ConfigReadResponse_unstable | ConfigReadAllResponse_unstable | DefaultsReadResponse_unstable | OnboardingImportScanResponse_unstable | OnboardingImportApplyResponse_unstable | ExportSessionResponse_unstable | ImportSessionResponse_unstable | ShareSessionNostrResponse_unstable | EncodeRecipeResponse_unstable | DecodeRecipeResponse_unstable | ScanRecipeResponse_unstable | ListRecipesResponse_unstable | SaveRecipeResponse_unstable | ParseRecipeResponse_unstable | RecipeToYamlResponse_unstable | ListSchedulesResponse_unstable | ListScheduleSessionsResponse_unstable | CreateScheduleResponse_unstable | UpdateScheduleResponse_unstable | RunScheduleNowResponse_unstable | KillRunningJobResponse_unstable | InspectRunningJobResponse_unstable | GetSessionInfoResponse_unstable | CreateSourceResponse_unstable | ListSourcesResponse_unstable | ListAgentMentionsResponse_unstable | ListSlashCommandsResponse_unstable | UpdateSourceResponse_unstable | ExportSourceResponse_unstable | ImportSourcesResponse_unstable | DictationTranscribeResponse_unstable | DictationConfigResponse_unstable | DictationModelsListResponse_unstable | DictationModelDownloadProgressResponse_unstable | LocalInferenceModelsListResponse_unstable | LocalInferenceModelDownloadResponse_unstable | LocalInferenceModelDownloadProgressResponse_unstable | LocalInferenceModelSettingsReadResponse_unstable | LocalInferenceModelSettingsUpdateResponse_unstable | LocalInferenceHuggingFaceSearchResponse_unstable | LocalInferenceHuggingFaceRepoVariantsResponse_unstable | LocalInferenceBuiltinChatTemplatesListResponse_unstable | unknown;
} | {
error: {
code: number;
+315 -2
View File
@@ -2369,6 +2369,301 @@ export const zDictationModelSelectRequest_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceModelsListRequest_unstable = z.record(z.unknown());
export const zLocalInferenceDownloadState = z.enum([
'NotDownloaded',
'Downloading',
'Downloaded'
]);
export const zLocalInferenceModelDownloadStatusDto = z.object({
state: zLocalInferenceDownloadState,
progressPercent: z.union([
z.number(),
z.null()
]).optional(),
bytesDownloaded: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
totalBytes: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
speedBps: z.union([
z.number().int().gte(0),
z.null()
]).optional()
});
export const zLocalInferenceSamplingConfig = z.union([
z.object({
type: z.literal('Greedy')
}),
z.object({
temperature: z.number(),
topK: z.number().int(),
topP: z.number(),
minP: z.number(),
seed: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
type: z.literal('Temperature')
}),
z.object({
tau: z.number(),
eta: z.number(),
seed: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
type: z.literal('MirostatV2')
})
]);
export const zLocalInferenceToolCallingMode = z.enum([
'auto',
'force_native',
'force_emulated'
]);
export const zLocalInferenceChatTemplate = z.union([
z.object({
type: z.literal('embedded')
}),
z.object({
name: z.string(),
type: z.literal('builtin')
}),
z.object({
template: z.string(),
type: z.literal('custom_inline')
})
]);
export const zLocalInferenceModelSettingsDto = z.object({
backendId: z.union([
z.string(),
z.null()
]).optional(),
contextSize: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
maxOutputTokens: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
draftModel: z.union([
z.string(),
z.null()
]).optional(),
sampling: zLocalInferenceSamplingConfig.optional().default({
type: 'Temperature',
temperature: 0.800000011920929,
topK: 40,
topP: 0.949999988079071,
minP: 0.05000000074505806
}),
repeatPenalty: z.number(),
repeatLastN: z.number().int(),
frequencyPenalty: z.number(),
presencePenalty: z.number(),
nBatch: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
nGpuLayers: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
useMlock: z.boolean(),
flashAttention: z.union([
z.boolean(),
z.null()
]).optional(),
nThreads: z.union([
z.number().int(),
z.null()
]).optional(),
toolCalling: zLocalInferenceToolCallingMode.optional().default('auto'),
chatTemplate: zLocalInferenceChatTemplate.optional().default({ type: 'embedded' }),
enableThinking: z.boolean(),
visionCapable: z.boolean(),
imageTokenEstimate: z.number().int().gte(0),
mmprojSizeBytes: z.number().int().gte(0)
});
export const zLocalInferenceModelDto = z.object({
id: z.string(),
repoId: z.string(),
filename: z.string(),
quantization: z.string(),
sizeBytes: z.number().int().gte(0),
status: zLocalInferenceModelDownloadStatusDto,
recommended: z.boolean(),
settings: zLocalInferenceModelSettingsDto,
visionCapable: z.boolean(),
mmprojStatus: z.union([
zLocalInferenceModelDownloadStatusDto,
z.null()
]).optional()
});
export const zLocalInferenceModelsListResponse_unstable = z.object({
models: z.array(zLocalInferenceModelDto)
});
export const zLocalInferenceModelDownloadRequest_unstable = z.object({
spec: z.string(),
backendId: z.union([
z.string(),
z.null()
]).optional(),
variantId: z.union([
z.string(),
z.null()
]).optional()
});
export const zLocalInferenceModelDownloadResponse_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceModelDownloadProgressRequest_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceDownloadProgressDto = z.object({
modelId: z.string(),
status: z.string(),
bytesDownloaded: z.number().int().gte(0),
totalBytes: z.number().int().gte(0),
progressPercent: z.number(),
speedBps: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
etaSeconds: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
error: z.union([
z.string(),
z.null()
]).optional(),
taskExited: z.boolean()
});
export const zLocalInferenceModelDownloadProgressResponse_unstable = z.object({
progress: z.union([
zLocalInferenceDownloadProgressDto,
z.null()
]).optional()
});
export const zLocalInferenceModelDownloadCancelRequest_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceModelDeleteRequest_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceModelSettingsReadRequest_unstable = z.object({
modelId: z.string()
});
export const zLocalInferenceModelSettingsReadResponse_unstable = z.object({
settings: zLocalInferenceModelSettingsDto
});
export const zLocalInferenceModelSettingsUpdateRequest_unstable = z.object({
modelId: z.string(),
settings: zLocalInferenceModelSettingsDto
});
export const zLocalInferenceModelSettingsUpdateResponse_unstable = z.object({
settings: zLocalInferenceModelSettingsDto
});
export const zLocalInferenceHuggingFaceSearchRequest_unstable = z.object({
query: z.string(),
limit: z.union([
z.number().int().gte(0),
z.null()
]).optional()
});
export const zLocalInferenceHfGgufFileDto = z.object({
filename: z.string(),
sizeBytes: z.number().int().gte(0),
quantization: z.string(),
downloadUrl: z.string()
});
export const zLocalInferenceHfModelVariantDto = z.object({
variantId: z.string(),
label: z.string(),
backendId: z.string(),
format: z.string(),
modelId: z.string(),
downloadId: z.string(),
sizeBytes: z.number().int().gte(0),
filename: z.union([
z.string(),
z.null()
]).optional(),
downloadUrl: z.union([
z.string(),
z.null()
]).optional(),
description: z.string(),
qualityRank: z.number().int().gte(0).lte(255),
sharded: z.boolean(),
supported: z.boolean(),
unsupportedReason: z.union([
z.string(),
z.null()
]).optional()
});
export const zLocalInferenceHfModelInfoDto = z.object({
repoId: z.string(),
author: z.string(),
modelName: z.string(),
downloads: z.number().int().gte(0),
ggufFiles: z.array(zLocalInferenceHfGgufFileDto).optional().default([]),
variants: z.array(zLocalInferenceHfModelVariantDto).optional().default([])
});
export const zLocalInferenceHuggingFaceSearchResponse_unstable = z.object({
models: z.array(zLocalInferenceHfModelInfoDto)
});
export const zLocalInferenceHuggingFaceRepoVariantsRequest_unstable = z.object({
repoId: z.string()
});
export const zLocalInferenceHuggingFaceRepoVariantsResponse_unstable = z.object({
variants: z.array(zLocalInferenceHfModelVariantDto),
recommendedIndex: z.union([
z.number().int().gte(0),
z.null()
]).optional(),
availableMemoryBytes: z.number().int().gte(0),
downloadedQuants: z.array(z.string()),
downloadedVariants: z.array(z.string())
});
export const zLocalInferenceBuiltinChatTemplatesListRequest_unstable = z.record(z.unknown());
export const zLocalInferenceBuiltinChatTemplatesListResponse_unstable = z.object({
templates: z.array(z.string())
});
/**
* Streaming context-window usage update for a session.
*/
@@ -2545,7 +2840,17 @@ export const zExtRequest = z.object({
zDictationModelDownloadProgressRequest_unstable,
zDictationModelCancelRequest_unstable,
zDictationModelDeleteRequest_unstable,
zDictationModelSelectRequest_unstable
zDictationModelSelectRequest_unstable,
zLocalInferenceModelsListRequest_unstable,
zLocalInferenceModelDownloadRequest_unstable,
zLocalInferenceModelDownloadProgressRequest_unstable,
zLocalInferenceModelDownloadCancelRequest_unstable,
zLocalInferenceModelDeleteRequest_unstable,
zLocalInferenceModelSettingsReadRequest_unstable,
zLocalInferenceModelSettingsUpdateRequest_unstable,
zLocalInferenceHuggingFaceSearchRequest_unstable,
zLocalInferenceHuggingFaceRepoVariantsRequest_unstable,
zLocalInferenceBuiltinChatTemplatesListRequest_unstable
]),
z.union([
z.record(z.unknown()),
@@ -2624,7 +2929,15 @@ export const zExtResponse = z.union([
zDictationTranscribeResponse_unstable,
zDictationConfigResponse_unstable,
zDictationModelsListResponse_unstable,
zDictationModelDownloadProgressResponse_unstable
zDictationModelDownloadProgressResponse_unstable,
zLocalInferenceModelsListResponse_unstable,
zLocalInferenceModelDownloadResponse_unstable,
zLocalInferenceModelDownloadProgressResponse_unstable,
zLocalInferenceModelSettingsReadResponse_unstable,
zLocalInferenceModelSettingsUpdateResponse_unstable,
zLocalInferenceHuggingFaceSearchResponse_unstable,
zLocalInferenceHuggingFaceRepoVariantsResponse_unstable,
zLocalInferenceBuiltinChatTemplatesListResponse_unstable
]),
z.unknown()
]).optional()