sa2kit 3.4.0 → 3.6.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/{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.d.mts +314 -154
- package/dist/index.d.ts +314 -154
- package/dist/index.js +1055 -369
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1005 -360
- 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 -50
- 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/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
package/dist/index.mjs
CHANGED
|
@@ -37512,8 +37512,66 @@ AI\u56DE\u5E94\uFF1A"`;
|
|
|
37512
37512
|
))));
|
|
37513
37513
|
};
|
|
37514
37514
|
|
|
37515
|
-
// src/common/
|
|
37516
|
-
var
|
|
37515
|
+
// src/common/aiApi/types.ts
|
|
37516
|
+
var CORE_LLM_COMPLETION_TASK_ID = "core.llmCompletion";
|
|
37517
|
+
var CORE_STRUCTURED_MULTIMODAL_TASK_ID = "core.structuredMultimodal";
|
|
37518
|
+
var CORE_CONNECTIVITY_TEST_TASK_ID = "core.connectivityTest";
|
|
37519
|
+
|
|
37520
|
+
// src/common/aiApi/resolveConfig.ts
|
|
37521
|
+
var DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
37522
|
+
var DEFAULT_TEXT_MODEL = "gpt-4o-mini";
|
|
37523
|
+
var DEFAULT_VISION_MODEL = "gpt-4o-mini";
|
|
37524
|
+
var DEFAULT_AUDIO_MODEL = "whisper-1";
|
|
37525
|
+
var DEFAULT_AUDIO_STRATEGY = "auto";
|
|
37526
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
37527
|
+
var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
37528
|
+
var DEFAULT_MAX_AUDIO_BYTES = 25 * 1024 * 1024;
|
|
37529
|
+
function readEnv(name) {
|
|
37530
|
+
if (typeof process === "undefined" || !process.env) {
|
|
37531
|
+
return "";
|
|
37532
|
+
}
|
|
37533
|
+
return process.env[name]?.trim() ?? "";
|
|
37534
|
+
}
|
|
37535
|
+
function resolveAiConnectionConfig(...sources) {
|
|
37536
|
+
const merged = {};
|
|
37537
|
+
for (const source of sources) {
|
|
37538
|
+
if (!source) continue;
|
|
37539
|
+
Object.assign(merged, source);
|
|
37540
|
+
}
|
|
37541
|
+
const envApiKey = readEnv("AI_API_KEY") || readEnv("OPENAI_API_KEY");
|
|
37542
|
+
const apiKey = merged.apiKey?.trim() || envApiKey;
|
|
37543
|
+
if (!apiKey) {
|
|
37544
|
+
return null;
|
|
37545
|
+
}
|
|
37546
|
+
const baseUrl = merged.baseUrl?.trim() || readEnv("AI_BASE_URL") || DEFAULT_BASE_URL;
|
|
37547
|
+
const visionModel = merged.visionModel?.trim() || readEnv("AI_VISION_MODEL") || merged.model?.trim() || DEFAULT_VISION_MODEL;
|
|
37548
|
+
const textModel = merged.textModel?.trim() || readEnv("AI_TEXT_MODEL") || merged.model?.trim() || visionModel || DEFAULT_TEXT_MODEL;
|
|
37549
|
+
const audioModel = merged.audioModel?.trim() || readEnv("AI_AUDIO_MODEL") || DEFAULT_AUDIO_MODEL;
|
|
37550
|
+
const envAudioStrategy = readEnv("AI_AUDIO_STRATEGY");
|
|
37551
|
+
const audioStrategy = merged.audioStrategy ?? (envAudioStrategy || DEFAULT_AUDIO_STRATEGY);
|
|
37552
|
+
return {
|
|
37553
|
+
apiKey,
|
|
37554
|
+
baseUrl,
|
|
37555
|
+
model: merged.model?.trim() || textModel,
|
|
37556
|
+
textModel,
|
|
37557
|
+
visionModel,
|
|
37558
|
+
audioModel,
|
|
37559
|
+
audioStrategy,
|
|
37560
|
+
timeoutMs: merged.timeoutMs ?? Number(readEnv("AI_TIMEOUT_MS") || DEFAULT_TIMEOUT_MS),
|
|
37561
|
+
maxImageBytes: merged.maxImageBytes ?? Number(readEnv("AI_MAX_IMAGE_BYTES") || DEFAULT_MAX_IMAGE_BYTES),
|
|
37562
|
+
maxAudioBytes: merged.maxAudioBytes ?? Number(readEnv("AI_MAX_AUDIO_BYTES") || DEFAULT_MAX_AUDIO_BYTES)
|
|
37563
|
+
};
|
|
37564
|
+
}
|
|
37565
|
+
function requireAiConnectionConfig(...sources) {
|
|
37566
|
+
const config = resolveAiConnectionConfig(...sources);
|
|
37567
|
+
if (!config) {
|
|
37568
|
+
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");
|
|
37569
|
+
}
|
|
37570
|
+
return config;
|
|
37571
|
+
}
|
|
37572
|
+
|
|
37573
|
+
// src/common/aiApi/requestJson.ts
|
|
37574
|
+
async function requestJson(options) {
|
|
37517
37575
|
const { url, method = "POST", headers = {}, body, timeoutMs, requestAdapter } = options;
|
|
37518
37576
|
if (requestAdapter) {
|
|
37519
37577
|
return requestAdapter.request({
|
|
@@ -37531,7 +37589,7 @@ var requestJson = async (options) => {
|
|
|
37531
37589
|
const response = await fetch(url, {
|
|
37532
37590
|
method,
|
|
37533
37591
|
headers,
|
|
37534
|
-
body: body ? JSON.stringify(body) : void 0,
|
|
37592
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
37535
37593
|
signal: controller?.signal
|
|
37536
37594
|
});
|
|
37537
37595
|
const text3 = await response.text();
|
|
@@ -37544,8 +37602,9 @@ var requestJson = async (options) => {
|
|
|
37544
37602
|
}
|
|
37545
37603
|
}
|
|
37546
37604
|
if (!response.ok) {
|
|
37547
|
-
const
|
|
37548
|
-
const
|
|
37605
|
+
const record = data;
|
|
37606
|
+
const errorMessage2 = (typeof record?.error === "object" ? record.error?.message : record?.error) || record?.message || `Request failed with status ${response.status}`;
|
|
37607
|
+
const error = new Error(String(errorMessage2));
|
|
37549
37608
|
error.status = response.status;
|
|
37550
37609
|
error.data = data;
|
|
37551
37610
|
throw error;
|
|
@@ -37556,404 +37615,990 @@ var requestJson = async (options) => {
|
|
|
37556
37615
|
clearTimeout(timeoutId);
|
|
37557
37616
|
}
|
|
37558
37617
|
}
|
|
37559
|
-
}
|
|
37618
|
+
}
|
|
37560
37619
|
|
|
37561
|
-
// src/common/
|
|
37620
|
+
// src/common/aiApi/callChat.ts
|
|
37562
37621
|
var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
37563
|
-
var
|
|
37564
|
-
|
|
37565
|
-
|
|
37566
|
-
|
|
37567
|
-
|
|
37568
|
-
|
|
37569
|
-
|
|
37570
|
-
if (!toolCalls || !toolCalls.length) {
|
|
37571
|
-
return void 0;
|
|
37622
|
+
var DEFAULT_TEXT_MODEL2 = "gpt-4o-mini";
|
|
37623
|
+
function joinUrl(base, path) {
|
|
37624
|
+
return `${base.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
37625
|
+
}
|
|
37626
|
+
function resolveMessages(input) {
|
|
37627
|
+
if (input.messages?.length) {
|
|
37628
|
+
return input.messages;
|
|
37572
37629
|
}
|
|
37573
|
-
|
|
37574
|
-
|
|
37575
|
-
|
|
37576
|
-
|
|
37577
|
-
|
|
37578
|
-
|
|
37579
|
-
}
|
|
37580
|
-
}));
|
|
37581
|
-
};
|
|
37582
|
-
var normalizeUsage = (usage) => {
|
|
37583
|
-
if (!usage) {
|
|
37584
|
-
return void 0;
|
|
37630
|
+
const messages = [];
|
|
37631
|
+
if (input.systemPrompt?.trim()) {
|
|
37632
|
+
messages.push({ role: "system", content: input.systemPrompt.trim() });
|
|
37633
|
+
}
|
|
37634
|
+
if (input.userPrompt?.trim()) {
|
|
37635
|
+
messages.push({ role: "user", content: input.userPrompt.trim() });
|
|
37585
37636
|
}
|
|
37637
|
+
return messages;
|
|
37638
|
+
}
|
|
37639
|
+
async function callChat(options) {
|
|
37640
|
+
const {
|
|
37641
|
+
baseUrl,
|
|
37642
|
+
apiKey,
|
|
37643
|
+
model,
|
|
37644
|
+
systemPrompt,
|
|
37645
|
+
userPrompt,
|
|
37646
|
+
messages,
|
|
37647
|
+
temperature,
|
|
37648
|
+
maxTokens,
|
|
37649
|
+
topP,
|
|
37650
|
+
stop,
|
|
37651
|
+
timeoutMs = 6e4,
|
|
37652
|
+
requestAdapter
|
|
37653
|
+
} = options;
|
|
37654
|
+
if (!baseUrl?.trim()) {
|
|
37655
|
+
throw new Error("baseUrl \u4E3A\u5FC5\u586B");
|
|
37656
|
+
}
|
|
37657
|
+
if (!apiKey?.trim()) {
|
|
37658
|
+
throw new Error("apiKey \u4E3A\u5FC5\u586B");
|
|
37659
|
+
}
|
|
37660
|
+
const resolvedMessages = resolveMessages({ systemPrompt, userPrompt, messages });
|
|
37661
|
+
if (!resolvedMessages.length) {
|
|
37662
|
+
throw new Error("userPrompt \u6216 messages \u81F3\u5C11\u63D0\u4F9B\u4E00\u9879");
|
|
37663
|
+
}
|
|
37664
|
+
const resolvedModel = model?.trim() || DEFAULT_TEXT_MODEL2;
|
|
37665
|
+
const payload = {
|
|
37666
|
+
model: resolvedModel,
|
|
37667
|
+
messages: resolvedMessages
|
|
37668
|
+
};
|
|
37669
|
+
if (temperature !== void 0) payload.temperature = temperature;
|
|
37670
|
+
if (maxTokens !== void 0) payload.max_tokens = maxTokens;
|
|
37671
|
+
if (topP !== void 0) payload.top_p = topP;
|
|
37672
|
+
if (stop !== void 0) payload.stop = stop;
|
|
37673
|
+
const raw = await requestJson({
|
|
37674
|
+
url: joinUrl(baseUrl.trim(), "chat/completions"),
|
|
37675
|
+
method: "POST",
|
|
37676
|
+
headers: {
|
|
37677
|
+
"Content-Type": "application/json",
|
|
37678
|
+
Authorization: `Bearer ${apiKey.trim()}`
|
|
37679
|
+
},
|
|
37680
|
+
body: payload,
|
|
37681
|
+
timeoutMs,
|
|
37682
|
+
requestAdapter
|
|
37683
|
+
});
|
|
37684
|
+
const content = raw.choices?.[0]?.message?.content ?? "";
|
|
37685
|
+
const usage = raw.usage ? {
|
|
37686
|
+
promptTokens: raw.usage.prompt_tokens,
|
|
37687
|
+
completionTokens: raw.usage.completion_tokens,
|
|
37688
|
+
totalTokens: raw.usage.total_tokens
|
|
37689
|
+
} : void 0;
|
|
37586
37690
|
return {
|
|
37587
|
-
|
|
37588
|
-
|
|
37589
|
-
|
|
37590
|
-
|
|
37591
|
-
};
|
|
37592
|
-
|
|
37593
|
-
|
|
37594
|
-
|
|
37595
|
-
|
|
37596
|
-
|
|
37597
|
-
|
|
37598
|
-
|
|
37691
|
+
content,
|
|
37692
|
+
model: raw.model ?? resolvedModel,
|
|
37693
|
+
usage,
|
|
37694
|
+
raw
|
|
37695
|
+
};
|
|
37696
|
+
}
|
|
37697
|
+
|
|
37698
|
+
// src/common/aiApi/callCompletion.ts
|
|
37699
|
+
async function callCompletion(params, clientSettings) {
|
|
37700
|
+
const config = requireAiConnectionConfig(params.connection, clientSettings);
|
|
37701
|
+
const model = params.model || config.textModel;
|
|
37702
|
+
const result = await callChat({
|
|
37703
|
+
baseUrl: config.baseUrl,
|
|
37704
|
+
apiKey: config.apiKey,
|
|
37705
|
+
model,
|
|
37706
|
+
systemPrompt: params.systemPrompt,
|
|
37707
|
+
userPrompt: params.userPrompt,
|
|
37708
|
+
temperature: params.temperature,
|
|
37709
|
+
maxTokens: params.maxTokens,
|
|
37710
|
+
timeoutMs: config.timeoutMs
|
|
37711
|
+
});
|
|
37712
|
+
return {
|
|
37713
|
+
content: result.content,
|
|
37714
|
+
model: result.model || model,
|
|
37715
|
+
raw: result.raw
|
|
37716
|
+
};
|
|
37717
|
+
}
|
|
37718
|
+
|
|
37719
|
+
// src/common/aiApi/modelHeuristics.ts
|
|
37720
|
+
var NON_CHAT_PATTERNS = [
|
|
37721
|
+
/embed/i,
|
|
37722
|
+
/whisper/i,
|
|
37723
|
+
/tts/i,
|
|
37724
|
+
/dall-e/i,
|
|
37725
|
+
/moderation/i,
|
|
37726
|
+
/realtime/i,
|
|
37727
|
+
/audio/i,
|
|
37728
|
+
/transcrib/i,
|
|
37729
|
+
/sora/i
|
|
37730
|
+
];
|
|
37731
|
+
var VISION_HINT_PATTERNS = [
|
|
37732
|
+
/^gpt-4o/i,
|
|
37733
|
+
/^gpt-4-turbo/i,
|
|
37734
|
+
/^gpt-4-vision/i,
|
|
37735
|
+
/^gpt-4\.1/i,
|
|
37736
|
+
/claude-3/i,
|
|
37737
|
+
/gemini.*(pro|flash|vision)/i,
|
|
37738
|
+
/qwen.*vl/i,
|
|
37739
|
+
/vision/i,
|
|
37740
|
+
/-vl/i,
|
|
37741
|
+
/llava/i,
|
|
37742
|
+
/doubao.*vision/i,
|
|
37743
|
+
/glm-4v/i,
|
|
37744
|
+
/internvl/i,
|
|
37745
|
+
/pixtral/i,
|
|
37746
|
+
/deepseek-vl/i
|
|
37747
|
+
];
|
|
37748
|
+
var PREFERRED_VISION_MODELS = [
|
|
37749
|
+
"gpt-4o-mini",
|
|
37750
|
+
"gpt-4o",
|
|
37751
|
+
"gpt-4-turbo",
|
|
37752
|
+
"gpt-4-vision-preview",
|
|
37753
|
+
"gpt-4.1-mini",
|
|
37754
|
+
"gpt-4.1",
|
|
37755
|
+
"claude-3-5-sonnet-latest",
|
|
37756
|
+
"claude-3-5-haiku-latest",
|
|
37757
|
+
"gemini-2.0-flash",
|
|
37758
|
+
"gemini-1.5-flash",
|
|
37759
|
+
"gemini-1.5-pro",
|
|
37760
|
+
"qwen-vl-max",
|
|
37761
|
+
"qwen2-vl-72b-instruct"
|
|
37762
|
+
];
|
|
37763
|
+
function isChatModel(id) {
|
|
37764
|
+
return !NON_CHAT_PATTERNS.some((pattern) => pattern.test(id));
|
|
37765
|
+
}
|
|
37766
|
+
var NATIVE_AUDIO_CHAT_PATTERNS = [
|
|
37767
|
+
/^gpt-4o/i,
|
|
37768
|
+
/^gpt-4-turbo/i,
|
|
37769
|
+
/^gpt-4\.1/i,
|
|
37770
|
+
/gemini-2\.0/i,
|
|
37771
|
+
/gemini-1\.5/i
|
|
37772
|
+
];
|
|
37773
|
+
var STT_MODEL_PATTERNS = [/whisper/i, /transcrib/i, /sensevoice/i, /paraformer/i];
|
|
37774
|
+
function isLikelyNativeAudioChatModel(id) {
|
|
37775
|
+
const trimmed = id.trim();
|
|
37776
|
+
if (!trimmed) return false;
|
|
37777
|
+
if (STT_MODEL_PATTERNS.some((pattern) => pattern.test(trimmed))) return false;
|
|
37778
|
+
return NATIVE_AUDIO_CHAT_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
37779
|
+
}
|
|
37780
|
+
function isLikelySttModel(id) {
|
|
37781
|
+
return STT_MODEL_PATTERNS.some((pattern) => pattern.test(id.trim()));
|
|
37782
|
+
}
|
|
37783
|
+
function filterSttModels(modelIds) {
|
|
37784
|
+
return modelIds.filter(isLikelySttModel).sort((a, b) => a.localeCompare(b));
|
|
37785
|
+
}
|
|
37786
|
+
var PREFERRED_STT_MODELS = ["whisper-1", "whisper-large-v3", "whisper-large-v3-turbo"];
|
|
37787
|
+
function pickDefaultSttModel(modelIds, current) {
|
|
37788
|
+
const sttModels = filterSttModels(modelIds);
|
|
37789
|
+
if (sttModels.length === 0) return void 0;
|
|
37790
|
+
const trimmedCurrent = current?.trim();
|
|
37791
|
+
if (trimmedCurrent && sttModels.includes(trimmedCurrent)) {
|
|
37792
|
+
return trimmedCurrent;
|
|
37793
|
+
}
|
|
37794
|
+
for (const preferred of PREFERRED_STT_MODELS) {
|
|
37795
|
+
const match = sttModels.find((id) => id === preferred || id.startsWith(`${preferred}-`));
|
|
37796
|
+
if (match) return match;
|
|
37797
|
+
}
|
|
37798
|
+
return sttModels[0];
|
|
37799
|
+
}
|
|
37800
|
+
function isLikelyVisionModel(id) {
|
|
37801
|
+
return VISION_HINT_PATTERNS.some((pattern) => pattern.test(id));
|
|
37802
|
+
}
|
|
37803
|
+
var TEXT_ONLY_MODEL_PATTERNS = [
|
|
37804
|
+
/^deepseek-chat/i,
|
|
37805
|
+
/^deepseek-reasoner/i,
|
|
37806
|
+
/^deepseek-r1/i,
|
|
37807
|
+
/^deepseek-v[34](?!.*vl)/i,
|
|
37808
|
+
/^gpt-3\.5/i,
|
|
37809
|
+
/^o1-mini/i,
|
|
37810
|
+
/^o3-mini/i
|
|
37811
|
+
];
|
|
37812
|
+
function isKnownTextOnlyModel(id) {
|
|
37813
|
+
const trimmed = id.trim();
|
|
37814
|
+
if (!trimmed) return false;
|
|
37815
|
+
return TEXT_ONLY_MODEL_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
37816
|
+
}
|
|
37817
|
+
function filterChatModels(modelIds) {
|
|
37818
|
+
return modelIds.filter(isChatModel).sort((a, b) => a.localeCompare(b));
|
|
37819
|
+
}
|
|
37820
|
+
function filterVisionModels(modelIds) {
|
|
37821
|
+
return filterChatModels(modelIds).filter((id) => isLikelyVisionModel(id) && !isKnownTextOnlyModel(id)).sort((a, b) => a.localeCompare(b));
|
|
37822
|
+
}
|
|
37823
|
+
function matchesPreferred(modelId, preferred) {
|
|
37824
|
+
return modelId === preferred || modelId.startsWith(`${preferred}-`);
|
|
37825
|
+
}
|
|
37826
|
+
function pickDefaultVisionModel(modelIds, current) {
|
|
37827
|
+
const visionModels = filterVisionModels(modelIds);
|
|
37828
|
+
if (visionModels.length === 0) return void 0;
|
|
37829
|
+
const trimmedCurrent = current?.trim();
|
|
37830
|
+
if (trimmedCurrent && visionModels.includes(trimmedCurrent)) {
|
|
37831
|
+
return trimmedCurrent;
|
|
37599
37832
|
}
|
|
37600
|
-
|
|
37601
|
-
|
|
37833
|
+
for (const preferred of PREFERRED_VISION_MODELS) {
|
|
37834
|
+
const match = visionModels.find((id) => matchesPreferred(id, preferred));
|
|
37835
|
+
if (match) return match;
|
|
37602
37836
|
}
|
|
37603
|
-
|
|
37604
|
-
|
|
37605
|
-
|
|
37606
|
-
|
|
37607
|
-
|
|
37608
|
-
|
|
37609
|
-
|
|
37610
|
-
|
|
37611
|
-
}));
|
|
37837
|
+
return visionModels[0];
|
|
37838
|
+
}
|
|
37839
|
+
|
|
37840
|
+
// src/common/aiApi/visionMessageFormats.ts
|
|
37841
|
+
function detectVisionMessageFormat(baseUrl) {
|
|
37842
|
+
const normalized = baseUrl.toLowerCase();
|
|
37843
|
+
if (normalized.includes("ollama") || normalized.includes(":11434") || normalized.includes("11434/")) {
|
|
37844
|
+
return "ollama";
|
|
37612
37845
|
}
|
|
37613
|
-
return
|
|
37614
|
-
}
|
|
37615
|
-
|
|
37616
|
-
return
|
|
37617
|
-
|
|
37618
|
-
|
|
37619
|
-
|
|
37620
|
-
|
|
37621
|
-
|
|
37622
|
-
|
|
37623
|
-
|
|
37624
|
-
|
|
37625
|
-
}
|
|
37626
|
-
|
|
37627
|
-
|
|
37628
|
-
|
|
37629
|
-
|
|
37630
|
-
|
|
37631
|
-
|
|
37632
|
-
|
|
37633
|
-
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
37640
|
-
|
|
37846
|
+
return "openai";
|
|
37847
|
+
}
|
|
37848
|
+
function assertVisionCapableModel(modelId, options) {
|
|
37849
|
+
if (!options?.hasImages) return;
|
|
37850
|
+
const model = modelId.trim();
|
|
37851
|
+
if (!model) {
|
|
37852
|
+
throw new Error("\u8BC6\u56FE\u9700\u8981\u9009\u62E9\u89C6\u89C9\u6A21\u578B\uFF0C\u8BF7\u5728 AI \u8BBE\u7F6E\u4E2D\u914D\u7F6E");
|
|
37853
|
+
}
|
|
37854
|
+
const format = options.baseUrl ? detectVisionMessageFormat(options.baseUrl) : "openai";
|
|
37855
|
+
if (format === "ollama") return;
|
|
37856
|
+
if (isKnownTextOnlyModel(model)) {
|
|
37857
|
+
throw new Error(
|
|
37858
|
+
`\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`
|
|
37859
|
+
);
|
|
37860
|
+
}
|
|
37861
|
+
if (!isLikelyVisionModel(model)) {
|
|
37862
|
+
throw new Error(
|
|
37863
|
+
`\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`
|
|
37864
|
+
);
|
|
37865
|
+
}
|
|
37866
|
+
}
|
|
37867
|
+
function isImageUrlVariantError(message) {
|
|
37868
|
+
return /unknown variant [`']?image_url[`']?/i.test(message) || /expected [`']?text[`']?/i.test(message) || /does not support.*image/i.test(message);
|
|
37869
|
+
}
|
|
37870
|
+
function toVisionApiErrorMessage(rawMessage, modelId) {
|
|
37871
|
+
if (isImageUrlVariantError(rawMessage)) {
|
|
37872
|
+
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`;
|
|
37873
|
+
}
|
|
37874
|
+
return rawMessage;
|
|
37875
|
+
}
|
|
37876
|
+
|
|
37877
|
+
// src/common/aiApi/audioStrategy.ts
|
|
37878
|
+
function resolveAudioHandling(options) {
|
|
37879
|
+
if (!options.hasAudio) {
|
|
37880
|
+
return "none";
|
|
37881
|
+
}
|
|
37882
|
+
if (options.strategy === "stt") {
|
|
37883
|
+
return "stt";
|
|
37884
|
+
}
|
|
37885
|
+
if (options.strategy === "native") {
|
|
37886
|
+
return "native";
|
|
37887
|
+
}
|
|
37888
|
+
if (detectVisionMessageFormat(options.baseUrl) === "ollama") {
|
|
37889
|
+
return "stt";
|
|
37890
|
+
}
|
|
37891
|
+
if (isLikelyNativeAudioChatModel(options.model)) {
|
|
37892
|
+
return "native";
|
|
37893
|
+
}
|
|
37894
|
+
return "stt";
|
|
37895
|
+
}
|
|
37896
|
+
function isAudioInputError(message) {
|
|
37897
|
+
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);
|
|
37898
|
+
}
|
|
37899
|
+
function appendTranscriptionsToPrompt(userPrompt, transcriptions) {
|
|
37900
|
+
if (transcriptions.length === 0) {
|
|
37901
|
+
return userPrompt;
|
|
37902
|
+
}
|
|
37903
|
+
const blocks = transcriptions.map((text3, index2) => {
|
|
37904
|
+
const label = transcriptions.length === 1 ? "[\u8BED\u97F3\u8F6C\u5199]" : `[\u8BED\u97F3\u8F6C\u5199 ${index2 + 1}]`;
|
|
37905
|
+
return `${label}
|
|
37906
|
+
${text3.trim()}`;
|
|
37907
|
+
});
|
|
37908
|
+
const joined = blocks.join("\n\n");
|
|
37909
|
+
const trimmedPrompt = userPrompt.trim();
|
|
37910
|
+
return trimmedPrompt ? `${trimmedPrompt}
|
|
37911
|
+
|
|
37912
|
+
${joined}` : joined;
|
|
37913
|
+
}
|
|
37914
|
+
|
|
37915
|
+
// src/common/aiApi/audioUtils.ts
|
|
37916
|
+
var ALLOWED_MIME = /* @__PURE__ */ new Set([
|
|
37917
|
+
"audio/wav",
|
|
37918
|
+
"audio/x-wav",
|
|
37919
|
+
"audio/mpeg",
|
|
37920
|
+
"audio/mp3",
|
|
37921
|
+
"audio/mp4",
|
|
37922
|
+
"audio/webm",
|
|
37923
|
+
"audio/ogg",
|
|
37924
|
+
"audio/flac"
|
|
37925
|
+
]);
|
|
37926
|
+
var MIME_TO_FORMAT = {
|
|
37927
|
+
"audio/wav": "wav",
|
|
37928
|
+
"audio/x-wav": "wav",
|
|
37929
|
+
"audio/mpeg": "mp3",
|
|
37930
|
+
"audio/mp3": "mp3",
|
|
37931
|
+
"audio/mp4": "mp4",
|
|
37932
|
+
"audio/webm": "webm",
|
|
37933
|
+
"audio/ogg": "ogg",
|
|
37934
|
+
"audio/flac": "flac"
|
|
37935
|
+
};
|
|
37936
|
+
function mimeToAudioFormat(mimeType) {
|
|
37937
|
+
return MIME_TO_FORMAT[mimeType] ?? mimeType.split("/").pop() ?? "wav";
|
|
37938
|
+
}
|
|
37939
|
+
function assertValidAudioInput(audio, maxAudioBytes = 25 * 1024 * 1024) {
|
|
37940
|
+
if (!audio.base64?.trim()) {
|
|
37941
|
+
throw new Error("\u97F3\u9891\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A");
|
|
37942
|
+
}
|
|
37943
|
+
if (!ALLOWED_MIME.has(audio.mimeType)) {
|
|
37944
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684\u97F3\u9891\u683C\u5F0F: ${audio.mimeType}`);
|
|
37945
|
+
}
|
|
37946
|
+
const byteLength = estimateBase64ByteLength(audio.base64);
|
|
37947
|
+
if (byteLength > maxAudioBytes) {
|
|
37948
|
+
throw new Error(`\u97F3\u9891\u8FC7\u5927\uFF0C\u6700\u5927 ${Math.round(maxAudioBytes / 1024 / 1024)}MB`);
|
|
37949
|
+
}
|
|
37950
|
+
}
|
|
37951
|
+
function estimateBase64ByteLength(base64) {
|
|
37952
|
+
if (typeof Buffer !== "undefined") {
|
|
37953
|
+
return Buffer.byteLength(base64, "base64");
|
|
37954
|
+
}
|
|
37955
|
+
const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
37956
|
+
return Math.floor(base64.length * 3 / 4) - padding;
|
|
37957
|
+
}
|
|
37958
|
+
async function fileToAiAudioInput(file) {
|
|
37959
|
+
const buffer = await file.arrayBuffer();
|
|
37960
|
+
if (typeof Buffer !== "undefined") {
|
|
37961
|
+
return {
|
|
37962
|
+
base64: Buffer.from(buffer).toString("base64"),
|
|
37963
|
+
mimeType: file.type || "audio/wav"
|
|
37964
|
+
};
|
|
37965
|
+
}
|
|
37966
|
+
const bytes = new Uint8Array(buffer);
|
|
37967
|
+
let binary = "";
|
|
37968
|
+
for (let index2 = 0; index2 < bytes.length; index2 += 1) {
|
|
37969
|
+
binary += String.fromCharCode(bytes[index2]);
|
|
37970
|
+
}
|
|
37971
|
+
return { base64: btoa(binary), mimeType: file.type || "audio/wav" };
|
|
37972
|
+
}
|
|
37973
|
+
function base64ToBlob(base64, mimeType) {
|
|
37974
|
+
if (typeof Buffer !== "undefined") {
|
|
37975
|
+
const bytes2 = Buffer.from(base64, "base64");
|
|
37976
|
+
return new Blob([bytes2], { type: mimeType });
|
|
37977
|
+
}
|
|
37978
|
+
const binary = atob(base64);
|
|
37979
|
+
const bytes = new Uint8Array(binary.length);
|
|
37980
|
+
for (let index2 = 0; index2 < binary.length; index2 += 1) {
|
|
37981
|
+
bytes[index2] = binary.charCodeAt(index2);
|
|
37982
|
+
}
|
|
37983
|
+
return new Blob([bytes], { type: mimeType });
|
|
37984
|
+
}
|
|
37985
|
+
|
|
37986
|
+
// src/common/aiApi/multimodalMessageFormats.ts
|
|
37987
|
+
function buildMultimodalMessages(options) {
|
|
37988
|
+
const { systemPrompt, userPrompt, images, nativeAudios, format } = options;
|
|
37989
|
+
const hasImages = images.length > 0;
|
|
37990
|
+
const hasNativeAudio = nativeAudios.length > 0;
|
|
37991
|
+
if (format === "ollama" && hasImages) {
|
|
37992
|
+
return [
|
|
37993
|
+
{ role: "system", content: systemPrompt },
|
|
37994
|
+
{
|
|
37995
|
+
role: "user",
|
|
37996
|
+
content: userPrompt,
|
|
37997
|
+
images: images.map((image) => image.base64)
|
|
37641
37998
|
}
|
|
37642
|
-
|
|
37643
|
-
|
|
37999
|
+
];
|
|
38000
|
+
}
|
|
38001
|
+
const userContent = [{ type: "text", text: userPrompt }];
|
|
38002
|
+
for (const image of images) {
|
|
38003
|
+
userContent.push({
|
|
38004
|
+
type: "image_url",
|
|
38005
|
+
image_url: {
|
|
38006
|
+
url: `data:${image.mimeType};base64,${image.base64}`
|
|
37644
38007
|
}
|
|
37645
|
-
|
|
37646
|
-
|
|
38008
|
+
});
|
|
38009
|
+
}
|
|
38010
|
+
for (const audio of nativeAudios) {
|
|
38011
|
+
userContent.push({
|
|
38012
|
+
type: "input_audio",
|
|
38013
|
+
input_audio: {
|
|
38014
|
+
data: audio.base64,
|
|
38015
|
+
format: mimeToAudioFormat(audio.mimeType)
|
|
37647
38016
|
}
|
|
37648
|
-
|
|
37649
|
-
|
|
37650
|
-
|
|
37651
|
-
|
|
37652
|
-
|
|
37653
|
-
|
|
37654
|
-
|
|
37655
|
-
|
|
37656
|
-
|
|
37657
|
-
|
|
37658
|
-
|
|
37659
|
-
|
|
37660
|
-
|
|
37661
|
-
|
|
37662
|
-
|
|
37663
|
-
|
|
37664
|
-
|
|
37665
|
-
|
|
37666
|
-
|
|
37667
|
-
content,
|
|
37668
|
-
message: assistantMessage,
|
|
37669
|
-
usage: normalizeUsage(response.usage),
|
|
37670
|
-
toolCalls,
|
|
37671
|
-
raw: response
|
|
37672
|
-
};
|
|
37673
|
-
}
|
|
37674
|
-
};
|
|
37675
|
-
};
|
|
38017
|
+
});
|
|
38018
|
+
}
|
|
38019
|
+
const useStructuredContent = hasImages || hasNativeAudio;
|
|
38020
|
+
return [
|
|
38021
|
+
{ role: "system", content: systemPrompt },
|
|
38022
|
+
{ role: "user", content: useStructuredContent ? userContent : userPrompt }
|
|
38023
|
+
];
|
|
38024
|
+
}
|
|
38025
|
+
function assertMultimodalCapableModel(modelId, options) {
|
|
38026
|
+
assertVisionCapableModel(modelId, {
|
|
38027
|
+
baseUrl: options.baseUrl,
|
|
38028
|
+
hasImages: options.hasImages
|
|
38029
|
+
});
|
|
38030
|
+
if (!options.hasNativeAudio) return;
|
|
38031
|
+
const format = options.baseUrl ? detectVisionMessageFormat(options.baseUrl) : "openai";
|
|
38032
|
+
if (format === "ollama") {
|
|
38033
|
+
throw new Error('\u5F53\u524D Ollama \u8FDE\u63A5\u4E0D\u652F\u6301 chat \u5185\u5D4C\u97F3\u9891\uFF0C\u8BF7\u6539\u7528 audioStrategy: "stt" \u6216 "auto"');
|
|
38034
|
+
}
|
|
38035
|
+
}
|
|
37676
38036
|
|
|
37677
|
-
// src/common/
|
|
37678
|
-
var
|
|
37679
|
-
|
|
37680
|
-
if (
|
|
37681
|
-
|
|
38037
|
+
// src/common/aiApi/imageUtils.ts
|
|
38038
|
+
var ALLOWED_MIME2 = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
|
|
38039
|
+
function assertValidImageInput(image, maxImageBytes = 5 * 1024 * 1024) {
|
|
38040
|
+
if (!image.base64?.trim()) {
|
|
38041
|
+
throw new Error("\u56FE\u7247\u6570\u636E\u4E0D\u80FD\u4E3A\u7A7A");
|
|
37682
38042
|
}
|
|
37683
|
-
if (
|
|
37684
|
-
|
|
38043
|
+
if (!ALLOWED_MIME2.has(image.mimeType)) {
|
|
38044
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F: ${image.mimeType}`);
|
|
37685
38045
|
}
|
|
37686
|
-
|
|
37687
|
-
|
|
37688
|
-
|
|
37689
|
-
|
|
37690
|
-
}
|
|
37691
|
-
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
|
|
37696
|
-
|
|
37697
|
-
|
|
37698
|
-
|
|
37699
|
-
|
|
38046
|
+
const byteLength = estimateBase64ByteLength2(image.base64);
|
|
38047
|
+
if (byteLength > maxImageBytes) {
|
|
38048
|
+
throw new Error(`\u56FE\u7247\u8FC7\u5927\uFF0C\u6700\u5927 ${Math.round(maxImageBytes / 1024 / 1024)}MB`);
|
|
38049
|
+
}
|
|
38050
|
+
}
|
|
38051
|
+
function estimateBase64ByteLength2(base64) {
|
|
38052
|
+
if (typeof Buffer !== "undefined") {
|
|
38053
|
+
return Buffer.byteLength(base64, "base64");
|
|
38054
|
+
}
|
|
38055
|
+
const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
|
|
38056
|
+
return Math.floor(base64.length * 3 / 4) - padding;
|
|
38057
|
+
}
|
|
38058
|
+
async function fileToAiImageInput(file) {
|
|
38059
|
+
const buffer = await file.arrayBuffer();
|
|
38060
|
+
if (typeof Buffer !== "undefined") {
|
|
38061
|
+
return {
|
|
38062
|
+
base64: Buffer.from(buffer).toString("base64"),
|
|
38063
|
+
mimeType: file.type || "image/jpeg"
|
|
38064
|
+
};
|
|
38065
|
+
}
|
|
38066
|
+
const bytes = new Uint8Array(buffer);
|
|
38067
|
+
let binary = "";
|
|
38068
|
+
for (let index2 = 0; index2 < bytes.length; index2 += 1) {
|
|
38069
|
+
binary += String.fromCharCode(bytes[index2]);
|
|
38070
|
+
}
|
|
38071
|
+
const base64 = btoa(binary);
|
|
38072
|
+
return { base64, mimeType: file.type || "image/jpeg" };
|
|
38073
|
+
}
|
|
38074
|
+
|
|
38075
|
+
// src/common/aiApi/mediaUtils.ts
|
|
38076
|
+
function splitMediaByKind(media) {
|
|
38077
|
+
const images = [];
|
|
38078
|
+
const audios = [];
|
|
38079
|
+
for (const item of media) {
|
|
38080
|
+
if (item.kind === "image") {
|
|
38081
|
+
images.push(item);
|
|
38082
|
+
} else {
|
|
38083
|
+
audios.push(item);
|
|
37700
38084
|
}
|
|
37701
38085
|
}
|
|
37702
|
-
return
|
|
37703
|
-
}
|
|
37704
|
-
|
|
37705
|
-
const
|
|
37706
|
-
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
}
|
|
37712
|
-
|
|
37713
|
-
|
|
37714
|
-
|
|
37715
|
-
|
|
37716
|
-
|
|
37717
|
-
|
|
37718
|
-
|
|
37719
|
-
|
|
37720
|
-
|
|
37721
|
-
|
|
37722
|
-
|
|
38086
|
+
return { images, audios };
|
|
38087
|
+
}
|
|
38088
|
+
function assertValidMultimodalMedia(media, limits) {
|
|
38089
|
+
const items = media ?? [];
|
|
38090
|
+
const { images, audios } = splitMediaByKind(items);
|
|
38091
|
+
const maxImages = limits.maxImages ?? 8;
|
|
38092
|
+
const maxAudios = limits.maxAudios ?? 4;
|
|
38093
|
+
if (images.length > maxImages) {
|
|
38094
|
+
throw new Error(`\u56FE\u7247\u6570\u91CF\u8FC7\u591A\uFF0C\u6700\u591A ${maxImages} \u5F20`);
|
|
38095
|
+
}
|
|
38096
|
+
if (audios.length > maxAudios) {
|
|
38097
|
+
throw new Error(`\u97F3\u9891\u6570\u91CF\u8FC7\u591A\uFF0C\u6700\u591A ${maxAudios} \u6BB5`);
|
|
38098
|
+
}
|
|
38099
|
+
for (const image of images) {
|
|
38100
|
+
assertValidImageInput(
|
|
38101
|
+
{ base64: image.base64, mimeType: image.mimeType },
|
|
38102
|
+
limits.maxImageBytes
|
|
38103
|
+
);
|
|
38104
|
+
}
|
|
38105
|
+
for (const audio of audios) {
|
|
38106
|
+
assertValidAudioInput(
|
|
38107
|
+
{ base64: audio.base64, mimeType: audio.mimeType },
|
|
38108
|
+
limits.maxAudioBytes
|
|
38109
|
+
);
|
|
38110
|
+
}
|
|
38111
|
+
return items;
|
|
38112
|
+
}
|
|
38113
|
+
|
|
38114
|
+
// src/common/aiApi/transcribeAudio.ts
|
|
38115
|
+
function joinUrl2(baseUrl, path) {
|
|
38116
|
+
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
38117
|
+
}
|
|
38118
|
+
async function transcribeAudio(options) {
|
|
38119
|
+
const { audio, config, model, language } = options;
|
|
38120
|
+
const sttModel = model?.trim() || config.audioModel;
|
|
38121
|
+
const blob = base64ToBlob(audio.base64, audio.mimeType);
|
|
38122
|
+
const extension = mimeToAudioFormat(audio.mimeType);
|
|
38123
|
+
const form = new FormData();
|
|
38124
|
+
form.append("file", blob, `audio.${extension}`);
|
|
38125
|
+
form.append("model", sttModel);
|
|
38126
|
+
if (language?.trim()) {
|
|
38127
|
+
form.append("language", language.trim());
|
|
38128
|
+
}
|
|
38129
|
+
const controller = new AbortController();
|
|
38130
|
+
const timeoutId = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
38131
|
+
try {
|
|
38132
|
+
const response = await fetch(joinUrl2(config.baseUrl, "audio/transcriptions"), {
|
|
38133
|
+
method: "POST",
|
|
38134
|
+
headers: {
|
|
38135
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
38136
|
+
},
|
|
38137
|
+
body: form,
|
|
38138
|
+
signal: controller.signal
|
|
37723
38139
|
});
|
|
37724
|
-
|
|
37725
|
-
|
|
37726
|
-
|
|
37727
|
-
|
|
37728
|
-
|
|
38140
|
+
const text3 = await response.text();
|
|
38141
|
+
let data = null;
|
|
38142
|
+
if (text3) {
|
|
38143
|
+
try {
|
|
38144
|
+
data = JSON.parse(text3);
|
|
38145
|
+
} catch {
|
|
38146
|
+
data = text3;
|
|
38147
|
+
}
|
|
37729
38148
|
}
|
|
37730
|
-
|
|
37731
|
-
|
|
37732
|
-
|
|
37733
|
-
|
|
37734
|
-
const messages = [];
|
|
37735
|
-
if (systemPrompt) {
|
|
37736
|
-
messages.push({ role: "system", content: systemPrompt });
|
|
38149
|
+
if (!response.ok) {
|
|
38150
|
+
const record = data;
|
|
38151
|
+
const errorMessage2 = (typeof record?.error === "object" ? record.error?.message : record?.error) || record?.message || `STT \u8BF7\u6C42\u5931\u8D25 (${response.status})`;
|
|
38152
|
+
throw new Error(String(errorMessage2));
|
|
37737
38153
|
}
|
|
37738
|
-
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37742
|
-
|
|
38154
|
+
if (typeof data === "string") {
|
|
38155
|
+
return data.trim();
|
|
38156
|
+
}
|
|
38157
|
+
const parsed = data;
|
|
38158
|
+
return (parsed.text ?? "").trim();
|
|
38159
|
+
} finally {
|
|
38160
|
+
clearTimeout(timeoutId);
|
|
38161
|
+
}
|
|
38162
|
+
}
|
|
38163
|
+
async function transcribeAudios(audios, config, model) {
|
|
38164
|
+
const results = [];
|
|
38165
|
+
for (const audio of audios) {
|
|
38166
|
+
results.push(await transcribeAudio({ audio, config, model }));
|
|
38167
|
+
}
|
|
38168
|
+
return results;
|
|
38169
|
+
}
|
|
38170
|
+
|
|
38171
|
+
// src/common/aiApi/callMultimodalChat.ts
|
|
38172
|
+
function joinUrl3(baseUrl, path) {
|
|
38173
|
+
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
38174
|
+
}
|
|
38175
|
+
async function requestMultimodalChat(options) {
|
|
38176
|
+
const payload = {
|
|
38177
|
+
model: options.model,
|
|
38178
|
+
messages: options.messages,
|
|
38179
|
+
temperature: options.temperature ?? 0.2
|
|
37743
38180
|
};
|
|
38181
|
+
if (options.maxTokens !== void 0) {
|
|
38182
|
+
payload.max_tokens = options.maxTokens;
|
|
38183
|
+
}
|
|
38184
|
+
if (options.jsonMode) {
|
|
38185
|
+
payload.response_format = { type: "json_object" };
|
|
38186
|
+
}
|
|
38187
|
+
const raw = await requestJson({
|
|
38188
|
+
url: joinUrl3(options.config.baseUrl, "chat/completions"),
|
|
38189
|
+
method: "POST",
|
|
38190
|
+
headers: {
|
|
38191
|
+
"Content-Type": "application/json",
|
|
38192
|
+
Authorization: `Bearer ${options.config.apiKey}`
|
|
38193
|
+
},
|
|
38194
|
+
body: payload,
|
|
38195
|
+
timeoutMs: options.config.timeoutMs
|
|
38196
|
+
});
|
|
37744
38197
|
return {
|
|
37745
|
-
|
|
37746
|
-
|
|
38198
|
+
content: raw.choices?.[0]?.message?.content ?? "",
|
|
38199
|
+
model: raw.model ?? options.model,
|
|
38200
|
+
raw
|
|
37747
38201
|
};
|
|
37748
|
-
}
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
normalized[key] = String(value);
|
|
37758
|
-
}
|
|
38202
|
+
}
|
|
38203
|
+
function toAudioInputs(audios) {
|
|
38204
|
+
return audios.map((audio) => ({ base64: audio.base64, mimeType: audio.mimeType }));
|
|
38205
|
+
}
|
|
38206
|
+
async function callMultimodalChat(params, clientSettings) {
|
|
38207
|
+
const config = requireAiConnectionConfig(params.connection, clientSettings);
|
|
38208
|
+
const media = assertValidMultimodalMedia(params.media, {
|
|
38209
|
+
maxImageBytes: config.maxImageBytes,
|
|
38210
|
+
maxAudioBytes: config.maxAudioBytes
|
|
37759
38211
|
});
|
|
37760
|
-
|
|
37761
|
-
|
|
38212
|
+
const { images, audios } = splitMediaByKind(media);
|
|
38213
|
+
const hasImages = images.length > 0;
|
|
38214
|
+
const hasAudio = audios.length > 0;
|
|
38215
|
+
const model = params.model || (hasImages ? config.visionModel : hasAudio ? config.visionModel : config.textModel);
|
|
38216
|
+
const strategy = params.audioStrategy ?? config.audioStrategy;
|
|
38217
|
+
const format = detectVisionMessageFormat(config.baseUrl);
|
|
38218
|
+
let audioHandling = resolveAudioHandling({
|
|
38219
|
+
hasAudio,
|
|
38220
|
+
strategy,
|
|
38221
|
+
model,
|
|
38222
|
+
baseUrl: config.baseUrl
|
|
38223
|
+
});
|
|
38224
|
+
let userPrompt = params.userPrompt;
|
|
38225
|
+
let transcriptions;
|
|
38226
|
+
let nativeAudios = [];
|
|
38227
|
+
if (audioHandling === "stt" && hasAudio) {
|
|
38228
|
+
transcriptions = await transcribeAudios(toAudioInputs(audios), config);
|
|
38229
|
+
userPrompt = appendTranscriptionsToPrompt(userPrompt, transcriptions);
|
|
38230
|
+
} else if (audioHandling === "native" && hasAudio) {
|
|
38231
|
+
nativeAudios = audios;
|
|
38232
|
+
}
|
|
38233
|
+
assertMultimodalCapableModel(model, {
|
|
38234
|
+
baseUrl: config.baseUrl,
|
|
38235
|
+
hasImages,
|
|
38236
|
+
hasNativeAudio: nativeAudios.length > 0
|
|
38237
|
+
});
|
|
38238
|
+
const messages = buildMultimodalMessages({
|
|
38239
|
+
systemPrompt: params.systemPrompt,
|
|
38240
|
+
userPrompt,
|
|
38241
|
+
images,
|
|
38242
|
+
nativeAudios,
|
|
38243
|
+
format
|
|
38244
|
+
});
|
|
38245
|
+
try {
|
|
38246
|
+
const result = await requestMultimodalChat({
|
|
38247
|
+
config,
|
|
38248
|
+
model,
|
|
38249
|
+
messages,
|
|
38250
|
+
temperature: params.temperature,
|
|
38251
|
+
maxTokens: params.maxTokens,
|
|
38252
|
+
jsonMode: params.jsonMode
|
|
38253
|
+
});
|
|
38254
|
+
return {
|
|
38255
|
+
...result,
|
|
38256
|
+
audioHandling,
|
|
38257
|
+
transcriptions
|
|
38258
|
+
};
|
|
38259
|
+
} catch (error) {
|
|
38260
|
+
const message = error instanceof Error ? error.message : "AI \u8BF7\u6C42\u5931\u8D25";
|
|
38261
|
+
if (audioHandling === "native" && hasAudio && strategy === "auto" && isAudioInputError(message)) {
|
|
38262
|
+
transcriptions = await transcribeAudios(toAudioInputs(audios), config);
|
|
38263
|
+
userPrompt = appendTranscriptionsToPrompt(params.userPrompt, transcriptions);
|
|
38264
|
+
audioHandling = "stt";
|
|
38265
|
+
const fallbackMessages = buildMultimodalMessages({
|
|
38266
|
+
systemPrompt: params.systemPrompt,
|
|
38267
|
+
userPrompt,
|
|
38268
|
+
images,
|
|
38269
|
+
nativeAudios: [],
|
|
38270
|
+
format
|
|
38271
|
+
});
|
|
38272
|
+
const result = await requestMultimodalChat({
|
|
38273
|
+
config,
|
|
38274
|
+
model,
|
|
38275
|
+
messages: fallbackMessages,
|
|
38276
|
+
temperature: params.temperature,
|
|
38277
|
+
maxTokens: params.maxTokens,
|
|
38278
|
+
jsonMode: params.jsonMode
|
|
38279
|
+
});
|
|
38280
|
+
return {
|
|
38281
|
+
...result,
|
|
38282
|
+
audioHandling,
|
|
38283
|
+
transcriptions
|
|
38284
|
+
};
|
|
38285
|
+
}
|
|
38286
|
+
throw new Error(toVisionApiErrorMessage(message, model));
|
|
38287
|
+
}
|
|
38288
|
+
}
|
|
37762
38289
|
|
|
37763
|
-
// src/common/
|
|
37764
|
-
|
|
37765
|
-
|
|
37766
|
-
|
|
38290
|
+
// src/common/aiApi/jsonUtils.ts
|
|
38291
|
+
function extractJsonObject(text3) {
|
|
38292
|
+
const trimmed = text3.trim();
|
|
38293
|
+
if (!trimmed) {
|
|
38294
|
+
throw new Error("\u6A21\u578B\u8FD4\u56DE\u4E3A\u7A7A");
|
|
37767
38295
|
}
|
|
37768
|
-
|
|
37769
|
-
|
|
37770
|
-
|
|
37771
|
-
|
|
37772
|
-
return resolved[key] ?? "";
|
|
38296
|
+
try {
|
|
38297
|
+
const parsed = JSON.parse(trimmed);
|
|
38298
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
38299
|
+
return parsed;
|
|
37773
38300
|
}
|
|
37774
|
-
|
|
37775
|
-
});
|
|
37776
|
-
};
|
|
37777
|
-
|
|
37778
|
-
// src/common/ai/llm/client/createChatSession.ts
|
|
37779
|
-
var buildBaseMessages = (systemPrompt, initialMessages) => {
|
|
37780
|
-
const baseMessages = [];
|
|
37781
|
-
if (systemPrompt) {
|
|
37782
|
-
baseMessages.push({ role: "system", content: systemPrompt });
|
|
38301
|
+
} catch {
|
|
37783
38302
|
}
|
|
37784
|
-
|
|
37785
|
-
|
|
38303
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
38304
|
+
if (fenced?.[1]) {
|
|
38305
|
+
const parsed = JSON.parse(fenced[1].trim());
|
|
38306
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
38307
|
+
return parsed;
|
|
38308
|
+
}
|
|
37786
38309
|
}
|
|
37787
|
-
|
|
37788
|
-
};
|
|
37789
|
-
|
|
37790
|
-
|
|
37791
|
-
|
|
37792
|
-
|
|
37793
|
-
|
|
37794
|
-
};
|
|
37795
|
-
const setSystemPrompt = (prompt2) => {
|
|
37796
|
-
systemPrompt = prompt2;
|
|
37797
|
-
reset();
|
|
37798
|
-
};
|
|
37799
|
-
const sendMessage = async (input, requestOptions = {}) => {
|
|
37800
|
-
const { template, variables, ...chatOptions } = requestOptions;
|
|
37801
|
-
const activeTemplate = template ?? options.template;
|
|
37802
|
-
const mergedVariables = { input, ...variables ?? {} };
|
|
37803
|
-
const content = activeTemplate ? applyPromptTemplate(activeTemplate, mergedVariables) : input;
|
|
37804
|
-
const userMessage = { role: "user", content };
|
|
37805
|
-
const nextMessages = [...messages, userMessage];
|
|
37806
|
-
const response = await options.client.sendChat({
|
|
37807
|
-
messages: nextMessages,
|
|
37808
|
-
...chatOptions
|
|
37809
|
-
});
|
|
37810
|
-
const assistantMessage = response.message ?? {
|
|
37811
|
-
role: "assistant",
|
|
37812
|
-
content: response.content,
|
|
37813
|
-
toolCalls: response.toolCalls
|
|
37814
|
-
};
|
|
37815
|
-
messages = [...nextMessages, assistantMessage];
|
|
37816
|
-
return response;
|
|
37817
|
-
};
|
|
37818
|
-
return {
|
|
37819
|
-
getMessages: () => messages,
|
|
37820
|
-
setSystemPrompt,
|
|
37821
|
-
reset,
|
|
37822
|
-
sendMessage
|
|
37823
|
-
};
|
|
37824
|
-
};
|
|
37825
|
-
var buildBaseMessages2 = (systemPrompt, initialMessages) => {
|
|
37826
|
-
const baseMessages = [];
|
|
37827
|
-
if (systemPrompt) {
|
|
37828
|
-
baseMessages.push({ role: "system", content: systemPrompt });
|
|
38310
|
+
const start = trimmed.indexOf("{");
|
|
38311
|
+
const end = trimmed.lastIndexOf("}");
|
|
38312
|
+
if (start >= 0 && end > start) {
|
|
38313
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1));
|
|
38314
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
38315
|
+
return parsed;
|
|
38316
|
+
}
|
|
37829
38317
|
}
|
|
37830
|
-
|
|
37831
|
-
|
|
38318
|
+
throw new Error("\u65E0\u6CD5\u89E3\u6790\u6A21\u578B JSON \u8F93\u51FA");
|
|
38319
|
+
}
|
|
38320
|
+
|
|
38321
|
+
// src/common/aiApi/listModels.ts
|
|
38322
|
+
function joinUrl4(baseUrl, path) {
|
|
38323
|
+
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
38324
|
+
}
|
|
38325
|
+
function parseModelIds(raw) {
|
|
38326
|
+
if (Array.isArray(raw.data)) {
|
|
38327
|
+
return raw.data.map((item) => item.id).filter((id) => Boolean(id));
|
|
37832
38328
|
}
|
|
37833
|
-
|
|
37834
|
-
|
|
37835
|
-
|
|
37836
|
-
|
|
37837
|
-
|
|
37838
|
-
|
|
37839
|
-
|
|
37840
|
-
)
|
|
37841
|
-
|
|
37842
|
-
|
|
37843
|
-
const
|
|
37844
|
-
|
|
37845
|
-
|
|
37846
|
-
|
|
37847
|
-
|
|
37848
|
-
};
|
|
37849
|
-
const reset = useCallback(() => {
|
|
37850
|
-
syncMessages(buildBaseMessages2(systemPromptRef.current, options.initialMessages));
|
|
37851
|
-
setStatus("idle");
|
|
37852
|
-
setError(null);
|
|
37853
|
-
setLastResponse(null);
|
|
37854
|
-
}, [options.initialMessages]);
|
|
37855
|
-
const setSystemPrompt = useCallback(
|
|
37856
|
-
(prompt2) => {
|
|
37857
|
-
systemPromptRef.current = prompt2;
|
|
37858
|
-
reset();
|
|
37859
|
-
},
|
|
37860
|
-
[reset]
|
|
37861
|
-
);
|
|
37862
|
-
const sendMessage = useCallback(
|
|
37863
|
-
async (input, requestOptions = {}) => {
|
|
37864
|
-
const { template, variables, ...chatOptions } = requestOptions;
|
|
37865
|
-
const activeTemplate = template ?? templateRef.current;
|
|
37866
|
-
const mergedVariables = { input, ...variables ?? {} };
|
|
37867
|
-
const content = activeTemplate ? applyPromptTemplate(activeTemplate, mergedVariables) : input;
|
|
37868
|
-
const userMessage = { role: "user", content };
|
|
37869
|
-
const nextMessages = [...messagesRef.current, userMessage];
|
|
37870
|
-
syncMessages(nextMessages);
|
|
37871
|
-
setStatus("loading");
|
|
37872
|
-
setError(null);
|
|
37873
|
-
try {
|
|
37874
|
-
const response = await options.client.sendChat({
|
|
37875
|
-
messages: nextMessages,
|
|
37876
|
-
...chatOptions
|
|
37877
|
-
});
|
|
37878
|
-
const assistantMessage = response.message ?? {
|
|
37879
|
-
role: "assistant",
|
|
37880
|
-
content: response.content,
|
|
37881
|
-
toolCalls: response.toolCalls
|
|
37882
|
-
};
|
|
37883
|
-
const updatedMessages = [...nextMessages, assistantMessage];
|
|
37884
|
-
syncMessages(updatedMessages);
|
|
37885
|
-
setStatus("success");
|
|
37886
|
-
setLastResponse(response);
|
|
37887
|
-
return response;
|
|
37888
|
-
} catch (err) {
|
|
37889
|
-
const nextError = err instanceof Error ? err : new Error(String(err));
|
|
37890
|
-
setStatus("error");
|
|
37891
|
-
setError(nextError);
|
|
37892
|
-
throw nextError;
|
|
37893
|
-
}
|
|
38329
|
+
if (Array.isArray(raw.models)) {
|
|
38330
|
+
return raw.models.map((item) => typeof item === "string" ? item : item.id).filter((id) => Boolean(id));
|
|
38331
|
+
}
|
|
38332
|
+
return [];
|
|
38333
|
+
}
|
|
38334
|
+
async function listOpenAiCompatibleModels(clientSettings, currentVisionModel) {
|
|
38335
|
+
const config = resolveAiConnectionConfig(clientSettings);
|
|
38336
|
+
if (!config) {
|
|
38337
|
+
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");
|
|
38338
|
+
}
|
|
38339
|
+
const raw = await requestJson({
|
|
38340
|
+
url: joinUrl4(config.baseUrl, "models"),
|
|
38341
|
+
method: "GET",
|
|
38342
|
+
headers: {
|
|
38343
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
37894
38344
|
},
|
|
37895
|
-
|
|
37896
|
-
);
|
|
37897
|
-
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
|
|
37903
|
-
|
|
37904
|
-
|
|
37905
|
-
|
|
37906
|
-
};
|
|
37907
|
-
};
|
|
38345
|
+
timeoutMs: config.timeoutMs
|
|
38346
|
+
});
|
|
38347
|
+
const modelIds = parseModelIds(raw);
|
|
38348
|
+
if (modelIds.length === 0) {
|
|
38349
|
+
throw new Error("\u63A5\u53E3\u672A\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
|
|
38350
|
+
}
|
|
38351
|
+
const models = filterChatModels(modelIds);
|
|
38352
|
+
const visionModels = filterVisionModels(modelIds);
|
|
38353
|
+
const suggestedVisionModel = pickDefaultVisionModel(modelIds, currentVisionModel);
|
|
38354
|
+
return { models, visionModels, suggestedVisionModel };
|
|
38355
|
+
}
|
|
37908
38356
|
|
|
37909
|
-
// src/common/
|
|
37910
|
-
var
|
|
37911
|
-
|
|
37912
|
-
|
|
37913
|
-
|
|
37914
|
-
|
|
37915
|
-
description: skill.description
|
|
37916
|
-
}
|
|
37917
|
-
};
|
|
37918
|
-
if (skill.inputSchema) {
|
|
37919
|
-
toolDefinition.function.parameters = skill.inputSchema;
|
|
38357
|
+
// src/common/aiApi/taskRegistry.ts
|
|
38358
|
+
var registry2 = /* @__PURE__ */ new Map();
|
|
38359
|
+
function registerAiTask(task) {
|
|
38360
|
+
if (registry2.has(task.id)) {
|
|
38361
|
+
console.warn(`[aiApi] task "${task.id}" already registered, skipping duplicate`);
|
|
38362
|
+
return;
|
|
37920
38363
|
}
|
|
37921
|
-
|
|
37922
|
-
}
|
|
37923
|
-
|
|
37924
|
-
|
|
37925
|
-
|
|
37926
|
-
|
|
37927
|
-
|
|
37928
|
-
|
|
38364
|
+
registry2.set(task.id, task);
|
|
38365
|
+
}
|
|
38366
|
+
function getAiTask(taskId) {
|
|
38367
|
+
return registry2.get(taskId);
|
|
38368
|
+
}
|
|
38369
|
+
function listAiTasks() {
|
|
38370
|
+
return Array.from(registry2.keys());
|
|
38371
|
+
}
|
|
38372
|
+
function clearAiTasksForTest() {
|
|
38373
|
+
registry2.clear();
|
|
38374
|
+
}
|
|
38375
|
+
|
|
38376
|
+
// src/common/aiApi/runTask.ts
|
|
38377
|
+
function extractInputConnection(input) {
|
|
38378
|
+
if (!input || typeof input !== "object") return void 0;
|
|
38379
|
+
const connection = input.connection;
|
|
38380
|
+
return connection && typeof connection === "object" ? connection : void 0;
|
|
38381
|
+
}
|
|
38382
|
+
async function runAiTask(taskId, input, ctx = {}) {
|
|
38383
|
+
const started = Date.now();
|
|
38384
|
+
if (!resolveAiConnectionConfig(extractInputConnection(input), ctx.clientSettings)) {
|
|
38385
|
+
return {
|
|
38386
|
+
success: false,
|
|
38387
|
+
taskId,
|
|
38388
|
+
error: {
|
|
38389
|
+
code: "AI_CONFIG_MISSING",
|
|
38390
|
+
message: "\u672A\u914D\u7F6E AI API Key\uFF0C\u8BF7\u4F20\u5165 clientSettings.connection \u6216\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF AI_API_KEY"
|
|
38391
|
+
}
|
|
38392
|
+
};
|
|
38393
|
+
}
|
|
38394
|
+
const task = getAiTask(taskId);
|
|
38395
|
+
if (!task) {
|
|
38396
|
+
return {
|
|
38397
|
+
success: false,
|
|
38398
|
+
taskId,
|
|
38399
|
+
error: {
|
|
38400
|
+
code: "TASK_NOT_FOUND",
|
|
38401
|
+
message: `\u672A\u6CE8\u518C\u7684\u4EFB\u52A1: ${taskId}`
|
|
38402
|
+
}
|
|
38403
|
+
};
|
|
38404
|
+
}
|
|
38405
|
+
try {
|
|
38406
|
+
const validated = task.validateInput(input);
|
|
38407
|
+
const result = await task.execute(validated, ctx);
|
|
38408
|
+
return {
|
|
38409
|
+
success: true,
|
|
38410
|
+
taskId,
|
|
38411
|
+
data: result.data,
|
|
38412
|
+
meta: {
|
|
38413
|
+
model: result.meta?.model ?? "unknown",
|
|
38414
|
+
latencyMs: Date.now() - started,
|
|
38415
|
+
provider: result.meta?.provider ?? "openai-compatible",
|
|
38416
|
+
confidence: result.meta?.confidence,
|
|
38417
|
+
rawSummary: result.meta?.rawSummary
|
|
38418
|
+
}
|
|
38419
|
+
};
|
|
38420
|
+
} catch (error) {
|
|
38421
|
+
const message = error instanceof Error ? error.message : "AI \u4EFB\u52A1\u6267\u884C\u5931\u8D25";
|
|
38422
|
+
const code = classifyError(message);
|
|
38423
|
+
return {
|
|
38424
|
+
success: false,
|
|
38425
|
+
taskId,
|
|
38426
|
+
error: { code, message },
|
|
38427
|
+
meta: {
|
|
38428
|
+
model: "unknown",
|
|
38429
|
+
latencyMs: Date.now() - started
|
|
38430
|
+
}
|
|
38431
|
+
};
|
|
37929
38432
|
}
|
|
37930
|
-
|
|
37931
|
-
|
|
38433
|
+
}
|
|
38434
|
+
function classifyError(message) {
|
|
38435
|
+
if (message.includes("\u672A\u914D\u7F6E") || message.includes("AI_API_KEY") || message.includes("apiKey")) {
|
|
38436
|
+
return "AI_CONFIG_MISSING";
|
|
37932
38437
|
}
|
|
37933
|
-
|
|
37934
|
-
return
|
|
38438
|
+
if (message.includes("\u56FE\u7247") || message.includes("\u683C\u5F0F")) {
|
|
38439
|
+
return message.includes("\u8FC7\u5927") ? "PAYLOAD_TOO_LARGE" : "UNSUPPORTED_MEDIA";
|
|
37935
38440
|
}
|
|
37936
|
-
|
|
37937
|
-
return
|
|
38441
|
+
if (message.includes("\u89E3\u6790") || message.includes("JSON")) {
|
|
38442
|
+
return "AI_PARSE_FAILED";
|
|
37938
38443
|
}
|
|
37939
|
-
|
|
37940
|
-
return
|
|
38444
|
+
if (message.includes("\u5FC5\u586B") || message.includes("\u65E0\u6548")) {
|
|
38445
|
+
return "INVALID_INPUT";
|
|
37941
38446
|
}
|
|
37942
|
-
|
|
37943
|
-
|
|
37944
|
-
|
|
37945
|
-
|
|
38447
|
+
return "AI_REQUEST_FAILED";
|
|
38448
|
+
}
|
|
38449
|
+
|
|
38450
|
+
// src/common/aiApi/tasks/coreLlmCompletion.ts
|
|
38451
|
+
function isTextCompletionInput(input) {
|
|
38452
|
+
if (!input || typeof input !== "object") return false;
|
|
38453
|
+
const value = input;
|
|
38454
|
+
return typeof value.userPrompt === "string" && value.userPrompt.trim().length > 0;
|
|
38455
|
+
}
|
|
38456
|
+
var coreLlmCompletionTask = {
|
|
38457
|
+
id: CORE_LLM_COMPLETION_TASK_ID,
|
|
38458
|
+
description: "\u901A\u7528\u6587\u672C\u8865\u5168\uFF1Asystem/user \u63D0\u793A\u8BCD \u2192 \u6A21\u578B\u6587\u672C",
|
|
38459
|
+
validateInput(input) {
|
|
38460
|
+
if (!isTextCompletionInput(input)) {
|
|
38461
|
+
throw new Error("userPrompt \u4E3A\u5FC5\u586B");
|
|
37946
38462
|
}
|
|
37947
|
-
return
|
|
38463
|
+
return input;
|
|
38464
|
+
},
|
|
38465
|
+
async execute(input, ctx) {
|
|
38466
|
+
const result = await callCompletion(
|
|
38467
|
+
{
|
|
38468
|
+
systemPrompt: input.systemPrompt,
|
|
38469
|
+
userPrompt: input.userPrompt,
|
|
38470
|
+
model: input.model,
|
|
38471
|
+
temperature: input.temperature,
|
|
38472
|
+
maxTokens: input.maxTokens,
|
|
38473
|
+
connection: input.connection
|
|
38474
|
+
},
|
|
38475
|
+
ctx.clientSettings
|
|
38476
|
+
);
|
|
38477
|
+
return {
|
|
38478
|
+
data: {
|
|
38479
|
+
content: result.content,
|
|
38480
|
+
rawText: result.content
|
|
38481
|
+
},
|
|
38482
|
+
meta: {
|
|
38483
|
+
model: result.model,
|
|
38484
|
+
provider: "openai-compatible"
|
|
38485
|
+
}
|
|
38486
|
+
};
|
|
37948
38487
|
}
|
|
37949
|
-
|
|
37950
|
-
|
|
38488
|
+
};
|
|
38489
|
+
|
|
38490
|
+
// src/common/aiApi/tasks/coreStructuredMultimodal.ts
|
|
38491
|
+
function isStructuredMultimodalInput(input) {
|
|
38492
|
+
if (!input || typeof input !== "object") return false;
|
|
38493
|
+
const value = input;
|
|
38494
|
+
return typeof value.systemPrompt === "string" && typeof value.userPrompt === "string";
|
|
38495
|
+
}
|
|
38496
|
+
var coreStructuredMultimodalTask = {
|
|
38497
|
+
id: CORE_STRUCTURED_MULTIMODAL_TASK_ID,
|
|
38498
|
+
description: "\u901A\u7528\u7ED3\u6784\u5316\u591A\u6A21\u6001\u4EFB\u52A1\uFF1A\u6587\u672C + \u53EF\u9009\u56FE\u7247/\u8BED\u97F3 \u2192 JSON",
|
|
38499
|
+
validateInput(input) {
|
|
38500
|
+
if (!isStructuredMultimodalInput(input)) {
|
|
38501
|
+
throw new Error("systemPrompt \u4E0E userPrompt \u4E3A\u5FC5\u586B");
|
|
38502
|
+
}
|
|
38503
|
+
const config = resolveAiConnectionConfig(input.connection);
|
|
38504
|
+
assertValidMultimodalMedia(input.media, {
|
|
38505
|
+
maxImageBytes: config?.maxImageBytes ?? 5 * 1024 * 1024,
|
|
38506
|
+
maxAudioBytes: config?.maxAudioBytes ?? 25 * 1024 * 1024
|
|
38507
|
+
});
|
|
38508
|
+
return input;
|
|
38509
|
+
},
|
|
38510
|
+
async execute(input, ctx) {
|
|
38511
|
+
const schemaHint = input.jsonSchemaHint ? `
|
|
38512
|
+
|
|
38513
|
+
\u8BF7\u4E25\u683C\u8F93\u51FA JSON \u5BF9\u8C61\uFF0C\u7ED3\u6784\u53C2\u8003\uFF1A
|
|
38514
|
+
${input.jsonSchemaHint}` : "\n\n\u8BF7\u4E25\u683C\u8F93\u51FA JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u5305\u542B Markdown \u4EE3\u7801\u5757\u3002";
|
|
38515
|
+
const result = await callMultimodalChat(
|
|
38516
|
+
{
|
|
38517
|
+
systemPrompt: input.systemPrompt,
|
|
38518
|
+
userPrompt: `${input.userPrompt}${schemaHint}`,
|
|
38519
|
+
media: input.media,
|
|
38520
|
+
model: input.model,
|
|
38521
|
+
temperature: input.temperature ?? 0.2,
|
|
38522
|
+
maxTokens: input.maxTokens,
|
|
38523
|
+
jsonMode: true,
|
|
38524
|
+
audioStrategy: input.audioStrategy,
|
|
38525
|
+
connection: input.connection
|
|
38526
|
+
},
|
|
38527
|
+
ctx.clientSettings
|
|
38528
|
+
);
|
|
38529
|
+
const json2 = extractJsonObject(result.content);
|
|
38530
|
+
return {
|
|
38531
|
+
data: {
|
|
38532
|
+
json: json2,
|
|
38533
|
+
rawText: result.content
|
|
38534
|
+
},
|
|
38535
|
+
meta: {
|
|
38536
|
+
model: result.model,
|
|
38537
|
+
provider: "openai-compatible",
|
|
38538
|
+
rawSummary: result.audioHandling ? `audio=${result.audioHandling}` : void 0
|
|
38539
|
+
}
|
|
38540
|
+
};
|
|
37951
38541
|
}
|
|
37952
38542
|
};
|
|
37953
|
-
|
|
37954
|
-
|
|
38543
|
+
|
|
38544
|
+
// src/common/aiApi/tasks/coreConnectivityTest.ts
|
|
38545
|
+
var coreConnectivityTestTask = {
|
|
38546
|
+
id: CORE_CONNECTIVITY_TEST_TASK_ID,
|
|
38547
|
+
description: "\u6D4B\u8BD5 AI API \u8FDE\u901A\u6027\uFF08\u8F7B\u91CF\u6587\u672C\u8BF7\u6C42\uFF09",
|
|
38548
|
+
validateInput() {
|
|
38549
|
+
return {};
|
|
38550
|
+
},
|
|
38551
|
+
async execute(_input, ctx) {
|
|
38552
|
+
const config = resolveAiConnectionConfig(ctx.clientSettings);
|
|
38553
|
+
if (!config) {
|
|
38554
|
+
throw new Error("\u672A\u914D\u7F6E AI API Key");
|
|
38555
|
+
}
|
|
38556
|
+
const result = await callChat({
|
|
38557
|
+
baseUrl: config.baseUrl,
|
|
38558
|
+
apiKey: config.apiKey,
|
|
38559
|
+
model: config.textModel,
|
|
38560
|
+
systemPrompt: "You are a connectivity probe. Reply with JSON only.",
|
|
38561
|
+
userPrompt: 'Return JSON: {"ok":true,"reply":"OK"}',
|
|
38562
|
+
temperature: 0,
|
|
38563
|
+
maxTokens: 32,
|
|
38564
|
+
timeoutMs: config.timeoutMs
|
|
38565
|
+
});
|
|
38566
|
+
let ok = false;
|
|
38567
|
+
let reply = result.content.trim();
|
|
38568
|
+
try {
|
|
38569
|
+
const json2 = extractJsonObject(result.content);
|
|
38570
|
+
ok = json2.ok === true || json2.ok === "true";
|
|
38571
|
+
reply = String(json2.reply ?? result.content).trim();
|
|
38572
|
+
} catch {
|
|
38573
|
+
ok = /ok/i.test(result.content);
|
|
38574
|
+
}
|
|
38575
|
+
if (!ok && !reply) {
|
|
38576
|
+
throw new Error("\u6A21\u578B\u672A\u8FD4\u56DE\u6709\u6548\u54CD\u5E94");
|
|
38577
|
+
}
|
|
38578
|
+
return {
|
|
38579
|
+
data: { ok: true, reply: reply || "OK" },
|
|
38580
|
+
meta: {
|
|
38581
|
+
model: result.model,
|
|
38582
|
+
provider: "openai-compatible"
|
|
38583
|
+
}
|
|
38584
|
+
};
|
|
38585
|
+
}
|
|
37955
38586
|
};
|
|
37956
38587
|
|
|
37957
|
-
|
|
38588
|
+
// src/common/aiApi/registerCoreTasks.ts
|
|
38589
|
+
var registered = false;
|
|
38590
|
+
function registerCoreAiTasks() {
|
|
38591
|
+
if (registered) return;
|
|
38592
|
+
registerAiTask(coreLlmCompletionTask);
|
|
38593
|
+
registerAiTask(coreStructuredMultimodalTask);
|
|
38594
|
+
registerAiTask(coreConnectivityTestTask);
|
|
38595
|
+
registered = true;
|
|
38596
|
+
}
|
|
38597
|
+
var ensureCoreAiTasksRegistered = registerCoreAiTasks;
|
|
38598
|
+
function resetCoreAiTasksForTest() {
|
|
38599
|
+
registered = false;
|
|
38600
|
+
}
|
|
38601
|
+
|
|
38602
|
+
export { AboutWithDefaults_default as About, About_default as AboutBase, Dialog as AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, DialogDescription as AlertDialogDescription, DialogFooter as AlertDialogFooter, DialogHeader as AlertDialogHeader, DialogOverlay as AlertDialogOverlay, DialogPortal as AlertDialogPortal, DialogTitle as AlertDialogTitle, DialogTrigger as AlertDialogTrigger, AutoOpenModal, Avatar, AvatarFallback, AvatarImage, BackButton, BackgroundRemover, Badge, BoothVaultService, Button, CORE_CONNECTIVITY_TEST_TASK_ID, CORE_LLM_COMPLETION_TASK_ID, CORE_STRUCTURED_MULTIMODAL_TASK_ID, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryFilter, CollisionBalls, CompletionFilterComponent, ConfirmModal, ConsoleLoggerAdapter, Contact_default as Contact, DANMAKU_MAX_LENGTH, DANMAKU_TRACK_COUNT, DEFAULT_FESTIVAL_CARD_CONFIG, DEFAULT_MAX_ACTIVE_FIREWORKS, DEFAULT_MAX_PARTICLES, DEFAULT_OPENAI_BASE_URL, DEFAULT_TEXT_MODEL2 as DEFAULT_TEXT_MODEL, DanmakuPanel, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DraggableExperimentGrid, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EnhancedAvatar, ExperimentCard, ExperimentGrid, ExperimentItemGrid, FIREWORK_KIND_LABELS, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, FilterButtonGroup, FireworksCanvas, FireworksControlPanel, FloatingMenu_default as FloatingMenu, FloatingMenuExample_default as FloatingMenuExample, GenericOrderManager, Grid, Home_default as Home, ImageMappingPanel, Input, Label, LocalImageMappingPanel, LogLevel, Logger, MIKU_PALETTE, MikuContestPersistentService, MikuContestService, MikuContestStateDbService, MikuFireworks3D, Modal, NORMAL_PALETTE, Navigation_default as Navigation, NavigationItem_default as NavigationItem, NavigationToggle_default as NavigationToggle, OCRScanner, PageHeader, PermissionGuard, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProfileButton, ProfileModal, Progress, ProjectCarousel, ScreenReceiverPanel, ScrollArea, ScrollBar, SearchBox, SearchResultHint, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SentimentAnalyzer, Separator, Dialog as Sheet, DialogClose as SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, DialogOverlay as SheetOverlay, DialogPortal as SheetPortal, SheetTitle, DialogTrigger as SheetTrigger, SmartAssistant, SortControl, SortModeToggle, SortableExperimentItem, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Timeline, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserInfoBar, WebSocketTransport, appendTranscriptionsToPrompt, arrayUtils, assertMultimodalCapableModel, assertValidAudioInput, assertValidImageInput, assertValidMultimodalMedia, assertVisionCapableModel, badgeVariants, base64ToBlob, bubbleShooter_exports as bubbleShooter, buildMultimodalMessages, business_exports as business, buttonVariants, callChat, callCompletion, callMultimodalChat, checkVoteEligibility, clearAiTasksForTest, cn, common_exports as common, coreConnectivityTestTask, coreLlmCompletionTask, coreStructuredMultimodalTask, createCreateSubmissionHandler, createDefaultMikuContestConfig, createExportSubmissionsHandler, createGetContestSnapshotHandler, createInMemoryFestivalCardDb, createListSubmissionsHandler, createLogger, createMikuContestApiClient, createMikuContestDrizzlePersistenceAdapter, createMikuContestPersistentService, createMikuContestService, createResetVotesHandler, createReviewSubmissionHandler, createSetVoterRestrictionHandler, createUpdateContestConfigHandler, createVoteHandler, debugUtils, defaultMikuVotingRules, defaultVocaloidBoothConfig, detectVisionMessageFormat, ensureCoreAiTasksRegistered, errorUtils, extractJsonObject, fileToAiAudioInput, fileToAiImageInput, fileUtils, filterChatModels, filterExperiments, filterSttModels, filterVisionModels, formatTime, generateMatchCode, getAiTask, getAllTags, getCategoryColor, getCategoryDisplayName, getCompletionFilterDisplayName, getCompletionStatusColor, getCompletionStatusText, getExperimentCounts, isAudioInputError, isImageUrlVariantError, isKnownTextOnlyModel, isLikelyNativeAudioChatModel, isLikelySttModel, isLikelyVisionModel, japaneseUtils, listAiTasks, listOpenAiCompatibleModels, logger, mikuContestConfigs, mikuContestDbService, mikuContestNotices, mikuContestSubmissions, mikuContestVoterRestrictions, mikuContestVotes, mimeToAudioFormat, miniapp_exports as miniappService, miniapp_exports2 as miniappUI, normalizeFestivalCardConfig, normalizeMatchCode, normalizeVocaloidBoothConfig, pickDefaultSttModel, pickDefaultVisionModel, registerAiTask, registerCoreAiTasks, requestJson, requireAiConnectionConfig, resetCoreAiTasksForTest, resizeFestivalCardPages, resolveAiConnectionConfig, resolveAudioHandling, resolveScreenReceiverSignalUrl, runAiTask, sortByVotesDesc, sortExperiments, splitMediaByKind, stringUtils, toVisionApiErrorMessage, toVoteDayKey, transcribeAudio, transcribeAudios, useAsyncStorage, useBackgroundRemoval, useDanmakuController, useElectronStorage, useFestivalCardConfig, useFireworksEngine, useFireworksRealtime, useLocalStorage, useMikuContest, useOCR, useScreenReceiver, useSentimentAnalysis, useStorage, useTaroStorage, useTextGeneration, validateExperiment, validateMikuSubmissionInput, validators, web_exports2 as webService, web_exports3 as webUI };
|
|
37958
38603
|
//# sourceMappingURL=index.mjs.map
|
|
37959
38604
|
//# sourceMappingURL=index.mjs.map
|