refactor(models): share listing cache lifecycle wiring
This commit is contained in:
1 parent
8394a200b9
commit
a36745f6ee
7 files changed
+35
-21
No files matched your search
@@ -5,7 +5,7 @@ import { ACCOUNT_QUOTA_EXCEEDED_CODE, APP_IDENTITY, LlmError, QUOTA_EXCEEDED_COD
|
||||
import type {} from '@deepseek-ai/dsh-deepseek-account'
|
||||
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'
|
||||
import {
|
||||
catalogModelInfo, ModelListingCache, refuseListedModel, registerDeepSeekProvider, requestModelListing, resolveAdapterOptions,
|
||||
catalogModelInfo, createModelListingCache, refuseListedModel, registerDeepSeekProvider, requestModelListing, resolveAdapterOptions,
|
||||
resolveListedCatalog,
|
||||
} from '@deepseek-ai/dsh-llm-deepseek'
|
||||
import type { DeepSeekRequestAuth, ListedCatalog, ListingView, ModelListing, ResolvedDeepSeekOptions } from '@deepseek-ai/dsh-llm-deepseek'
|
||||
@@ -50,15 +50,10 @@ export function apply(ctx: Context, config: Config): void {
|
||||
},
|
||||
}
|
||||
}
|
||||
const catalog = new ModelListingCache({
|
||||
const catalog = createModelListingCache(ctx, name, PROVIDER, {
|
||||
settings: () => settings().catalog,
|
||||
credential: async url => account()?.resolveToken(url),
|
||||
load: (url, token, signal) => requestModelListing(url, { [TOKEN_HEADER]: token }, signal),
|
||||
now: () => Date.now(),
|
||||
// ACP reserves stdout for protocol messages, including during successful catalog loads.
|
||||
info: (message) => { console.error(LOG_TAG, message) },
|
||||
warn: (message) => { console.warn(LOG_TAG, message) },
|
||||
changed: () => { ctx.emit('llm/models-updated', PROVIDER) },
|
||||
})
|
||||
let loggedListing: ModelListing | undefined
|
||||
/** Resolve the catalog, reporting each listing's hidden entries and dropped badges once. */
|
||||
@@ -103,5 +98,4 @@ export function apply(ctx: Context, config: Config): void {
|
||||
return offered.map(model => catalogModelInfo(provider, model, badges.get(model.id)))
|
||||
},
|
||||
})
|
||||
ctx.effect(() => () => catalog.dispose(), 'llm-deepseek-account: model catalog')
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import type { LlmModelBadge } from '@deepseek-ai/dsh-llm'
|
||||
import type {} from '@deepseek-ai/cordis-plugin-loader'
|
||||
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'
|
||||
import {
|
||||
catalogModelInfo, ModelListingCache, refuseListedModel, registerDeepSeekProvider, requestModelListing, resolveListedCatalog,
|
||||
catalogModelInfo, createModelListingCache, refuseListedModel, registerDeepSeekProvider, requestModelListing, resolveListedCatalog,
|
||||
} from '@deepseek-ai/dsh-llm-deepseek'
|
||||
import type { CatalogRefusal, DeepSeekCatalogModel, ListingView, ModelListing } from '@deepseek-ai/dsh-llm-deepseek'
|
||||
import { Config, plainOptions, resolveAdapterOptions } from './config.ts'
|
||||
@@ -57,15 +57,10 @@ export function apply(ctx: Context, config: Config): void {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
const listings = new ModelListingCache({
|
||||
const listings = createModelListingCache(ctx, name, PROVIDER, {
|
||||
settings: () => options().listing,
|
||||
credential: async () => presentApiKey(options()),
|
||||
load: (url, apiKey, signal) => requestModelListing(url, { 'x-api-key': apiKey }, signal),
|
||||
now: () => Date.now(),
|
||||
// ACP reserves stdout for protocol messages, including during successful catalog loads.
|
||||
info: (message) => { console.error(LOG_TAG, message) },
|
||||
warn: (message) => { console.warn(LOG_TAG, message) },
|
||||
changed: () => { ctx.emit('llm/models-updated', PROVIDER) },
|
||||
})
|
||||
let reportedListing: ModelListing | undefined
|
||||
/** Resolve the catalog, reporting each listing's hidden entries and dropped badges once. */
|
||||
@@ -131,5 +126,4 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (next !== following) listings.invalidate()
|
||||
following = next
|
||||
})
|
||||
ctx.effect(() => () => listings.dispose(), 'llm-deepseek-api-key: model listing')
|
||||
}
|
||||
@@ -39,8 +39,8 @@
|
||||
en: f13f24b504dcfb50
|
||||
zh: 69b0ed9e2adf3394
|
||||
/deepseek-ai-dsh-llm-deepseek/use-this-package/failures-and-recovery:
|
||||
en: a7553dc4db7d8495
|
||||
zh: f59a1e111fbe798f
|
||||
en: a8bbaf16292d4d61
|
||||
zh: e9ef390254446caf
|
||||
/deepseek-ai-dsh-llm-deepseek/understand-the-implementation:
|
||||
en: 1598ac8a8bb9b6bf
|
||||
zh: fdd089c5ae7722cd
|
||||
|
||||
@@ -118,7 +118,7 @@ Successful Files responses must contain valid JSON. JSON decoding failures from
|
||||
|
||||
Non-2xx responses fail with stable codes: `AUTH` (401/403), `QUOTA`, `RATE_LIMIT`, `CONTEXT_WINDOW_EXCEEDED`, `INVALID_REQUEST`, `SERVER`, and `HTTP_<status>` otherwise; pre-response transport failures throw `TRANSPORT`, caller aborts throw `ABORTED`, and stream-idle expiry throws `TIMEOUT`. A request-extension field collision, or a rejected preparation from a directly supplied `prepareExtensions`, fails with `REQUEST_EXTENSION` and appends the cause chain to the message; a post-2xx acceptance failure is logged and the response still streams. A normalized-image rejection names every plausible attachment and its durable position when the provider does not identify a file id. Stale-file rejection removes every named mapping (or every mapping used by the attempt) in one upload-index update and permits one replacement model request. Protocol violations throw `STREAM_CLOSED` or `MALFORMED_RESPONSE`, and a terminal `stop` with no content blocks becomes `EMPTY_RESPONSE`, which the default retry policy retries. A request on the official route without an API key fails with `MISSING_CREDENTIAL`, and a malformed credential fails with `INVALID_CREDENTIAL` naming the reference to fix — never any part of the key.
|
||||
|
||||
Provider plugins own catalog availability. While its API key resolves, the API-key route serves its configured `models`, or the official model listing while `models` is the built-in catalog on the official endpoint; the account route serves its account listing while signed in (see the [API-key](../llm-deepseek-api-key/README.md) and [account](../llm-deepseek-account/README.md) providers). The transport supplies the bundled catalog, the catalog-entry field schemas, and capability resolution, plus the listing machinery both routes share: `requestModelListing`, the credential-scoped `ModelListingCache`, and `resolveListedCatalog` with its client-version gate, listed badges, and `refuseListedModel`; `catalogModelInfo` attaches a listed badge to the advertised entry. A provider may also supply `connect(signal)`, which resolves the connection for exact-model lookups and prepared calls after provider-owned work such as a catalog load, and `admit(connection, model)`, which refuses a model before network I/O.
|
||||
Provider plugins own catalog availability. While its API key resolves, the API-key route serves its configured `models`, or the official model listing while `models` is the built-in catalog on the official endpoint; the account route serves its account listing while signed in (see the [API-key](../llm-deepseek-api-key/README.md) and [account](../llm-deepseek-account/README.md) providers). The transport supplies the bundled catalog, the catalog-entry field schemas, and capability resolution, plus the listing machinery both routes share: `requestModelListing`, the credential-scoped `ModelListingCache` (owned by `createModelListingCache` for automatic refresh, notifications, and plugin cleanup), and `resolveListedCatalog` with its client-version gate, listed badges, and `refuseListedModel`; `catalogModelInfo` attaches a listed badge to the advertised entry. A provider may also supply `connect(signal)`, which resolves the connection for exact-model lookups and prepared calls after provider-owned work such as a catalog load, and `admit(connection, model)`, which refuses a model before network I/O.
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ Files 模式通过 `maxRequestFilesBytes` 与 `maxImagesPerRequest` 限制保留
|
||||
|
||||
非 2xx 响应以稳定 code 失败:`AUTH`(401/403)、`QUOTA`、`RATE_LIMIT`、`CONTEXT_WINDOW_EXCEEDED`、`INVALID_REQUEST`、`SERVER` 以及其他情况的 `HTTP_<status>`;响应前传输失败抛出 `TRANSPORT`,调用方中止抛出 `ABORTED`,流空闲超时抛出 `TIMEOUT`。请求扩展字段冲突,或直接提供的 `prepareExtensions` 拒绝准备时,使用 `REQUEST_EXTENSION` 失败,并在消息中附上原因链;2xx 后接受失败只记录告警,响应仍会继续流式返回。当提供方未指出 file id 时,规范化图片拒绝会列出所有可能附件及其持久位置。陈旧文件拒绝会在一次上传索引更新中删除全部点名映射(或该次尝试使用的全部映射),并允许一次替换模型请求。协议违规抛出 `STREAM_CLOSED` 或 `MALFORMED_RESPONSE`;不带内容块的终止 `stop` 变成 `EMPTY_RESPONSE`,默认重试策略会重试它。官方路由缺少 API Key 的请求以 `MISSING_CREDENTIAL` 失败;格式错误的凭据以 `INVALID_CREDENTIAL` 失败,并点名需要修复的引用——绝不包含密钥的任何部分。
|
||||
|
||||
提供方插件负责目录可用性:API key 路由在能解析到 API key 时提供配置中的 `models`,在官方端点上且 `models` 为内置目录时改为提供官方模型列表;账号路由在登录时提供账号的模型列表(见 [API key](../llm-deepseek-api-key/README.zh.md) 与[账号](../llm-deepseek-account/README.zh.md)提供方)。传输层提供内置目录、目录条目字段的 schema 和能力解析,以及两条路由共用的列表机制:`requestModelListing`、按凭据区分的 `ModelListingCache`,以及带客户端版本门控和列表角标的 `resolveListedCatalog` 与 `refuseListedModel`;`catalogModelInfo` 把列表角标附到公布的条目上。提供方还可以提供 `connect(signal)`:在提供方自己的工作(例如加载目录)完成后,为精确模型查询和预备调用解析连接;以及 `admit(connection, model)`:在网络请求之前拒绝某个模型。
|
||||
提供方插件负责目录可用性:API key 路由在能解析到 API key 时提供配置中的 `models`,在官方端点上且 `models` 为内置目录时改为提供官方模型列表;账号路由在登录时提供账号的模型列表(见 [API key](../llm-deepseek-api-key/README.zh.md) 与[账号](../llm-deepseek-account/README.zh.md)提供方)。传输层提供内置目录、目录条目字段的 schema 和能力解析,以及两条路由共用的列表机制:`requestModelListing`、按凭据区分的 `ModelListingCache`(由 `createModelListingCache` 持有,负责自动刷新、通知与插件清理),以及带客户端版本门控和列表角标的 `resolveListedCatalog` 与 `refuseListedModel`;`catalogModelInfo` 把列表角标附到公布的条目上。提供方还可以提供 `connect(signal)`:在提供方自己的工作(例如加载目录)完成后,为精确模型查询和预备调用解析连接;以及 `admit(connection, model)`:在网络请求之前拒绝某个模型。
|
||||
|
||||
-----
|
||||
|
||||
|
||||
@@ -8,6 +8,32 @@ import { getOrCreateAnonymousUserId, type AnonymousUserId } from '@deepseek-ai/d
|
||||
import { deepEqualJson } from '@deepseek-ai/dsh-util-values'
|
||||
import { DeepSeekAdapter } from './adapter.ts'
|
||||
import type { DeepSeekAdapterOptions, DeepSeekConnectionOptions } from './types.ts'
|
||||
import { ModelListingCache, type ListingCacheDependencies } from './listing-cache.ts'
|
||||
|
||||
/**
|
||||
* Own a route's listing refreshes, stderr diagnostics, and catalog-change notifications.
|
||||
* @param ctx - provider plugin lifetime.
|
||||
* @param owner - plugin name used in diagnostics.
|
||||
* @param provider - route whose catalog changes.
|
||||
* @param dependencies - current configuration, credentials, and listing transport.
|
||||
* @returns the cache, disposed with the provider after its active work settles.
|
||||
*/
|
||||
export function createModelListingCache(
|
||||
ctx: Context, owner: string, provider: string,
|
||||
dependencies: Pick<ListingCacheDependencies, 'settings' | 'credential' | 'load'>,
|
||||
): ModelListingCache {
|
||||
const tag = `[${owner}]`
|
||||
const cache = new ModelListingCache({
|
||||
...dependencies,
|
||||
now: () => Date.now(),
|
||||
// ACP reserves stdout for protocol messages, including during successful catalog loads.
|
||||
info: (message) => { console.error(tag, message) },
|
||||
warn: (message) => { console.warn(tag, message) },
|
||||
changed: () => { ctx.emit('llm/models-updated', provider) },
|
||||
})
|
||||
ctx.effect(() => () => cache.dispose(), `${owner}: model listing`)
|
||||
return cache
|
||||
}
|
||||
|
||||
/**
|
||||
* Register one provider with request-local transport services and live retry policy.
|
||||
|
||||
@@ -38,7 +38,7 @@ export type { DeepSeekUploadRecord } from './upload-index.ts'
|
||||
export type { RequestDefaults } from './types.ts'
|
||||
|
||||
export { catalogModelInfo } from './model-info.ts'
|
||||
export { registerDeepSeekProvider } from './host.ts'
|
||||
export { createModelListingCache, registerDeepSeekProvider } from './host.ts'
|
||||
export { LISTING_TIMEOUT, modelListingURL, parseModelListing, requestModelListing } from './listing.ts'
|
||||
export type { ClientRequirement, ListedBadge, ListedCapabilities, ListedFacts, ListingEntry, ModelListing } from './listing.ts'
|
||||
export { DEFAULT_MODELS_MAX_AGE_MS, DEFAULT_MODELS_TIMEOUT_MS, modelListingConfigFields, resolveListingTiming } from './listing-config.ts'
|
||||
|
||||
Reference in new issue
Block a user