c36a5170f8
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 5m28s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m29s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 6m6s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 6m6s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m46s
Project CI / AI game creator shell Rust crates (push) Successful in 2m51s
Project CI / Frontend tests (push) Successful in 4m44s
Project CI / Native shell tests (push) Successful in 6m31s
Project CI / Repository checks (push) Successful in 4m17s
Project CI / Backend tests (push) Successful in 7m48s
Project CI / AI game creator shell web tests (push) Successful in 3m43s
- 本地配置新增 llm.customEnabled 与 llm.visibleModels,显式开启后保留自定义连接和勾选列表,官方路由下仍清空凭据 - 配置迁移始终写出 customEnabled、visibleModels、apiKey、baseUrl、model、apiKind、reasoningEffort,便于手写自定义连接 - 新增 discover_game_creator_llm_models 命令,直连自定义端点 GET /models,限制超时与响应大小并脱敏错误 - codex_app_server 自定义模式改用配置里的地址与 Key 走本地凭据代理,不再经过平台 /api/llm,也不回退官方路由 - 模型目录按官方与自定义来源隔离缓存,自定义模式只展示勾选模型,选项失效时回退第一项 - 设置页在自定义模式展示 API 地址、API Key、协议与推理档,并提供模型读取、搜索、勾选与已勾选预览 - 补齐固定项排版与读取按钮样式,同步 AGC 模型选择规范、实施计划与共享概览
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { resolveTauriInvoke } from '../app/tauri';
|
|
import type { GameCreatorAppConfigView } from '../app/types';
|
|
import { type ClientLlmModelCatalog, loadClientLlmModels } from './clientApi';
|
|
|
|
let cached: ClientLlmModelCatalog | null = null;
|
|
let inFlight: Promise<ClientLlmModelCatalog> | null = null;
|
|
let source = '';
|
|
let generation = 0;
|
|
let localRevision = 0;
|
|
export const LLM_CONFIG_CHANGED_EVENT = 'agc-llm-config-changed';
|
|
export class LlmModelCatalogConfigError extends Error {}
|
|
|
|
export function notifyLlmConfigChanged() {
|
|
generation += 1;
|
|
cached = null;
|
|
inFlight = null;
|
|
source = '';
|
|
window.dispatchEvent(new Event(LLM_CONFIG_CHANGED_EVENT));
|
|
}
|
|
|
|
async function loadEffectiveCatalog(epoch: number) {
|
|
const invoke = resolveTauriInvoke();
|
|
let config: GameCreatorAppConfigView | undefined;
|
|
try {
|
|
config = invoke
|
|
? await invoke<GameCreatorAppConfigView>('read_game_creator_app_config')
|
|
: undefined;
|
|
} catch (error) {
|
|
if (epoch === generation) cached = null;
|
|
throw new LlmModelCatalogConfigError('读取客户端配置失败');
|
|
}
|
|
if (epoch !== generation) throw new Error('模型配置已更新');
|
|
const llm = config?.config.llm;
|
|
const nextSource = llm?.customEnabled
|
|
? JSON.stringify(['custom', llm.baseUrl, llm.visibleModels ?? []])
|
|
: 'official';
|
|
if (source !== nextSource) {
|
|
source = nextSource;
|
|
cached = null;
|
|
}
|
|
if (llm?.customEnabled) {
|
|
if (cached) return cached;
|
|
const ids = llm.visibleModels ?? [];
|
|
return {
|
|
defaultModelId: ids[0] ?? '',
|
|
models: ids.map((id) => ({ id, displayName: id })),
|
|
revision: --localRevision,
|
|
};
|
|
}
|
|
return loadClientLlmModels();
|
|
}
|
|
|
|
/** 最近一次成功读取的模型目录,用于首屏渲染与刷新失败时兜底。 */
|
|
export function cachedLlmModelCatalog() {
|
|
return cached;
|
|
}
|
|
|
|
/**
|
|
* 读取模型目录;同一时刻只保留一个在途请求,重复触发复用同一结果。
|
|
* 读取失败时保留上一次成功目录,不覆盖调用方已生效的选择。
|
|
*/
|
|
export function refreshLlmModelCatalog() {
|
|
if (inFlight) return inFlight;
|
|
const epoch = generation;
|
|
const request = loadEffectiveCatalog(epoch)
|
|
.then((catalog) => {
|
|
if (epoch !== generation) throw new Error('模型配置已更新');
|
|
cached = catalog;
|
|
return catalog;
|
|
})
|
|
.finally(() => {
|
|
if (inFlight === request) inFlight = null;
|
|
});
|
|
inFlight = request;
|
|
return request;
|
|
}
|
|
|
|
export function resetLlmModelCatalogCacheForTest() {
|
|
generation += 1;
|
|
source = '';
|
|
cached = null;
|
|
inFlight = null;
|
|
}
|