mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Once a pi-ai route became a declaration rather than a catalog lookup, adding an OpenAI-compatible gateway meant knowing its model ids up front. Most such endpoints publish that list at `GET /models`, but no seam operation could ask: every one is keyed by a registered provider route, and the provider being added has no route, no stored profile, and no stored credential — the endpoint and key are values in a form. Interrogation is therefore keyed by settings namespace, which a configuration surface already holds from the configurable-provider directory. `registerModelDiscovery` offers it per namespace, `discoverModels` asks, and the request carries the draft itself. The reply is candidates, not a catalog: every field but the id is optional because most listings disclose nothing else, and adopting one is a settings write like any other. Nothing here reads or writes settings or credentials, so `settings.yaml` still decides what a route serves. `llm.discoverModels` carries the same draft over the wire. Its apiKey is the third and last payload a secret may ride, and it is never stored, logged, or echoed; every refusal folds into `model-discovery-failed`, naming the endpoint asked but never the credential offered. The pi-ai side is a plain GET for OpenAI-compatible protocols only — their listing shape is the one gateways, self-hosted servers, and the official endpoints agree on. Others say so, sending the user to hand-entry rather than reporting a guessed shape as an empty provider. The reply is read under a four-megabyte ceiling held on the bytes actually received, because the endpoint is a URL the user typed.
78 lines
3.2 KiB
TypeScript
78 lines
3.2 KiB
TypeScript
/**
|
|
* llm domain contract: host-scoped provider topology for configuration
|
|
* surfaces. `llm.providers` merges the configurable-provider directory
|
|
* (which providers CAN be configured, and where their settings live) with the
|
|
* live route registry; `llm.models` is the session-independent model catalog
|
|
* (the same groups as `session.models`, without the per-session current
|
|
* target). Both invalidate on the `host/models-changed` frame.
|
|
*/
|
|
|
|
import type { RpcRequest, RpcResponse } from './rpc.ts'
|
|
import type { ModelCatalogFailure, ModelProviderGroup } from './sessions.ts'
|
|
|
|
/** Wire view of one configurable provider. */
|
|
export interface ConfigurableProviderView {
|
|
/** Provider route key (`deepseek-official`, `openai`, …). */
|
|
provider: string
|
|
/** Human-readable name for configuration surfaces. */
|
|
displayName: string
|
|
/** Settings namespace whose section configures this provider. */
|
|
settingsNs: string
|
|
/** Path from that section's root to the provider's profile object (empty = whole section). */
|
|
settingsPath: string[]
|
|
/** Whether the route is currently registered (its models are requestable). */
|
|
active: boolean
|
|
}
|
|
|
|
/** Llm-domain unary methods (the map keys llm.* of RpcMethodMap). */
|
|
export interface LlmApi {
|
|
/**
|
|
* List every configurable provider with its live/dormant state, in
|
|
* directory declaration order. Routes registered outside the directory
|
|
* (an adapter that never declared configurability) are appended with their
|
|
* registration identity and no settings address.
|
|
*/
|
|
providers(request: RpcRequest<{}>): Promise<RpcResponse<{ providers: ConfigurableProviderView[] }>>
|
|
|
|
/**
|
|
* Host-scoped model catalog over every registered provider route: the
|
|
* settings surface's models view, needing no session. Per-provider listing
|
|
* failures ride `failures` without failing the sound groups.
|
|
*/
|
|
models(request: RpcRequest<{}>): Promise<RpcResponse<{ groups: ModelProviderGroup[]; failures: ModelCatalogFailure[] }>>
|
|
|
|
/**
|
|
* Interrogate a provider endpoint the configuration surface is still
|
|
* drafting, and return the models it advertises for the user to adopt.
|
|
*
|
|
* The payload is the draft, not a stored route: `settingsNs` selects the
|
|
* adapter family that knows how to read the listing, and the endpoint,
|
|
* protocol, and key come from the form. Nothing is written — the reply is
|
|
* candidates, and only a later `settings.mutate` decides what a route
|
|
* serves. `apiKey` is therefore accepted here but never stored, logged, or
|
|
* echoed back; a provider whose key is already stored omits it and the
|
|
* endpoint answers unauthenticated or refuses.
|
|
*/
|
|
discoverModels(
|
|
request: RpcRequest<{
|
|
settingsNs: string
|
|
baseURL: string
|
|
api?: string
|
|
apiKey?: string
|
|
}>,
|
|
signal?: AbortSignal,
|
|
): Promise<RpcResponse<{ models: DiscoveredModelView[] }>>
|
|
}
|
|
|
|
/** Wire view of one model an interrogated endpoint advertises. */
|
|
export interface DiscoveredModelView {
|
|
/** Model id the endpoint accepts. */
|
|
id: string
|
|
/** Human-readable name when the endpoint supplies one. */
|
|
name?: string
|
|
/** Maximum combined request and response context, when disclosed. */
|
|
contextWindow?: number
|
|
/** Maximum output tokens, when disclosed. */
|
|
maxTokens?: number
|
|
}
|