Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,402 changes: 701 additions & 701 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[install]
registry = "https://registry.npmjs.org/"

[loader]
".md" = "text"
10 changes: 10 additions & 0 deletions changelog/2026-07-01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 2026-07-01

### 新增
- 渠道(Provider)新增「自动同步上游模型」选项:
- 渠道编辑/新建表单模型列表下方新增复选框 `autoSyncModels`(i18n key `providers.autoSyncModelsLabel` / `autoSyncModelsHint`)。
- 开启并保存时,后端先请求上游 `/v1/models` 校验:失败则整体报错、不落库;成功则立即用上游模型列表覆盖该渠道的自定义模型列表。(`config.ts` 的 `syncEntryModelsFromUpstream` 在 `createProvider` / `updateProvider` 中调用,更新时仅在「新开启」或「地址/类型/认证变更」时触发即时同步。)
- 服务端每 24h 定时任务(`server.ts` 的 `setInterval` + `runAutoModelSync`)遍历所有开启该选项且启用中的渠道,重新拉取上游模型列表并写库;单个渠道失败只记日志、不影响其他渠道;上游返回空列表时保留原有模型不清空。
- 数据库 `console_providers` 新增 `auto_sync_models`(integer,默认 0)与 `models_synced_at`(bigint,记录最近同步时间)两列,迁移文件 `drizzle/0010_*.sql`。
- 新增 `src/upstream-models.ts` 统一上游 `/v1/models` 拉取逻辑(URL 拼接、认证头、超时、解析),console 的两个上游模型拉取路由(`upstream-models` / `upstream-models-preview`)与自动同步逻辑复用同一实现。
- 渠道配置导出(export)JSON 中包含 `autoSyncModels` 字段。
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ type ProviderFormState = {
apiKeyDirty: boolean
clearAuth: boolean
extraFieldsJson: string
autoSyncModels: boolean
models: ModelRowState[]
}

Expand Down Expand Up @@ -251,6 +252,7 @@ function createFormState(provider?: ProviderInfo): ProviderFormState {
? JSON.stringify(extraFields, null, 2)
: ""
})(),
autoSyncModels: provider?.autoSyncModels ?? false,
models: provider?.models.length
? provider.models.map((model) => createModelRow(model))
: [createModelRow()],
Expand Down Expand Up @@ -305,6 +307,7 @@ function buildProviderPayload(
routingVisibility: state.routingVisibility,
responsesMode: state.type === "openai" ? state.responsesMode : null,
extraFields: parseExtraJson(state.extraFieldsJson),
autoSyncModels: state.autoSyncModels,
}

const explicitHeader = state.authHeader === "auto" ? undefined : state.authHeader
Expand Down Expand Up @@ -794,6 +797,7 @@ export function ProvidersPage({
auth: p.auth,
responsesMode: p.responsesMode,
extraFields: p.extraFields,
autoSyncModels: p.autoSyncModels,
})),
}
setConfigJson(JSON.stringify(exportData, null, 2))
Expand Down Expand Up @@ -985,6 +989,24 @@ export function ProvidersPage({
{t("providers.syncButton")}
</Button>
</div>
<div className="mt-2 flex items-start gap-2 rounded-[10px] border border-input bg-muted/30 px-3 py-2">
<Checkbox
id="pane-auto-sync-models"
checked={formState.autoSyncModels}
onCheckedChange={(checked) =>
setFormState((current) => ({ ...current, autoSyncModels: checked === true }))
}
className="mt-0.5"
/>
<label htmlFor="pane-auto-sync-models" className="cursor-pointer select-none">
<span className="block text-[13px] font-medium text-foreground">
{t("providers.autoSyncModelsLabel")}
</span>
<span className="mt-0.5 block text-[11.5px] leading-snug text-muted-foreground">
{t("providers.autoSyncModelsHint")}
</span>
</label>
</div>
</Field>

{formState.type === "openai" ? (
Expand Down
2 changes: 2 additions & 0 deletions console/ai-proxy-dashboard/src/features/dashboard/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export type ProviderInfo = {
responsesMode?: OpenAiResponsesMode
extraFields: Record<string, unknown> | null
providerUuid: string
autoSyncModels?: boolean
healthStatus?: "healthy" | "degraded" | "down" | "no-data"
}

Expand All @@ -264,6 +265,7 @@ export type ProviderMutationPayload = {
} | null
responsesMode?: OpenAiResponsesMode | null
extraFields?: Record<string, unknown> | null
autoSyncModels?: boolean
}

export type ManagedApiKey = {
Expand Down
2 changes: 2 additions & 0 deletions console/ai-proxy-dashboard/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ export default {
extraFieldsInvalidJson: "Extra fields must be valid JSON object",
extraFieldsInvalidObj: "Extra fields must be a JSON object",
modelsLabel: "Models",
autoSyncModelsLabel: "Auto-sync upstream models",
autoSyncModelsHint: "When enabled, saving first validates the upstream /v1/models endpoint; on success the model list is immediately replaced with the upstream list, then re-synced automatically every 24 hours.",
modelIdHeader: "Model ID",
noModels: "No models. Click Add to create one.",
clearAllModels: "Clear",
Expand Down
2 changes: 2 additions & 0 deletions console/ai-proxy-dashboard/src/i18n/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ export default {
extraFieldsInvalidJson: "高级字段必须是合法 JSON 对象",
extraFieldsInvalidObj: "高级字段必须是 JSON 对象",
modelsLabel: "模型列表",
autoSyncModelsLabel: "自动同步上游模型",
autoSyncModelsHint: "开启后保存时会先校验上游 /v1/models,成功则立即用上游列表覆盖当前模型,之后每 24 小时自动同步一次。",
modelIdHeader: "模型 ID",
noModels: "暂无模型,点击添加",
clearAllModels: "清空",
Expand Down
2 changes: 2 additions & 0 deletions drizzle/0010_unusual_cammi.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "console_providers" ADD COLUMN "auto_sync_models" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
ALTER TABLE "console_providers" ADD COLUMN "models_synced_at" bigint;
Loading
Loading