mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-17 20:31:47 +02:00
ui : gate llama.cpp-only features by backend capabilities
Assisted-by: pi:llama.cpp/DeepSeek-V4.1-Flash
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import type { BackendPreset, BackendProtocol } from '$lib/types';
|
import type { BackendCapabilities, BackendPreset, BackendProtocol } from '$lib/types';
|
||||||
|
|
||||||
/** Version sent with the Anthropic Messages API. */
|
/** Version sent with the Anthropic Messages API. */
|
||||||
export const ANTHROPIC_API_VERSION = '2023-06-01';
|
export const ANTHROPIC_API_VERSION = '2023-06-01';
|
||||||
@@ -18,6 +18,34 @@ export const DEFAULT_BACKEND_MODELS_PATH = '/v1/models';
|
|||||||
/** Id of the built-in backend that points at the server serving this UI. */
|
/** Id of the built-in backend that points at the server serving this UI. */
|
||||||
export const LOCAL_BACKEND_ID = 'local';
|
export const LOCAL_BACKEND_ID = 'local';
|
||||||
|
|
||||||
|
/** Capabilities of a full llama.cpp server. */
|
||||||
|
const LLAMA_CPP_CAPABILITIES: BackendCapabilities = {
|
||||||
|
corsProxy: true,
|
||||||
|
loadUnload: true,
|
||||||
|
props: true,
|
||||||
|
router: true,
|
||||||
|
slots: true,
|
||||||
|
statusFeed: true,
|
||||||
|
tools: true
|
||||||
|
};
|
||||||
|
/** Capabilities of a plain OpenAI- or Anthropic-compatible endpoint. */
|
||||||
|
const COMPATIBLE_CAPABILITIES: BackendCapabilities = {
|
||||||
|
corsProxy: false,
|
||||||
|
loadUnload: false,
|
||||||
|
props: false,
|
||||||
|
router: false,
|
||||||
|
slots: false,
|
||||||
|
statusFeed: false,
|
||||||
|
tools: false
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Capabilities per backend protocol. */
|
||||||
|
export const BACKEND_CAPABILITIES: Record<BackendProtocol, BackendCapabilities> = {
|
||||||
|
anthropic: COMPATIBLE_CAPABILITIES,
|
||||||
|
'llama.cpp': LLAMA_CPP_CAPABILITIES,
|
||||||
|
openai: COMPATIBLE_CAPABILITIES
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ready-made endpoints offered when adding a backend. `custom` intentionally
|
* Ready-made endpoints offered when adding a backend. `custom` intentionally
|
||||||
* carries no URL so the user starts from an empty form.
|
* carries no URL so the user starts from an empty form.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
StreamConnectionState
|
StreamConnectionState
|
||||||
} from '$lib/enums';
|
} from '$lib/enums';
|
||||||
import { modelsStore } from '$lib/stores/models/index.svelte';
|
import { modelsStore } from '$lib/stores/models/index.svelte';
|
||||||
|
import { serverStore } from '$lib/stores/server.svelte';
|
||||||
import { settingsStore } from '$lib/stores/settings/index.svelte';
|
import { settingsStore } from '$lib/stores/settings/index.svelte';
|
||||||
import type { DatabaseMessageExtraMcpPrompt, DatabaseMessageExtraMcpResource } from '$lib/types';
|
import type { DatabaseMessageExtraMcpPrompt, DatabaseMessageExtraMcpResource } from '$lib/types';
|
||||||
import type {
|
import type {
|
||||||
@@ -87,6 +88,9 @@ export class ChatService {
|
|||||||
* @returns {Promise<boolean>} Promise that resolves to true if all slots are idle, false if any is processing
|
* @returns {Promise<boolean>} Promise that resolves to true if all slots are idle, false if any is processing
|
||||||
*/
|
*/
|
||||||
static async areAllSlotsIdle(model?: string | null, signal?: AbortSignal): Promise<boolean> {
|
static async areAllSlotsIdle(model?: string | null, signal?: AbortSignal): Promise<boolean> {
|
||||||
|
// the /slots endpoint only exists on llama.cpp servers
|
||||||
|
if (!serverStore.capabilities.slots) return true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = model ? `${API_SLOTS.LIST}?model=${encodeURIComponent(model)}` : API_SLOTS.LIST;
|
const url = model ? `${API_SLOTS.LIST}?model=${encodeURIComponent(model)}` : API_SLOTS.LIST;
|
||||||
const res = await fetch(apiUrl(url), { signal });
|
const res = await fetch(apiUrl(url), { signal });
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
* PropsService for the /props fetch.
|
* PropsService for the /props fetch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BACKEND_CAPABILITIES } from '$lib/constants';
|
||||||
import { ServerRole } from '$lib/enums';
|
import { ServerRole } from '$lib/enums';
|
||||||
import { PropsService } from '$lib/services/props.service';
|
import { PropsService } from '$lib/services/props.service';
|
||||||
|
import type { BackendCapabilities } from '$lib/types';
|
||||||
import { ApiError } from '$lib/utils';
|
import { ApiError } from '$lib/utils';
|
||||||
|
import { getBackend } from '$lib/utils/api-base';
|
||||||
|
import { getBackendCapabilities } from '$lib/utils/backend';
|
||||||
|
|
||||||
const LOADING_RETRY_INTERVAL_MS = 1000;
|
const LOADING_RETRY_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
@@ -21,6 +25,13 @@ class ServerStore {
|
|||||||
private fetchPromise: Promise<void> | null = null;
|
private fetchPromise: Promise<void> | null = null;
|
||||||
private retryTimer: ReturnType<typeof setTimeout> | null = null;
|
private retryTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
/** Features of the active backend. Defaults to full llama.cpp support. */
|
||||||
|
get capabilities(): BackendCapabilities {
|
||||||
|
const backend = getBackend();
|
||||||
|
|
||||||
|
return backend ? getBackendCapabilities(backend) : BACKEND_CAPABILITIES['llama.cpp'];
|
||||||
|
}
|
||||||
|
|
||||||
get contextSize(): number | null {
|
get contextSize(): number | null {
|
||||||
const nCtx = this.props?.default_generation_settings?.n_ctx;
|
const nCtx = this.props?.default_generation_settings?.n_ctx;
|
||||||
|
|
||||||
@@ -63,6 +74,15 @@ class ServerStore {
|
|||||||
|
|
||||||
this.clearRetryTimer();
|
this.clearRetryTimer();
|
||||||
|
|
||||||
|
// External backends expose no /props endpoint. Keep MODEL-mode defaults so
|
||||||
|
// role detection and generation defaults degrade instead of failing.
|
||||||
|
if (!this.capabilities.props) {
|
||||||
|
this.clear();
|
||||||
|
this.role = ServerRole.MODEL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!background) {
|
if (!background) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { ToolsService } from '$lib/services/tools.service';
|
|||||||
// direct imports between stores, not via the barrel, to avoid circular deps
|
// direct imports between stores, not via the barrel, to avoid circular deps
|
||||||
import { mcpStore } from '$lib/stores/mcp/index.svelte';
|
import { mcpStore } from '$lib/stores/mcp/index.svelte';
|
||||||
import { modelsStore } from '$lib/stores/models/index.svelte';
|
import { modelsStore } from '$lib/stores/models/index.svelte';
|
||||||
|
import { serverStore } from '$lib/stores/server.svelte';
|
||||||
import { settingsStore } from '$lib/stores/settings/index.svelte';
|
import { settingsStore } from '$lib/stores/settings/index.svelte';
|
||||||
import type { OpenAIToolDefinition, ToolEntry, ToolGroup } from '$lib/types';
|
import type { OpenAIToolDefinition, ToolEntry, ToolGroup } from '$lib/types';
|
||||||
import { ApiError, buildSandboxToolDefinition } from '$lib/utils';
|
import { ApiError, buildSandboxToolDefinition } from '$lib/utils';
|
||||||
@@ -232,6 +233,14 @@ class ToolsStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchServerTools(): Promise<void> {
|
async fetchServerTools(): Promise<void> {
|
||||||
|
// the /tools endpoint only exists on llama.cpp servers
|
||||||
|
if (!serverStore.capabilities.tools) {
|
||||||
|
this._serverTools = [];
|
||||||
|
this.cwdAwareTools = new SvelteSet();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._loading) return;
|
if (this._loading) return;
|
||||||
|
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
|
|||||||
Vendored
+22
@@ -10,6 +10,28 @@
|
|||||||
/** Request/response shape a backend speaks. */
|
/** Request/response shape a backend speaks. */
|
||||||
export type BackendProtocol = 'llama.cpp' | 'openai' | 'anthropic';
|
export type BackendProtocol = 'llama.cpp' | 'openai' | 'anthropic';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Features a backend supports. A llama.cpp server exposes extra endpoints on
|
||||||
|
* top of the OpenAI-compatible API; plain OpenAI- and Anthropic-compatible
|
||||||
|
* endpoints only provide chat and model listing.
|
||||||
|
*/
|
||||||
|
export interface BackendCapabilities {
|
||||||
|
/** llama-server's /cors-proxy endpoint for cross-origin MCP requests. */
|
||||||
|
corsProxy: boolean;
|
||||||
|
/** Router-mode model load/unload. */
|
||||||
|
loadUnload: boolean;
|
||||||
|
/** The /props endpoint with server role and generation defaults. */
|
||||||
|
props: boolean;
|
||||||
|
/** Multi-model router mode. */
|
||||||
|
router: boolean;
|
||||||
|
/** The /slots introspection endpoint. */
|
||||||
|
slots: boolean;
|
||||||
|
/** The /models/sse load and download progress feed. */
|
||||||
|
statusFeed: boolean;
|
||||||
|
/** The /tools listing and execution endpoint. */
|
||||||
|
tools: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/** One configured API endpoint. */
|
/** One configured API endpoint. */
|
||||||
export interface Backend {
|
export interface Backend {
|
||||||
/** Bearer token / API key used for this backend. */
|
/** Bearer token / API key used for this backend. */
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export type {
|
|||||||
} from './api';
|
} from './api';
|
||||||
|
|
||||||
// Backend types
|
// Backend types
|
||||||
export type { Backend, BackendPreset, BackendProtocol } from './backend';
|
export type { Backend, BackendCapabilities, BackendPreset, BackendProtocol } from './backend';
|
||||||
|
|
||||||
// HuggingFace types
|
// HuggingFace types
|
||||||
export type {
|
export type {
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
BACKEND_CAPABILITIES,
|
||||||
BACKEND_ID_PREFIX,
|
BACKEND_ID_PREFIX,
|
||||||
BACKEND_PROTOCOLS,
|
BACKEND_PROTOCOLS,
|
||||||
DEFAULT_BACKEND_CHAT_PATH,
|
DEFAULT_BACKEND_CHAT_PATH,
|
||||||
DEFAULT_BACKEND_MODELS_PATH,
|
DEFAULT_BACKEND_MODELS_PATH,
|
||||||
LOCAL_BACKEND_ID
|
LOCAL_BACKEND_ID
|
||||||
} from '$lib/constants';
|
} from '$lib/constants';
|
||||||
import type { Backend, BackendProtocol } from '$lib/types';
|
import type { Backend, BackendCapabilities, BackendProtocol } from '$lib/types';
|
||||||
|
|
||||||
/** Absolute chat completions URL for a backend. */
|
/** Absolute chat completions URL for a backend. */
|
||||||
export function backendChatUrl(backend: Backend): string {
|
export function backendChatUrl(backend: Backend): string {
|
||||||
@@ -25,6 +26,11 @@ export function backendModelsUrl(backend: Backend): string {
|
|||||||
return joinBackendUrl(backend.baseUrl, backend.modelsPath ?? DEFAULT_BACKEND_MODELS_PATH);
|
return joinBackendUrl(backend.baseUrl, backend.modelsPath ?? DEFAULT_BACKEND_MODELS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Features a backend supports, derived from its protocol. */
|
||||||
|
export function getBackendCapabilities(backend: Backend): BackendCapabilities {
|
||||||
|
return BACKEND_CAPABILITIES[backend.protocol] ?? BACKEND_CAPABILITIES.openai;
|
||||||
|
}
|
||||||
|
|
||||||
/** The built-in backend pointing at the server that serves this UI. */
|
/** The built-in backend pointing at the server that serves this UI. */
|
||||||
export function createLocalBackend(apiKey?: string, enabled = true): Backend {
|
export function createLocalBackend(apiKey?: string, enabled = true): Backend {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export {
|
|||||||
backendChatUrl,
|
backendChatUrl,
|
||||||
backendModelsUrl,
|
backendModelsUrl,
|
||||||
createLocalBackend,
|
createLocalBackend,
|
||||||
|
getBackendCapabilities,
|
||||||
parseBackendsSettings
|
parseBackendsSettings
|
||||||
} from './backend';
|
} from './backend';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user