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.
- package/dist/business/index.d.mts +2 -7
- package/dist/business/index.d.ts +2 -7
- package/dist/business/index.js +1761 -8045
- package/dist/business/index.js.map +1 -1
- package/dist/business/index.mjs +1169 -7452
- package/dist/business/index.mjs.map +1 -1
- package/dist/business/portfolio/index.d.mts +1 -2
- package/dist/business/portfolio/index.d.ts +1 -2
- package/dist/{chunk-KVYHCGRY.js → chunk-7Z3XR2Y4.js} +552 -263
- package/dist/chunk-7Z3XR2Y4.js.map +1 -0
- package/dist/chunk-XPY45Y75.js +1143 -0
- package/dist/chunk-XPY45Y75.js.map +1 -0
- package/dist/{chunk-YIRPPMCN.mjs → chunk-XSTMLLJV.mjs} +474 -198
- package/dist/chunk-XSTMLLJV.mjs.map +1 -0
- package/dist/chunk-ZJLS5JU5.mjs +1090 -0
- package/dist/chunk-ZJLS5JU5.mjs.map +1 -0
- package/dist/common/aiApi/client/index.d.mts +71 -0
- package/dist/common/aiApi/client/index.d.ts +71 -0
- package/dist/common/aiApi/client/index.js +165 -0
- package/dist/common/aiApi/client/index.js.map +1 -0
- package/dist/common/aiApi/client/index.mjs +151 -0
- package/dist/common/aiApi/client/index.mjs.map +1 -0
- package/dist/common/aiApi/index.d.mts +184 -0
- package/dist/common/aiApi/index.d.ts +184 -0
- package/dist/common/aiApi/index.js +217 -0
- package/dist/common/aiApi/index.mjs +4 -0
- package/dist/common/aiApi/server/index.d.mts +3 -0
- package/dist/common/aiApi/server/index.d.ts +3 -0
- package/dist/common/aiApi/server/index.js +217 -0
- package/dist/common/aiApi/server/index.mjs +4 -0
- package/dist/common/components/index.js +176 -177
- package/dist/common/components/index.mjs +1 -2
- package/dist/common/index.js +2 -3
- package/dist/common/index.mjs +1 -2
- package/dist/{index-BSmd4ikf.d.ts → index-B4wDXFL0.d.mts} +39 -2
- package/dist/{index-r2-zE3iC.d.mts → index-B4wDXFL0.d.ts} +39 -2
- package/dist/{index-DLLPTprx.d.mts → index-B9vXYzok.d.mts} +48 -13
- package/dist/{index-VFDbZxVM.d.ts → index-BZ0MhRau.d.ts} +48 -13
- package/dist/index.d.mts +703 -181
- package/dist/index.d.ts +703 -181
- package/dist/index.js +2921 -7257
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2161 -6538
- package/dist/index.mjs.map +1 -1
- package/dist/types-CiqMQ-uu.d.mts +166 -0
- package/dist/types-CiqMQ-uu.d.ts +166 -0
- package/package.json +15 -60
- package/dist/CollisionBalls-DgKtscU2.d.mts +0 -41
- package/dist/CollisionBalls-DgKtscU2.d.ts +0 -41
- package/dist/business/calendar/index.d.mts +0 -6
- package/dist/business/calendar/index.d.ts +0 -6
- package/dist/business/calendar/index.js +0 -7433
- package/dist/business/calendar/index.js.map +0 -1
- package/dist/business/calendar/index.mjs +0 -7257
- package/dist/business/calendar/index.mjs.map +0 -1
- package/dist/business/calendar/routes/index.d.mts +0 -191
- package/dist/business/calendar/routes/index.d.ts +0 -191
- package/dist/business/calendar/routes/index.js +0 -844
- package/dist/business/calendar/routes/index.js.map +0 -1
- package/dist/business/calendar/routes/index.mjs +0 -826
- package/dist/business/calendar/routes/index.mjs.map +0 -1
- package/dist/chunk-3R6JHA6D.js +0 -120
- package/dist/chunk-3R6JHA6D.js.map +0 -1
- package/dist/chunk-4PJM4752.js +0 -4
- package/dist/chunk-4PJM4752.js.map +0 -1
- package/dist/chunk-7PMT4L4I.js +0 -324
- package/dist/chunk-7PMT4L4I.js.map +0 -1
- package/dist/chunk-FY2X3LYR.mjs +0 -3
- package/dist/chunk-FY2X3LYR.mjs.map +0 -1
- package/dist/chunk-GS4SAW25.mjs +0 -116
- package/dist/chunk-GS4SAW25.mjs.map +0 -1
- package/dist/chunk-HL4H2HF6.js +0 -279
- package/dist/chunk-HL4H2HF6.js.map +0 -1
- package/dist/chunk-IJIQUMAK.mjs +0 -272
- package/dist/chunk-IJIQUMAK.mjs.map +0 -1
- package/dist/chunk-KVYHCGRY.js.map +0 -1
- package/dist/chunk-MMDSZIXD.mjs +0 -286
- package/dist/chunk-MMDSZIXD.mjs.map +0 -1
- package/dist/chunk-N2O3OX5Y.mjs +0 -243
- package/dist/chunk-N2O3OX5Y.mjs.map +0 -1
- package/dist/chunk-RRQ2X26Z.js +0 -106
- package/dist/chunk-RRQ2X26Z.js.map +0 -1
- package/dist/chunk-RVNQI6BI.js +0 -249
- package/dist/chunk-RVNQI6BI.js.map +0 -1
- package/dist/chunk-UJUWDF7M.mjs +0 -336
- package/dist/chunk-UJUWDF7M.mjs.map +0 -1
- package/dist/chunk-VCKXK6V5.js +0 -345
- package/dist/chunk-VCKXK6V5.js.map +0 -1
- package/dist/chunk-VIEXDTNF.mjs +0 -100
- package/dist/chunk-VIEXDTNF.mjs.map +0 -1
- package/dist/chunk-YIRPPMCN.mjs.map +0 -1
- package/dist/common/ai/llm/core/index.d.mts +0 -70
- package/dist/common/ai/llm/core/index.d.ts +0 -70
- package/dist/common/ai/llm/core/index.js +0 -54
- package/dist/common/ai/llm/core/index.mjs +0 -5
- package/dist/common/ai/llm/electron/index.d.mts +0 -6
- package/dist/common/ai/llm/electron/index.d.ts +0 -6
- package/dist/common/ai/llm/electron/index.js +0 -67
- package/dist/common/ai/llm/electron/index.mjs +0 -10
- package/dist/common/ai/llm/index.d.mts +0 -3
- package/dist/common/ai/llm/index.d.ts +0 -3
- package/dist/common/ai/llm/index.js +0 -54
- package/dist/common/ai/llm/index.js.map +0 -1
- package/dist/common/ai/llm/index.mjs +0 -5
- package/dist/common/ai/llm/index.mjs.map +0 -1
- package/dist/common/ai/llm/miniapp/index.d.mts +0 -6
- package/dist/common/ai/llm/miniapp/index.d.ts +0 -6
- package/dist/common/ai/llm/miniapp/index.js +0 -59
- package/dist/common/ai/llm/miniapp/index.js.map +0 -1
- package/dist/common/ai/llm/miniapp/index.mjs +0 -6
- package/dist/common/ai/llm/miniapp/index.mjs.map +0 -1
- package/dist/common/ai/llm/rn/index.d.mts +0 -6
- package/dist/common/ai/llm/rn/index.d.ts +0 -6
- package/dist/common/ai/llm/rn/index.js +0 -59
- package/dist/common/ai/llm/rn/index.js.map +0 -1
- package/dist/common/ai/llm/rn/index.mjs +0 -6
- package/dist/common/ai/llm/rn/index.mjs.map +0 -1
- package/dist/common/ai/llm/ui/electron/index.d.mts +0 -5
- package/dist/common/ai/llm/ui/electron/index.d.ts +0 -5
- package/dist/common/ai/llm/ui/electron/index.js +0 -22
- package/dist/common/ai/llm/ui/electron/index.js.map +0 -1
- package/dist/common/ai/llm/ui/electron/index.mjs +0 -9
- package/dist/common/ai/llm/ui/electron/index.mjs.map +0 -1
- package/dist/common/ai/llm/ui/miniapp/index.d.mts +0 -9
- package/dist/common/ai/llm/ui/miniapp/index.d.ts +0 -9
- package/dist/common/ai/llm/ui/miniapp/index.js +0 -14
- package/dist/common/ai/llm/ui/miniapp/index.js.map +0 -1
- package/dist/common/ai/llm/ui/miniapp/index.mjs +0 -5
- package/dist/common/ai/llm/ui/miniapp/index.mjs.map +0 -1
- package/dist/common/ai/llm/ui/rn/index.d.mts +0 -9
- package/dist/common/ai/llm/ui/rn/index.d.ts +0 -9
- package/dist/common/ai/llm/ui/rn/index.js +0 -14
- package/dist/common/ai/llm/ui/rn/index.js.map +0 -1
- package/dist/common/ai/llm/ui/rn/index.mjs +0 -5
- package/dist/common/ai/llm/ui/rn/index.mjs.map +0 -1
- package/dist/common/ai/llm/ui/web/index.d.mts +0 -15
- package/dist/common/ai/llm/ui/web/index.d.ts +0 -15
- package/dist/common/ai/llm/ui/web/index.js +0 -21
- package/dist/common/ai/llm/ui/web/index.js.map +0 -1
- package/dist/common/ai/llm/ui/web/index.mjs +0 -8
- package/dist/common/ai/llm/ui/web/index.mjs.map +0 -1
- package/dist/common/ai/llm/web/index.d.mts +0 -6
- package/dist/common/ai/llm/web/index.d.ts +0 -6
- package/dist/common/ai/llm/web/index.js +0 -66
- package/dist/common/ai/llm/web/index.js.map +0 -1
- package/dist/common/ai/llm/web/index.mjs +0 -9
- package/dist/common/ai/llm/web/index.mjs.map +0 -1
- package/dist/index-BMgdH5dL.d.mts +0 -1716
- package/dist/index-IXMAeTtN.d.ts +0 -1716
- package/dist/types-B2rs_jq1.d.mts +0 -38
- package/dist/types-DgACCUpT.d.ts +0 -122
- package/dist/types-DwS2Eg0q.d.ts +0 -38
- package/dist/types-LU_BGSzk.d.mts +0 -122
- /package/dist/common/{ai/llm/core → aiApi}/index.js.map +0 -0
- /package/dist/common/{ai/llm/core → aiApi}/index.mjs.map +0 -0
- /package/dist/common/{ai/llm/electron → aiApi/server}/index.js.map +0 -0
- /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
|