sa2kit 3.4.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/dist/business/index.d.mts +2 -7
  2. package/dist/business/index.d.ts +2 -7
  3. package/dist/business/index.js +1761 -8045
  4. package/dist/business/index.js.map +1 -1
  5. package/dist/business/index.mjs +1169 -7452
  6. package/dist/business/index.mjs.map +1 -1
  7. package/dist/business/portfolio/index.d.mts +1 -2
  8. package/dist/business/portfolio/index.d.ts +1 -2
  9. package/dist/{chunk-KVYHCGRY.js → chunk-7Z3XR2Y4.js} +552 -263
  10. package/dist/chunk-7Z3XR2Y4.js.map +1 -0
  11. package/dist/chunk-XPY45Y75.js +1143 -0
  12. package/dist/chunk-XPY45Y75.js.map +1 -0
  13. package/dist/{chunk-YIRPPMCN.mjs → chunk-XSTMLLJV.mjs} +474 -198
  14. package/dist/chunk-XSTMLLJV.mjs.map +1 -0
  15. package/dist/chunk-ZJLS5JU5.mjs +1090 -0
  16. package/dist/chunk-ZJLS5JU5.mjs.map +1 -0
  17. package/dist/common/aiApi/client/index.d.mts +71 -0
  18. package/dist/common/aiApi/client/index.d.ts +71 -0
  19. package/dist/common/aiApi/client/index.js +165 -0
  20. package/dist/common/aiApi/client/index.js.map +1 -0
  21. package/dist/common/aiApi/client/index.mjs +151 -0
  22. package/dist/common/aiApi/client/index.mjs.map +1 -0
  23. package/dist/common/aiApi/index.d.mts +184 -0
  24. package/dist/common/aiApi/index.d.ts +184 -0
  25. package/dist/common/aiApi/index.js +217 -0
  26. package/dist/common/aiApi/index.mjs +4 -0
  27. package/dist/common/aiApi/server/index.d.mts +3 -0
  28. package/dist/common/aiApi/server/index.d.ts +3 -0
  29. package/dist/common/aiApi/server/index.js +217 -0
  30. package/dist/common/aiApi/server/index.mjs +4 -0
  31. package/dist/common/components/index.js +176 -177
  32. package/dist/common/components/index.mjs +1 -2
  33. package/dist/common/index.js +2 -3
  34. package/dist/common/index.mjs +1 -2
  35. package/dist/{index-BSmd4ikf.d.ts → index-B4wDXFL0.d.mts} +39 -2
  36. package/dist/{index-r2-zE3iC.d.mts → index-B4wDXFL0.d.ts} +39 -2
  37. package/dist/{index-DLLPTprx.d.mts → index-B9vXYzok.d.mts} +48 -13
  38. package/dist/{index-VFDbZxVM.d.ts → index-BZ0MhRau.d.ts} +48 -13
  39. package/dist/index.d.mts +703 -181
  40. package/dist/index.d.ts +703 -181
  41. package/dist/index.js +2921 -7257
  42. package/dist/index.js.map +1 -1
  43. package/dist/index.mjs +2161 -6538
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/types-CiqMQ-uu.d.mts +166 -0
  46. package/dist/types-CiqMQ-uu.d.ts +166 -0
  47. package/package.json +15 -60
  48. package/dist/CollisionBalls-DgKtscU2.d.mts +0 -41
  49. package/dist/CollisionBalls-DgKtscU2.d.ts +0 -41
  50. package/dist/business/calendar/index.d.mts +0 -6
  51. package/dist/business/calendar/index.d.ts +0 -6
  52. package/dist/business/calendar/index.js +0 -7433
  53. package/dist/business/calendar/index.js.map +0 -1
  54. package/dist/business/calendar/index.mjs +0 -7257
  55. package/dist/business/calendar/index.mjs.map +0 -1
  56. package/dist/business/calendar/routes/index.d.mts +0 -191
  57. package/dist/business/calendar/routes/index.d.ts +0 -191
  58. package/dist/business/calendar/routes/index.js +0 -844
  59. package/dist/business/calendar/routes/index.js.map +0 -1
  60. package/dist/business/calendar/routes/index.mjs +0 -826
  61. package/dist/business/calendar/routes/index.mjs.map +0 -1
  62. package/dist/chunk-3R6JHA6D.js +0 -120
  63. package/dist/chunk-3R6JHA6D.js.map +0 -1
  64. package/dist/chunk-4PJM4752.js +0 -4
  65. package/dist/chunk-4PJM4752.js.map +0 -1
  66. package/dist/chunk-7PMT4L4I.js +0 -324
  67. package/dist/chunk-7PMT4L4I.js.map +0 -1
  68. package/dist/chunk-FY2X3LYR.mjs +0 -3
  69. package/dist/chunk-FY2X3LYR.mjs.map +0 -1
  70. package/dist/chunk-GS4SAW25.mjs +0 -116
  71. package/dist/chunk-GS4SAW25.mjs.map +0 -1
  72. package/dist/chunk-HL4H2HF6.js +0 -279
  73. package/dist/chunk-HL4H2HF6.js.map +0 -1
  74. package/dist/chunk-IJIQUMAK.mjs +0 -272
  75. package/dist/chunk-IJIQUMAK.mjs.map +0 -1
  76. package/dist/chunk-KVYHCGRY.js.map +0 -1
  77. package/dist/chunk-MMDSZIXD.mjs +0 -286
  78. package/dist/chunk-MMDSZIXD.mjs.map +0 -1
  79. package/dist/chunk-N2O3OX5Y.mjs +0 -243
  80. package/dist/chunk-N2O3OX5Y.mjs.map +0 -1
  81. package/dist/chunk-RRQ2X26Z.js +0 -106
  82. package/dist/chunk-RRQ2X26Z.js.map +0 -1
  83. package/dist/chunk-RVNQI6BI.js +0 -249
  84. package/dist/chunk-RVNQI6BI.js.map +0 -1
  85. package/dist/chunk-UJUWDF7M.mjs +0 -336
  86. package/dist/chunk-UJUWDF7M.mjs.map +0 -1
  87. package/dist/chunk-VCKXK6V5.js +0 -345
  88. package/dist/chunk-VCKXK6V5.js.map +0 -1
  89. package/dist/chunk-VIEXDTNF.mjs +0 -100
  90. package/dist/chunk-VIEXDTNF.mjs.map +0 -1
  91. package/dist/chunk-YIRPPMCN.mjs.map +0 -1
  92. package/dist/common/ai/llm/core/index.d.mts +0 -70
  93. package/dist/common/ai/llm/core/index.d.ts +0 -70
  94. package/dist/common/ai/llm/core/index.js +0 -54
  95. package/dist/common/ai/llm/core/index.mjs +0 -5
  96. package/dist/common/ai/llm/electron/index.d.mts +0 -6
  97. package/dist/common/ai/llm/electron/index.d.ts +0 -6
  98. package/dist/common/ai/llm/electron/index.js +0 -67
  99. package/dist/common/ai/llm/electron/index.mjs +0 -10
  100. package/dist/common/ai/llm/index.d.mts +0 -3
  101. package/dist/common/ai/llm/index.d.ts +0 -3
  102. package/dist/common/ai/llm/index.js +0 -54
  103. package/dist/common/ai/llm/index.js.map +0 -1
  104. package/dist/common/ai/llm/index.mjs +0 -5
  105. package/dist/common/ai/llm/index.mjs.map +0 -1
  106. package/dist/common/ai/llm/miniapp/index.d.mts +0 -6
  107. package/dist/common/ai/llm/miniapp/index.d.ts +0 -6
  108. package/dist/common/ai/llm/miniapp/index.js +0 -59
  109. package/dist/common/ai/llm/miniapp/index.js.map +0 -1
  110. package/dist/common/ai/llm/miniapp/index.mjs +0 -6
  111. package/dist/common/ai/llm/miniapp/index.mjs.map +0 -1
  112. package/dist/common/ai/llm/rn/index.d.mts +0 -6
  113. package/dist/common/ai/llm/rn/index.d.ts +0 -6
  114. package/dist/common/ai/llm/rn/index.js +0 -59
  115. package/dist/common/ai/llm/rn/index.js.map +0 -1
  116. package/dist/common/ai/llm/rn/index.mjs +0 -6
  117. package/dist/common/ai/llm/rn/index.mjs.map +0 -1
  118. package/dist/common/ai/llm/ui/electron/index.d.mts +0 -5
  119. package/dist/common/ai/llm/ui/electron/index.d.ts +0 -5
  120. package/dist/common/ai/llm/ui/electron/index.js +0 -22
  121. package/dist/common/ai/llm/ui/electron/index.js.map +0 -1
  122. package/dist/common/ai/llm/ui/electron/index.mjs +0 -9
  123. package/dist/common/ai/llm/ui/electron/index.mjs.map +0 -1
  124. package/dist/common/ai/llm/ui/miniapp/index.d.mts +0 -9
  125. package/dist/common/ai/llm/ui/miniapp/index.d.ts +0 -9
  126. package/dist/common/ai/llm/ui/miniapp/index.js +0 -14
  127. package/dist/common/ai/llm/ui/miniapp/index.js.map +0 -1
  128. package/dist/common/ai/llm/ui/miniapp/index.mjs +0 -5
  129. package/dist/common/ai/llm/ui/miniapp/index.mjs.map +0 -1
  130. package/dist/common/ai/llm/ui/rn/index.d.mts +0 -9
  131. package/dist/common/ai/llm/ui/rn/index.d.ts +0 -9
  132. package/dist/common/ai/llm/ui/rn/index.js +0 -14
  133. package/dist/common/ai/llm/ui/rn/index.js.map +0 -1
  134. package/dist/common/ai/llm/ui/rn/index.mjs +0 -5
  135. package/dist/common/ai/llm/ui/rn/index.mjs.map +0 -1
  136. package/dist/common/ai/llm/ui/web/index.d.mts +0 -15
  137. package/dist/common/ai/llm/ui/web/index.d.ts +0 -15
  138. package/dist/common/ai/llm/ui/web/index.js +0 -21
  139. package/dist/common/ai/llm/ui/web/index.js.map +0 -1
  140. package/dist/common/ai/llm/ui/web/index.mjs +0 -8
  141. package/dist/common/ai/llm/ui/web/index.mjs.map +0 -1
  142. package/dist/common/ai/llm/web/index.d.mts +0 -6
  143. package/dist/common/ai/llm/web/index.d.ts +0 -6
  144. package/dist/common/ai/llm/web/index.js +0 -66
  145. package/dist/common/ai/llm/web/index.js.map +0 -1
  146. package/dist/common/ai/llm/web/index.mjs +0 -9
  147. package/dist/common/ai/llm/web/index.mjs.map +0 -1
  148. package/dist/index-BMgdH5dL.d.mts +0 -1716
  149. package/dist/index-IXMAeTtN.d.ts +0 -1716
  150. package/dist/types-B2rs_jq1.d.mts +0 -38
  151. package/dist/types-DgACCUpT.d.ts +0 -122
  152. package/dist/types-DwS2Eg0q.d.ts +0 -38
  153. package/dist/types-LU_BGSzk.d.mts +0 -122
  154. /package/dist/common/{ai/llm/core → aiApi}/index.js.map +0 -0
  155. /package/dist/common/{ai/llm/core → aiApi}/index.mjs.map +0 -0
  156. /package/dist/common/{ai/llm/electron → aiApi/server}/index.js.map +0 -0
  157. /package/dist/common/{ai/llm/electron → aiApi/server}/index.mjs.map +0 -0
@@ -0,0 +1,1143 @@
1
+ 'use strict';
2
+
3
+ // src/common/aiApi/types.ts
4
+ var CORE_LLM_COMPLETION_TASK_ID = "core.llmCompletion";
5
+ var CORE_STRUCTURED_MULTIMODAL_TASK_ID = "core.structuredMultimodal";
6
+ var CORE_CONNECTIVITY_TEST_TASK_ID = "core.connectivityTest";
7
+
8
+ // src/common/aiApi/resolveConfig.ts
9
+ var DEFAULT_BASE_URL = "https://api.openai.com/v1";
10
+ var DEFAULT_TEXT_MODEL = "gpt-4o-mini";
11
+ var DEFAULT_VISION_MODEL = "gpt-4o-mini";
12
+ var DEFAULT_AUDIO_MODEL = "whisper-1";
13
+ var DEFAULT_AUDIO_STRATEGY = "auto";
14
+ var DEFAULT_TIMEOUT_MS = 6e4;
15
+ var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
16
+ var DEFAULT_MAX_AUDIO_BYTES = 25 * 1024 * 1024;
17
+ function readEnv(name) {
18
+ if (typeof process === "undefined" || !process.env) {
19
+ return "";
20
+ }
21
+ return process.env[name]?.trim() ?? "";
22
+ }
23
+ function resolveAiConnectionConfig(...sources) {
24
+ const merged = {};
25
+ for (const source of sources) {
26
+ if (!source) continue;
27
+ Object.assign(merged, source);
28
+ }
29
+ const envApiKey = readEnv("AI_API_KEY") || readEnv("OPENAI_API_KEY");
30
+ const apiKey = merged.apiKey?.trim() || envApiKey;
31
+ if (!apiKey) {
32
+ return null;
33
+ }
34
+ const baseUrl = merged.baseUrl?.trim() || readEnv("AI_BASE_URL") || DEFAULT_BASE_URL;
35
+ const visionModel = merged.visionModel?.trim() || readEnv("AI_VISION_MODEL") || merged.model?.trim() || DEFAULT_VISION_MODEL;
36
+ const textModel = merged.textModel?.trim() || readEnv("AI_TEXT_MODEL") || merged.model?.trim() || visionModel || DEFAULT_TEXT_MODEL;
37
+ const audioModel = merged.audioModel?.trim() || readEnv("AI_AUDIO_MODEL") || DEFAULT_AUDIO_MODEL;
38
+ const envAudioStrategy = readEnv("AI_AUDIO_STRATEGY");
39
+ const audioStrategy = merged.audioStrategy ?? (envAudioStrategy || DEFAULT_AUDIO_STRATEGY);
40
+ return {
41
+ apiKey,
42
+ baseUrl,
43
+ model: merged.model?.trim() || textModel,
44
+ textModel,
45
+ visionModel,
46
+ audioModel,
47
+ audioStrategy,
48
+ timeoutMs: merged.timeoutMs ?? Number(readEnv("AI_TIMEOUT_MS") || DEFAULT_TIMEOUT_MS),
49
+ maxImageBytes: merged.maxImageBytes ?? Number(readEnv("AI_MAX_IMAGE_BYTES") || DEFAULT_MAX_IMAGE_BYTES),
50
+ maxAudioBytes: merged.maxAudioBytes ?? Number(readEnv("AI_MAX_AUDIO_BYTES") || DEFAULT_MAX_AUDIO_BYTES)
51
+ };
52
+ }
53
+ function requireAiConnectionConfig(...sources) {
54
+ const config = resolveAiConnectionConfig(...sources);
55
+ if (!config) {
56
+ throw new Error("\u672A\u914D\u7F6E AI API Key\uFF0C\u8BF7\u4F20\u5165 connection / clientSettings \u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AI_API_KEY");
57
+ }
58
+ return config;
59
+ }
60
+
61
+ // src/common/aiApi/requestJson.ts
62
+ async function requestJson(options) {
63
+ const { url, method = "POST", headers = {}, body, timeoutMs, requestAdapter } = options;
64
+ if (requestAdapter) {
65
+ return requestAdapter.request({
66
+ url,
67
+ method,
68
+ headers,
69
+ body
70
+ });
71
+ }
72
+ const controller = timeoutMs ? new AbortController() : void 0;
73
+ const timeoutId = timeoutMs ? setTimeout(() => {
74
+ controller?.abort();
75
+ }, timeoutMs) : void 0;
76
+ try {
77
+ const response = await fetch(url, {
78
+ method,
79
+ headers,
80
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
81
+ signal: controller?.signal
82
+ });
83
+ const text = await response.text();
84
+ let data = null;
85
+ if (text) {
86
+ try {
87
+ data = JSON.parse(text);
88
+ } catch {
89
+ data = text;
90
+ }
91
+ }
92
+ if (!response.ok) {
93
+ const record = data;
94
+ const errorMessage = (typeof record?.error === "object" ? record.error?.message : record?.error) || record?.message || `Request failed with status ${response.status}`;
95
+ const error = new Error(String(errorMessage));
96
+ error.status = response.status;
97
+ error.data = data;
98
+ throw error;
99
+ }
100
+ return data;
101
+ } finally {
102
+ if (timeoutId) {
103
+ clearTimeout(timeoutId);
104
+ }
105
+ }
106
+ }
107
+
108
+ // src/common/aiApi/callChat.ts
109
+ var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
110
+ var DEFAULT_TEXT_MODEL2 = "gpt-4o-mini";
111
+ function joinUrl(base, path) {
112
+ return `${base.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
113
+ }
114
+ function resolveMessages(input) {
115
+ if (input.messages?.length) {
116
+ return input.messages;
117
+ }
118
+ const messages = [];
119
+ if (input.systemPrompt?.trim()) {
120
+ messages.push({ role: "system", content: input.systemPrompt.trim() });
121
+ }
122
+ if (input.userPrompt?.trim()) {
123
+ messages.push({ role: "user", content: input.userPrompt.trim() });
124
+ }
125
+ return messages;
126
+ }
127
+ async function callChat(options) {
128
+ const {
129
+ baseUrl,
130
+ apiKey,
131
+ model,
132
+ systemPrompt,
133
+ userPrompt,
134
+ messages,
135
+ temperature,
136
+ maxTokens,
137
+ topP,
138
+ stop,
139
+ timeoutMs = 6e4,
140
+ requestAdapter
141
+ } = options;
142
+ if (!baseUrl?.trim()) {
143
+ throw new Error("baseUrl \u4E3A\u5FC5\u586B");
144
+ }
145
+ if (!apiKey?.trim()) {
146
+ throw new Error("apiKey \u4E3A\u5FC5\u586B");
147
+ }
148
+ const resolvedMessages = resolveMessages({ systemPrompt, userPrompt, messages });
149
+ if (!resolvedMessages.length) {
150
+ throw new Error("userPrompt \u6216 messages \u81F3\u5C11\u63D0\u4F9B\u4E00\u9879");
151
+ }
152
+ const resolvedModel = model?.trim() || DEFAULT_TEXT_MODEL2;
153
+ const payload = {
154
+ model: resolvedModel,
155
+ messages: resolvedMessages
156
+ };
157
+ if (temperature !== void 0) payload.temperature = temperature;
158
+ if (maxTokens !== void 0) payload.max_tokens = maxTokens;
159
+ if (topP !== void 0) payload.top_p = topP;
160
+ if (stop !== void 0) payload.stop = stop;
161
+ const raw = await requestJson({
162
+ url: joinUrl(baseUrl.trim(), "chat/completions"),
163
+ method: "POST",
164
+ headers: {
165
+ "Content-Type": "application/json",
166
+ Authorization: `Bearer ${apiKey.trim()}`
167
+ },
168
+ body: payload,
169
+ timeoutMs,
170
+ requestAdapter
171
+ });
172
+ const content = raw.choices?.[0]?.message?.content ?? "";
173
+ const usage = raw.usage ? {
174
+ promptTokens: raw.usage.prompt_tokens,
175
+ completionTokens: raw.usage.completion_tokens,
176
+ totalTokens: raw.usage.total_tokens
177
+ } : void 0;
178
+ return {
179
+ content,
180
+ model: raw.model ?? resolvedModel,
181
+ usage,
182
+ raw
183
+ };
184
+ }
185
+
186
+ // src/common/aiApi/callCompletion.ts
187
+ async function callCompletion(params, clientSettings) {
188
+ const config = requireAiConnectionConfig(params.connection, clientSettings);
189
+ const model = params.model || config.textModel;
190
+ const result = await callChat({
191
+ baseUrl: config.baseUrl,
192
+ apiKey: config.apiKey,
193
+ model,
194
+ systemPrompt: params.systemPrompt,
195
+ userPrompt: params.userPrompt,
196
+ temperature: params.temperature,
197
+ maxTokens: params.maxTokens,
198
+ timeoutMs: config.timeoutMs
199
+ });
200
+ return {
201
+ content: result.content,
202
+ model: result.model || model,
203
+ raw: result.raw
204
+ };
205
+ }
206
+
207
+ // src/common/aiApi/modelHeuristics.ts
208
+ var NON_CHAT_PATTERNS = [
209
+ /embed/i,
210
+ /whisper/i,
211
+ /tts/i,
212
+ /dall-e/i,
213
+ /moderation/i,
214
+ /realtime/i,
215
+ /audio/i,
216
+ /transcrib/i,
217
+ /sora/i
218
+ ];
219
+ var VISION_HINT_PATTERNS = [
220
+ /^gpt-4o/i,
221
+ /^gpt-4-turbo/i,
222
+ /^gpt-4-vision/i,
223
+ /^gpt-4\.1/i,
224
+ /claude-3/i,
225
+ /gemini.*(pro|flash|vision)/i,
226
+ /qwen.*vl/i,
227
+ /vision/i,
228
+ /-vl/i,
229
+ /llava/i,
230
+ /doubao.*vision/i,
231
+ /glm-4v/i,
232
+ /internvl/i,
233
+ /pixtral/i,
234
+ /deepseek-vl/i
235
+ ];
236
+ var PREFERRED_VISION_MODELS = [
237
+ "gpt-4o-mini",
238
+ "gpt-4o",
239
+ "gpt-4-turbo",
240
+ "gpt-4-vision-preview",
241
+ "gpt-4.1-mini",
242
+ "gpt-4.1",
243
+ "claude-3-5-sonnet-latest",
244
+ "claude-3-5-haiku-latest",
245
+ "gemini-2.0-flash",
246
+ "gemini-1.5-flash",
247
+ "gemini-1.5-pro",
248
+ "qwen-vl-max",
249
+ "qwen2-vl-72b-instruct"
250
+ ];
251
+ function isChatModel(id) {
252
+ return !NON_CHAT_PATTERNS.some((pattern) => pattern.test(id));
253
+ }
254
+ var NATIVE_AUDIO_CHAT_PATTERNS = [
255
+ /^gpt-4o/i,
256
+ /^gpt-4-turbo/i,
257
+ /^gpt-4\.1/i,
258
+ /gemini-2\.0/i,
259
+ /gemini-1\.5/i
260
+ ];
261
+ var STT_MODEL_PATTERNS = [/whisper/i, /transcrib/i, /sensevoice/i, /paraformer/i];
262
+ function isLikelyNativeAudioChatModel(id) {
263
+ const trimmed = id.trim();
264
+ if (!trimmed) return false;
265
+ if (STT_MODEL_PATTERNS.some((pattern) => pattern.test(trimmed))) return false;
266
+ return NATIVE_AUDIO_CHAT_PATTERNS.some((pattern) => pattern.test(trimmed));
267
+ }
268
+ function isLikelySttModel(id) {
269
+ return STT_MODEL_PATTERNS.some((pattern) => pattern.test(id.trim()));
270
+ }
271
+ function filterSttModels(modelIds) {
272
+ return modelIds.filter(isLikelySttModel).sort((a, b) => a.localeCompare(b));
273
+ }
274
+ var PREFERRED_STT_MODELS = ["whisper-1", "whisper-large-v3", "whisper-large-v3-turbo"];
275
+ function pickDefaultSttModel(modelIds, current) {
276
+ const sttModels = filterSttModels(modelIds);
277
+ if (sttModels.length === 0) return void 0;
278
+ const trimmedCurrent = current?.trim();
279
+ if (trimmedCurrent && sttModels.includes(trimmedCurrent)) {
280
+ return trimmedCurrent;
281
+ }
282
+ for (const preferred of PREFERRED_STT_MODELS) {
283
+ const match = sttModels.find((id) => id === preferred || id.startsWith(`${preferred}-`));
284
+ if (match) return match;
285
+ }
286
+ return sttModels[0];
287
+ }
288
+ function isLikelyVisionModel(id) {
289
+ return VISION_HINT_PATTERNS.some((pattern) => pattern.test(id));
290
+ }
291
+ var TEXT_ONLY_MODEL_PATTERNS = [
292
+ /^deepseek-chat/i,
293
+ /^deepseek-reasoner/i,
294
+ /^deepseek-r1/i,
295
+ /^deepseek-v[34](?!.*vl)/i,
296
+ /^gpt-3\.5/i,
297
+ /^o1-mini/i,
298
+ /^o3-mini/i
299
+ ];
300
+ function isKnownTextOnlyModel(id) {
301
+ const trimmed = id.trim();
302
+ if (!trimmed) return false;
303
+ return TEXT_ONLY_MODEL_PATTERNS.some((pattern) => pattern.test(trimmed));
304
+ }
305
+ function filterChatModels(modelIds) {
306
+ return modelIds.filter(isChatModel).sort((a, b) => a.localeCompare(b));
307
+ }
308
+ function filterVisionModels(modelIds) {
309
+ return filterChatModels(modelIds).filter((id) => isLikelyVisionModel(id) && !isKnownTextOnlyModel(id)).sort((a, b) => a.localeCompare(b));
310
+ }
311
+ function matchesPreferred(modelId, preferred) {
312
+ return modelId === preferred || modelId.startsWith(`${preferred}-`);
313
+ }
314
+ function pickDefaultVisionModel(modelIds, current) {
315
+ const visionModels = filterVisionModels(modelIds);
316
+ if (visionModels.length === 0) return void 0;
317
+ const trimmedCurrent = current?.trim();
318
+ if (trimmedCurrent && visionModels.includes(trimmedCurrent)) {
319
+ return trimmedCurrent;
320
+ }
321
+ for (const preferred of PREFERRED_VISION_MODELS) {
322
+ const match = visionModels.find((id) => matchesPreferred(id, preferred));
323
+ if (match) return match;
324
+ }
325
+ return visionModels[0];
326
+ }
327
+
328
+ // src/common/aiApi/visionMessageFormats.ts
329
+ function detectVisionMessageFormat(baseUrl) {
330
+ const normalized = baseUrl.toLowerCase();
331
+ if (normalized.includes("ollama") || normalized.includes(":11434") || normalized.includes("11434/")) {
332
+ return "ollama";
333
+ }
334
+ return "openai";
335
+ }
336
+ function assertVisionCapableModel(modelId, options) {
337
+ if (!options?.hasImages) return;
338
+ const model = modelId.trim();
339
+ if (!model) {
340
+ throw new Error("\u8BC6\u56FE\u9700\u8981\u9009\u62E9\u89C6\u89C9\u6A21\u578B\uFF0C\u8BF7\u5728 AI \u8BBE\u7F6E\u4E2D\u914D\u7F6E");
341
+ }
342
+ const format = options.baseUrl ? detectVisionMessageFormat(options.baseUrl) : "openai";
343
+ if (format === "ollama") return;
344
+ if (isKnownTextOnlyModel(model)) {
345
+ throw new Error(
346
+ `\u5F53\u524D\u6A21\u578B\u300C${model}\u300D\u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\u3002\u8BF7\u6539\u7528\u89C6\u89C9\u6A21\u578B\uFF0C\u4F8B\u5982 gpt-4o-mini\u3001qwen-vl-max\u3001gemini-1.5-flash\u3001deepseek-vl \u7B49\uFF08DeepSeek \u6587\u672C\u6A21\u578B\u65E0\u6CD5\u8BC6\u56FE\uFF09\u3002`
347
+ );
348
+ }
349
+ if (!isLikelyVisionModel(model)) {
350
+ throw new Error(
351
+ `\u6A21\u578B\u300C${model}\u300D\u53EF\u80FD\u4E0D\u652F\u6301\u56FE\u7247\u8BC6\u56FE\u3002\u8BF7\u5728 AI \u8BBE\u7F6E\u4E2D\u9009\u62E9\u540D\u79F0\u542B vl\u3001vision\u3001gpt-4o\u3001gemini \u7B49\u6807\u8BC6\u7684\u89C6\u89C9\u6A21\u578B\u3002`
352
+ );
353
+ }
354
+ }
355
+ function isImageUrlVariantError(message) {
356
+ return /unknown variant [`']?image_url[`']?/i.test(message) || /expected [`']?text[`']?/i.test(message) || /does not support.*image/i.test(message);
357
+ }
358
+ function toVisionApiErrorMessage(rawMessage, modelId) {
359
+ if (isImageUrlVariantError(rawMessage)) {
360
+ return `\u5F53\u524D\u6A21\u578B\u300C${modelId}\u300D\u4E0D\u63A5\u53D7\u56FE\u7247\u8BF7\u6C42\uFF08${rawMessage}\uFF09\u3002\u8BF7\u66F4\u6362\u4E3A\u652F\u6301\u8BC6\u56FE\u7684\u89C6\u89C9\u6A21\u578B\u3002`;
361
+ }
362
+ return rawMessage;
363
+ }
364
+
365
+ // src/common/aiApi/audioStrategy.ts
366
+ function resolveAudioHandling(options) {
367
+ if (!options.hasAudio) {
368
+ return "none";
369
+ }
370
+ if (options.strategy === "stt") {
371
+ return "stt";
372
+ }
373
+ if (options.strategy === "native") {
374
+ return "native";
375
+ }
376
+ if (detectVisionMessageFormat(options.baseUrl) === "ollama") {
377
+ return "stt";
378
+ }
379
+ if (isLikelyNativeAudioChatModel(options.model)) {
380
+ return "native";
381
+ }
382
+ return "stt";
383
+ }
384
+ function isAudioInputError(message) {
385
+ return /input_audio/i.test(message) || /unknown variant [`']?input_audio[`']?/i.test(message) || /does not support.*audio/i.test(message) || /audio input/i.test(message) || /invalid audio/i.test(message);
386
+ }
387
+ function appendTranscriptionsToPrompt(userPrompt, transcriptions) {
388
+ if (transcriptions.length === 0) {
389
+ return userPrompt;
390
+ }
391
+ const blocks = transcriptions.map((text, index) => {
392
+ const label = transcriptions.length === 1 ? "[\u8BED\u97F3\u8F6C\u5199]" : `[\u8BED\u97F3\u8F6C\u5199 ${index + 1}]`;
393
+ return `${label}
394
+ ${text.trim()}`;
395
+ });
396
+ const joined = blocks.join("\n\n");
397
+ const trimmedPrompt = userPrompt.trim();
398
+ return trimmedPrompt ? `${trimmedPrompt}
399
+
400
+ ${joined}` : joined;
401
+ }
402
+
403
+ // src/common/aiApi/audioUtils.ts
404
+ var ALLOWED_MIME = /* @__PURE__ */ new Set([
405
+ "audio/wav",
406
+ "audio/x-wav",
407
+ "audio/mpeg",
408
+ "audio/mp3",
409
+ "audio/mp4",
410
+ "audio/webm",
411
+ "audio/ogg",
412
+ "audio/flac"
413
+ ]);
414
+ var MIME_TO_FORMAT = {
415
+ "audio/wav": "wav",
416
+ "audio/x-wav": "wav",
417
+ "audio/mpeg": "mp3",
418
+ "audio/mp3": "mp3",
419
+ "audio/mp4": "mp4",
420
+ "audio/webm": "webm",
421
+ "audio/ogg": "ogg",
422
+ "audio/flac": "flac"
423
+ };
424
+ function mimeToAudioFormat(mimeType) {
425
+ return MIME_TO_FORMAT[mimeType] ?? mimeType.split("/").pop() ?? "wav";
426
+ }
427
+ function assertValidAudioInput(audio, maxAudioBytes = 25 * 1024 * 1024) {
428
+ if (!audio.base64?.trim()) {
429
+ throw new Error("\u97F3\u9891\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A");
430
+ }
431
+ if (!ALLOWED_MIME.has(audio.mimeType)) {
432
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u97F3\u9891\u683C\u5F0F: ${audio.mimeType}`);
433
+ }
434
+ const byteLength = estimateBase64ByteLength(audio.base64);
435
+ if (byteLength > maxAudioBytes) {
436
+ throw new Error(`\u97F3\u9891\u8FC7\u5927\uFF0C\u6700\u5927 ${Math.round(maxAudioBytes / 1024 / 1024)}MB`);
437
+ }
438
+ }
439
+ function estimateBase64ByteLength(base64) {
440
+ if (typeof Buffer !== "undefined") {
441
+ return Buffer.byteLength(base64, "base64");
442
+ }
443
+ const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
444
+ return Math.floor(base64.length * 3 / 4) - padding;
445
+ }
446
+ async function fileToAiAudioInput(file) {
447
+ const buffer = await file.arrayBuffer();
448
+ if (typeof Buffer !== "undefined") {
449
+ return {
450
+ base64: Buffer.from(buffer).toString("base64"),
451
+ mimeType: file.type || "audio/wav"
452
+ };
453
+ }
454
+ const bytes = new Uint8Array(buffer);
455
+ let binary = "";
456
+ for (let index = 0; index < bytes.length; index += 1) {
457
+ binary += String.fromCharCode(bytes[index]);
458
+ }
459
+ return { base64: btoa(binary), mimeType: file.type || "audio/wav" };
460
+ }
461
+ function base64ToBlob(base64, mimeType) {
462
+ if (typeof Buffer !== "undefined") {
463
+ const bytes2 = Buffer.from(base64, "base64");
464
+ return new Blob([bytes2], { type: mimeType });
465
+ }
466
+ const binary = atob(base64);
467
+ const bytes = new Uint8Array(binary.length);
468
+ for (let index = 0; index < binary.length; index += 1) {
469
+ bytes[index] = binary.charCodeAt(index);
470
+ }
471
+ return new Blob([bytes], { type: mimeType });
472
+ }
473
+
474
+ // src/common/aiApi/multimodalMessageFormats.ts
475
+ function buildMultimodalMessages(options) {
476
+ const { systemPrompt, userPrompt, images, nativeAudios, format } = options;
477
+ const hasImages = images.length > 0;
478
+ const hasNativeAudio = nativeAudios.length > 0;
479
+ if (format === "ollama" && hasImages) {
480
+ return [
481
+ { role: "system", content: systemPrompt },
482
+ {
483
+ role: "user",
484
+ content: userPrompt,
485
+ images: images.map((image) => image.base64)
486
+ }
487
+ ];
488
+ }
489
+ const userContent = [{ type: "text", text: userPrompt }];
490
+ for (const image of images) {
491
+ userContent.push({
492
+ type: "image_url",
493
+ image_url: {
494
+ url: `data:${image.mimeType};base64,${image.base64}`
495
+ }
496
+ });
497
+ }
498
+ for (const audio of nativeAudios) {
499
+ userContent.push({
500
+ type: "input_audio",
501
+ input_audio: {
502
+ data: audio.base64,
503
+ format: mimeToAudioFormat(audio.mimeType)
504
+ }
505
+ });
506
+ }
507
+ const useStructuredContent = hasImages || hasNativeAudio;
508
+ return [
509
+ { role: "system", content: systemPrompt },
510
+ { role: "user", content: useStructuredContent ? userContent : userPrompt }
511
+ ];
512
+ }
513
+ function assertMultimodalCapableModel(modelId, options) {
514
+ assertVisionCapableModel(modelId, {
515
+ baseUrl: options.baseUrl,
516
+ hasImages: options.hasImages
517
+ });
518
+ if (!options.hasNativeAudio) return;
519
+ const format = options.baseUrl ? detectVisionMessageFormat(options.baseUrl) : "openai";
520
+ if (format === "ollama") {
521
+ throw new Error('\u5F53\u524D Ollama \u8FDE\u63A5\u4E0D\u652F\u6301 chat \u5185\u5D4C\u97F3\u9891\uFF0C\u8BF7\u6539\u7528 audioStrategy: "stt" \u6216 "auto"');
522
+ }
523
+ }
524
+
525
+ // src/common/aiApi/imageUtils.ts
526
+ var ALLOWED_MIME2 = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
527
+ function assertValidImageInput(image, maxImageBytes = 5 * 1024 * 1024) {
528
+ if (!image.base64?.trim()) {
529
+ throw new Error("\u56FE\u7247\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A");
530
+ }
531
+ if (!ALLOWED_MIME2.has(image.mimeType)) {
532
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${image.mimeType}`);
533
+ }
534
+ const byteLength = estimateBase64ByteLength2(image.base64);
535
+ if (byteLength > maxImageBytes) {
536
+ throw new Error(`\u56FE\u7247\u8FC7\u5927\uFF0C\u6700\u5927 ${Math.round(maxImageBytes / 1024 / 1024)}MB`);
537
+ }
538
+ }
539
+ function estimateBase64ByteLength2(base64) {
540
+ if (typeof Buffer !== "undefined") {
541
+ return Buffer.byteLength(base64, "base64");
542
+ }
543
+ const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
544
+ return Math.floor(base64.length * 3 / 4) - padding;
545
+ }
546
+ async function fileToAiImageInput(file) {
547
+ const buffer = await file.arrayBuffer();
548
+ if (typeof Buffer !== "undefined") {
549
+ return {
550
+ base64: Buffer.from(buffer).toString("base64"),
551
+ mimeType: file.type || "image/jpeg"
552
+ };
553
+ }
554
+ const bytes = new Uint8Array(buffer);
555
+ let binary = "";
556
+ for (let index = 0; index < bytes.length; index += 1) {
557
+ binary += String.fromCharCode(bytes[index]);
558
+ }
559
+ const base64 = btoa(binary);
560
+ return { base64, mimeType: file.type || "image/jpeg" };
561
+ }
562
+
563
+ // src/common/aiApi/mediaUtils.ts
564
+ function splitMediaByKind(media) {
565
+ const images = [];
566
+ const audios = [];
567
+ for (const item of media) {
568
+ if (item.kind === "image") {
569
+ images.push(item);
570
+ } else {
571
+ audios.push(item);
572
+ }
573
+ }
574
+ return { images, audios };
575
+ }
576
+ function assertValidMultimodalMedia(media, limits) {
577
+ const items = media ?? [];
578
+ const { images, audios } = splitMediaByKind(items);
579
+ const maxImages = limits.maxImages ?? 8;
580
+ const maxAudios = limits.maxAudios ?? 4;
581
+ if (images.length > maxImages) {
582
+ throw new Error(`\u56FE\u7247\u6570\u91CF\u8FC7\u591A\uFF0C\u6700\u591A ${maxImages} \u5F20`);
583
+ }
584
+ if (audios.length > maxAudios) {
585
+ throw new Error(`\u97F3\u9891\u6570\u91CF\u8FC7\u591A\uFF0C\u6700\u591A ${maxAudios} \u6BB5`);
586
+ }
587
+ for (const image of images) {
588
+ assertValidImageInput(
589
+ { base64: image.base64, mimeType: image.mimeType },
590
+ limits.maxImageBytes
591
+ );
592
+ }
593
+ for (const audio of audios) {
594
+ assertValidAudioInput(
595
+ { base64: audio.base64, mimeType: audio.mimeType },
596
+ limits.maxAudioBytes
597
+ );
598
+ }
599
+ return items;
600
+ }
601
+
602
+ // src/common/aiApi/transcribeAudio.ts
603
+ function joinUrl2(baseUrl, path) {
604
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
605
+ }
606
+ async function transcribeAudio(options) {
607
+ const { audio, config, model, language } = options;
608
+ const sttModel = model?.trim() || config.audioModel;
609
+ const blob = base64ToBlob(audio.base64, audio.mimeType);
610
+ const extension = mimeToAudioFormat(audio.mimeType);
611
+ const form = new FormData();
612
+ form.append("file", blob, `audio.${extension}`);
613
+ form.append("model", sttModel);
614
+ if (language?.trim()) {
615
+ form.append("language", language.trim());
616
+ }
617
+ const controller = new AbortController();
618
+ const timeoutId = setTimeout(() => controller.abort(), config.timeoutMs);
619
+ try {
620
+ const response = await fetch(joinUrl2(config.baseUrl, "audio/transcriptions"), {
621
+ method: "POST",
622
+ headers: {
623
+ Authorization: `Bearer ${config.apiKey}`
624
+ },
625
+ body: form,
626
+ signal: controller.signal
627
+ });
628
+ const text = await response.text();
629
+ let data = null;
630
+ if (text) {
631
+ try {
632
+ data = JSON.parse(text);
633
+ } catch {
634
+ data = text;
635
+ }
636
+ }
637
+ if (!response.ok) {
638
+ const record = data;
639
+ const errorMessage = (typeof record?.error === "object" ? record.error?.message : record?.error) || record?.message || `STT \u8BF7\u6C42\u5931\u8D25 (${response.status})`;
640
+ throw new Error(String(errorMessage));
641
+ }
642
+ if (typeof data === "string") {
643
+ return data.trim();
644
+ }
645
+ const parsed = data;
646
+ return (parsed.text ?? "").trim();
647
+ } finally {
648
+ clearTimeout(timeoutId);
649
+ }
650
+ }
651
+ async function transcribeAudios(audios, config, model) {
652
+ const results = [];
653
+ for (const audio of audios) {
654
+ results.push(await transcribeAudio({ audio, config, model }));
655
+ }
656
+ return results;
657
+ }
658
+
659
+ // src/common/aiApi/callMultimodalChat.ts
660
+ function joinUrl3(baseUrl, path) {
661
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
662
+ }
663
+ async function requestMultimodalChat(options) {
664
+ const payload = {
665
+ model: options.model,
666
+ messages: options.messages,
667
+ temperature: options.temperature ?? 0.2
668
+ };
669
+ if (options.maxTokens !== void 0) {
670
+ payload.max_tokens = options.maxTokens;
671
+ }
672
+ if (options.jsonMode) {
673
+ payload.response_format = { type: "json_object" };
674
+ }
675
+ const raw = await requestJson({
676
+ url: joinUrl3(options.config.baseUrl, "chat/completions"),
677
+ method: "POST",
678
+ headers: {
679
+ "Content-Type": "application/json",
680
+ Authorization: `Bearer ${options.config.apiKey}`
681
+ },
682
+ body: payload,
683
+ timeoutMs: options.config.timeoutMs
684
+ });
685
+ return {
686
+ content: raw.choices?.[0]?.message?.content ?? "",
687
+ model: raw.model ?? options.model,
688
+ raw
689
+ };
690
+ }
691
+ function toAudioInputs(audios) {
692
+ return audios.map((audio) => ({ base64: audio.base64, mimeType: audio.mimeType }));
693
+ }
694
+ async function callMultimodalChat(params, clientSettings) {
695
+ const config = requireAiConnectionConfig(params.connection, clientSettings);
696
+ const media = assertValidMultimodalMedia(params.media, {
697
+ maxImageBytes: config.maxImageBytes,
698
+ maxAudioBytes: config.maxAudioBytes
699
+ });
700
+ const { images, audios } = splitMediaByKind(media);
701
+ const hasImages = images.length > 0;
702
+ const hasAudio = audios.length > 0;
703
+ const model = params.model || (hasImages ? config.visionModel : hasAudio ? config.visionModel : config.textModel);
704
+ const strategy = params.audioStrategy ?? config.audioStrategy;
705
+ const format = detectVisionMessageFormat(config.baseUrl);
706
+ let audioHandling = resolveAudioHandling({
707
+ hasAudio,
708
+ strategy,
709
+ model,
710
+ baseUrl: config.baseUrl
711
+ });
712
+ let userPrompt = params.userPrompt;
713
+ let transcriptions;
714
+ let nativeAudios = [];
715
+ if (audioHandling === "stt" && hasAudio) {
716
+ transcriptions = await transcribeAudios(toAudioInputs(audios), config);
717
+ userPrompt = appendTranscriptionsToPrompt(userPrompt, transcriptions);
718
+ } else if (audioHandling === "native" && hasAudio) {
719
+ nativeAudios = audios;
720
+ }
721
+ assertMultimodalCapableModel(model, {
722
+ baseUrl: config.baseUrl,
723
+ hasImages,
724
+ hasNativeAudio: nativeAudios.length > 0
725
+ });
726
+ const messages = buildMultimodalMessages({
727
+ systemPrompt: params.systemPrompt,
728
+ userPrompt,
729
+ images,
730
+ nativeAudios,
731
+ format
732
+ });
733
+ try {
734
+ const result = await requestMultimodalChat({
735
+ config,
736
+ model,
737
+ messages,
738
+ temperature: params.temperature,
739
+ maxTokens: params.maxTokens,
740
+ jsonMode: params.jsonMode
741
+ });
742
+ return {
743
+ ...result,
744
+ audioHandling,
745
+ transcriptions
746
+ };
747
+ } catch (error) {
748
+ const message = error instanceof Error ? error.message : "AI \u8BF7\u6C42\u5931\u8D25";
749
+ if (audioHandling === "native" && hasAudio && strategy === "auto" && isAudioInputError(message)) {
750
+ transcriptions = await transcribeAudios(toAudioInputs(audios), config);
751
+ userPrompt = appendTranscriptionsToPrompt(params.userPrompt, transcriptions);
752
+ audioHandling = "stt";
753
+ const fallbackMessages = buildMultimodalMessages({
754
+ systemPrompt: params.systemPrompt,
755
+ userPrompt,
756
+ images,
757
+ nativeAudios: [],
758
+ format
759
+ });
760
+ const result = await requestMultimodalChat({
761
+ config,
762
+ model,
763
+ messages: fallbackMessages,
764
+ temperature: params.temperature,
765
+ maxTokens: params.maxTokens,
766
+ jsonMode: params.jsonMode
767
+ });
768
+ return {
769
+ ...result,
770
+ audioHandling,
771
+ transcriptions
772
+ };
773
+ }
774
+ throw new Error(toVisionApiErrorMessage(message, model));
775
+ }
776
+ }
777
+
778
+ // src/common/aiApi/jsonUtils.ts
779
+ function extractJsonObject(text) {
780
+ const trimmed = text.trim();
781
+ if (!trimmed) {
782
+ throw new Error("\u6A21\u578B\u8FD4\u56DE\u4E3A\u7A7A");
783
+ }
784
+ try {
785
+ const parsed = JSON.parse(trimmed);
786
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
787
+ return parsed;
788
+ }
789
+ } catch {
790
+ }
791
+ const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
792
+ if (fenced?.[1]) {
793
+ const parsed = JSON.parse(fenced[1].trim());
794
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
795
+ return parsed;
796
+ }
797
+ }
798
+ const start = trimmed.indexOf("{");
799
+ const end = trimmed.lastIndexOf("}");
800
+ if (start >= 0 && end > start) {
801
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
802
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
803
+ return parsed;
804
+ }
805
+ }
806
+ throw new Error("\u65E0\u6CD5\u89E3\u6790\u6A21\u578B JSON \u8F93\u51FA");
807
+ }
808
+
809
+ // src/common/aiApi/listModels.ts
810
+ function joinUrl4(baseUrl, path) {
811
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
812
+ }
813
+ function parseModelIds(raw) {
814
+ if (Array.isArray(raw.data)) {
815
+ return raw.data.map((item) => item.id).filter((id) => Boolean(id));
816
+ }
817
+ if (Array.isArray(raw.models)) {
818
+ return raw.models.map((item) => typeof item === "string" ? item : item.id).filter((id) => Boolean(id));
819
+ }
820
+ return [];
821
+ }
822
+ async function listOpenAiCompatibleModels(clientSettings, currentVisionModel) {
823
+ const config = resolveAiConnectionConfig(clientSettings);
824
+ if (!config) {
825
+ throw new Error("\u672A\u914D\u7F6E AI API Key\uFF0C\u8BF7\u5728\u8BBE\u7F6E\u4E2D\u586B\u5199\u6216\u914D\u7F6E\u670D\u52A1\u7AEF\u73AF\u5883\u53D8\u91CF");
826
+ }
827
+ const raw = await requestJson({
828
+ url: joinUrl4(config.baseUrl, "models"),
829
+ method: "GET",
830
+ headers: {
831
+ Authorization: `Bearer ${config.apiKey}`
832
+ },
833
+ timeoutMs: config.timeoutMs
834
+ });
835
+ const modelIds = parseModelIds(raw);
836
+ if (modelIds.length === 0) {
837
+ throw new Error("\u63A5\u53E3\u672A\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
838
+ }
839
+ const models = filterChatModels(modelIds);
840
+ const visionModels = filterVisionModels(modelIds);
841
+ const suggestedVisionModel = pickDefaultVisionModel(modelIds, currentVisionModel);
842
+ return { models, visionModels, suggestedVisionModel };
843
+ }
844
+
845
+ // src/common/aiApi/taskRegistry.ts
846
+ var registry = /* @__PURE__ */ new Map();
847
+ function registerAiTask(task) {
848
+ if (registry.has(task.id)) {
849
+ console.warn(`[aiApi] task "${task.id}" already registered, skipping duplicate`);
850
+ return;
851
+ }
852
+ registry.set(task.id, task);
853
+ }
854
+ function getAiTask(taskId) {
855
+ return registry.get(taskId);
856
+ }
857
+ function listAiTasks() {
858
+ return Array.from(registry.keys());
859
+ }
860
+ function clearAiTasksForTest() {
861
+ registry.clear();
862
+ }
863
+
864
+ // src/common/aiApi/runTask.ts
865
+ function extractInputConnection(input) {
866
+ if (!input || typeof input !== "object") return void 0;
867
+ const connection = input.connection;
868
+ return connection && typeof connection === "object" ? connection : void 0;
869
+ }
870
+ async function runAiTask(taskId, input, ctx = {}) {
871
+ const started = Date.now();
872
+ if (!resolveAiConnectionConfig(extractInputConnection(input), ctx.clientSettings)) {
873
+ return {
874
+ success: false,
875
+ taskId,
876
+ error: {
877
+ code: "AI_CONFIG_MISSING",
878
+ message: "\u672A\u914D\u7F6E AI API Key\uFF0C\u8BF7\u4F20\u5165 clientSettings.connection \u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AI_API_KEY"
879
+ }
880
+ };
881
+ }
882
+ const task = getAiTask(taskId);
883
+ if (!task) {
884
+ return {
885
+ success: false,
886
+ taskId,
887
+ error: {
888
+ code: "TASK_NOT_FOUND",
889
+ message: `\u672A\u6CE8\u518C\u7684\u4EFB\u52A1: ${taskId}`
890
+ }
891
+ };
892
+ }
893
+ try {
894
+ const validated = task.validateInput(input);
895
+ const result = await task.execute(validated, ctx);
896
+ return {
897
+ success: true,
898
+ taskId,
899
+ data: result.data,
900
+ meta: {
901
+ model: result.meta?.model ?? "unknown",
902
+ latencyMs: Date.now() - started,
903
+ provider: result.meta?.provider ?? "openai-compatible",
904
+ confidence: result.meta?.confidence,
905
+ rawSummary: result.meta?.rawSummary
906
+ }
907
+ };
908
+ } catch (error) {
909
+ const message = error instanceof Error ? error.message : "AI \u4EFB\u52A1\u6267\u884C\u5931\u8D25";
910
+ const code = classifyError(message);
911
+ return {
912
+ success: false,
913
+ taskId,
914
+ error: { code, message },
915
+ meta: {
916
+ model: "unknown",
917
+ latencyMs: Date.now() - started
918
+ }
919
+ };
920
+ }
921
+ }
922
+ function classifyError(message) {
923
+ if (message.includes("\u672A\u914D\u7F6E") || message.includes("AI_API_KEY") || message.includes("apiKey")) {
924
+ return "AI_CONFIG_MISSING";
925
+ }
926
+ if (message.includes("\u56FE\u7247") || message.includes("\u683C\u5F0F")) {
927
+ return message.includes("\u8FC7\u5927") ? "PAYLOAD_TOO_LARGE" : "UNSUPPORTED_MEDIA";
928
+ }
929
+ if (message.includes("\u89E3\u6790") || message.includes("JSON")) {
930
+ return "AI_PARSE_FAILED";
931
+ }
932
+ if (message.includes("\u5FC5\u586B") || message.includes("\u65E0\u6548")) {
933
+ return "INVALID_INPUT";
934
+ }
935
+ return "AI_REQUEST_FAILED";
936
+ }
937
+
938
+ // src/common/aiApi/tasks/coreLlmCompletion.ts
939
+ function isTextCompletionInput(input) {
940
+ if (!input || typeof input !== "object") return false;
941
+ const value = input;
942
+ return typeof value.userPrompt === "string" && value.userPrompt.trim().length > 0;
943
+ }
944
+ var coreLlmCompletionTask = {
945
+ id: CORE_LLM_COMPLETION_TASK_ID,
946
+ description: "\u901A\u7528\u6587\u672C\u8865\u5168\uFF1Asystem/user \u63D0\u793A\u8BCD \u2192 \u6A21\u578B\u6587\u672C",
947
+ validateInput(input) {
948
+ if (!isTextCompletionInput(input)) {
949
+ throw new Error("userPrompt \u4E3A\u5FC5\u586B");
950
+ }
951
+ return input;
952
+ },
953
+ async execute(input, ctx) {
954
+ const result = await callCompletion(
955
+ {
956
+ systemPrompt: input.systemPrompt,
957
+ userPrompt: input.userPrompt,
958
+ model: input.model,
959
+ temperature: input.temperature,
960
+ maxTokens: input.maxTokens,
961
+ connection: input.connection
962
+ },
963
+ ctx.clientSettings
964
+ );
965
+ return {
966
+ data: {
967
+ content: result.content,
968
+ rawText: result.content
969
+ },
970
+ meta: {
971
+ model: result.model,
972
+ provider: "openai-compatible"
973
+ }
974
+ };
975
+ }
976
+ };
977
+
978
+ // src/common/aiApi/tasks/coreStructuredMultimodal.ts
979
+ function isStructuredMultimodalInput(input) {
980
+ if (!input || typeof input !== "object") return false;
981
+ const value = input;
982
+ return typeof value.systemPrompt === "string" && typeof value.userPrompt === "string";
983
+ }
984
+ var coreStructuredMultimodalTask = {
985
+ id: CORE_STRUCTURED_MULTIMODAL_TASK_ID,
986
+ description: "\u901A\u7528\u7ED3\u6784\u5316\u591A\u6A21\u6001\u4EFB\u52A1\uFF1A\u6587\u672C + \u53EF\u9009\u56FE\u7247/\u8BED\u97F3 \u2192 JSON",
987
+ validateInput(input) {
988
+ if (!isStructuredMultimodalInput(input)) {
989
+ throw new Error("systemPrompt \u4E0E userPrompt \u4E3A\u5FC5\u586B");
990
+ }
991
+ const config = resolveAiConnectionConfig(input.connection);
992
+ assertValidMultimodalMedia(input.media, {
993
+ maxImageBytes: config?.maxImageBytes ?? 5 * 1024 * 1024,
994
+ maxAudioBytes: config?.maxAudioBytes ?? 25 * 1024 * 1024
995
+ });
996
+ return input;
997
+ },
998
+ async execute(input, ctx) {
999
+ const schemaHint = input.jsonSchemaHint ? `
1000
+
1001
+ \u8BF7\u4E25\u683C\u8F93\u51FA JSON \u5BF9\u8C61\uFF0C\u7ED3\u6784\u53C2\u8003\uFF1A
1002
+ ${input.jsonSchemaHint}` : "\n\n\u8BF7\u4E25\u683C\u8F93\u51FA JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u5305\u542B Markdown \u4EE3\u7801\u5757\u3002";
1003
+ const result = await callMultimodalChat(
1004
+ {
1005
+ systemPrompt: input.systemPrompt,
1006
+ userPrompt: `${input.userPrompt}${schemaHint}`,
1007
+ media: input.media,
1008
+ model: input.model,
1009
+ temperature: input.temperature ?? 0.2,
1010
+ maxTokens: input.maxTokens,
1011
+ jsonMode: true,
1012
+ audioStrategy: input.audioStrategy,
1013
+ connection: input.connection
1014
+ },
1015
+ ctx.clientSettings
1016
+ );
1017
+ const json = extractJsonObject(result.content);
1018
+ return {
1019
+ data: {
1020
+ json,
1021
+ rawText: result.content
1022
+ },
1023
+ meta: {
1024
+ model: result.model,
1025
+ provider: "openai-compatible",
1026
+ rawSummary: result.audioHandling ? `audio=${result.audioHandling}` : void 0
1027
+ }
1028
+ };
1029
+ }
1030
+ };
1031
+
1032
+ // src/common/aiApi/tasks/coreConnectivityTest.ts
1033
+ var coreConnectivityTestTask = {
1034
+ id: CORE_CONNECTIVITY_TEST_TASK_ID,
1035
+ description: "\u6D4B\u8BD5 AI API \u8FDE\u901A\u6027\uFF08\u8F7B\u91CF\u6587\u672C\u8BF7\u6C42\uFF09",
1036
+ validateInput() {
1037
+ return {};
1038
+ },
1039
+ async execute(_input, ctx) {
1040
+ const config = resolveAiConnectionConfig(ctx.clientSettings);
1041
+ if (!config) {
1042
+ throw new Error("\u672A\u914D\u7F6E AI API Key");
1043
+ }
1044
+ const result = await callChat({
1045
+ baseUrl: config.baseUrl,
1046
+ apiKey: config.apiKey,
1047
+ model: config.textModel,
1048
+ systemPrompt: "You are a connectivity probe. Reply with JSON only.",
1049
+ userPrompt: 'Return JSON: {"ok":true,"reply":"OK"}',
1050
+ temperature: 0,
1051
+ maxTokens: 32,
1052
+ timeoutMs: config.timeoutMs
1053
+ });
1054
+ let ok = false;
1055
+ let reply = result.content.trim();
1056
+ try {
1057
+ const json = extractJsonObject(result.content);
1058
+ ok = json.ok === true || json.ok === "true";
1059
+ reply = String(json.reply ?? result.content).trim();
1060
+ } catch {
1061
+ ok = /ok/i.test(result.content);
1062
+ }
1063
+ if (!ok && !reply) {
1064
+ throw new Error("\u6A21\u578B\u672A\u8FD4\u56DE\u6709\u6548\u54CD\u5E94");
1065
+ }
1066
+ return {
1067
+ data: { ok: true, reply: reply || "OK" },
1068
+ meta: {
1069
+ model: result.model,
1070
+ provider: "openai-compatible"
1071
+ }
1072
+ };
1073
+ }
1074
+ };
1075
+
1076
+ // src/common/aiApi/registerCoreTasks.ts
1077
+ var registered = false;
1078
+ function registerCoreAiTasks() {
1079
+ if (registered) return;
1080
+ registerAiTask(coreLlmCompletionTask);
1081
+ registerAiTask(coreStructuredMultimodalTask);
1082
+ registerAiTask(coreConnectivityTestTask);
1083
+ registered = true;
1084
+ }
1085
+ var ensureCoreAiTasksRegistered = registerCoreAiTasks;
1086
+ function resetCoreAiTasksForTest() {
1087
+ registered = false;
1088
+ }
1089
+
1090
+ exports.CORE_CONNECTIVITY_TEST_TASK_ID = CORE_CONNECTIVITY_TEST_TASK_ID;
1091
+ exports.CORE_LLM_COMPLETION_TASK_ID = CORE_LLM_COMPLETION_TASK_ID;
1092
+ exports.CORE_STRUCTURED_MULTIMODAL_TASK_ID = CORE_STRUCTURED_MULTIMODAL_TASK_ID;
1093
+ exports.DEFAULT_OPENAI_BASE_URL = DEFAULT_OPENAI_BASE_URL;
1094
+ exports.DEFAULT_TEXT_MODEL = DEFAULT_TEXT_MODEL2;
1095
+ exports.appendTranscriptionsToPrompt = appendTranscriptionsToPrompt;
1096
+ exports.assertMultimodalCapableModel = assertMultimodalCapableModel;
1097
+ exports.assertValidAudioInput = assertValidAudioInput;
1098
+ exports.assertValidImageInput = assertValidImageInput;
1099
+ exports.assertValidMultimodalMedia = assertValidMultimodalMedia;
1100
+ exports.assertVisionCapableModel = assertVisionCapableModel;
1101
+ exports.base64ToBlob = base64ToBlob;
1102
+ exports.buildMultimodalMessages = buildMultimodalMessages;
1103
+ exports.callChat = callChat;
1104
+ exports.callCompletion = callCompletion;
1105
+ exports.callMultimodalChat = callMultimodalChat;
1106
+ exports.clearAiTasksForTest = clearAiTasksForTest;
1107
+ exports.coreConnectivityTestTask = coreConnectivityTestTask;
1108
+ exports.coreLlmCompletionTask = coreLlmCompletionTask;
1109
+ exports.coreStructuredMultimodalTask = coreStructuredMultimodalTask;
1110
+ exports.detectVisionMessageFormat = detectVisionMessageFormat;
1111
+ exports.ensureCoreAiTasksRegistered = ensureCoreAiTasksRegistered;
1112
+ exports.extractJsonObject = extractJsonObject;
1113
+ exports.fileToAiAudioInput = fileToAiAudioInput;
1114
+ exports.fileToAiImageInput = fileToAiImageInput;
1115
+ exports.filterChatModels = filterChatModels;
1116
+ exports.filterSttModels = filterSttModels;
1117
+ exports.filterVisionModels = filterVisionModels;
1118
+ exports.getAiTask = getAiTask;
1119
+ exports.isAudioInputError = isAudioInputError;
1120
+ exports.isImageUrlVariantError = isImageUrlVariantError;
1121
+ exports.isKnownTextOnlyModel = isKnownTextOnlyModel;
1122
+ exports.isLikelyNativeAudioChatModel = isLikelyNativeAudioChatModel;
1123
+ exports.isLikelySttModel = isLikelySttModel;
1124
+ exports.isLikelyVisionModel = isLikelyVisionModel;
1125
+ exports.listAiTasks = listAiTasks;
1126
+ exports.listOpenAiCompatibleModels = listOpenAiCompatibleModels;
1127
+ exports.mimeToAudioFormat = mimeToAudioFormat;
1128
+ exports.pickDefaultSttModel = pickDefaultSttModel;
1129
+ exports.pickDefaultVisionModel = pickDefaultVisionModel;
1130
+ exports.registerAiTask = registerAiTask;
1131
+ exports.registerCoreAiTasks = registerCoreAiTasks;
1132
+ exports.requestJson = requestJson;
1133
+ exports.requireAiConnectionConfig = requireAiConnectionConfig;
1134
+ exports.resetCoreAiTasksForTest = resetCoreAiTasksForTest;
1135
+ exports.resolveAiConnectionConfig = resolveAiConnectionConfig;
1136
+ exports.resolveAudioHandling = resolveAudioHandling;
1137
+ exports.runAiTask = runAiTask;
1138
+ exports.splitMediaByKind = splitMediaByKind;
1139
+ exports.toVisionApiErrorMessage = toVisionApiErrorMessage;
1140
+ exports.transcribeAudio = transcribeAudio;
1141
+ exports.transcribeAudios = transcribeAudios;
1142
+ //# sourceMappingURL=chunk-XPY45Y75.js.map
1143
+ //# sourceMappingURL=chunk-XPY45Y75.js.map