doc_id: model-metadata-firstscreen-optimization title: “perf(desktop): remove models.dev metadata from the renderer startup path” language: en source_language: en implementation_status: current document_status: current translation_status: synced last_verified: 2026-09-07 owners:
Status (verified 2026-09-07): this optimization is implemented — the five startup import paths listed below are cut in the current source. One additional static path remains and is recorded as accepted debt:
AppShellOverlays(app-shell.tsx:170) →useAppShellCommands→command-palette-commands.ts:50importsisRetiredProviderfrom@maka/core/provider-registry, whosePROVIDER_REGISTRYis built frommodel-metadata.generated— a runtime import, not a lazy command-palette entry — so the acceptance criterion's blanket exclusion of every startup transitive metadata dependency is not fully satisfied. The Problem is kept as the 2026-08-04 record of the pre-optimization state.
Most users configure only a few providers, but Maka currently loads metadata for every provider and hundreds of models on startup. This data should remain behind the main-process authority boundary, with the renderer receiving only the lightweight projection needed for the current UI.
The Desktop AppShell startup path statically loads packages/core/src/model-metadata.generated.ts. The file is generated during installation or build from the committed models.dev snapshot; the 2026-08-04 measurement below was about 520 KB / 13,988 lines and contained full metadata for roughly 44 providers and hundreds of models.
Measured from the 2026-08-04 renderer build:
| Artifact | Size |
|---|---|
model-metadata.generated.ts source | 520 KB |
EmptyState-*.js shared chunk containing the metadata | 644 KB |
model-catalog-choices-*.js chunk | 98 KB |
index-*.js entry | 253 KB |
| 29 modulepreload chunks combined | 1,769 KB |
The EmptyState-*.js chunk contains 312 references matching model names such as claude-opus, gpt-5., and gemini-2., confirming that the snapshot is part of the startup artifact. Electron reads these files locally, so the main cost is renderer-main-thread parsing and evaluation rather than network I/O.
Five independent runtime import paths make the metadata reachable at startup:
thinkingVariantsForModel → model-thinking.ts → model-metadata.tsbuildChatModelChoices → model-catalog-choices.ts → model-catalog.ts@maka/ui modelMenuGroups → PROVIDER_REGISTRYprovider-display.tsx → PROVIDER_REGISTRYOnboardingHero → RECOMMENDED_PROVIDER_TYPESEach path eventually reaches model-metadata.generated.ts. Removing only one path, or assigning the metadata to a Vite manualChunks entry, does not remove the static startup dependency.
The first screen needs only model choices, their thinking levels, provider heading labels, and local display copy for four onboarding providers. Rich metadata such as pricing, context windows, full capabilities, and lifecycle information is used only by the lazy-loaded SettingsModal.
Reuse the existing onboarding:getSnapshot path. The main process already loads the metadata and should provide the renderer with the lightweight startup projection:
The renderer consumes this projection instead of reading the model catalog or metadata at startup. Connection changes continue to use the existing connections:event → onboarding snapshot refresh flow; no new IPC channel is needed.
The session health notice uses the last completed snapshot while an event-triggered refresh is in flight, then updates from that pull; it does not wait for another invalidation cycle. Credential lookup failures are projected conservatively as hasSecret: false. This replaces the renderer's former optimistic true fallback on probe errors, so an unreadable credential surfaces the existing repair path instead of hiding a likely send failure.
Remove the remaining provider-registry dependencies from the startup path:
modelMenuGroups receives the required label from the startup projection instead of reading PROVIDER_REGISTRY.providerDisplay uses the existing exhaustive PROVIDER_DISPLAY_COPY; an unknown cross-version type falls back to the type string and generic local description instead of PROVIDER_REGISTRY.RECOMMENDED_PROVIDER_TYPES at runtime.Full metadata remains available to the main process and lazy-loaded SettingsModal. This renderer optimization does not otherwise change the metadata generation flow.
Acceptance criteria:
model-metadata.generated.ts, model-metadata.ts, provider-registry.ts, model-catalog.ts, and model-thinking.ts.model-catalog-choices.ts; shell-chat-model-selection.ts deliberately remains on the static path as the lightweight selector behind useShellChatModel — this optimization removes the heavy metadata modules, not that selector.claude-opus|gpt-5\.|gemini-2\. returns zero; full metadata exists only on lazy Settings paths.manualChunks: changes file placement but does not break a static import path, so the metadata chunk would still load and execute at startup.connections:listModelChoices IPC channel: duplicates the existing prefetched and connection-invalidated onboarding snapshot flow.ProviderType.状态(2026-09-07 核验): 该优化已落地——下面列出的五条首屏依赖链在当前源码中均已切断。另有一条静态路径仍存在,现记录为已接受的遗留:
AppShellOverlays(app-shell.tsx:170)→useAppShellCommands→command-palette-commands.ts:50从@maka/core/provider-registry导入isRetiredProvider,而PROVIDER_REGISTRY由model-metadata.generated构建——这是运行时导入而非懒加载的命令面板入口,因此验收标准中“首屏全部静态传递依赖排除 metadata”的绝对表述并未完全满足。Problem 一节保留的是 2026-08-04 优化前状态的记录。
大多数用户只配置少数几个 provider,但 Maka 当前会在启动时加载全部 provider 和数百个模型的元数据。完整目录应留在 main process 的权威边界内,renderer 只接收当前界面所需的轻量投影。
桌面端 AppShell 的首屏静态依赖会加载 packages/core/src/model-metadata.generated.ts。该文件在安装或构建时由 committed models.dev snapshot 生成;下面记录的 2026-08-04 实测约为 520 KB、13,988 行,包含约 44 个 provider 和数百个模型的完整元数据。
2026-08-04 的 renderer 构建实测:
| 产物 | 大小 |
|---|---|
model-metadata.generated.ts 源文件 | 520 KB |
含元数据的 EmptyState-*.js 共享 chunk | 644 KB |
model-catalog-choices-*.js chunk | 98 KB |
index-*.js 入口 | 253 KB |
| 29 个 modulepreload chunk 合计 | 1,769 KB |
EmptyState-*.js 中可检出 312 处 claude-opus、gpt-5.、gemini-2. 等模型名引用,说明完整快照已进入首屏产物。Electron 从本地磁盘读取这些文件,主要问题不是网络请求,而是 renderer 主线程需要同步解析和执行这批首屏并不需要的数据。
目前有五条独立的首屏运行时依赖链可以触达完整元数据:
thinkingVariantsForModel → model-thinking.ts → model-metadata.tsbuildChatModelChoices → model-catalog-choices.ts → model-catalog.ts@maka/ui 的 modelMenuGroups → PROVIDER_REGISTRYprovider-display.tsx → PROVIDER_REGISTRYOnboardingHero → RECOMMENDED_PROVIDER_TYPES这些链最终都会进入 model-metadata.generated.ts。只处理其中一条或使用 Vite manualChunks 都不会解除首屏静态依赖。
首屏实际只需要模型选项、对应的 thinking levels、provider heading label,以及 4 个首次引导 provider 的本地展示信息。pricing、context window、完整 capabilities、lifecycle 等富元数据只在懒加载的 SettingsModal 中使用。
复用现有 onboarding:getSnapshot,由已经加载元数据的 main process 向 renderer 提供首屏所需的轻量投影:
Renderer 使用 snapshot 数据渲染首屏,不再自行读取 model catalog 或 model metadata。connection 发生变化时,继续复用现有 connections:event → onboarding snapshot refresh 更新投影,不新增 IPC channel。
Session health notice 在 event 触发的异步刷新完成前继续使用上一份 snapshot,当前 pull 返回后立即更新,不需要再等下一轮 invalidation。Credential lookup 失败时会保守投影为 hasSecret: false;这取代了 renderer 旧逻辑在 probe 报错时乐观返回 true 的行为,使凭据无法读取时进入已有修复路径,而不是隐藏一次很可能失败的发送。
同时切断其余 provider registry 依赖:
modelMenuGroups 从首屏投影获取所需 label,不再直接读取 PROVIDER_REGISTRY。providerDisplay 使用已有且类型完整的 PROVIDER_DISPLAY_COPY;遇到跨版本未知 type 时直接显示 type 和通用本地描述,不再 fallback 到 PROVIDER_REGISTRY。RECOMMENDED_PROVIDER_TYPES。完整元数据继续保留在 main process 和懒加载的 SettingsModal 中;这项 renderer 优化本身不再改变元数据生成流程。
验收标准:
model-metadata.generated.ts、model-metadata.ts、provider-registry.ts、model-catalog.ts 或 model-thinking.ts。model-catalog-choices.ts;shell-chat-model-selection.ts 作为 useShellChatModel 背后的轻量选择器有意保留在静态路径上——本优化移除的是重量级元数据模块,不含这个选择器。claude-opus|gpt-5\.|gemini-2\. 为 0;完整元数据只存在于设置页懒加载路径。manualChunks:只能改变模块所属文件,不能切断静态 import;首屏仍会加载并执行元数据 chunk。connections:listModelChoices IPC:现有 onboarding snapshot 已经在首屏预取,并监听 connection 变更;新 channel 会重复现有机制。ProviderType 的本地文案,重复传输没有必要。