@midscene/core 1.12.6-beta-20260910093036.0 → 1.12.6
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/es/agent/agent.mjs +12 -2
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs +49 -0
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/call.mjs +56 -0
- package/dist/es/ai-model/service-caller/call.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/chat-completion/call.mjs +101 -0
- package/dist/es/ai-model/service-caller/chat-completion/call.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs +80 -0
- package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/chat-completion/stream.mjs +87 -0
- package/dist/es/ai-model/service-caller/chat-completion/stream.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/chat-completion/types.mjs +0 -0
- package/dist/es/ai-model/service-caller/chat-completion/utils.mjs +35 -0
- package/dist/es/ai-model/service-caller/chat-completion/utils.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/codex/call-codex.mjs +77 -0
- package/dist/es/ai-model/service-caller/codex/call-codex.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/{codex-app-server.mjs → codex/codex-app-server.mjs} +8 -17
- package/dist/es/ai-model/service-caller/codex/codex-app-server.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/index.mjs +5 -582
- package/dist/es/ai-model/service-caller/openai-client.mjs +88 -0
- package/dist/es/ai-model/service-caller/openai-client.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/proxy.mjs +66 -0
- package/dist/es/ai-model/service-caller/proxy.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/utils.mjs +83 -0
- package/dist/es/ai-model/service-caller/utils.mjs.map +1 -0
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/test-run-report.mjs.map +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/lib/agent/agent.js +12 -2
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/service-caller/call-ai.js +89 -0
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -0
- package/dist/lib/ai-model/service-caller/call.js +90 -0
- package/dist/lib/ai-model/service-caller/call.js.map +1 -0
- package/dist/lib/ai-model/service-caller/chat-completion/call.js +135 -0
- package/dist/lib/ai-model/service-caller/chat-completion/call.js.map +1 -0
- package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js +114 -0
- package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js.map +1 -0
- package/dist/lib/ai-model/service-caller/chat-completion/stream.js +121 -0
- package/dist/lib/ai-model/service-caller/chat-completion/stream.js.map +1 -0
- package/dist/lib/ai-model/service-caller/chat-completion/types.js +20 -0
- package/dist/lib/ai-model/service-caller/chat-completion/types.js.map +1 -0
- package/dist/lib/ai-model/service-caller/chat-completion/utils.js +75 -0
- package/dist/lib/ai-model/service-caller/chat-completion/utils.js.map +1 -0
- package/dist/lib/ai-model/service-caller/codex/call-codex.js +111 -0
- package/dist/lib/ai-model/service-caller/codex/call-codex.js.map +1 -0
- package/dist/lib/ai-model/service-caller/{codex-app-server.js → codex/codex-app-server.js} +8 -17
- package/dist/lib/ai-model/service-caller/codex/codex-app-server.js.map +1 -0
- package/dist/lib/ai-model/service-caller/index.js +11 -596
- package/dist/lib/ai-model/service-caller/index.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai-client.js +132 -0
- package/dist/lib/ai-model/service-caller/openai-client.js.map +1 -0
- package/dist/lib/ai-model/service-caller/proxy.js +100 -0
- package/dist/lib/ai-model/service-caller/proxy.js.map +1 -0
- package/dist/lib/ai-model/service-caller/utils.js +144 -0
- package/dist/lib/ai-model/service-caller/utils.js.map +1 -0
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/test-run-report.js.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/types/ai-model/service-caller/call-ai.d.ts +26 -0
- package/dist/types/ai-model/service-caller/call.d.ts +4 -0
- package/dist/types/ai-model/service-caller/chat-completion/call.d.ts +2 -0
- package/dist/types/ai-model/service-caller/chat-completion/non-stream.d.ts +2 -0
- package/dist/types/ai-model/service-caller/chat-completion/stream.d.ts +2 -0
- package/dist/types/ai-model/service-caller/chat-completion/types.d.ts +25 -0
- package/dist/types/ai-model/service-caller/chat-completion/utils.d.ts +12 -0
- package/dist/types/ai-model/service-caller/codex/call-codex.d.ts +2 -0
- package/dist/types/ai-model/service-caller/{codex-app-server.d.ts → codex/codex-app-server.d.ts} +4 -3
- package/dist/types/ai-model/service-caller/index.d.ts +4 -74
- package/dist/types/ai-model/service-caller/openai-client.d.ts +14 -0
- package/dist/types/ai-model/service-caller/proxy.d.ts +4 -0
- package/dist/types/ai-model/service-caller/types.d.ts +35 -0
- package/dist/types/ai-model/service-caller/utils.d.ts +40 -0
- package/dist/types/test-run-report.d.ts +0 -1
- package/dist/types/types.d.ts +3 -2
- package/package.json +4 -4
- package/dist/es/ai-model/service-caller/codex-app-server.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/index.mjs.map +0 -1
- package/dist/lib/ai-model/service-caller/codex-app-server.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/openai-client.mjs","sources":["../../../../src/ai-model/service-caller/openai-client.ts"],"sourcesContent":["import {\n type IModelConfig,\n MIDSCENE_LANGFUSE_DEBUG,\n MIDSCENE_LANGSMITH_DEBUG,\n type TModelFamily,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\nimport OpenAI from 'openai';\nimport { getVersion } from '../../utils';\nimport {\n type OpenAIErrorResponseContext,\n wrapOpenAICompatibleFetch,\n} from './openai-error';\nimport { createProxyAgent } from './proxy';\nimport { resolveEffectiveTimeoutMs } from './request-timeout';\n\nconst createAndWrapClient = async ({\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n createOpenAIClient,\n effectiveTimeoutMs,\n proxyAgent,\n executionId,\n openAIErrorResponseContext,\n}: {\n openaiBaseURL: IModelConfig['openaiBaseURL'];\n openaiApiKey: IModelConfig['openaiApiKey'];\n openaiExtraConfig: IModelConfig['openaiExtraConfig'];\n createOpenAIClient: IModelConfig['createOpenAIClient'];\n effectiveTimeoutMs: number | null;\n proxyAgent: Awaited<ReturnType<typeof createProxyAgent>>;\n executionId: string;\n openAIErrorResponseContext: OpenAIErrorResponseContext;\n}): Promise<OpenAI> => {\n const warnClient = getDebug('ai:call', { console: true });\n\n const openAIOptions = {\n baseURL: openaiBaseURL,\n apiKey: openaiApiKey,\n // Use fetchOptions.dispatcher for fetch-based SDK instead of httpAgent\n // Note: Type assertion needed due to undici version mismatch between dependencies\n ...(proxyAgent ? { fetchOptions: { dispatcher: proxyAgent as any } } : {}),\n ...openaiExtraConfig,\n // Midscene exposes this setting through MIDSCENE_*_INIT_CONFIG_JSON, so\n // defaultHeaders is expected to be a plain JSON object rather than another\n // HeadersLike representation supported by the OpenAI SDK.\n defaultHeaders: {\n ...(openaiExtraConfig?.defaultHeaders as\n | Record<string, string>\n | undefined),\n // These Midscene-owned headers intentionally override user-supplied\n // headers with the same names.\n 'x-midscene-version': getVersion(),\n 'x-midscene-execution-id': executionId,\n },\n fetch: wrapOpenAICompatibleFetch(openAIErrorResponseContext),\n // Midscene already handles retries in callAI(), so disable SDK-level retries\n // to avoid duplicate attempts and duplicated backoff latency.\n maxRetries: 0,\n // When disabled (timeoutMs === null) fall through to the SDK default so\n // only the caller-provided abortSignal can cancel the request.\n ...(effectiveTimeoutMs !== null ? { timeout: effectiveTimeoutMs } : {}),\n dangerouslyAllowBrowser: true,\n };\n\n const baseOpenAI = new OpenAI(openAIOptions);\n\n let openai: OpenAI = baseOpenAI;\n\n // LangSmith wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGSMITH_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langsmith is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langsmith wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langsmithModule = 'langsmith/wrappers';\n const { wrapOpenAI } = await import(langsmithModule);\n openai = wrapOpenAI(openai);\n }\n\n // Langfuse wrapper\n if (\n openai &&\n globalConfigManager.getEnvConfigInBoolean(MIDSCENE_LANGFUSE_DEBUG)\n ) {\n if (ifInBrowser) {\n throw new Error('langfuse is not supported in browser');\n }\n warnClient('DEBUGGING MODE: langfuse wrapper enabled');\n // Use variable to prevent static analysis by bundlers\n const langfuseModule = '@langfuse/openai';\n const { observeOpenAI } = await import(langfuseModule);\n openai = observeOpenAI(openai);\n }\n\n if (createOpenAIClient) {\n const wrappedClient = await createOpenAIClient(baseOpenAI, openAIOptions);\n\n if (wrappedClient) {\n openai = wrappedClient as OpenAI;\n }\n }\n\n return openai;\n};\n\nexport async function createChatClient({\n modelConfig,\n executionId,\n recordEvent,\n}: {\n modelConfig: IModelConfig;\n executionId: string;\n recordEvent?: (event: Record<string, unknown>) => void;\n}): Promise<{\n completion: OpenAI.Chat.Completions;\n modelName: string;\n modelDescription: string;\n modelFamily: TModelFamily | undefined;\n openAIErrorResponseContext: OpenAIErrorResponseContext;\n}> {\n const {\n socksProxy,\n httpProxy,\n modelName,\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n modelDescription,\n modelFamily,\n createOpenAIClient,\n timeout,\n } = modelConfig;\n\n const proxyAgent = await createProxyAgent({ socksProxy, httpProxy });\n\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs({ timeout });\n const openAIErrorResponseContext: OpenAIErrorResponseContext = {\n recordEvent,\n };\n\n const openai = await createAndWrapClient({\n openaiBaseURL,\n openaiApiKey,\n openaiExtraConfig,\n createOpenAIClient,\n effectiveTimeoutMs,\n proxyAgent,\n executionId,\n openAIErrorResponseContext,\n });\n\n return {\n completion: openai.chat.completions,\n modelName,\n modelDescription,\n modelFamily,\n openAIErrorResponseContext,\n };\n}\n"],"names":["createAndWrapClient","openaiBaseURL","openaiApiKey","openaiExtraConfig","createOpenAIClient","effectiveTimeoutMs","proxyAgent","executionId","openAIErrorResponseContext","warnClient","getDebug","openAIOptions","getVersion","wrapOpenAICompatibleFetch","baseOpenAI","OpenAI","openai","globalConfigManager","MIDSCENE_LANGSMITH_DEBUG","ifInBrowser","Error","langsmithModule","wrapOpenAI","MIDSCENE_LANGFUSE_DEBUG","langfuseModule","observeOpenAI","wrappedClient","createChatClient","modelConfig","recordEvent","socksProxy","httpProxy","modelName","modelDescription","modelFamily","timeout","createProxyAgent","resolveEffectiveTimeoutMs"],"mappings":";;;;;;;;AAkBA,MAAMA,sBAAsB,OAAO,EACjCC,aAAa,EACbC,YAAY,EACZC,iBAAiB,EACjBC,kBAAkB,EAClBC,kBAAkB,EAClBC,UAAU,EACVC,WAAW,EACXC,0BAA0B,EAU3B;IACC,MAAMC,aAAaC,SAAS,WAAW;QAAE,SAAS;IAAK;IAEvD,MAAMC,gBAAgB;QACpB,SAASV;QACT,QAAQC;QAGR,GAAII,aAAa;YAAE,cAAc;gBAAE,YAAYA;YAAkB;QAAE,IAAI,CAAC,CAAC;QACzE,GAAGH,iBAAiB;QAIpB,gBAAgB;YACd,GAAIA,mBAAmB,cAAc;YAKrC,sBAAsBS;YACtB,2BAA2BL;QAC7B;QACA,OAAOM,0BAA0BL;QAGjC,YAAY;QAGZ,GAAIH,AAAuB,SAAvBA,qBAA8B;YAAE,SAASA;QAAmB,IAAI,CAAC,CAAC;QACtE,yBAAyB;IAC3B;IAEA,MAAMS,aAAa,IAAIC,SAAOJ;IAE9B,IAAIK,SAAiBF;IAGrB,IACEE,UACAC,oBAAoB,qBAAqB,CAACC,2BAC1C;QACA,IAAIC,aACF,MAAM,IAAIC,MAAM;QAElBX,WAAW;QAEX,MAAMY,kBAAkB;QACxB,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAM,MAAM,CAACD;QACpCL,SAASM,WAAWN;IACtB;IAGA,IACEA,UACAC,oBAAoB,qBAAqB,CAACM,0BAC1C;QACA,IAAIJ,aACF,MAAM,IAAIC,MAAM;QAElBX,WAAW;QAEX,MAAMe,iBAAiB;QACvB,MAAM,EAAEC,aAAa,EAAE,GAAG,MAAM,MAAM,CAACD;QACvCR,SAASS,cAAcT;IACzB;IAEA,IAAIZ,oBAAoB;QACtB,MAAMsB,gBAAgB,MAAMtB,mBAAmBU,YAAYH;QAE3D,IAAIe,eACFV,SAASU;IAEb;IAEA,OAAOV;AACT;AAEO,eAAeW,iBAAiB,EACrCC,WAAW,EACXrB,WAAW,EACXsB,WAAW,EAKZ;IAOC,MAAM,EACJC,UAAU,EACVC,SAAS,EACTC,SAAS,EACT/B,aAAa,EACbC,YAAY,EACZC,iBAAiB,EACjB8B,gBAAgB,EAChBC,WAAW,EACX9B,kBAAkB,EAClB+B,OAAO,EACR,GAAGP;IAEJ,MAAMtB,aAAa,MAAM8B,iBAAiB;QAAEN;QAAYC;IAAU;IAElE,MAAM1B,qBAAqBgC,0BAA0B;QAAEF;IAAQ;IAC/D,MAAM3B,6BAAyD;QAC7DqB;IACF;IAEA,MAAMb,SAAS,MAAMhB,oBAAoB;QACvCC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;IACF;IAEA,OAAO;QACL,YAAYQ,OAAO,IAAI,CAAC,WAAW;QACnCgB;QACAC;QACAC;QACA1B;IACF;AACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getDebug } from "@midscene/shared/logger";
|
|
2
|
+
import { ifInBrowser } from "@midscene/shared/utils";
|
|
3
|
+
const sanitizeProxyUrl = (url)=>{
|
|
4
|
+
try {
|
|
5
|
+
const parsed = new URL(url);
|
|
6
|
+
if (parsed.username) {
|
|
7
|
+
parsed.password = '****';
|
|
8
|
+
return parsed.href;
|
|
9
|
+
}
|
|
10
|
+
return url;
|
|
11
|
+
} catch {
|
|
12
|
+
return url;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const createProxyAgent = async ({ socksProxy, httpProxy })=>{
|
|
16
|
+
const warnProxy = getDebug('ai:call:proxy', {
|
|
17
|
+
console: true
|
|
18
|
+
});
|
|
19
|
+
const debugProxy = getDebug('ai:call:proxy');
|
|
20
|
+
if (httpProxy) {
|
|
21
|
+
debugProxy('using http proxy', sanitizeProxyUrl(httpProxy));
|
|
22
|
+
if (ifInBrowser) warnProxy('HTTP proxy is configured but not supported in browser environment');
|
|
23
|
+
else {
|
|
24
|
+
const { loadUndici } = await import("#proxy-deps");
|
|
25
|
+
const { ProxyAgent } = await loadUndici();
|
|
26
|
+
const proxyAgent = new ProxyAgent({
|
|
27
|
+
uri: httpProxy
|
|
28
|
+
});
|
|
29
|
+
return proxyAgent;
|
|
30
|
+
}
|
|
31
|
+
} else if (socksProxy) {
|
|
32
|
+
debugProxy('using socks proxy', sanitizeProxyUrl(socksProxy));
|
|
33
|
+
if (ifInBrowser) warnProxy('SOCKS proxy is configured but not supported in browser environment');
|
|
34
|
+
else try {
|
|
35
|
+
const { loadFetchSocks } = await import("#proxy-deps");
|
|
36
|
+
const { socksDispatcher } = await loadFetchSocks();
|
|
37
|
+
const proxyUrl = new URL(socksProxy);
|
|
38
|
+
if (!proxyUrl.hostname) throw new Error('SOCKS proxy URL must include a valid hostname');
|
|
39
|
+
const port = Number.parseInt(proxyUrl.port, 10);
|
|
40
|
+
if (!proxyUrl.port || Number.isNaN(port)) throw new Error('SOCKS proxy URL must include a valid port');
|
|
41
|
+
const protocol = proxyUrl.protocol.replace(':', '');
|
|
42
|
+
const socksType = 'socks4' === protocol ? 4 : 'socks5' === protocol ? 5 : 5;
|
|
43
|
+
const proxyAgent = socksDispatcher({
|
|
44
|
+
type: socksType,
|
|
45
|
+
host: proxyUrl.hostname,
|
|
46
|
+
port,
|
|
47
|
+
...proxyUrl.username ? {
|
|
48
|
+
userId: decodeURIComponent(proxyUrl.username),
|
|
49
|
+
password: decodeURIComponent(proxyUrl.password || '')
|
|
50
|
+
} : {}
|
|
51
|
+
});
|
|
52
|
+
debugProxy('socks proxy configured successfully', {
|
|
53
|
+
type: socksType,
|
|
54
|
+
host: proxyUrl.hostname,
|
|
55
|
+
port: port
|
|
56
|
+
});
|
|
57
|
+
return proxyAgent;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
warnProxy('Failed to configure SOCKS proxy:', error);
|
|
60
|
+
throw new Error(`Invalid SOCKS proxy URL: ${socksProxy}. Expected format: socks4://host:port, socks5://host:port, or with authentication: socks5://user:pass@host:port`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
export { createProxyAgent };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=proxy.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/proxy.mjs","sources":["../../../../src/ai-model/service-caller/proxy.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { ifInBrowser } from '@midscene/shared/utils';\n\n// Helper function to sanitize proxy URL for logging (remove credentials)\n// Uses URL API instead of regex to avoid ReDoS vulnerabilities\nconst sanitizeProxyUrl = (url: string): string => {\n try {\n const parsed = new URL(url);\n if (parsed.username) {\n // Keep username for debugging, hide password for security\n parsed.password = '****';\n return parsed.href;\n }\n return url;\n } catch {\n // If URL parsing fails, return original URL (will be caught later)\n return url;\n }\n};\n\nexport const createProxyAgent = async ({\n socksProxy,\n httpProxy,\n}: {\n socksProxy: string | undefined;\n httpProxy: string | undefined;\n}): Promise<unknown> => {\n const warnProxy = getDebug('ai:call:proxy', { console: true });\n const debugProxy = getDebug('ai:call:proxy');\n\n if (httpProxy) {\n debugProxy('using http proxy', sanitizeProxyUrl(httpProxy));\n if (ifInBrowser) {\n warnProxy(\n 'HTTP proxy is configured but not supported in browser environment',\n );\n } else {\n const { loadUndici } = await import('#proxy-deps');\n const { ProxyAgent } = await loadUndici();\n const proxyAgent = new ProxyAgent({\n uri: httpProxy,\n // Note: authentication is handled via the URI (e.g., http://user:pass@proxy.com:8080)\n });\n return proxyAgent;\n }\n } else if (socksProxy) {\n debugProxy('using socks proxy', sanitizeProxyUrl(socksProxy));\n if (ifInBrowser) {\n warnProxy(\n 'SOCKS proxy is configured but not supported in browser environment',\n );\n } else {\n try {\n const { loadFetchSocks } = await import('#proxy-deps');\n const { socksDispatcher } = await loadFetchSocks();\n // Parse SOCKS proxy URL (e.g., socks5://127.0.0.1:1080)\n const proxyUrl = new URL(socksProxy);\n\n // Validate hostname\n if (!proxyUrl.hostname) {\n throw new Error('SOCKS proxy URL must include a valid hostname');\n }\n\n // Validate and parse port\n const port = Number.parseInt(proxyUrl.port, 10);\n if (!proxyUrl.port || Number.isNaN(port)) {\n throw new Error('SOCKS proxy URL must include a valid port');\n }\n\n // Parse SOCKS version from protocol\n const protocol = proxyUrl.protocol.replace(':', '');\n const socksType =\n protocol === 'socks4' ? 4 : protocol === 'socks5' ? 5 : 5;\n\n const proxyAgent = socksDispatcher({\n type: socksType,\n host: proxyUrl.hostname,\n port,\n ...(proxyUrl.username\n ? {\n userId: decodeURIComponent(proxyUrl.username),\n password: decodeURIComponent(proxyUrl.password || ''),\n }\n : {}),\n });\n debugProxy('socks proxy configured successfully', {\n type: socksType,\n host: proxyUrl.hostname,\n port: port,\n });\n return proxyAgent;\n } catch (error) {\n warnProxy('Failed to configure SOCKS proxy:', error);\n throw new Error(\n `Invalid SOCKS proxy URL: ${socksProxy}. Expected format: socks4://host:port, socks5://host:port, or with authentication: socks5://user:pass@host:port`,\n );\n }\n }\n }\n};\n"],"names":["sanitizeProxyUrl","url","parsed","URL","createProxyAgent","socksProxy","httpProxy","warnProxy","getDebug","debugProxy","ifInBrowser","loadUndici","ProxyAgent","proxyAgent","loadFetchSocks","socksDispatcher","proxyUrl","Error","port","Number","protocol","socksType","decodeURIComponent","error"],"mappings":";;AAKA,MAAMA,mBAAmB,CAACC;IACxB,IAAI;QACF,MAAMC,SAAS,IAAIC,IAAIF;QACvB,IAAIC,OAAO,QAAQ,EAAE;YAEnBA,OAAO,QAAQ,GAAG;YAClB,OAAOA,OAAO,IAAI;QACpB;QACA,OAAOD;IACT,EAAE,OAAM;QAEN,OAAOA;IACT;AACF;AAEO,MAAMG,mBAAmB,OAAO,EACrCC,UAAU,EACVC,SAAS,EAIV;IACC,MAAMC,YAAYC,SAAS,iBAAiB;QAAE,SAAS;IAAK;IAC5D,MAAMC,aAAaD,SAAS;IAE5B,IAAIF,WAAW;QACbG,WAAW,oBAAoBT,iBAAiBM;QAChD,IAAII,aACFH,UACE;aAEG;YACL,MAAM,EAAEI,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC;YACpC,MAAM,EAAEC,UAAU,EAAE,GAAG,MAAMD;YAC7B,MAAME,aAAa,IAAID,WAAW;gBAChC,KAAKN;YAEP;YACA,OAAOO;QACT;IACF,OAAO,IAAIR,YAAY;QACrBI,WAAW,qBAAqBT,iBAAiBK;QACjD,IAAIK,aACFH,UACE;aAGF,IAAI;YACF,MAAM,EAAEO,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC;YACxC,MAAM,EAAEC,eAAe,EAAE,GAAG,MAAMD;YAElC,MAAME,WAAW,IAAIb,IAAIE;YAGzB,IAAI,CAACW,SAAS,QAAQ,EACpB,MAAM,IAAIC,MAAM;YAIlB,MAAMC,OAAOC,OAAO,QAAQ,CAACH,SAAS,IAAI,EAAE;YAC5C,IAAI,CAACA,SAAS,IAAI,IAAIG,OAAO,KAAK,CAACD,OACjC,MAAM,IAAID,MAAM;YAIlB,MAAMG,WAAWJ,SAAS,QAAQ,CAAC,OAAO,CAAC,KAAK;YAChD,MAAMK,YACJD,AAAa,aAAbA,WAAwB,IAAIA,AAAa,aAAbA,WAAwB,IAAI;YAE1D,MAAMP,aAAaE,gBAAgB;gBACjC,MAAMM;gBACN,MAAML,SAAS,QAAQ;gBACvBE;gBACA,GAAIF,SAAS,QAAQ,GACjB;oBACE,QAAQM,mBAAmBN,SAAS,QAAQ;oBAC5C,UAAUM,mBAAmBN,SAAS,QAAQ,IAAI;gBACpD,IACA,CAAC,CAAC;YACR;YACAP,WAAW,uCAAuC;gBAChD,MAAMY;gBACN,MAAML,SAAS,QAAQ;gBACvB,MAAME;YACR;YACA,OAAOL;QACT,EAAE,OAAOU,OAAO;YACdhB,UAAU,oCAAoCgB;YAC9C,MAAM,IAAIN,MACR,CAAC,yBAAyB,EAAEZ,WAAW,+GAA+G,CAAC;QAE3J;IAEJ;AACF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
3
|
+
value: value,
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true
|
|
7
|
+
});
|
|
8
|
+
else obj[key] = value;
|
|
9
|
+
return obj;
|
|
10
|
+
}
|
|
11
|
+
class AIResponseParseError extends Error {
|
|
12
|
+
constructor(message, rawResponse, usage, rawChoiceMessage, reasoningContent){
|
|
13
|
+
super(message), _define_property(this, "usage", void 0), _define_property(this, "rawResponse", void 0), _define_property(this, "rawChoiceMessage", void 0), _define_property(this, "reasoningContent", void 0);
|
|
14
|
+
this.name = 'AIResponseParseError';
|
|
15
|
+
this.rawResponse = rawResponse;
|
|
16
|
+
this.usage = usage;
|
|
17
|
+
this.rawChoiceMessage = rawChoiceMessage;
|
|
18
|
+
this.reasoningContent = reasoningContent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const INTERNAL_CALL_ID_FIELD = '_midscene_call_id';
|
|
22
|
+
let internalCallIdCounter = 0;
|
|
23
|
+
function nextInternalCallId() {
|
|
24
|
+
internalCallIdCounter += 1;
|
|
25
|
+
return `call_${internalCallIdCounter}`;
|
|
26
|
+
}
|
|
27
|
+
function stringifyForDebug(value) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
} catch (_error) {
|
|
31
|
+
return String(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getLatestSuccessfulResponseRequestId(context) {
|
|
35
|
+
return context.responseRequestIds?.reduce((latestRequestId, response)=>response.ok ? response.requestId : latestRequestId, void 0);
|
|
36
|
+
}
|
|
37
|
+
function getLatestResponseAttempt(context) {
|
|
38
|
+
return context.httpResponses?.at(-1)?.attempt ?? 1;
|
|
39
|
+
}
|
|
40
|
+
function getErrorMessage(error) {
|
|
41
|
+
return error instanceof Error ? error.message : String(error);
|
|
42
|
+
}
|
|
43
|
+
function toError(error) {
|
|
44
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
45
|
+
}
|
|
46
|
+
function normalizeRetryCount(retryCount) {
|
|
47
|
+
if ('number' != typeof retryCount || !Number.isFinite(retryCount)) return 1;
|
|
48
|
+
return Math.max(0, Math.floor(retryCount));
|
|
49
|
+
}
|
|
50
|
+
function appendAIRequestFailureSummary(error, attemptErrors, maxAttempts) {
|
|
51
|
+
const failedAttempts = attemptErrors.length;
|
|
52
|
+
const retries = Math.max(0, failedAttempts - 1);
|
|
53
|
+
const retryLabel = 1 === retries ? 'retry' : 'retries';
|
|
54
|
+
const originalMessage = error.message;
|
|
55
|
+
const previousAttemptErrors = attemptErrors.slice(0, -1);
|
|
56
|
+
error.message = `AI model request failed after ${retries} ${retryLabel} (${failedAttempts}/${maxAttempts} attempts). Last error: ${originalMessage}`;
|
|
57
|
+
if (0 === previousAttemptErrors.length) return error;
|
|
58
|
+
const details = previousAttemptErrors.map(({ attempt, error })=>`Attempt ${attempt}: ${getErrorMessage(error)}`).join('\n');
|
|
59
|
+
error.message = `${error.message}\nPrevious AI call attempt errors:\n${details}`;
|
|
60
|
+
return error;
|
|
61
|
+
}
|
|
62
|
+
const buildUsageInfo = ({ usageData, requestId, timeCost, modelName, modelDescription, responseModelName, slot, internalCallId })=>{
|
|
63
|
+
if (!usageData) return;
|
|
64
|
+
const cachedInputTokens = usageData?.prompt_tokens_details?.cached_tokens;
|
|
65
|
+
return {
|
|
66
|
+
...usageData,
|
|
67
|
+
prompt_tokens: usageData.prompt_tokens ?? 0,
|
|
68
|
+
completion_tokens: usageData.completion_tokens ?? 0,
|
|
69
|
+
total_tokens: usageData.total_tokens ?? 0,
|
|
70
|
+
cached_input: cachedInputTokens ?? 0,
|
|
71
|
+
time_cost: timeCost ?? 0,
|
|
72
|
+
model_name: modelName,
|
|
73
|
+
model_description: modelDescription,
|
|
74
|
+
response_model_name: responseModelName,
|
|
75
|
+
slot,
|
|
76
|
+
intent: void 0,
|
|
77
|
+
request_id: requestId ?? void 0,
|
|
78
|
+
[INTERNAL_CALL_ID_FIELD]: internalCallId
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
export { AIResponseParseError, INTERNAL_CALL_ID_FIELD, appendAIRequestFailureSummary, buildUsageInfo, getLatestResponseAttempt, getLatestSuccessfulResponseRequestId, nextInternalCallId, normalizeRetryCount, stringifyForDebug, toError };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/utils.mjs","sources":["../../../../src/ai-model/service-caller/utils.ts"],"sourcesContent":["import type { AIUsageInfo } from '@/types';\nimport type OpenAI from 'openai';\nimport type { ModelRuntime } from '../models';\nimport type { OpenAIErrorResponseContext } from './openai-error';\n\n// Error class that preserves usage and rawResponse when AI call parsing fails\nexport class AIResponseParseError extends Error {\n usage?: AIUsageInfo;\n /**\n * Adapter-extracted content used by Midscene for parsing. This is not the\n * full provider response or choices[0].message.\n */\n rawResponse: string;\n rawChoiceMessage?: unknown;\n reasoningContent?: string;\n\n constructor(\n message: string,\n rawResponse: string,\n usage?: AIUsageInfo,\n rawChoiceMessage?: unknown,\n reasoningContent?: string,\n ) {\n super(message);\n this.name = 'AIResponseParseError';\n this.rawResponse = rawResponse;\n this.usage = usage;\n this.rawChoiceMessage = rawChoiceMessage;\n this.reasoningContent = reasoningContent;\n }\n}\n\n/**\n * Internal field name stamped onto every AIUsageInfo shaped by callAI().\n * Used for cross-path dedup when the provider does not return a request_id.\n */\nexport const INTERNAL_CALL_ID_FIELD = '_midscene_call_id';\n\nlet internalCallIdCounter = 0;\nexport function nextInternalCallId(): string {\n internalCallIdCounter += 1;\n return `call_${internalCallIdCounter}`;\n}\n\nexport function stringifyForDebug(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch (_error) {\n return String(value);\n }\n}\n\nexport function getLatestSuccessfulResponseRequestId(\n context: OpenAIErrorResponseContext,\n): string | undefined {\n return context.responseRequestIds?.reduce<string | undefined>(\n (latestRequestId, response) =>\n response.ok ? response.requestId : latestRequestId,\n undefined,\n );\n}\n\nexport function getLatestResponseAttempt(context: OpenAIErrorResponseContext) {\n return context.httpResponses?.at(-1)?.attempt ?? 1;\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function normalizeRetryCount(retryCount: unknown): number {\n if (typeof retryCount !== 'number' || !Number.isFinite(retryCount)) {\n return 1;\n }\n\n return Math.max(0, Math.floor(retryCount));\n}\n\nexport function appendAIRequestFailureSummary<T extends Error>(\n error: T,\n attemptErrors: Array<{ attempt: number; error: unknown }>,\n maxAttempts: number,\n): T {\n const failedAttempts = attemptErrors.length;\n const retries = Math.max(0, failedAttempts - 1);\n const retryLabel = retries === 1 ? 'retry' : 'retries';\n const originalMessage = error.message;\n const previousAttemptErrors = attemptErrors.slice(0, -1);\n\n error.message = `AI model request failed after ${retries} ${retryLabel} (${failedAttempts}/${maxAttempts} attempts). Last error: ${originalMessage}`;\n\n if (previousAttemptErrors.length === 0) {\n return error;\n }\n\n const details = previousAttemptErrors\n .map(\n ({ attempt, error }) => `Attempt ${attempt}: ${getErrorMessage(error)}`,\n )\n .join('\\n');\n\n error.message = `${error.message}\\nPrevious AI call attempt errors:\\n${details}`;\n return error;\n}\n\nexport const buildUsageInfo = ({\n usageData,\n requestId,\n timeCost,\n modelName,\n modelDescription,\n responseModelName,\n slot,\n internalCallId,\n}: {\n usageData?: OpenAI.CompletionUsage;\n requestId?: string | null;\n timeCost?: number;\n modelName: string;\n modelDescription: string;\n responseModelName?: string;\n slot: ModelRuntime['config']['slot'];\n internalCallId: string;\n}): AIUsageInfo | undefined => {\n if (!usageData) return undefined;\n\n const cachedInputTokens = (\n usageData as { prompt_tokens_details?: { cached_tokens?: number } }\n )?.prompt_tokens_details?.cached_tokens;\n\n return {\n ...usageData,\n prompt_tokens: usageData.prompt_tokens ?? 0,\n completion_tokens: usageData.completion_tokens ?? 0,\n total_tokens: usageData.total_tokens ?? 0,\n cached_input: cachedInputTokens ?? 0,\n time_cost: timeCost ?? 0,\n model_name: modelName,\n model_description: modelDescription,\n response_model_name: responseModelName,\n slot,\n // Left undefined at the raw call layer. The agent's onUsage callback\n // fills it from modelConfig.slot for metrics collection, and task\n // layers use withUsageIntent() to stamp a more specific semantic\n // intent (e.g. 'planning', 'insight') when attaching usage to tasks.\n intent: undefined,\n request_id: requestId ?? undefined,\n // Internal stable ID for cross-path dedup when request_id is absent.\n [INTERNAL_CALL_ID_FIELD]: internalCallId,\n } satisfies AIUsageInfo;\n};\n"],"names":["AIResponseParseError","Error","message","rawResponse","usage","rawChoiceMessage","reasoningContent","INTERNAL_CALL_ID_FIELD","internalCallIdCounter","nextInternalCallId","stringifyForDebug","value","JSON","_error","String","getLatestSuccessfulResponseRequestId","context","latestRequestId","response","undefined","getLatestResponseAttempt","getErrorMessage","error","toError","normalizeRetryCount","retryCount","Number","Math","appendAIRequestFailureSummary","attemptErrors","maxAttempts","failedAttempts","retries","retryLabel","originalMessage","previousAttemptErrors","details","attempt","buildUsageInfo","usageData","requestId","timeCost","modelName","modelDescription","responseModelName","slot","internalCallId","cachedInputTokens"],"mappings":"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"}
|