@midscene/core 1.12.8 → 1.12.9-beta-20260917120835.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/es/agent/utils.mjs +1 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs +151 -40
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/codex/call-codex.mjs +26 -14
- package/dist/es/ai-model/service-caller/codex/call-codex.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/codex/codex-app-server.mjs +12 -16
- package/dist/es/ai-model/service-caller/codex/codex-app-server.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/index.mjs +3 -3
- package/dist/es/ai-model/service-caller/model-call.mjs +49 -0
- package/dist/es/ai-model/service-caller/model-call.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs +69 -0
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/chat-completion.mjs +50 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/chat-completion.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/non-stream.mjs +37 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/non-stream.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs +78 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/utils.mjs +15 -0
- package/dist/es/ai-model/service-caller/openai/chat-completion/utils.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/{openai-client.mjs → openai/openai-client.mjs} +9 -21
- package/dist/es/ai-model/service-caller/openai/openai-client.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/{openai-error.mjs → openai/openai-request-context.mjs} +14 -46
- package/dist/es/ai-model/service-caller/openai/openai-request-context.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/proxy/index.mjs +2 -0
- package/dist/es/ai-model/service-caller/proxy/proxy-deps.node.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/proxy/proxy-deps.stub.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/{proxy.mjs → proxy/proxy.mjs} +2 -2
- package/dist/es/ai-model/service-caller/proxy/proxy.mjs.map +1 -0
- package/dist/es/ai-model/service-caller/request-timeout.mjs +59 -18
- package/dist/es/ai-model/service-caller/request-timeout.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/semantic-retry.mjs +9 -4
- package/dist/es/ai-model/service-caller/semantic-retry.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/utils.mjs +25 -8
- package/dist/es/ai-model/service-caller/utils.mjs.map +1 -1
- package/dist/es/device/index.mjs +44 -21
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/ai-model/service-caller/call-ai.js +151 -46
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/codex/call-codex.js +30 -15
- package/dist/lib/ai-model/service-caller/codex/call-codex.js.map +1 -1
- package/dist/lib/ai-model/service-caller/codex/codex-app-server.js +12 -16
- package/dist/lib/ai-model/service-caller/codex/codex-app-server.js.map +1 -1
- package/dist/lib/ai-model/service-caller/index.js +7 -7
- package/dist/lib/ai-model/service-caller/model-call.js +89 -0
- package/dist/lib/ai-model/service-caller/model-call.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/call-openai.js +103 -0
- package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/chat-completion.js +87 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/chat-completion.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/non-stream.js +71 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/non-stream.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js +112 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js.map +1 -0
- package/dist/lib/ai-model/service-caller/openai/chat-completion/types.js.map +1 -0
- package/dist/lib/ai-model/service-caller/{chat-completion → openai/chat-completion}/utils.js +3 -29
- package/dist/lib/ai-model/service-caller/openai/chat-completion/utils.js.map +1 -0
- package/dist/lib/ai-model/service-caller/{openai-client.js → openai/openai-client.js} +9 -21
- package/dist/lib/ai-model/service-caller/openai/openai-client.js.map +1 -0
- package/dist/lib/ai-model/service-caller/{openai-error.js → openai/openai-request-context.js} +14 -46
- package/dist/lib/ai-model/service-caller/openai/openai-request-context.js.map +1 -0
- package/dist/lib/ai-model/service-caller/proxy/index.js +38 -0
- package/dist/lib/ai-model/service-caller/proxy/index.js.map +1 -0
- package/dist/lib/ai-model/service-caller/proxy/proxy-deps.node.js.map +1 -0
- package/dist/lib/ai-model/service-caller/proxy/proxy-deps.stub.js.map +1 -0
- package/dist/lib/ai-model/service-caller/{proxy.js → proxy/proxy.js} +4 -4
- package/dist/lib/ai-model/service-caller/proxy/proxy.js.map +1 -0
- package/dist/lib/ai-model/service-caller/request-timeout.js +67 -20
- package/dist/lib/ai-model/service-caller/request-timeout.js.map +1 -1
- package/dist/lib/ai-model/service-caller/semantic-retry.js +9 -4
- package/dist/lib/ai-model/service-caller/semantic-retry.js.map +1 -1
- package/dist/lib/ai-model/service-caller/utils.js +30 -13
- package/dist/lib/ai-model/service-caller/utils.js.map +1 -1
- package/dist/lib/device/index.js +56 -21
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/report-html-template.js +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 +2 -24
- package/dist/types/ai-model/service-caller/codex/call-codex.d.ts +7 -1
- package/dist/types/ai-model/service-caller/codex/codex-app-server.d.ts +1 -2
- package/dist/types/ai-model/service-caller/index.d.ts +3 -3
- package/dist/types/ai-model/service-caller/model-call.d.ts +26 -0
- package/dist/types/ai-model/service-caller/openai/call-openai.d.ts +3 -0
- package/dist/types/ai-model/service-caller/openai/chat-completion/chat-completion.d.ts +10 -0
- package/dist/types/ai-model/service-caller/openai/chat-completion/non-stream.d.ts +3 -0
- package/dist/types/ai-model/service-caller/openai/chat-completion/stream.d.ts +3 -0
- package/dist/types/ai-model/service-caller/openai/chat-completion/types.d.ts +10 -0
- package/dist/types/ai-model/service-caller/openai/chat-completion/utils.d.ts +5 -0
- package/dist/types/ai-model/service-caller/openai/openai-client.d.ts +14 -0
- package/dist/types/ai-model/service-caller/openai/openai-request-context.d.ts +18 -0
- package/dist/types/ai-model/service-caller/proxy/index.d.ts +1 -0
- package/dist/types/ai-model/service-caller/{proxy.d.ts → proxy/proxy.d.ts} +1 -1
- package/dist/types/ai-model/service-caller/request-timeout.d.ts +14 -11
- package/dist/types/ai-model/service-caller/types.d.ts +19 -0
- package/dist/types/ai-model/service-caller/utils.d.ts +10 -4
- package/dist/types/device/index.d.ts +28 -6
- package/dist/types/types.d.ts +5 -0
- package/package.json +6 -6
- package/dist/es/ai-model/service-caller/call.mjs +0 -56
- package/dist/es/ai-model/service-caller/call.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/chat-completion/call.mjs +0 -101
- package/dist/es/ai-model/service-caller/chat-completion/call.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs +0 -80
- package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/chat-completion/stream.mjs +0 -87
- package/dist/es/ai-model/service-caller/chat-completion/stream.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/chat-completion/utils.mjs +0 -35
- package/dist/es/ai-model/service-caller/chat-completion/utils.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/openai-client.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/openai-error.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/proxy-deps.node.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/proxy-deps.stub.mjs.map +0 -1
- package/dist/es/ai-model/service-caller/proxy.mjs.map +0 -1
- package/dist/lib/ai-model/service-caller/call.js +0 -90
- package/dist/lib/ai-model/service-caller/call.js.map +0 -1
- package/dist/lib/ai-model/service-caller/chat-completion/call.js +0 -135
- package/dist/lib/ai-model/service-caller/chat-completion/call.js.map +0 -1
- package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js +0 -114
- package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js.map +0 -1
- package/dist/lib/ai-model/service-caller/chat-completion/stream.js +0 -121
- package/dist/lib/ai-model/service-caller/chat-completion/stream.js.map +0 -1
- package/dist/lib/ai-model/service-caller/chat-completion/types.js.map +0 -1
- package/dist/lib/ai-model/service-caller/chat-completion/utils.js.map +0 -1
- package/dist/lib/ai-model/service-caller/openai-client.js.map +0 -1
- package/dist/lib/ai-model/service-caller/openai-error.js.map +0 -1
- package/dist/lib/ai-model/service-caller/proxy-deps.node.js.map +0 -1
- package/dist/lib/ai-model/service-caller/proxy-deps.stub.js.map +0 -1
- package/dist/lib/ai-model/service-caller/proxy.js.map +0 -1
- package/dist/types/ai-model/service-caller/call.d.ts +0 -4
- package/dist/types/ai-model/service-caller/chat-completion/call.d.ts +0 -2
- package/dist/types/ai-model/service-caller/chat-completion/non-stream.d.ts +0 -2
- package/dist/types/ai-model/service-caller/chat-completion/stream.d.ts +0 -2
- package/dist/types/ai-model/service-caller/chat-completion/types.d.ts +0 -25
- package/dist/types/ai-model/service-caller/chat-completion/utils.d.ts +0 -12
- package/dist/types/ai-model/service-caller/openai-client.d.ts +0 -14
- package/dist/types/ai-model/service-caller/openai-error.d.ts +0 -26
- /package/dist/es/ai-model/service-caller/{chat-completion → openai/chat-completion}/types.mjs +0 -0
- /package/dist/es/ai-model/service-caller/{proxy-deps.node.mjs → proxy/proxy-deps.node.mjs} +0 -0
- /package/dist/es/ai-model/service-caller/{proxy-deps.stub.mjs → proxy/proxy-deps.stub.mjs} +0 -0
- /package/dist/lib/ai-model/service-caller/{chat-completion → openai/chat-completion}/types.js +0 -0
- /package/dist/lib/ai-model/service-caller/{proxy-deps.node.js → proxy/proxy-deps.node.js} +0 -0
- /package/dist/lib/ai-model/service-caller/{proxy-deps.stub.js → proxy/proxy-deps.stub.js} +0 -0
- /package/dist/types/ai-model/service-caller/{proxy-deps.node.d.ts → proxy/proxy-deps.node.d.ts} +0 -0
- /package/dist/types/ai-model/service-caller/{proxy-deps.stub.d.ts → proxy/proxy-deps.stub.d.ts} +0 -0
package/dist/es/agent/utils.mjs
CHANGED
|
@@ -153,7 +153,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
const getMidsceneVersion = ()=>"1.12.
|
|
156
|
+
const getMidsceneVersion = ()=>"1.12.9-beta-20260917120835.0";
|
|
157
157
|
const parsePrompt = (prompt)=>{
|
|
158
158
|
if ('string' == typeof prompt) return {
|
|
159
159
|
textPrompt: prompt,
|
|
@@ -1,49 +1,160 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { getDebug } from "@midscene/shared/logger";
|
|
2
|
+
import { assert, uuid } from "@midscene/shared/utils";
|
|
3
|
+
import { callCodex, prepareCodexCall } from "./codex/call-codex.mjs";
|
|
4
|
+
import { isCodexAppServerProvider } from "./codex/codex-app-server.mjs";
|
|
5
|
+
import { isModelCallRecordingEnabled, recordModelCallEvent } from "./model-call-recorder.mjs";
|
|
6
|
+
import { callOpenAI } from "./openai/call-openai.mjs";
|
|
7
|
+
import { prepareChatCompletion } from "./openai/chat-completion/chat-completion.mjs";
|
|
8
|
+
import { buildRequestAbortSignal, resolveEffectiveTimeoutMs, runWithAbortSignal, waitForRetry } from "./request-timeout.mjs";
|
|
9
|
+
import { appendAIRequestFailureSummary, buildUsageInfo, nextInternalCallId, normalizeRetryCount, toError } from "./utils.mjs";
|
|
10
|
+
function _define_property(obj, key, value) {
|
|
11
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
12
|
+
value: value,
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true
|
|
11
16
|
});
|
|
12
|
-
|
|
13
|
-
return
|
|
14
|
-
content: jsonContent,
|
|
15
|
-
contentString: response.content,
|
|
16
|
-
usage: response.usage,
|
|
17
|
-
reasoning_content: response.reasoning_content,
|
|
18
|
-
rawChoiceMessage: response.rawChoiceMessage
|
|
19
|
-
};
|
|
17
|
+
else obj[key] = value;
|
|
18
|
+
return obj;
|
|
20
19
|
}
|
|
21
|
-
async function
|
|
20
|
+
async function callAI(messages, modelRuntime, options) {
|
|
21
|
+
options?.abortSignal?.throwIfAborted();
|
|
22
|
+
const isStreaming = options?.stream === true;
|
|
23
|
+
if (isStreaming) assert('function' == typeof options?.onChunk, 'onChunk is required when stream is true');
|
|
24
|
+
const executionId = modelRuntime.executionId ?? `unscoped-${uuid()}`;
|
|
25
|
+
const internalCallId = nextInternalCallId();
|
|
22
26
|
const { config: modelConfig } = modelRuntime;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
const prepare = async ()=>isCodexAppServerProvider(modelConfig.openaiBaseURL) ? {
|
|
28
|
+
protocol: 'codex',
|
|
29
|
+
input: prepareCodexCall({
|
|
30
|
+
messages,
|
|
31
|
+
modelRuntime,
|
|
32
|
+
options
|
|
33
|
+
})
|
|
34
|
+
} : {
|
|
35
|
+
protocol: 'chat-completion',
|
|
36
|
+
input: await prepareChatCompletion({
|
|
37
|
+
messages,
|
|
38
|
+
modelRuntime,
|
|
39
|
+
options
|
|
40
|
+
})
|
|
41
|
+
};
|
|
42
|
+
const prepared = options?.abortSignal ? await runWithAbortSignal(options.abortSignal, prepare) : await prepare();
|
|
43
|
+
const result = await callModelWithRetry({
|
|
44
|
+
messages,
|
|
45
|
+
modelRuntime,
|
|
46
|
+
options,
|
|
47
|
+
executionId,
|
|
48
|
+
internalCallId,
|
|
49
|
+
prepared
|
|
37
50
|
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
const { rawUsage, timeCost, totalTimeCost, retryCount, requestId, responseModelName, ...response } = result;
|
|
52
|
+
const usage = buildUsageInfo({
|
|
53
|
+
usageData: rawUsage,
|
|
54
|
+
timeCost,
|
|
55
|
+
totalTimeCost,
|
|
56
|
+
retryCount,
|
|
57
|
+
requestId,
|
|
58
|
+
responseModelName,
|
|
59
|
+
modelName: modelConfig.modelName,
|
|
60
|
+
modelDescription: modelConfig.modelDescription,
|
|
61
|
+
slot: modelConfig.slot,
|
|
62
|
+
internalCallId
|
|
63
|
+
});
|
|
64
|
+
const debugProfileStats = getDebug('ai:profile:stats');
|
|
65
|
+
const debugProfileDetail = getDebug('ai:profile:detail');
|
|
66
|
+
debugProfileStats(`model, ${modelConfig.modelName}, mode, ${modelConfig.modelFamily || 'default'}, streaming, ${response.isStreamed}, prompt-tokens, ${usage?.prompt_tokens ?? ''}, completion-tokens, ${usage?.completion_tokens ?? ''}, total-tokens, ${usage?.total_tokens ?? ''}, cached-input, ${usage?.cached_input ?? ''}, cost-ms, ${timeCost ?? ''}, requestId, ${requestId ?? ''}, slot, ${modelConfig.slot}, configured-temperature, ${modelConfig.temperature ?? ''}`);
|
|
67
|
+
debugProfileDetail(`model usage detail: ${JSON.stringify(usage)}`);
|
|
68
|
+
if (usage && modelRuntime.onUsage) modelRuntime.onUsage(usage);
|
|
41
69
|
return {
|
|
42
|
-
|
|
43
|
-
usage
|
|
44
|
-
|
|
70
|
+
...response,
|
|
71
|
+
usage
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
class NonRetryableModelCallError extends Error {
|
|
75
|
+
constructor(cause){
|
|
76
|
+
super(cause.message, {
|
|
77
|
+
cause
|
|
78
|
+
}), _define_property(this, "cause", void 0);
|
|
79
|
+
this.cause = cause;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function callModelWithRetry(input) {
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
const { modelRuntime: { config }, options } = input;
|
|
85
|
+
const maxAttempts = normalizeRetryCount(config.retryCount) + 1;
|
|
86
|
+
const retryInterval = config.retryInterval ?? 2000;
|
|
87
|
+
const attemptErrors = [];
|
|
88
|
+
for(let attempt = 1;; attempt++){
|
|
89
|
+
options?.abortSignal?.throwIfAborted();
|
|
90
|
+
try {
|
|
91
|
+
const result = await callModelOnce(input, attempt);
|
|
92
|
+
return {
|
|
93
|
+
...result,
|
|
94
|
+
totalTimeCost: Date.now() - startTime,
|
|
95
|
+
retryCount: attempt - 1
|
|
96
|
+
};
|
|
97
|
+
} catch (error) {
|
|
98
|
+
options?.abortSignal?.throwIfAborted();
|
|
99
|
+
if (error instanceof NonRetryableModelCallError) throw error.cause;
|
|
100
|
+
const lastError = toError(error);
|
|
101
|
+
attemptErrors.push({
|
|
102
|
+
attempt,
|
|
103
|
+
error: lastError
|
|
104
|
+
});
|
|
105
|
+
if (attempt === maxAttempts) throw appendAIRequestFailureSummary(lastError, attemptErrors, maxAttempts);
|
|
106
|
+
getDebug('ai:call', {
|
|
107
|
+
console: true
|
|
108
|
+
})(`AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`);
|
|
109
|
+
}
|
|
110
|
+
await waitForRetry(retryInterval, options?.abortSignal);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function callModelOnce({ messages, modelRuntime, options, executionId, internalCallId, prepared }, attempt) {
|
|
114
|
+
const { config: modelConfig } = modelRuntime;
|
|
115
|
+
const recordEvent = isModelCallRecordingEnabled() ? (event)=>{
|
|
116
|
+
recordModelCallEvent({
|
|
117
|
+
executionId,
|
|
118
|
+
callId: internalCallId,
|
|
119
|
+
semanticRetryAttempt: options?.semanticRetryAttempt,
|
|
120
|
+
slot: modelConfig.slot,
|
|
121
|
+
intent: modelConfig.intent,
|
|
122
|
+
modelFamily: modelConfig.modelFamily,
|
|
123
|
+
...event,
|
|
124
|
+
attempt
|
|
125
|
+
});
|
|
126
|
+
} : void 0;
|
|
127
|
+
const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig.timeout);
|
|
128
|
+
const { signal: requestSignal, cleanup } = buildRequestAbortSignal(effectiveTimeoutMs, options?.abortSignal);
|
|
129
|
+
let deliveredChunk = false;
|
|
130
|
+
const onChunk = options?.onChunk;
|
|
131
|
+
const context = {
|
|
132
|
+
messages,
|
|
133
|
+
modelRuntime,
|
|
134
|
+
executionId,
|
|
135
|
+
recordEvent,
|
|
136
|
+
requestSignal,
|
|
137
|
+
effectiveTimeoutMs,
|
|
138
|
+
options: onChunk ? {
|
|
139
|
+
...options,
|
|
140
|
+
onChunk: (chunk)=>{
|
|
141
|
+
requestSignal.throwIfAborted();
|
|
142
|
+
deliveredChunk = true;
|
|
143
|
+
onChunk(chunk);
|
|
144
|
+
}
|
|
145
|
+
} : options
|
|
45
146
|
};
|
|
147
|
+
try {
|
|
148
|
+
return await runWithAbortSignal(requestSignal, ()=>'codex' === prepared.protocol ? callCodex(context, prepared.input) : callOpenAI(context, prepared.input));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
options?.abortSignal?.throwIfAborted();
|
|
151
|
+
const lastError = toError(error);
|
|
152
|
+
if (deliveredChunk) throw new NonRetryableModelCallError(lastError);
|
|
153
|
+
throw lastError;
|
|
154
|
+
} finally{
|
|
155
|
+
cleanup();
|
|
156
|
+
}
|
|
46
157
|
}
|
|
47
|
-
export {
|
|
158
|
+
export { callAI };
|
|
48
159
|
|
|
49
160
|
//# sourceMappingURL=call-ai.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/call-ai.mjs","sources":["../../../../src/ai-model/service-caller/call-ai.ts"],"sourcesContent":["import type { AIUsageInfo } from '@/types';\nimport { assert } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport type { ModelRuntime } from '../models';\nimport { type JsonParserSource, assertJsonObject } from '../shared/json';\nimport { callAI } from './call';\nimport {\n callAiAndParseWithRetry,\n withSemanticRetryFeedback,\n} from './semantic-retry';\nimport type { CallAIOptions } from './types';\nimport type { AIArgs } from './types';\nimport { AIResponseParseError } from './utils';\n\nexport type AIObjectResponse<T> = {\n // TODO: `content` is a misleading name here because this is already the parsed object response. Consider renaming it to `object` or `data`.\n content: T;\n contentString: string;\n usage?: AIUsageInfo;\n reasoning_content?: string;\n rawChoiceMessage?: unknown;\n};\n\nexport function parseAIObjectResponse<T>(\n response: Awaited<ReturnType<typeof callAI>>,\n modelRuntime: ModelRuntime,\n jsonParserSource: JsonParserSource = 'generic-object',\n): AIObjectResponse<T> {\n const { adapter } = modelRuntime;\n assert(response, 'empty response');\n const jsonContent = adapter.jsonParser(response.content, {\n source: jsonParserSource,\n });\n assertJsonObject(jsonContent);\n return {\n content: jsonContent as T,\n contentString: response.content,\n usage: response.usage,\n reasoning_content: response.reasoning_content,\n rawChoiceMessage: response.rawChoiceMessage,\n };\n}\n\nexport async function callAIWithObjectResponse<T>(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: {\n abortSignal?: AbortSignal;\n jsonParserSource?: JsonParserSource;\n retryTimes?: number;\n retryInterval?: number;\n },\n): Promise<AIObjectResponse<T>> {\n const { config: modelConfig } = modelRuntime;\n return callAiAndParseWithRetry({\n callAi: (retryAttempt, previousParseError) =>\n callAI(\n withSemanticRetryFeedback(messages, previousParseError),\n modelRuntime,\n {\n abortSignal: options?.abortSignal,\n expectedJsonObjectResponse: true,\n semanticRetryAttempt: retryAttempt,\n },\n ),\n parseResponse: (response) =>\n parseAIObjectResponse<T>(\n response,\n modelRuntime,\n options?.jsonParserSource,\n ),\n toParseError: (error, response) => {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return new AIResponseParseError(\n errorMessage,\n response.content,\n response.usage,\n response.rawChoiceMessage,\n response.reasoning_content,\n );\n },\n parseRetryTimes: options?.retryTimes ?? modelConfig.retryCount,\n parseRetryInterval: options?.retryInterval ?? modelConfig.retryInterval,\n abortSignal: options?.abortSignal,\n });\n}\n\nexport async function callAIWithStringResponse(\n msgs: AIArgs,\n modelRuntime: ModelRuntime,\n options?: Pick<CallAIOptions, 'abortSignal' | 'requiresOriginalImageDetail'>,\n): Promise<{\n content: string;\n usage?: AIUsageInfo;\n rawChoiceMessage?: unknown;\n}> {\n const { content, usage, rawChoiceMessage } = await callAI(\n msgs,\n modelRuntime,\n options,\n );\n return { content, usage, rawChoiceMessage };\n}\n"],"names":["parseAIObjectResponse","response","modelRuntime","jsonParserSource","adapter","assert","jsonContent","assertJsonObject","callAIWithObjectResponse","messages","options","modelConfig","callAiAndParseWithRetry","retryAttempt","previousParseError","callAI","withSemanticRetryFeedback","error","errorMessage","Error","String","AIResponseParseError","callAIWithStringResponse","msgs","content","usage","rawChoiceMessage"],"mappings":";;;;;AAuBO,SAASA,sBACdC,QAA4C,EAC5CC,YAA0B,EAC1BC,mBAAqC,gBAAgB;IAErD,MAAM,EAAEC,OAAO,EAAE,GAAGF;IACpBG,OAAOJ,UAAU;IACjB,MAAMK,cAAcF,QAAQ,UAAU,CAACH,SAAS,OAAO,EAAE;QACvD,QAAQE;IACV;IACAI,iBAAiBD;IACjB,OAAO;QACL,SAASA;QACT,eAAeL,SAAS,OAAO;QAC/B,OAAOA,SAAS,KAAK;QACrB,mBAAmBA,SAAS,iBAAiB;QAC7C,kBAAkBA,SAAS,gBAAgB;IAC7C;AACF;AAEO,eAAeO,yBACpBC,QAAsC,EACtCP,YAA0B,EAC1BQ,OAKC;IAED,MAAM,EAAE,QAAQC,WAAW,EAAE,GAAGT;IAChC,OAAOU,wBAAwB;QAC7B,QAAQ,CAACC,cAAcC,qBACrBC,OACEC,0BAA0BP,UAAUK,qBACpCZ,cACA;gBACE,aAAaQ,SAAS;gBACtB,4BAA4B;gBAC5B,sBAAsBG;YACxB;QAEJ,eAAe,CAACZ,WACdD,sBACEC,UACAC,cACAQ,SAAS;QAEb,cAAc,CAACO,OAAOhB;YACpB,MAAMiB,eACJD,iBAAiBE,QAAQF,MAAM,OAAO,GAAGG,OAAOH;YAClD,OAAO,IAAII,qBACTH,cACAjB,SAAS,OAAO,EAChBA,SAAS,KAAK,EACdA,SAAS,gBAAgB,EACzBA,SAAS,iBAAiB;QAE9B;QACA,iBAAiBS,SAAS,cAAcC,YAAY,UAAU;QAC9D,oBAAoBD,SAAS,iBAAiBC,YAAY,aAAa;QACvE,aAAaD,SAAS;IACxB;AACF;AAEO,eAAeY,yBACpBC,IAAY,EACZrB,YAA0B,EAC1BQ,OAA4E;IAM5E,MAAM,EAAEc,OAAO,EAAEC,KAAK,EAAEC,gBAAgB,EAAE,GAAG,MAAMX,OACjDQ,MACArB,cACAQ;IAEF,OAAO;QAAEc;QAASC;QAAOC;IAAiB;AAC5C"}
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/call-ai.mjs","sources":["../../../../src/ai-model/service-caller/call-ai.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { assert, uuid } from '@midscene/shared/utils';\nimport type { ChatCompletionMessageParam } from 'openai/resources/index';\nimport type { ModelRuntime } from '../models';\nimport { callCodex, prepareCodexCall } from './codex/call-codex';\nimport { isCodexAppServerProvider } from './codex/codex-app-server';\nimport {\n isModelCallRecordingEnabled,\n recordModelCallEvent,\n} from './model-call-recorder';\nimport { callOpenAI } from './openai/call-openai';\nimport { prepareChatCompletion } from './openai/chat-completion/chat-completion';\nimport {\n buildRequestAbortSignal,\n resolveEffectiveTimeoutMs,\n runWithAbortSignal,\n waitForRetry,\n} from './request-timeout';\nimport type {\n AICallResult,\n CallAIOptions,\n ModelCallContext,\n ModelCallResult,\n} from './types';\nimport {\n appendAIRequestFailureSummary,\n buildUsageInfo,\n nextInternalCallId,\n normalizeRetryCount,\n toError,\n} from './utils';\n\nexport async function callAI(\n messages: ChatCompletionMessageParam[],\n modelRuntime: ModelRuntime,\n options?: CallAIOptions,\n): Promise<AICallResult> {\n options?.abortSignal?.throwIfAborted();\n const isStreaming = options?.stream === true;\n if (isStreaming) {\n assert(\n typeof options?.onChunk === 'function',\n 'onChunk is required when stream is true',\n );\n }\n\n // Low-level callers without a TaskRunner still need a stable ID for the\n // lifetime of this model call (including its network retries).\n const executionId = modelRuntime.executionId ?? `unscoped-${uuid()}`;\n\n // Stable internal ID for this call, used by the agent to deduplicate usage\n // across the onUsage callback and the task-dump-based collectUsageMetrics()\n // path when the provider does not return a request_id.\n const internalCallId = nextInternalCallId();\n\n const { config: modelConfig } = modelRuntime;\n\n const prepare = async (): Promise<PreparedModelInput> =>\n isCodexAppServerProvider(modelConfig.openaiBaseURL)\n ? {\n protocol: 'codex',\n input: prepareCodexCall({ messages, modelRuntime, options }),\n }\n : {\n protocol: 'chat-completion',\n input: await prepareChatCompletion({\n messages,\n modelRuntime,\n options,\n }),\n };\n\n const prepared = options?.abortSignal\n ? await runWithAbortSignal(options.abortSignal, prepare)\n : await prepare();\n\n const result = await callModelWithRetry({\n messages,\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n });\n\n const {\n rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n ...response\n } = result;\n\n const usage = buildUsageInfo({\n usageData: rawUsage,\n timeCost,\n totalTimeCost,\n retryCount,\n requestId,\n responseModelName,\n modelName: modelConfig.modelName,\n modelDescription: modelConfig.modelDescription,\n slot: modelConfig.slot,\n internalCallId,\n });\n\n const debugProfileStats = getDebug('ai:profile:stats');\n const debugProfileDetail = getDebug('ai:profile:detail');\n debugProfileStats(\n `model, ${modelConfig.modelName}, mode, ${modelConfig.modelFamily || 'default'}, streaming, ${response.isStreamed}, prompt-tokens, ${usage?.prompt_tokens ?? ''}, completion-tokens, ${usage?.completion_tokens ?? ''}, total-tokens, ${usage?.total_tokens ?? ''}, cached-input, ${usage?.cached_input ?? ''}, cost-ms, ${timeCost ?? ''}, requestId, ${requestId ?? ''}, slot, ${modelConfig.slot}, configured-temperature, ${modelConfig.temperature ?? ''}`,\n );\n debugProfileDetail(`model usage detail: ${JSON.stringify(usage)}`);\n\n if (usage && modelRuntime.onUsage) {\n modelRuntime.onUsage(usage);\n }\n return { ...response, usage };\n}\n\ntype PreparedModelInput =\n | { protocol: 'codex'; input: ReturnType<typeof prepareCodexCall> }\n | {\n protocol: 'chat-completion';\n input: Awaited<ReturnType<typeof prepareChatCompletion>>;\n };\n\ntype ModelCallInput = {\n messages: ChatCompletionMessageParam[];\n modelRuntime: ModelRuntime;\n options?: CallAIOptions;\n executionId: string;\n internalCallId: string;\n prepared: PreparedModelInput;\n};\n\n/** Carries the original failure across the retry boundary without replaying output. */\nclass NonRetryableModelCallError extends Error {\n readonly cause: Error;\n\n constructor(cause: Error) {\n super(cause.message, { cause });\n this.cause = cause;\n }\n}\n\nasync function callModelWithRetry(\n input: ModelCallInput,\n): Promise<ModelCallResult & { totalTimeCost: number; retryCount: number }> {\n const startTime = Date.now();\n const {\n modelRuntime: { config },\n options,\n } = input;\n const maxAttempts = normalizeRetryCount(config.retryCount) + 1;\n const retryInterval = config.retryInterval ?? 2000;\n const attemptErrors: Array<{ attempt: number; error: unknown }> = [];\n\n for (let attempt = 1; ; attempt++) {\n options?.abortSignal?.throwIfAborted();\n try {\n const result = await callModelOnce(input, attempt);\n return {\n ...result,\n totalTimeCost: Date.now() - startTime,\n retryCount: attempt - 1,\n };\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n if (error instanceof NonRetryableModelCallError) {\n throw error.cause;\n }\n const lastError = toError(error);\n attemptErrors.push({ attempt, error: lastError });\n if (attempt === maxAttempts) {\n throw appendAIRequestFailureSummary(\n lastError,\n attemptErrors,\n maxAttempts,\n );\n }\n getDebug('ai:call', { console: true })(\n `AI call failed (attempt ${attempt}/${maxAttempts}), retrying in ${retryInterval}ms... Error: ${lastError.message}`,\n );\n }\n await waitForRetry(retryInterval, options?.abortSignal);\n }\n}\n\nasync function callModelOnce(\n {\n messages,\n modelRuntime,\n options,\n executionId,\n internalCallId,\n prepared,\n }: ModelCallInput,\n attempt: number,\n): Promise<ModelCallResult> {\n const { config: modelConfig } = modelRuntime;\n const recordEvent = isModelCallRecordingEnabled()\n ? (event: Record<string, unknown>) => {\n void recordModelCallEvent({\n executionId,\n callId: internalCallId,\n semanticRetryAttempt: options?.semanticRetryAttempt,\n slot: modelConfig.slot,\n intent: modelConfig.intent,\n modelFamily: modelConfig.modelFamily,\n ...event,\n attempt,\n });\n }\n : undefined;\n const effectiveTimeoutMs = resolveEffectiveTimeoutMs(modelConfig.timeout);\n const { signal: requestSignal, cleanup } = buildRequestAbortSignal(\n effectiveTimeoutMs,\n options?.abortSignal,\n );\n let deliveredChunk = false;\n const onChunk = options?.onChunk;\n const context: ModelCallContext = {\n messages,\n modelRuntime,\n executionId,\n recordEvent,\n requestSignal,\n effectiveTimeoutMs,\n options: onChunk\n ? {\n ...options,\n onChunk: (chunk) => {\n requestSignal.throwIfAborted();\n deliveredChunk = true;\n onChunk(chunk);\n },\n }\n : options,\n };\n try {\n return await runWithAbortSignal(requestSignal, () =>\n prepared.protocol === 'codex'\n ? callCodex(context, prepared.input)\n : callOpenAI(context, prepared.input),\n );\n } catch (error) {\n options?.abortSignal?.throwIfAborted();\n const lastError = toError(error);\n if (deliveredChunk) {\n throw new NonRetryableModelCallError(lastError);\n }\n throw lastError;\n } finally {\n cleanup();\n }\n}\n"],"names":["callAI","messages","modelRuntime","options","isStreaming","assert","executionId","uuid","internalCallId","nextInternalCallId","modelConfig","prepare","isCodexAppServerProvider","prepareCodexCall","prepareChatCompletion","prepared","runWithAbortSignal","result","callModelWithRetry","rawUsage","timeCost","totalTimeCost","retryCount","requestId","responseModelName","response","usage","buildUsageInfo","debugProfileStats","getDebug","debugProfileDetail","JSON","NonRetryableModelCallError","Error","cause","input","startTime","Date","config","maxAttempts","normalizeRetryCount","retryInterval","attemptErrors","attempt","callModelOnce","error","lastError","toError","appendAIRequestFailureSummary","waitForRetry","recordEvent","isModelCallRecordingEnabled","event","recordModelCallEvent","undefined","effectiveTimeoutMs","resolveEffectiveTimeoutMs","requestSignal","cleanup","buildRequestAbortSignal","deliveredChunk","onChunk","context","chunk","callCodex","callOpenAI"],"mappings":"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"}
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import { applyImageDetail } from "../utils.mjs";
|
|
1
2
|
import { callAIWithCodexAppServer } from "./codex-app-server.mjs";
|
|
2
|
-
const
|
|
3
|
+
const prepareCodexCall = ({ messages, modelRuntime, options })=>{
|
|
3
4
|
const { config: modelConfig, adapter } = modelRuntime;
|
|
5
|
+
const { config, imageDetail } = adapter.buildCodexAppServerParams({
|
|
6
|
+
intent: modelConfig.intent,
|
|
7
|
+
userConfig: {
|
|
8
|
+
reasoningEnabled: modelConfig.reasoningEnabled,
|
|
9
|
+
reasoningEffort: modelConfig.reasoningEffort,
|
|
10
|
+
reasoningBudget: modelConfig.reasoningBudget
|
|
11
|
+
},
|
|
12
|
+
requiresOriginalImageDetail: options?.requiresOriginalImageDetail
|
|
13
|
+
});
|
|
14
|
+
const messagesWithImageDetail = applyImageDetail({
|
|
15
|
+
imageDetail,
|
|
16
|
+
messages
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
messages: messagesWithImageDetail,
|
|
20
|
+
requestParams: config
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const callCodex = async ({ modelRuntime, options, recordEvent, requestSignal }, { messages, requestParams })=>{
|
|
24
|
+
const { config: modelConfig } = modelRuntime;
|
|
4
25
|
let protocolChunkSequence = 0;
|
|
5
26
|
const codexStartTime = Date.now();
|
|
6
27
|
const recordCodexEvent = recordEvent ? (event)=>{
|
|
@@ -21,23 +42,14 @@ const callCodex = async ({ messages, modelRuntime, options, recordEvent })=>{
|
|
|
21
42
|
});
|
|
22
43
|
} : void 0;
|
|
23
44
|
try {
|
|
24
|
-
const { config, imageDetail } = adapter.buildCodexAppServerParams({
|
|
25
|
-
intent: modelConfig.intent,
|
|
26
|
-
userConfig: {
|
|
27
|
-
reasoningEnabled: modelConfig.reasoningEnabled,
|
|
28
|
-
reasoningEffort: modelConfig.reasoningEffort,
|
|
29
|
-
reasoningBudget: modelConfig.reasoningBudget
|
|
30
|
-
},
|
|
31
|
-
requiresOriginalImageDetail: options?.requiresOriginalImageDetail
|
|
32
|
-
});
|
|
33
45
|
const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {
|
|
34
46
|
stream: options?.stream,
|
|
35
47
|
onChunk: options?.onChunk,
|
|
36
|
-
params:
|
|
37
|
-
abortSignal:
|
|
38
|
-
imageDetail,
|
|
48
|
+
params: requestParams,
|
|
49
|
+
abortSignal: requestSignal,
|
|
39
50
|
onRecordEvent: recordCodexEvent
|
|
40
51
|
});
|
|
52
|
+
requestSignal.throwIfAborted();
|
|
41
53
|
const { protocolMetadata, usage: rawUsage, ...response } = codexResult;
|
|
42
54
|
const timeCost = Date.now() - codexStartTime;
|
|
43
55
|
recordEvent?.({
|
|
@@ -72,6 +84,6 @@ const callCodex = async ({ messages, modelRuntime, options, recordEvent })=>{
|
|
|
72
84
|
throw error;
|
|
73
85
|
}
|
|
74
86
|
};
|
|
75
|
-
export { callCodex };
|
|
87
|
+
export { callCodex, prepareCodexCall };
|
|
76
88
|
|
|
77
89
|
//# sourceMappingURL=call-codex.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-model/service-caller/codex/call-codex.mjs","sources":["../../../../../src/ai-model/service-caller/codex/call-codex.ts"],"sourcesContent":["import type { ModelCallContext, ModelCallResult } from '../types';\nimport {\n type CodexAppServerRecordEvent,\n callAIWithCodexAppServer,\n} from './codex-app-server';\n\nexport const
|
|
1
|
+
{"version":3,"file":"ai-model/service-caller/codex/call-codex.mjs","sources":["../../../../../src/ai-model/service-caller/codex/call-codex.ts"],"sourcesContent":["import type { ModelCallContext, ModelCallResult } from '../types';\nimport { applyImageDetail } from '../utils';\nimport {\n type CodexAppServerRecordEvent,\n callAIWithCodexAppServer,\n} from './codex-app-server';\n\nexport const prepareCodexCall = ({\n messages,\n modelRuntime,\n options,\n}: Pick<ModelCallContext, 'messages' | 'modelRuntime' | 'options'>) => {\n const { config: modelConfig, adapter } = modelRuntime;\n const { config, imageDetail } = adapter.buildCodexAppServerParams({\n intent: modelConfig.intent,\n userConfig: {\n reasoningEnabled: modelConfig.reasoningEnabled,\n reasoningEffort: modelConfig.reasoningEffort,\n reasoningBudget: modelConfig.reasoningBudget,\n },\n requiresOriginalImageDetail: options?.requiresOriginalImageDetail,\n });\n const messagesWithImageDetail = applyImageDetail({ imageDetail, messages });\n return { messages: messagesWithImageDetail, requestParams: config };\n};\n\nexport const callCodex = async (\n { modelRuntime, options, recordEvent, requestSignal }: ModelCallContext,\n { messages, requestParams }: ReturnType<typeof prepareCodexCall>,\n): Promise<ModelCallResult> => {\n const { config: modelConfig } = modelRuntime;\n let protocolChunkSequence = 0;\n const codexStartTime = Date.now();\n const recordCodexEvent = recordEvent\n ? (event: CodexAppServerRecordEvent) => {\n if (event.type === 'chunk') {\n protocolChunkSequence += 1;\n recordEvent({\n ...event,\n attempt: 1,\n sequence: protocolChunkSequence,\n provider: 'codex-app-server',\n });\n return;\n }\n\n recordEvent({\n ...event,\n attempt: 1,\n provider: 'codex-app-server',\n });\n }\n : undefined;\n\n try {\n const codexResult = await callAIWithCodexAppServer(messages, modelConfig, {\n stream: options?.stream,\n onChunk: options?.onChunk,\n params: requestParams,\n abortSignal: requestSignal,\n onRecordEvent: recordCodexEvent,\n });\n requestSignal.throwIfAborted();\n const { protocolMetadata, usage: rawUsage, ...response } = codexResult;\n const timeCost = Date.now() - codexStartTime;\n recordEvent?.({\n type: 'response',\n attempt: 1,\n provider: 'codex-app-server',\n final: {\n content: response.content,\n reasoningContent: response.reasoning_content,\n usage: rawUsage,\n timeCost,\n protocol: protocolMetadata,\n },\n });\n return {\n ...response,\n rawUsage,\n timeCost,\n requestId: protocolMetadata.turnId,\n };\n } catch (error) {\n recordEvent?.({\n type: 'error',\n attempt: 1,\n provider: 'codex-app-server',\n error:\n error instanceof Error\n ? {\n name: error.name,\n message: error.message,\n stack: error.stack,\n }\n : String(error),\n });\n throw error;\n }\n};\n"],"names":["prepareCodexCall","messages","modelRuntime","options","modelConfig","adapter","config","imageDetail","messagesWithImageDetail","applyImageDetail","callCodex","recordEvent","requestSignal","requestParams","protocolChunkSequence","codexStartTime","Date","recordCodexEvent","event","undefined","codexResult","callAIWithCodexAppServer","protocolMetadata","rawUsage","response","timeCost","error","Error","String"],"mappings":";;AAOO,MAAMA,mBAAmB,CAAC,EAC/BC,QAAQ,EACRC,YAAY,EACZC,OAAO,EACyD;IAChE,MAAM,EAAE,QAAQC,WAAW,EAAEC,OAAO,EAAE,GAAGH;IACzC,MAAM,EAAEI,MAAM,EAAEC,WAAW,EAAE,GAAGF,QAAQ,yBAAyB,CAAC;QAChE,QAAQD,YAAY,MAAM;QAC1B,YAAY;YACV,kBAAkBA,YAAY,gBAAgB;YAC9C,iBAAiBA,YAAY,eAAe;YAC5C,iBAAiBA,YAAY,eAAe;QAC9C;QACA,6BAA6BD,SAAS;IACxC;IACA,MAAMK,0BAA0BC,iBAAiB;QAAEF;QAAaN;IAAS;IACzE,OAAO;QAAE,UAAUO;QAAyB,eAAeF;IAAO;AACpE;AAEO,MAAMI,YAAY,OACvB,EAAER,YAAY,EAAEC,OAAO,EAAEQ,WAAW,EAAEC,aAAa,EAAoB,EACvE,EAAEX,QAAQ,EAAEY,aAAa,EAAuC;IAEhE,MAAM,EAAE,QAAQT,WAAW,EAAE,GAAGF;IAChC,IAAIY,wBAAwB;IAC5B,MAAMC,iBAAiBC,KAAK,GAAG;IAC/B,MAAMC,mBAAmBN,cACrB,CAACO;QACC,IAAIA,AAAe,YAAfA,MAAM,IAAI,EAAc;YAC1BJ,yBAAyB;YACzBH,YAAY;gBACV,GAAGO,KAAK;gBACR,SAAS;gBACT,UAAUJ;gBACV,UAAU;YACZ;YACA;QACF;QAEAH,YAAY;YACV,GAAGO,KAAK;YACR,SAAS;YACT,UAAU;QACZ;IACF,IACAC;IAEJ,IAAI;QACF,MAAMC,cAAc,MAAMC,yBAAyBpB,UAAUG,aAAa;YACxE,QAAQD,SAAS;YACjB,SAASA,SAAS;YAClB,QAAQU;YACR,aAAaD;YACb,eAAeK;QACjB;QACAL,cAAc,cAAc;QAC5B,MAAM,EAAEU,gBAAgB,EAAE,OAAOC,QAAQ,EAAE,GAAGC,UAAU,GAAGJ;QAC3D,MAAMK,WAAWT,KAAK,GAAG,KAAKD;QAC9BJ,cAAc;YACZ,MAAM;YACN,SAAS;YACT,UAAU;YACV,OAAO;gBACL,SAASa,SAAS,OAAO;gBACzB,kBAAkBA,SAAS,iBAAiB;gBAC5C,OAAOD;gBACPE;gBACA,UAAUH;YACZ;QACF;QACA,OAAO;YACL,GAAGE,QAAQ;YACXD;YACAE;YACA,WAAWH,iBAAiB,MAAM;QACpC;IACF,EAAE,OAAOI,OAAO;QACdf,cAAc;YACZ,MAAM;YACN,SAAS;YACT,UAAU;YACV,OACEe,iBAAiBC,QACb;gBACE,MAAMD,MAAM,IAAI;gBAChB,SAASA,MAAM,OAAO;gBACtB,OAAOA,MAAM,KAAK;YACpB,IACAE,OAAOF;QACf;QACA,MAAMA;IACR;AACF"}
|
|
@@ -11,7 +11,7 @@ function _define_property(obj, key, value) {
|
|
|
11
11
|
return obj;
|
|
12
12
|
}
|
|
13
13
|
const CODEX_PROVIDER_SCHEME = 'codex://';
|
|
14
|
-
const
|
|
14
|
+
const CODEX_INTERRUPT_TIMEOUT_MS = 5000;
|
|
15
15
|
const CODEX_DEFAULT_PROCESS_START_TIMEOUT_MS = 15000;
|
|
16
16
|
const CODEX_DEFAULT_CLEANUP_TIMEOUT_MS = 8000;
|
|
17
17
|
const CODEX_TEXT_INPUT_MAX_LENGTH = 262144;
|
|
@@ -81,7 +81,7 @@ const extractTextFromMessage = (message)=>{
|
|
|
81
81
|
}).filter(Boolean).join('\n');
|
|
82
82
|
return '';
|
|
83
83
|
};
|
|
84
|
-
const extractImageInputs = (message
|
|
84
|
+
const extractImageInputs = (message)=>{
|
|
85
85
|
const content = message.content;
|
|
86
86
|
if (!Array.isArray(content)) return [];
|
|
87
87
|
const inputs = [];
|
|
@@ -89,7 +89,7 @@ const extractImageInputs = (message, imageDetailOverride)=>{
|
|
|
89
89
|
if (!part || 'object' != typeof part) continue;
|
|
90
90
|
const partType = String(part.type || '');
|
|
91
91
|
const imageUrl = 'image_url' === partType ? toNonEmptyString(part.image_url?.url) : 'input_image' === partType ? toNonEmptyString(part.image_url || part.url) : void 0;
|
|
92
|
-
const imageDetail =
|
|
92
|
+
const imageDetail = toCodexImageDetail('image_url' === partType ? part.image_url?.detail : part.detail);
|
|
93
93
|
if (imageUrl) {
|
|
94
94
|
if (imageUrl.startsWith('/') || imageUrl.startsWith('./') || imageUrl.startsWith('../') || imageUrl.startsWith('file://')) {
|
|
95
95
|
const path = imageUrl.startsWith('file://') ? normalizeCodexLocalImagePath(imageUrl) : imageUrl;
|
|
@@ -113,7 +113,7 @@ const extractImageInputs = (message, imageDetailOverride)=>{
|
|
|
113
113
|
}
|
|
114
114
|
return inputs;
|
|
115
115
|
};
|
|
116
|
-
const buildCodexTurnPayloadFromMessages = (messages
|
|
116
|
+
const buildCodexTurnPayloadFromMessages = (messages)=>{
|
|
117
117
|
const developerInstructionParts = [];
|
|
118
118
|
const transcriptParts = [];
|
|
119
119
|
const imageInputs = [];
|
|
@@ -127,7 +127,7 @@ const buildCodexTurnPayloadFromMessages = (messages, imageDetailOverride)=>{
|
|
|
127
127
|
const roleTag = role.toUpperCase();
|
|
128
128
|
if (text.trim()) transcriptParts.push(`[${roleTag}]\n${text.trim()}`);
|
|
129
129
|
else transcriptParts.push(`[${roleTag}]\n(no text content)`);
|
|
130
|
-
if ('user' === role) imageInputs.push(...extractImageInputs(message
|
|
130
|
+
if ('user' === role) imageInputs.push(...extractImageInputs(message));
|
|
131
131
|
}
|
|
132
132
|
const fullTranscript = transcriptParts.join('\n\n');
|
|
133
133
|
const transcriptText = (fullTranscript.length > CODEX_TEXT_INPUT_MAX_LENGTH ? fullTranscript.slice(-CODEX_TEXT_INPUT_MAX_LENGTH) : fullTranscript) || 'Please answer the latest user request.';
|
|
@@ -175,11 +175,9 @@ class CodexAppServerConnection {
|
|
|
175
175
|
isClosed() {
|
|
176
176
|
return this.closed;
|
|
177
177
|
}
|
|
178
|
-
async runTurn({ messages, modelConfig, stream, onChunk, params, abortSignal,
|
|
179
|
-
const timeoutMs = modelConfig.timeout || CODEX_DEFAULT_TIMEOUT_MS;
|
|
180
|
-
const deadlineAt = Date.now() + timeoutMs;
|
|
178
|
+
async runTurn({ messages, modelConfig, stream, onChunk, params, abortSignal, onRecordEvent }) {
|
|
181
179
|
const isStreaming = !!(stream && onChunk);
|
|
182
|
-
const { developerInstructions, input } = buildCodexTurnPayloadFromMessages(messages
|
|
180
|
+
const { developerInstructions, input } = buildCodexTurnPayloadFromMessages(messages);
|
|
183
181
|
let threadId;
|
|
184
182
|
let turnId;
|
|
185
183
|
let latestErrorMessage;
|
|
@@ -219,7 +217,6 @@ class CodexAppServerConnection {
|
|
|
219
217
|
const threadStartResponse = await this.request({
|
|
220
218
|
method: 'thread/start',
|
|
221
219
|
params: threadStartParams,
|
|
222
|
-
deadlineAt,
|
|
223
220
|
abortSignal
|
|
224
221
|
});
|
|
225
222
|
onRecordEvent?.({
|
|
@@ -248,7 +245,6 @@ class CodexAppServerConnection {
|
|
|
248
245
|
const turnStartResponse = await this.request({
|
|
249
246
|
method: 'turn/start',
|
|
250
247
|
params: turnStartParams,
|
|
251
|
-
deadlineAt,
|
|
252
248
|
abortSignal
|
|
253
249
|
});
|
|
254
250
|
onRecordEvent?.({
|
|
@@ -264,7 +260,6 @@ class CodexAppServerConnection {
|
|
|
264
260
|
let turnStatus;
|
|
265
261
|
while(!turnStatus){
|
|
266
262
|
const message = await this.nextMessage({
|
|
267
|
-
deadlineAt,
|
|
268
263
|
abortSignal
|
|
269
264
|
});
|
|
270
265
|
if (this.isResponseMessage(message)) continue;
|
|
@@ -353,7 +348,7 @@ class CodexAppServerConnection {
|
|
|
353
348
|
threadId,
|
|
354
349
|
turnId
|
|
355
350
|
},
|
|
356
|
-
deadlineAt: Date.now() +
|
|
351
|
+
deadlineAt: Date.now() + CODEX_INTERRUPT_TIMEOUT_MS
|
|
357
352
|
}).catch(()=>{});
|
|
358
353
|
throw error;
|
|
359
354
|
} finally{
|
|
@@ -448,6 +443,7 @@ class CodexAppServerConnection {
|
|
|
448
443
|
return message?.id !== void 0 && (message?.result !== void 0 || message?.error !== void 0) && 'string' != typeof message?.method;
|
|
449
444
|
}
|
|
450
445
|
async request({ method, params, deadlineAt, abortSignal }) {
|
|
446
|
+
abortSignal?.throwIfAborted();
|
|
451
447
|
const requestId = this.nextRequestId++;
|
|
452
448
|
await this.sendMessage({
|
|
453
449
|
id: requestId,
|
|
@@ -554,9 +550,11 @@ class CodexAppServerConnection {
|
|
|
554
550
|
}
|
|
555
551
|
}
|
|
556
552
|
class CodexAppServerConnectionManager {
|
|
557
|
-
async runTurn({ messages, modelConfig, stream, onChunk, params, abortSignal,
|
|
553
|
+
async runTurn({ messages, modelConfig, stream, onChunk, params, abortSignal, onRecordEvent }) {
|
|
558
554
|
return this.runner.run(async ()=>{
|
|
555
|
+
abortSignal?.throwIfAborted();
|
|
559
556
|
const connection = await this.getConnection();
|
|
557
|
+
abortSignal?.throwIfAborted();
|
|
560
558
|
try {
|
|
561
559
|
return await connection.runTurn({
|
|
562
560
|
messages,
|
|
@@ -565,7 +563,6 @@ class CodexAppServerConnectionManager {
|
|
|
565
563
|
onChunk,
|
|
566
564
|
params,
|
|
567
565
|
abortSignal,
|
|
568
|
-
imageDetail,
|
|
569
566
|
onRecordEvent
|
|
570
567
|
});
|
|
571
568
|
} catch (error) {
|
|
@@ -606,7 +603,6 @@ async function callAIWithCodexAppServer(messages, modelConfig, options) {
|
|
|
606
603
|
onChunk: options?.onChunk,
|
|
607
604
|
params: options?.params,
|
|
608
605
|
abortSignal: options?.abortSignal,
|
|
609
|
-
imageDetail: options?.imageDetail,
|
|
610
606
|
onRecordEvent: options?.onRecordEvent
|
|
611
607
|
});
|
|
612
608
|
}
|