返回 oh-my-ppt
model-config-utils.ts
根目录 / src / main / config / model-config-utils.ts
1 import {
2 MODEL_TIMEOUT_PROFILES,
3 resolveModelTimeoutMs,
4 type ModelTimeoutProfile
5 } from '@shared/model-timeout'
6 import type { PPTDatabase } from '../db/database'
7 import { readAppLocale, uiText } from './locale-utils'
8 import { bindCurrentModelTemperatureControl } from '../agent-runtime/model'
9 import {
10 DEFAULT_THINKING_PARAMETER_MODE,
11 normalizeThinkingParameterMode,
12 type ThinkingParameterMode
13 } from '@shared/model-config'
14
15 export interface ActiveModelConfig {
16 id: string
17 name: string
18 provider: string
19 model: string
20 apiKey: string
21 baseUrl: string
22 maxTokens: number
23 disableTemperature: boolean
24 thinkingParameterMode: ThinkingParameterMode
25 }
26
27 export type ResolvedModelConfig = ActiveModelConfig
28
29 type ModelSettingsPort = Pick<PPTDatabase, 'getAllSettings'>
30 type ModelConfigDatabasePort = Pick<PPTDatabase, 'getActiveModelConfig' | 'getModelConfig' | 'getSetting'>
31 export type ModelConfigContext = {
32 db: ModelSettingsPort & ModelConfigDatabasePort
33 decryptApiKey(value: string): string
34 }
35
36 export async function resolveGlobalModelTimeouts(
37 ctx: { db: ModelSettingsPort }
38 ): Promise<Record<ModelTimeoutProfile, number>> {
39 const settings = await ctx.db.getAllSettings()
40 return Object.fromEntries(
41 MODEL_TIMEOUT_PROFILES.map((profile) => [
42 profile,
43 resolveModelTimeoutMs(settings[`timeout_ms_${profile}`], profile)
44 ])
45 ) as Record<ModelTimeoutProfile, number>
46 }
47
48 export async function resolveActiveModelConfig(
49 ctx: ModelConfigContext
50 ): Promise<ActiveModelConfig> {
51 const locale = await readAppLocale(ctx)
52 const config = await ctx.db.getActiveModelConfig()
53 if (!config) {
54 throw new Error(
55 uiText(
56 locale,
57 '请先前往系统设置添加并启用一个模型。',
58 'Add and activate a model in Settings first.'
59 )
60 )
61 }
62 return resolveModelConfigRow(ctx, config, {
63 locale,
64 missingPrefixZh: '当前启用模型',
65 missingPrefixEn: 'The active model'
66 })
67 }
68
69 const resolveModelConfigRow = (
70 ctx: Pick<ModelConfigContext, 'decryptApiKey'>,
71 config: {
72 id: string
73 name: string
74 provider: string
75 model: string
76 apiKey: string
77 baseUrl: string
78 maxTokens?: number | null
79 disableTemperature?: number | boolean | null
80 thinkingParameterMode?: string | null
81 },
82 options: {
83 locale: 'zh' | 'en'
84 missingPrefixZh: string
85 missingPrefixEn: string
86 }
87 ): ActiveModelConfig => {
88 const provider = String(config.provider || '').trim()
89 const model = String(config.model || '').trim()
90 const apiKey = ctx.decryptApiKey(config.apiKey).trim()
91 if (!provider) {
92 throw new Error(
93 uiText(
94 options.locale,
95 `${options.missingPrefixZh}缺少 provider,请到设置页检查。`,
96 `${options.missingPrefixEn} is missing provider. Check Settings.`
97 )
98 )
99 }
100 if (!model) {
101 throw new Error(
102 uiText(
103 options.locale,
104 `${options.missingPrefixZh}缺少 model,请到设置页检查。`,
105 `${options.missingPrefixEn} is missing model. Check Settings.`
106 )
107 )
108 }
109 if (!apiKey) {
110 throw new Error(
111 uiText(
112 options.locale,
113 `${options.missingPrefixZh}缺少 api_key,请到设置页检查。`,
114 `${options.missingPrefixEn} is missing api_key. Check Settings.`
115 )
116 )
117 }
118
119 const resolved = {
120 id: config.id,
121 name: config.name,
122 provider,
123 model,
124 apiKey,
125 baseUrl: String(config.baseUrl || '').trim(),
126 maxTokens: config.maxTokens || 4096,
127 disableTemperature: config.disableTemperature === 1 || config.disableTemperature === true,
128 thinkingParameterMode: normalizeThinkingParameterMode(
129 config.thinkingParameterMode || DEFAULT_THINKING_PARAMETER_MODE
130 )
131 }
132 bindCurrentModelTemperatureControl(resolved)
133 return resolved
134 }
135
136 export async function resolveModelConfigById(
137 ctx: ModelConfigContext,
138 modelConfigId: string
139 ): Promise<ResolvedModelConfig> {
140 const locale = await readAppLocale(ctx)
141 const id = modelConfigId.trim()
142 if (!id) {
143 throw new Error(uiText(locale, '请选择要使用的模型。', 'Choose a model to use.'))
144 }
145 const config = await ctx.db.getModelConfig(id)
146 if (!config) {
147 throw new Error(uiText(locale, '所选模型不存在,请重新选择。', 'The selected model no longer exists.'))
148 }
149 return resolveModelConfigRow(ctx, config, {
150 locale,
151 missingPrefixZh: '所选模型配置',
152 missingPrefixEn: 'The selected model'
153 })
154 }
155
156 export async function resolveModelConfigForTask(
157 ctx: ModelConfigContext,
158 args: {
159 modelConfigId?: string | null
160 purpose: string
161 }
162 ): Promise<ResolvedModelConfig> {
163 const id = typeof args.modelConfigId === 'string' ? args.modelConfigId.trim() : ''
164 if (id) return resolveModelConfigById(ctx, id)
165 return resolveActiveModelConfig(ctx)
166 }
167
167 lines TYPESCRIPT