@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.
Files changed (149) hide show
  1. package/dist/es/agent/utils.mjs +1 -1
  2. package/dist/es/ai-model/service-caller/call-ai.mjs +151 -40
  3. package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
  4. package/dist/es/ai-model/service-caller/codex/call-codex.mjs +26 -14
  5. package/dist/es/ai-model/service-caller/codex/call-codex.mjs.map +1 -1
  6. package/dist/es/ai-model/service-caller/codex/codex-app-server.mjs +12 -16
  7. package/dist/es/ai-model/service-caller/codex/codex-app-server.mjs.map +1 -1
  8. package/dist/es/ai-model/service-caller/index.mjs +3 -3
  9. package/dist/es/ai-model/service-caller/model-call.mjs +49 -0
  10. package/dist/es/ai-model/service-caller/model-call.mjs.map +1 -0
  11. package/dist/es/ai-model/service-caller/openai/call-openai.mjs +69 -0
  12. package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -0
  13. package/dist/es/ai-model/service-caller/openai/chat-completion/chat-completion.mjs +50 -0
  14. package/dist/es/ai-model/service-caller/openai/chat-completion/chat-completion.mjs.map +1 -0
  15. package/dist/es/ai-model/service-caller/openai/chat-completion/non-stream.mjs +37 -0
  16. package/dist/es/ai-model/service-caller/openai/chat-completion/non-stream.mjs.map +1 -0
  17. package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs +78 -0
  18. package/dist/es/ai-model/service-caller/openai/chat-completion/stream.mjs.map +1 -0
  19. package/dist/es/ai-model/service-caller/openai/chat-completion/utils.mjs +15 -0
  20. package/dist/es/ai-model/service-caller/openai/chat-completion/utils.mjs.map +1 -0
  21. package/dist/es/ai-model/service-caller/{openai-client.mjs → openai/openai-client.mjs} +9 -21
  22. package/dist/es/ai-model/service-caller/openai/openai-client.mjs.map +1 -0
  23. package/dist/es/ai-model/service-caller/{openai-error.mjs → openai/openai-request-context.mjs} +14 -46
  24. package/dist/es/ai-model/service-caller/openai/openai-request-context.mjs.map +1 -0
  25. package/dist/es/ai-model/service-caller/proxy/index.mjs +2 -0
  26. package/dist/es/ai-model/service-caller/proxy/proxy-deps.node.mjs.map +1 -0
  27. package/dist/es/ai-model/service-caller/proxy/proxy-deps.stub.mjs.map +1 -0
  28. package/dist/es/ai-model/service-caller/{proxy.mjs → proxy/proxy.mjs} +2 -2
  29. package/dist/es/ai-model/service-caller/proxy/proxy.mjs.map +1 -0
  30. package/dist/es/ai-model/service-caller/request-timeout.mjs +59 -18
  31. package/dist/es/ai-model/service-caller/request-timeout.mjs.map +1 -1
  32. package/dist/es/ai-model/service-caller/semantic-retry.mjs +9 -4
  33. package/dist/es/ai-model/service-caller/semantic-retry.mjs.map +1 -1
  34. package/dist/es/ai-model/service-caller/utils.mjs +25 -8
  35. package/dist/es/ai-model/service-caller/utils.mjs.map +1 -1
  36. package/dist/es/device/index.mjs +44 -21
  37. package/dist/es/device/index.mjs.map +1 -1
  38. package/dist/es/report-html-template.mjs +1 -1
  39. package/dist/es/types.mjs.map +1 -1
  40. package/dist/es/utils.mjs +1 -1
  41. package/dist/lib/agent/utils.js +1 -1
  42. package/dist/lib/ai-model/service-caller/call-ai.js +151 -46
  43. package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
  44. package/dist/lib/ai-model/service-caller/codex/call-codex.js +30 -15
  45. package/dist/lib/ai-model/service-caller/codex/call-codex.js.map +1 -1
  46. package/dist/lib/ai-model/service-caller/codex/codex-app-server.js +12 -16
  47. package/dist/lib/ai-model/service-caller/codex/codex-app-server.js.map +1 -1
  48. package/dist/lib/ai-model/service-caller/index.js +7 -7
  49. package/dist/lib/ai-model/service-caller/model-call.js +89 -0
  50. package/dist/lib/ai-model/service-caller/model-call.js.map +1 -0
  51. package/dist/lib/ai-model/service-caller/openai/call-openai.js +103 -0
  52. package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -0
  53. package/dist/lib/ai-model/service-caller/openai/chat-completion/chat-completion.js +87 -0
  54. package/dist/lib/ai-model/service-caller/openai/chat-completion/chat-completion.js.map +1 -0
  55. package/dist/lib/ai-model/service-caller/openai/chat-completion/non-stream.js +71 -0
  56. package/dist/lib/ai-model/service-caller/openai/chat-completion/non-stream.js.map +1 -0
  57. package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js +112 -0
  58. package/dist/lib/ai-model/service-caller/openai/chat-completion/stream.js.map +1 -0
  59. package/dist/lib/ai-model/service-caller/openai/chat-completion/types.js.map +1 -0
  60. package/dist/lib/ai-model/service-caller/{chat-completion → openai/chat-completion}/utils.js +3 -29
  61. package/dist/lib/ai-model/service-caller/openai/chat-completion/utils.js.map +1 -0
  62. package/dist/lib/ai-model/service-caller/{openai-client.js → openai/openai-client.js} +9 -21
  63. package/dist/lib/ai-model/service-caller/openai/openai-client.js.map +1 -0
  64. package/dist/lib/ai-model/service-caller/{openai-error.js → openai/openai-request-context.js} +14 -46
  65. package/dist/lib/ai-model/service-caller/openai/openai-request-context.js.map +1 -0
  66. package/dist/lib/ai-model/service-caller/proxy/index.js +38 -0
  67. package/dist/lib/ai-model/service-caller/proxy/index.js.map +1 -0
  68. package/dist/lib/ai-model/service-caller/proxy/proxy-deps.node.js.map +1 -0
  69. package/dist/lib/ai-model/service-caller/proxy/proxy-deps.stub.js.map +1 -0
  70. package/dist/lib/ai-model/service-caller/{proxy.js → proxy/proxy.js} +4 -4
  71. package/dist/lib/ai-model/service-caller/proxy/proxy.js.map +1 -0
  72. package/dist/lib/ai-model/service-caller/request-timeout.js +67 -20
  73. package/dist/lib/ai-model/service-caller/request-timeout.js.map +1 -1
  74. package/dist/lib/ai-model/service-caller/semantic-retry.js +9 -4
  75. package/dist/lib/ai-model/service-caller/semantic-retry.js.map +1 -1
  76. package/dist/lib/ai-model/service-caller/utils.js +30 -13
  77. package/dist/lib/ai-model/service-caller/utils.js.map +1 -1
  78. package/dist/lib/device/index.js +56 -21
  79. package/dist/lib/device/index.js.map +1 -1
  80. package/dist/lib/report-html-template.js +1 -1
  81. package/dist/lib/types.js.map +1 -1
  82. package/dist/lib/utils.js +1 -1
  83. package/dist/types/ai-model/service-caller/call-ai.d.ts +2 -24
  84. package/dist/types/ai-model/service-caller/codex/call-codex.d.ts +7 -1
  85. package/dist/types/ai-model/service-caller/codex/codex-app-server.d.ts +1 -2
  86. package/dist/types/ai-model/service-caller/index.d.ts +3 -3
  87. package/dist/types/ai-model/service-caller/model-call.d.ts +26 -0
  88. package/dist/types/ai-model/service-caller/openai/call-openai.d.ts +3 -0
  89. package/dist/types/ai-model/service-caller/openai/chat-completion/chat-completion.d.ts +10 -0
  90. package/dist/types/ai-model/service-caller/openai/chat-completion/non-stream.d.ts +3 -0
  91. package/dist/types/ai-model/service-caller/openai/chat-completion/stream.d.ts +3 -0
  92. package/dist/types/ai-model/service-caller/openai/chat-completion/types.d.ts +10 -0
  93. package/dist/types/ai-model/service-caller/openai/chat-completion/utils.d.ts +5 -0
  94. package/dist/types/ai-model/service-caller/openai/openai-client.d.ts +14 -0
  95. package/dist/types/ai-model/service-caller/openai/openai-request-context.d.ts +18 -0
  96. package/dist/types/ai-model/service-caller/proxy/index.d.ts +1 -0
  97. package/dist/types/ai-model/service-caller/{proxy.d.ts → proxy/proxy.d.ts} +1 -1
  98. package/dist/types/ai-model/service-caller/request-timeout.d.ts +14 -11
  99. package/dist/types/ai-model/service-caller/types.d.ts +19 -0
  100. package/dist/types/ai-model/service-caller/utils.d.ts +10 -4
  101. package/dist/types/device/index.d.ts +28 -6
  102. package/dist/types/types.d.ts +5 -0
  103. package/package.json +6 -6
  104. package/dist/es/ai-model/service-caller/call.mjs +0 -56
  105. package/dist/es/ai-model/service-caller/call.mjs.map +0 -1
  106. package/dist/es/ai-model/service-caller/chat-completion/call.mjs +0 -101
  107. package/dist/es/ai-model/service-caller/chat-completion/call.mjs.map +0 -1
  108. package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs +0 -80
  109. package/dist/es/ai-model/service-caller/chat-completion/non-stream.mjs.map +0 -1
  110. package/dist/es/ai-model/service-caller/chat-completion/stream.mjs +0 -87
  111. package/dist/es/ai-model/service-caller/chat-completion/stream.mjs.map +0 -1
  112. package/dist/es/ai-model/service-caller/chat-completion/utils.mjs +0 -35
  113. package/dist/es/ai-model/service-caller/chat-completion/utils.mjs.map +0 -1
  114. package/dist/es/ai-model/service-caller/openai-client.mjs.map +0 -1
  115. package/dist/es/ai-model/service-caller/openai-error.mjs.map +0 -1
  116. package/dist/es/ai-model/service-caller/proxy-deps.node.mjs.map +0 -1
  117. package/dist/es/ai-model/service-caller/proxy-deps.stub.mjs.map +0 -1
  118. package/dist/es/ai-model/service-caller/proxy.mjs.map +0 -1
  119. package/dist/lib/ai-model/service-caller/call.js +0 -90
  120. package/dist/lib/ai-model/service-caller/call.js.map +0 -1
  121. package/dist/lib/ai-model/service-caller/chat-completion/call.js +0 -135
  122. package/dist/lib/ai-model/service-caller/chat-completion/call.js.map +0 -1
  123. package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js +0 -114
  124. package/dist/lib/ai-model/service-caller/chat-completion/non-stream.js.map +0 -1
  125. package/dist/lib/ai-model/service-caller/chat-completion/stream.js +0 -121
  126. package/dist/lib/ai-model/service-caller/chat-completion/stream.js.map +0 -1
  127. package/dist/lib/ai-model/service-caller/chat-completion/types.js.map +0 -1
  128. package/dist/lib/ai-model/service-caller/chat-completion/utils.js.map +0 -1
  129. package/dist/lib/ai-model/service-caller/openai-client.js.map +0 -1
  130. package/dist/lib/ai-model/service-caller/openai-error.js.map +0 -1
  131. package/dist/lib/ai-model/service-caller/proxy-deps.node.js.map +0 -1
  132. package/dist/lib/ai-model/service-caller/proxy-deps.stub.js.map +0 -1
  133. package/dist/lib/ai-model/service-caller/proxy.js.map +0 -1
  134. package/dist/types/ai-model/service-caller/call.d.ts +0 -4
  135. package/dist/types/ai-model/service-caller/chat-completion/call.d.ts +0 -2
  136. package/dist/types/ai-model/service-caller/chat-completion/non-stream.d.ts +0 -2
  137. package/dist/types/ai-model/service-caller/chat-completion/stream.d.ts +0 -2
  138. package/dist/types/ai-model/service-caller/chat-completion/types.d.ts +0 -25
  139. package/dist/types/ai-model/service-caller/chat-completion/utils.d.ts +0 -12
  140. package/dist/types/ai-model/service-caller/openai-client.d.ts +0 -14
  141. package/dist/types/ai-model/service-caller/openai-error.d.ts +0 -26
  142. /package/dist/es/ai-model/service-caller/{chat-completion → openai/chat-completion}/types.mjs +0 -0
  143. /package/dist/es/ai-model/service-caller/{proxy-deps.node.mjs → proxy/proxy-deps.node.mjs} +0 -0
  144. /package/dist/es/ai-model/service-caller/{proxy-deps.stub.mjs → proxy/proxy-deps.stub.mjs} +0 -0
  145. /package/dist/lib/ai-model/service-caller/{chat-completion → openai/chat-completion}/types.js +0 -0
  146. /package/dist/lib/ai-model/service-caller/{proxy-deps.node.js → proxy/proxy-deps.node.js} +0 -0
  147. /package/dist/lib/ai-model/service-caller/{proxy-deps.stub.js → proxy/proxy-deps.stub.js} +0 -0
  148. /package/dist/types/ai-model/service-caller/{proxy-deps.node.d.ts → proxy/proxy-deps.node.d.ts} +0 -0
  149. /package/dist/types/ai-model/service-caller/{proxy-deps.stub.d.ts → proxy/proxy-deps.stub.d.ts} +0 -0
@@ -153,7 +153,7 @@ async function matchElementFromCache(context, cacheEntry, cachePrompt, cacheable
153
153
  return;
154
154
  }
155
155
  }
156
- const getMidsceneVersion = ()=>"1.12.8";
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 { assert } from "@midscene/shared/utils";
2
- import { assertJsonObject } from "../shared/json.mjs";
3
- import { callAI } from "./call.mjs";
4
- import { callAiAndParseWithRetry, withSemanticRetryFeedback } from "./semantic-retry.mjs";
5
- import { AIResponseParseError } from "./utils.mjs";
6
- function parseAIObjectResponse(response, modelRuntime, jsonParserSource = 'generic-object') {
7
- const { adapter } = modelRuntime;
8
- assert(response, 'empty response');
9
- const jsonContent = adapter.jsonParser(response.content, {
10
- source: jsonParserSource
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
- assertJsonObject(jsonContent);
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 callAIWithObjectResponse(messages, modelRuntime, options) {
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
- return callAiAndParseWithRetry({
24
- callAi: (retryAttempt, previousParseError)=>callAI(withSemanticRetryFeedback(messages, previousParseError), modelRuntime, {
25
- abortSignal: options?.abortSignal,
26
- expectedJsonObjectResponse: true,
27
- semanticRetryAttempt: retryAttempt
28
- }),
29
- parseResponse: (response)=>parseAIObjectResponse(response, modelRuntime, options?.jsonParserSource),
30
- toParseError: (error, response)=>{
31
- const errorMessage = error instanceof Error ? error.message : String(error);
32
- return new AIResponseParseError(errorMessage, response.content, response.usage, response.rawChoiceMessage, response.reasoning_content);
33
- },
34
- parseRetryTimes: options?.retryTimes ?? modelConfig.retryCount,
35
- parseRetryInterval: options?.retryInterval ?? modelConfig.retryInterval,
36
- abortSignal: options?.abortSignal
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
- async function callAIWithStringResponse(msgs, modelRuntime, options) {
40
- const { content, usage, rawChoiceMessage } = await callAI(msgs, modelRuntime, options);
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
- content,
43
- usage,
44
- rawChoiceMessage
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 { callAIWithObjectResponse, callAIWithStringResponse, parseAIObjectResponse };
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":";;;;;;;;;;;;;;;;;;;AAgCO,eAAeA,OACpBC,QAAsC,EACtCC,YAA0B,EAC1BC,OAAuB;IAEvBA,SAAS,aAAa;IACtB,MAAMC,cAAcD,SAAS,WAAW;IACxC,IAAIC,aACFC,OACE,AAA4B,cAA5B,OAAOF,SAAS,SAChB;IAMJ,MAAMG,cAAcJ,aAAa,WAAW,IAAI,CAAC,SAAS,EAAEK,QAAQ;IAKpE,MAAMC,iBAAiBC;IAEvB,MAAM,EAAE,QAAQC,WAAW,EAAE,GAAGR;IAEhC,MAAMS,UAAU,UACdC,yBAAyBF,YAAY,aAAa,IAC9C;YACE,UAAU;YACV,OAAOG,iBAAiB;gBAAEZ;gBAAUC;gBAAcC;YAAQ;QAC5D,IACA;YACE,UAAU;YACV,OAAO,MAAMW,sBAAsB;gBACjCb;gBACAC;gBACAC;YACF;QACF;IAEN,MAAMY,WAAWZ,SAAS,cACtB,MAAMa,mBAAmBb,QAAQ,WAAW,EAAEQ,WAC9C,MAAMA;IAEV,MAAMM,SAAS,MAAMC,mBAAmB;QACtCjB;QACAC;QACAC;QACAG;QACAE;QACAO;IACF;IAEA,MAAM,EACJI,QAAQ,EACRC,QAAQ,EACRC,aAAa,EACbC,UAAU,EACVC,SAAS,EACTC,iBAAiB,EACjB,GAAGC,UACJ,GAAGR;IAEJ,MAAMS,QAAQC,eAAe;QAC3B,WAAWR;QACXC;QACAC;QACAC;QACAC;QACAC;QACA,WAAWd,YAAY,SAAS;QAChC,kBAAkBA,YAAY,gBAAgB;QAC9C,MAAMA,YAAY,IAAI;QACtBF;IACF;IAEA,MAAMoB,oBAAoBC,SAAS;IACnC,MAAMC,qBAAqBD,SAAS;IACpCD,kBACE,CAAC,OAAO,EAAElB,YAAY,SAAS,CAAC,QAAQ,EAAEA,YAAY,WAAW,IAAI,UAAU,aAAa,EAAEe,SAAS,UAAU,CAAC,iBAAiB,EAAEC,OAAO,iBAAiB,GAAG,qBAAqB,EAAEA,OAAO,qBAAqB,GAAG,gBAAgB,EAAEA,OAAO,gBAAgB,GAAG,gBAAgB,EAAEA,OAAO,gBAAgB,GAAG,WAAW,EAAEN,YAAY,GAAG,aAAa,EAAEG,aAAa,GAAG,QAAQ,EAAEb,YAAY,IAAI,CAAC,0BAA0B,EAAEA,YAAY,WAAW,IAAI,IAAI;IAEjcoB,mBAAmB,CAAC,oBAAoB,EAAEC,KAAK,SAAS,CAACL,QAAQ;IAEjE,IAAIA,SAASxB,aAAa,OAAO,EAC/BA,aAAa,OAAO,CAACwB;IAEvB,OAAO;QAAE,GAAGD,QAAQ;QAAEC;IAAM;AAC9B;AAmBA,MAAMM,mCAAmCC;IAGvC,YAAYC,KAAY,CAAE;QACxB,KAAK,CAACA,MAAM,OAAO,EAAE;YAAEA;QAAM,IAH/B,uBAAS,SAAT;QAIE,IAAI,CAAC,KAAK,GAAGA;IACf;AACF;AAEA,eAAehB,mBACbiB,KAAqB;IAErB,MAAMC,YAAYC,KAAK,GAAG;IAC1B,MAAM,EACJ,cAAc,EAAEC,MAAM,EAAE,EACxBnC,OAAO,EACR,GAAGgC;IACJ,MAAMI,cAAcC,oBAAoBF,OAAO,UAAU,IAAI;IAC7D,MAAMG,gBAAgBH,OAAO,aAAa,IAAI;IAC9C,MAAMI,gBAA4D,EAAE;IAEpE,IAAK,IAAIC,UAAU,IAAKA,UAAW;QACjCxC,SAAS,aAAa;QACtB,IAAI;YACF,MAAMc,SAAS,MAAM2B,cAAcT,OAAOQ;YAC1C,OAAO;gBACL,GAAG1B,MAAM;gBACT,eAAeoB,KAAK,GAAG,KAAKD;gBAC5B,YAAYO,UAAU;YACxB;QACF,EAAE,OAAOE,OAAO;YACd1C,SAAS,aAAa;YACtB,IAAI0C,iBAAiBb,4BACnB,MAAMa,MAAM,KAAK;YAEnB,MAAMC,YAAYC,QAAQF;YAC1BH,cAAc,IAAI,CAAC;gBAAEC;gBAAS,OAAOG;YAAU;YAC/C,IAAIH,YAAYJ,aACd,MAAMS,8BACJF,WACAJ,eACAH;YAGJV,SAAS,WAAW;gBAAE,SAAS;YAAK,GAClC,CAAC,wBAAwB,EAAEc,QAAQ,CAAC,EAAEJ,YAAY,eAAe,EAAEE,cAAc,aAAa,EAAEK,UAAU,OAAO,EAAE;QAEvH;QACA,MAAMG,aAAaR,eAAetC,SAAS;IAC7C;AACF;AAEA,eAAeyC,cACb,EACE3C,QAAQ,EACRC,YAAY,EACZC,OAAO,EACPG,WAAW,EACXE,cAAc,EACdO,QAAQ,EACO,EACjB4B,OAAe;IAEf,MAAM,EAAE,QAAQjC,WAAW,EAAE,GAAGR;IAChC,MAAMgD,cAAcC,gCAChB,CAACC;QACMC,qBAAqB;YACxB/C;YACA,QAAQE;YACR,sBAAsBL,SAAS;YAC/B,MAAMO,YAAY,IAAI;YACtB,QAAQA,YAAY,MAAM;YAC1B,aAAaA,YAAY,WAAW;YACpC,GAAG0C,KAAK;YACRT;QACF;IACF,IACAW;IACJ,MAAMC,qBAAqBC,0BAA0B9C,YAAY,OAAO;IACxE,MAAM,EAAE,QAAQ+C,aAAa,EAAEC,OAAO,EAAE,GAAGC,wBACzCJ,oBACApD,SAAS;IAEX,IAAIyD,iBAAiB;IACrB,MAAMC,UAAU1D,SAAS;IACzB,MAAM2D,UAA4B;QAChC7D;QACAC;QACAI;QACA4C;QACAO;QACAF;QACA,SAASM,UACL;YACE,GAAG1D,OAAO;YACV,SAAS,CAAC4D;gBACRN,cAAc,cAAc;gBAC5BG,iBAAiB;gBACjBC,QAAQE;YACV;QACF,IACA5D;IACN;IACA,IAAI;QACF,OAAO,MAAMa,mBAAmByC,eAAe,IAC7C1C,AAAsB,YAAtBA,SAAS,QAAQ,GACbiD,UAAUF,SAAS/C,SAAS,KAAK,IACjCkD,WAAWH,SAAS/C,SAAS,KAAK;IAE1C,EAAE,OAAO8B,OAAO;QACd1C,SAAS,aAAa;QACtB,MAAM2C,YAAYC,QAAQF;QAC1B,IAAIe,gBACF,MAAM,IAAI5B,2BAA2Bc;QAEvC,MAAMA;IACR,SAAU;QACRY;IACF;AACF"}
@@ -1,6 +1,27 @@
1
+ import { applyImageDetail } from "../utils.mjs";
1
2
  import { callAIWithCodexAppServer } from "./codex-app-server.mjs";
2
- const callCodex = async ({ messages, modelRuntime, options, recordEvent })=>{
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: config,
37
- abortSignal: options?.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 callCodex = async ({\n messages,\n modelRuntime,\n options,\n recordEvent,\n}: ModelCallContext): Promise<ModelCallResult> => {\n const { config: modelConfig, adapter } = 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 { 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 codexResult = await callAIWithCodexAppServer(messages, modelConfig, {\n stream: options?.stream,\n onChunk: options?.onChunk,\n params: config,\n abortSignal: options?.abortSignal,\n imageDetail,\n onRecordEvent: recordCodexEvent,\n });\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":["callCodex","messages","modelRuntime","options","recordEvent","modelConfig","adapter","protocolChunkSequence","codexStartTime","Date","recordCodexEvent","event","undefined","config","imageDetail","codexResult","callAIWithCodexAppServer","protocolMetadata","rawUsage","response","timeCost","error","Error","String"],"mappings":";AAMO,MAAMA,YAAY,OAAO,EAC9BC,QAAQ,EACRC,YAAY,EACZC,OAAO,EACPC,WAAW,EACM;IACjB,MAAM,EAAE,QAAQC,WAAW,EAAEC,OAAO,EAAE,GAAGJ;IACzC,IAAIK,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,MAAM,EAAEC,MAAM,EAAEC,WAAW,EAAE,GAAGR,QAAQ,yBAAyB,CAAC;YAChE,QAAQD,YAAY,MAAM;YAC1B,YAAY;gBACV,kBAAkBA,YAAY,gBAAgB;gBAC9C,iBAAiBA,YAAY,eAAe;gBAC5C,iBAAiBA,YAAY,eAAe;YAC9C;YACA,6BAA6BF,SAAS;QACxC;QACA,MAAMY,cAAc,MAAMC,yBAAyBf,UAAUI,aAAa;YACxE,QAAQF,SAAS;YACjB,SAASA,SAAS;YAClB,QAAQU;YACR,aAAaV,SAAS;YACtBW;YACA,eAAeJ;QACjB;QACA,MAAM,EAAEO,gBAAgB,EAAE,OAAOC,QAAQ,EAAE,GAAGC,UAAU,GAAGJ;QAC3D,MAAMK,WAAWX,KAAK,GAAG,KAAKD;QAC9BJ,cAAc;YACZ,MAAM;YACN,SAAS;YACT,UAAU;YACV,OAAO;gBACL,SAASe,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;QACdjB,cAAc;YACZ,MAAM;YACN,SAAS;YACT,UAAU;YACV,OACEiB,iBAAiBC,QACb;gBACE,MAAMD,MAAM,IAAI;gBAChB,SAASA,MAAM,OAAO;gBACtB,OAAOA,MAAM,KAAK;YACpB,IACAE,OAAOF;QACf;QACA,MAAMA;IACR;AACF"}
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 CODEX_DEFAULT_TIMEOUT_MS = 600000;
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, imageDetailOverride)=>{
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 = imageDetailOverride ?? toCodexImageDetail('image_url' === partType ? part.image_url?.detail : part.detail);
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, imageDetailOverride)=>{
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, imageDetailOverride));
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, imageDetail, onRecordEvent }) {
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, imageDetail);
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() + 5000
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, imageDetail, onRecordEvent }) {
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
  }