aiexecode 1.0.72 → 1.0.74
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/package.json +1 -1
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/prompts/orchestrator.txt +449 -436
- package/src/ai_based/completion_judge.js +20 -18
- package/src/ai_based/orchestrator.js +15 -159
- package/src/system/ai_request.js +186 -82
- package/src/system/conversation_trimmer.js +133 -0
- package/src/system/tool_approval.js +17 -6
- package/src/tools/code_editor.js +0 -2
- package/src/tools/file_reader.js +15 -14
- package/src/util/exit_handler.js +9 -0
- package/src/util/output_formatter.js +12 -13
- /package/payload_viewer/out/_next/static/{6yTW1SraROIP1ebN-kxTS → S5OZ2xYxrJopcK8El7EbO}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{6yTW1SraROIP1ebN-kxTS → S5OZ2xYxrJopcK8El7EbO}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{6yTW1SraROIP1ebN-kxTS → S5OZ2xYxrJopcK8El7EbO}/_ssgManifest.js +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import dotenv from "dotenv";
|
|
2
|
-
import { request,
|
|
2
|
+
import { request, shouldRetryWithTrim, getModelForProvider } from "../system/ai_request.js";
|
|
3
3
|
import { getOrchestratorConversation } from "./orchestrator.js";
|
|
4
4
|
import { createSystemMessage } from "../util/prompt_loader.js";
|
|
5
5
|
import { createDebugLogger } from "../util/debug_log.js";
|
|
6
6
|
import { getCurrentTodos } from "../system/session_memory.js";
|
|
7
|
+
import { cleanupOrphanOutputs, trimConversation } from "../system/conversation_trimmer.js";
|
|
7
8
|
|
|
8
9
|
dotenv.config({ quiet: true });
|
|
9
10
|
|
|
@@ -116,16 +117,7 @@ function syncOrchestratorConversation() {
|
|
|
116
117
|
debugLog(`[syncOrchestratorConversation] END`);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
for (let i = 1; i < completionJudgeConversation.length - 1; i++) {
|
|
121
|
-
const candidate = completionJudgeConversation[i];
|
|
122
|
-
if (candidate?.role !== "system") {
|
|
123
|
-
completionJudgeConversation.splice(i, 1);
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
120
|
+
// 대화 cleanup 및 trim 함수는 conversation_trimmer 모듈에서 가져옴
|
|
129
121
|
|
|
130
122
|
async function dispatchCompletionJudgeRequest(options) {
|
|
131
123
|
debugLog(`[dispatchCompletionJudgeRequest] START`);
|
|
@@ -140,9 +132,10 @@ async function dispatchCompletionJudgeRequest(options) {
|
|
|
140
132
|
let attemptCount = 0;
|
|
141
133
|
while (true) {
|
|
142
134
|
attemptCount++;
|
|
143
|
-
debugLog(`[dispatchCompletionJudgeRequest] Attempt ${attemptCount}
|
|
135
|
+
debugLog(`[dispatchCompletionJudgeRequest] Attempt ${attemptCount} - starting cleanup...`);
|
|
136
|
+
cleanupOrphanOutputs(completionJudgeConversation);
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
debugLog(`[dispatchCompletionJudgeRequest] Conversation has ${completionJudgeConversation.length} entries`);
|
|
146
139
|
|
|
147
140
|
// Conversation 구조 분석
|
|
148
141
|
const conversationTypes = {};
|
|
@@ -151,7 +144,16 @@ async function dispatchCompletionJudgeRequest(options) {
|
|
|
151
144
|
conversationTypes[type] = (conversationTypes[type] || 0) + 1;
|
|
152
145
|
});
|
|
153
146
|
debugLog(`[dispatchCompletionJudgeRequest] Conversation structure: ${JSON.stringify(conversationTypes)}`);
|
|
154
|
-
|
|
147
|
+
|
|
148
|
+
// function_call_output 항목들 확인
|
|
149
|
+
const functionCallOutputs = completionJudgeConversation.filter(item => item.type === 'function_call_output');
|
|
150
|
+
debugLog(`[dispatchCompletionJudgeRequest] Found ${functionCallOutputs.length} function_call_output entries`);
|
|
151
|
+
|
|
152
|
+
functionCallOutputs.forEach((item, index) => {
|
|
153
|
+
debugLog(`[dispatchCompletionJudgeRequest] function_call_output[${index}] - call_id: ${item.call_id}, output length: ${item.output?.length || 0}, output preview: ${item.output?.substring(0, 200)}`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const { model, isGpt5Model, taskName } = options;
|
|
155
157
|
|
|
156
158
|
const requestPayload = {
|
|
157
159
|
model,
|
|
@@ -197,13 +199,13 @@ async function dispatchCompletionJudgeRequest(options) {
|
|
|
197
199
|
throw error;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
|
-
if (!
|
|
201
|
-
debugLog(`[dispatchCompletionJudgeRequest] Not
|
|
202
|
+
if (!shouldRetryWithTrim(error)) {
|
|
203
|
+
debugLog(`[dispatchCompletionJudgeRequest] Not recoverable by trimming, re-throwing`);
|
|
202
204
|
throw error;
|
|
203
205
|
}
|
|
204
206
|
|
|
205
|
-
debugLog(`[dispatchCompletionJudgeRequest]
|
|
206
|
-
const trimmed =
|
|
207
|
+
debugLog(`[dispatchCompletionJudgeRequest] Recoverable error detected, attempting to trim...`);
|
|
208
|
+
const trimmed = trimConversation(completionJudgeConversation);
|
|
207
209
|
debugLog(`[dispatchCompletionJudgeRequest] Trim result: ${trimmed}, new conversation length: ${completionJudgeConversation.length}`);
|
|
208
210
|
if (!trimmed) {
|
|
209
211
|
debugLog(`[dispatchCompletionJudgeRequest] Cannot trim further, re-throwing error`);
|
|
@@ -3,7 +3,8 @@ import dotenv from "dotenv";
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { truncateWithOmit } from "../util/text_formatter.js";
|
|
5
5
|
import { createSystemMessage } from "../util/prompt_loader.js";
|
|
6
|
-
import { request,
|
|
6
|
+
import { request, shouldRetryWithTrim, getModelForProvider } from "../system/ai_request.js";
|
|
7
|
+
import { cleanupOrphanOutputs, trimConversation } from "../system/conversation_trimmer.js";
|
|
7
8
|
import { runPythonCodeSchema, bashSchema } from "../system/code_executer.js";
|
|
8
9
|
import { readFileSchema, readFileRangeSchema } from "../tools/file_reader.js";
|
|
9
10
|
import { writeFileSchema, editFileRangeSchema, editFileReplaceSchema } from "../tools/code_editor.js";
|
|
@@ -120,18 +121,6 @@ export function recordOrchestratorToolResult({ toolCallId, toolName = '', argume
|
|
|
120
121
|
return null;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
if (toolName === 'read_file') {
|
|
124
|
-
const filePath = toolArguments.filePath || '';
|
|
125
|
-
const absolutePath = path.isAbsolute(filePath)
|
|
126
|
-
? filePath
|
|
127
|
-
: path.join(process.cwd(), filePath);
|
|
128
|
-
|
|
129
|
-
stdout = JSON.stringify({
|
|
130
|
-
absolute_file_path: absolutePath,
|
|
131
|
-
content: stdout
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
124
|
const payload = {
|
|
136
125
|
tool: toolName || null,
|
|
137
126
|
call_id: toolCallId,
|
|
@@ -154,146 +143,7 @@ export function recordOrchestratorToolResult({ toolCallId, toolName = '', argume
|
|
|
154
143
|
return toolResult;
|
|
155
144
|
}
|
|
156
145
|
|
|
157
|
-
|
|
158
|
-
if (!item || typeof item !== 'object') {
|
|
159
|
-
return [];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const callIds = new Set();
|
|
163
|
-
|
|
164
|
-
const maybeIds = [item.call_id, item.tool_call_id, item.id];
|
|
165
|
-
for (const id of maybeIds) {
|
|
166
|
-
if (typeof id === 'string' && id.trim().length) {
|
|
167
|
-
callIds.add(id);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (item.function && typeof item.function === 'object') {
|
|
172
|
-
const fnIds = [item.function.call_id, item.function.id];
|
|
173
|
-
for (const id of fnIds) {
|
|
174
|
-
if (typeof id === 'string' && id.trim().length) {
|
|
175
|
-
callIds.add(id);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (Array.isArray(item.tool_calls)) {
|
|
181
|
-
for (const toolCall of item.tool_calls) {
|
|
182
|
-
extractCallIdsFromItem(toolCall).forEach(id => callIds.add(id));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return Array.from(callIds);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function removeConversationEntriesByCallIds(callIds) {
|
|
190
|
-
if (!Array.isArray(callIds) || callIds.length === 0) {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const callIdSet = new Set(callIds.filter(id => typeof id === 'string' && id.trim().length));
|
|
195
|
-
if (!callIdSet.size) {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
for (let index = orchestratorConversation.length - 1; index >= 0; index--) {
|
|
200
|
-
const entry = orchestratorConversation[index];
|
|
201
|
-
if (!entry || typeof entry !== 'object') {
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (entry.type === 'function_call_output') {
|
|
206
|
-
if (callIdSet.has(entry.call_id)) {
|
|
207
|
-
orchestratorConversation.splice(index, 1);
|
|
208
|
-
}
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (Array.isArray(entry.tool_calls) && entry.tool_calls.length) {
|
|
213
|
-
const filteredToolCalls = entry.tool_calls.filter(toolCall => {
|
|
214
|
-
const ids = extractCallIdsFromItem(toolCall);
|
|
215
|
-
return !ids.some(id => callIdSet.has(id));
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
if (filteredToolCalls.length !== entry.tool_calls.length) {
|
|
219
|
-
if (filteredToolCalls.length === 0 && (!entry.content || entry.content.length === 0)) {
|
|
220
|
-
orchestratorConversation.splice(index, 1);
|
|
221
|
-
} else {
|
|
222
|
-
entry.tool_calls = filteredToolCalls;
|
|
223
|
-
}
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const entryCallIds = extractCallIdsFromItem(entry);
|
|
229
|
-
if (entryCallIds.some(id => callIdSet.has(id))) {
|
|
230
|
-
orchestratorConversation.splice(index, 1);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function cleanupOrchestratorConversation() {
|
|
236
|
-
debugLog(`[cleanupOrchestratorConversation] Starting cleanup, conversation length: ${orchestratorConversation.length}`);
|
|
237
|
-
|
|
238
|
-
const validCallIds = new Set();
|
|
239
|
-
|
|
240
|
-
for (const entry of orchestratorConversation) {
|
|
241
|
-
if (!entry || typeof entry !== 'object') {
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (entry.type === 'function_call') {
|
|
246
|
-
const ids = extractCallIdsFromItem(entry);
|
|
247
|
-
debugLog(`[cleanupOrchestratorConversation] Found function_call with call_ids: ${ids.join(', ')}`);
|
|
248
|
-
ids.forEach(id => validCallIds.add(id));
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (Array.isArray(entry.tool_calls)) {
|
|
252
|
-
for (const toolCall of entry.tool_calls) {
|
|
253
|
-
const ids = extractCallIdsFromItem(toolCall);
|
|
254
|
-
debugLog(`[cleanupOrchestratorConversation] Found tool_call with call_ids: ${ids.join(', ')}`);
|
|
255
|
-
ids.forEach(id => validCallIds.add(id));
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
debugLog(`[cleanupOrchestratorConversation] Valid call IDs: ${Array.from(validCallIds).join(', ')}`);
|
|
261
|
-
|
|
262
|
-
for (let index = orchestratorConversation.length - 1; index >= 0; index--) {
|
|
263
|
-
const entry = orchestratorConversation[index];
|
|
264
|
-
if (entry?.type === 'function_call_output') {
|
|
265
|
-
const callId = entry.call_id;
|
|
266
|
-
if (!callId || !validCallIds.has(callId)) {
|
|
267
|
-
debugLog(`[cleanupOrchestratorConversation] REMOVING function_call_output at index ${index} with call_id: ${callId} (not in validCallIds)`);
|
|
268
|
-
orchestratorConversation.splice(index, 1);
|
|
269
|
-
} else {
|
|
270
|
-
debugLog(`[cleanupOrchestratorConversation] KEEPING function_call_output at index ${index} with call_id: ${callId}, output length: ${entry.output?.length || 0}`);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
debugLog(`[cleanupOrchestratorConversation] Finished cleanup, conversation length: ${orchestratorConversation.length}`);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function trimOrchestratorConversation() {
|
|
279
|
-
if (orchestratorConversation.length <= 2) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
for (let i = 1; i < orchestratorConversation.length - 1; i++) {
|
|
284
|
-
const target = orchestratorConversation[i];
|
|
285
|
-
const callIds = extractCallIdsFromItem(target);
|
|
286
|
-
|
|
287
|
-
orchestratorConversation.splice(i, 1);
|
|
288
|
-
|
|
289
|
-
if (callIds.length) {
|
|
290
|
-
removeConversationEntriesByCallIds(callIds);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return true;
|
|
294
|
-
}
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
146
|
+
// 대화 cleanup 및 trim 함수는 conversation_trimmer 모듈에서 가져옴
|
|
297
147
|
|
|
298
148
|
async function dispatchOrchestratorRequest({ toolChoice }) {
|
|
299
149
|
debugLog(`[dispatchOrchestratorRequest] START - toolChoice: ${toolChoice}`);
|
|
@@ -309,7 +159,7 @@ async function dispatchOrchestratorRequest({ toolChoice }) {
|
|
|
309
159
|
while (true) {
|
|
310
160
|
attemptCount++;
|
|
311
161
|
debugLog(`[dispatchOrchestratorRequest] Attempt ${attemptCount} - starting cleanup...`);
|
|
312
|
-
|
|
162
|
+
cleanupOrphanOutputs(orchestratorConversation);
|
|
313
163
|
|
|
314
164
|
debugLog(`[dispatchOrchestratorRequest] Conversation has ${orchestratorConversation.length} entries`);
|
|
315
165
|
|
|
@@ -370,15 +220,21 @@ async function dispatchOrchestratorRequest({ toolChoice }) {
|
|
|
370
220
|
return response;
|
|
371
221
|
} catch (error) {
|
|
372
222
|
debugLog(`[dispatchOrchestratorRequest] API request failed: ${error.message}`);
|
|
373
|
-
debugLog(`[dispatchOrchestratorRequest] Error
|
|
223
|
+
debugLog(`[dispatchOrchestratorRequest] Error name: ${error.name}, type: ${error?.constructor?.name}`);
|
|
224
|
+
|
|
225
|
+
// AbortError는 즉시 전파 (세션 중단)
|
|
226
|
+
if (error.name === 'AbortError') {
|
|
227
|
+
debugLog(`[dispatchOrchestratorRequest] Request aborted by user, propagating AbortError`);
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
374
230
|
|
|
375
|
-
if (!
|
|
376
|
-
debugLog(`[dispatchOrchestratorRequest] Not
|
|
231
|
+
if (!shouldRetryWithTrim(error)) {
|
|
232
|
+
debugLog(`[dispatchOrchestratorRequest] Not recoverable by trimming, re-throwing`);
|
|
377
233
|
throw error;
|
|
378
234
|
}
|
|
379
235
|
|
|
380
|
-
debugLog(`[dispatchOrchestratorRequest]
|
|
381
|
-
const trimmed =
|
|
236
|
+
debugLog(`[dispatchOrchestratorRequest] Recoverable error detected, attempting to trim conversation...`);
|
|
237
|
+
const trimmed = trimConversation(orchestratorConversation);
|
|
382
238
|
debugLog(`[dispatchOrchestratorRequest] Trim result: ${trimmed}, new conversation length: ${orchestratorConversation.length}`);
|
|
383
239
|
if (!trimmed) {
|
|
384
240
|
debugLog(`[dispatchOrchestratorRequest] Cannot trim further, re-throwing error`);
|
package/src/system/ai_request.js
CHANGED
|
@@ -193,10 +193,36 @@ export async function request(taskName, requestPayload) {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
debugLog(`[request] Starting payload transformation...`);
|
|
196
|
+
const isValidJSON = (json) => {
|
|
197
|
+
if (typeof json !== 'string') return false;
|
|
198
|
+
try {
|
|
199
|
+
JSON.parse(json);
|
|
200
|
+
return true;
|
|
201
|
+
} catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (true && payloadCopy.input && Array.isArray(payloadCopy.input)) {
|
|
206
|
+
for (let i = payloadCopy.input.length - 1; i >= 0; i--) {
|
|
207
|
+
const msg = payloadCopy.input[i];
|
|
208
|
+
const { type, call_id, output } = msg;
|
|
209
|
+
if (type !== 'function_call_output') continue;
|
|
210
|
+
const parsedOutput = JSON.parse(output);
|
|
211
|
+
if (!isValidJSON(parsedOutput.stdout)) {
|
|
212
|
+
msg.output = parsedOutput.stdout;
|
|
213
|
+
} else {
|
|
214
|
+
parsedOutput.stdout = JSON.parse(parsedOutput.stdout);
|
|
215
|
+
if (parsedOutput.original_result) {
|
|
216
|
+
parsedOutput.stdout = ({ ...parsedOutput.original_result, ...(parsedOutput.stdout) });
|
|
217
|
+
delete parsedOutput.original_result;
|
|
218
|
+
}
|
|
219
|
+
msg.output = parsedOutput;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
196
223
|
if (payloadCopy.input && Array.isArray(payloadCopy.input)) {
|
|
197
224
|
const marker = {};
|
|
198
225
|
const remove_call_id = {};//[];
|
|
199
|
-
const response_message_call_id = {};
|
|
200
226
|
|
|
201
227
|
debugLog(`[request] Processing ${payloadCopy.input.length} input messages for deduplication and formatting`);
|
|
202
228
|
let processedCount = 0;
|
|
@@ -207,105 +233,182 @@ export async function request(taskName, requestPayload) {
|
|
|
207
233
|
if (type !== 'function_call_output') continue;
|
|
208
234
|
processedCount++;
|
|
209
235
|
|
|
210
|
-
|
|
236
|
+
if ((typeof output) === 'string') continue;
|
|
237
|
+
const { tool, stdout, stderr, exit_code, original_result } = output;
|
|
211
238
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const parsedStdout = JSON.parse(stdout);
|
|
219
|
-
if (!parsedStdout.absolute_file_path) continue;
|
|
220
|
-
let abolute_path = toAbsolutePath(parsedStdout.absolute_file_path)
|
|
221
|
-
const updated_content = parsedStdout.file_stats?.updated_content;
|
|
222
|
-
delete parsedStdout.absolute_file_path;
|
|
223
|
-
delete parsedStdout.file_stats;
|
|
224
|
-
if (marker[abolute_path]) {
|
|
225
|
-
parsedStdout.target_file_path;
|
|
226
|
-
} else {
|
|
227
|
-
if (updated_content) {
|
|
228
|
-
parsedStdout.updated_content = formatReadFileStdout({ file_lines: updated_content.split('\n') });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
marker[abolute_path] = true;
|
|
232
|
-
msg.output = JSON.stringify(parsedStdout, null, 2);
|
|
233
|
-
debugLog(`[ai_request] edit_file_range - new output length: ${msg.output.length}`);
|
|
234
|
-
}
|
|
235
|
-
else if (tool === 'edit_file_replace') {
|
|
236
|
-
const parsedStdout = JSON.parse(stdout);
|
|
237
|
-
if (!parsedStdout.absolute_file_path) continue;
|
|
238
|
-
let abolute_path = toAbsolutePath(parsedStdout.absolute_file_path)
|
|
239
|
-
const updated_content = parsedStdout.file_stats?.updated_content;
|
|
240
|
-
delete parsedStdout.absolute_file_path;
|
|
241
|
-
delete parsedStdout.file_stats;
|
|
242
|
-
if (marker[abolute_path]) {
|
|
243
|
-
parsedStdout.target_file_path;
|
|
244
|
-
} else {
|
|
245
|
-
if (updated_content) {
|
|
246
|
-
parsedStdout.updated_content = formatReadFileStdout({ file_lines: updated_content.split('\n') });
|
|
247
|
-
}
|
|
248
|
-
}
|
|
239
|
+
if (stdout?.operation_successful?.constructor === Boolean && !stdout.operation_successful) continue;
|
|
240
|
+
if (tool === 'edit_file_range' || tool === 'edit_file_replace') {
|
|
241
|
+
let abolute_path = toAbsolutePath(stdout.target_file_path);
|
|
242
|
+
const updated_content = stdout.file_stats?.updated_content;
|
|
243
|
+
delete stdout.file_stats;
|
|
244
|
+
if (!marker[abolute_path] && updated_content) stdout.updated_content = updated_content.split('\n');
|
|
249
245
|
marker[abolute_path] = true;
|
|
250
|
-
msg.output = JSON.stringify(parsedStdout, null, 2);
|
|
251
|
-
debugLog(`[ai_request] edit_file_replace - new output length: ${msg.output.length}`);
|
|
252
246
|
}
|
|
253
247
|
else if (tool === 'read_file') {
|
|
254
|
-
|
|
255
|
-
if (!parsedStdout.absolute_file_path) continue;
|
|
256
|
-
let abolute_path = toAbsolutePath(parsedStdout.absolute_file_path)
|
|
257
|
-
const content = parsedStdout.content;
|
|
248
|
+
let abolute_path = toAbsolutePath(stdout.target_file_path);
|
|
258
249
|
if (marker[abolute_path]) {
|
|
259
|
-
remove_call_id[call_id] = true
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
msg.output = content;
|
|
250
|
+
remove_call_id[call_id] = true;
|
|
251
|
+
stdout.file_content = '(Not displayed)';
|
|
263
252
|
}
|
|
264
253
|
marker[abolute_path] = true;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
msg.output
|
|
279
|
-
debugLog(`[ai_request] OTHER TOOL (${tool}) - AFTER: output length: ${msg.output.length} bytes (stdout: ${stdout?.length || 0}, stderr: ${stderr?.length || 0})`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
// debugLog(`[ai_request] OTHER TOOL (${tool}) - BEFORE: output is full JSON with stderr`);
|
|
257
|
+
// // run_python_code, bash 등의 경우 stdout과 stderr를 모두 포함
|
|
258
|
+
// let combinedOutput = '';
|
|
259
|
+
// if (stdout && stderr) {
|
|
260
|
+
// combinedOutput = `stdout:\n${stdout}\n\nstderr:\n${stderr}`;
|
|
261
|
+
// } else if (stdout) {
|
|
262
|
+
// combinedOutput = stdout;
|
|
263
|
+
// } else if (stderr) {
|
|
264
|
+
// combinedOutput = `stderr:\n${stderr}`;
|
|
265
|
+
// }
|
|
266
|
+
// msg.output = combinedOutput;
|
|
267
|
+
// debugLog(`[ai_request] OTHER TOOL (${tool}) - AFTER: output length: ${msg.output.length} bytes (stdout: ${stdout?.length || 0}, stderr: ${stderr?.length || 0})`);
|
|
280
268
|
}
|
|
281
269
|
}
|
|
282
270
|
|
|
283
271
|
debugLog(`[request] Processed ${processedCount} function_call_output entries`);
|
|
284
272
|
debugLog(`[request] Marked files for deduplication: ${Object.keys(marker).length}`);
|
|
285
273
|
|
|
286
|
-
|
|
274
|
+
}
|
|
275
|
+
if (true && payloadCopy.input && Array.isArray(payloadCopy.input)) {
|
|
276
|
+
const response_message_call_id = {};
|
|
287
277
|
for (let i = payloadCopy.input.length - 1; i >= 0; i--) {
|
|
288
278
|
const msg = payloadCopy.input[i];
|
|
289
279
|
const { type, call_id, name } = msg;
|
|
290
280
|
if (type !== 'function_call') continue;
|
|
291
281
|
if (name !== 'response_message') continue;
|
|
292
282
|
response_message_call_id[call_id] = true;
|
|
293
|
-
responseMessageCount++;
|
|
294
283
|
}
|
|
295
|
-
debugLog(`[request] Found ${responseMessageCount} response_message function calls`);
|
|
296
|
-
|
|
297
|
-
let simplifiedResponseMessages = 0;
|
|
298
284
|
for (let i = payloadCopy.input.length - 1; i >= 0; i--) {
|
|
299
285
|
const msg = payloadCopy.input[i];
|
|
300
286
|
const { type, call_id } = msg;
|
|
301
287
|
if (type !== 'function_call_output') continue;
|
|
302
288
|
if (!response_message_call_id[call_id]) continue;
|
|
303
|
-
msg.output =
|
|
304
|
-
|
|
289
|
+
msg.output = {
|
|
290
|
+
tool: 'response_message',
|
|
291
|
+
stdout: { message_displayed: true }
|
|
292
|
+
};
|
|
305
293
|
}
|
|
306
|
-
debugLog(`[request] Simplified ${simplifiedResponseMessages} response_message outputs`);
|
|
307
294
|
}
|
|
295
|
+
// Formatting
|
|
296
|
+
if (true && payloadCopy.input && Array.isArray(payloadCopy.input)) {
|
|
297
|
+
for (let i = payloadCopy.input.length - 1; i >= 0; i--) {
|
|
298
|
+
const msg = payloadCopy.input[i];
|
|
299
|
+
const { type, call_id, output } = msg;
|
|
300
|
+
|
|
301
|
+
if (type !== 'function_call_output') continue;
|
|
302
|
+
if (typeof output === 'string') continue;
|
|
303
|
+
const { tool, stdout, stderr, exit_code, original_result } = output;
|
|
304
|
+
if (false) await safeWriteFile('./__stdout/' + tool + '.' + (call_id) + '.json', JSON.stringify(stdout, null, 2));
|
|
305
|
+
|
|
306
|
+
// read_file
|
|
307
|
+
if (tool === 'read_file') {
|
|
308
|
+
if (!stdout.operation_successful) {
|
|
309
|
+
msg.output = stdout.error_message;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
if (stdout.file_content.includes('\n')) {
|
|
313
|
+
msg.output = formatReadFileStdout(stdout.file_content);
|
|
314
|
+
} else {
|
|
315
|
+
msg.output = (stdout.file_content);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// read_file_range
|
|
322
|
+
if (tool === 'read_file_range') {
|
|
323
|
+
if (!stdout.operation_successful) {
|
|
324
|
+
msg.output = stdout.error_message;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
msg.output = formatReadFileStdout(stdout.file_content, stdout.actual_range.start_line);
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// write_file
|
|
333
|
+
if (tool === 'write_file') {
|
|
334
|
+
if (!stdout.operation_successful) {
|
|
335
|
+
msg.output = stdout.error_message;
|
|
336
|
+
} else {
|
|
337
|
+
msg.output = `File written successfully: ${stdout.target_file_path}`;
|
|
338
|
+
}
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
308
341
|
|
|
342
|
+
// edit_file_replace
|
|
343
|
+
if (tool === 'edit_file_replace') {
|
|
344
|
+
if (!stdout.operation_successful) {
|
|
345
|
+
msg.output = stdout.error_message;
|
|
346
|
+
} else {
|
|
347
|
+
// const isString = stdout?.updated_content?.constructor === String;
|
|
348
|
+
const newObj = {
|
|
349
|
+
unified_diff_patch: stdout.diff_info.unified_diff_patch,
|
|
350
|
+
operation_successful: stdout.operation_successful,
|
|
351
|
+
updated_content: formatReadFileStdout(stdout?.updated_content?.join('\n')),
|
|
352
|
+
};
|
|
353
|
+
if (!(stdout?.updated_content)) delete newObj.updated_content;
|
|
354
|
+
// if (!isString) delete newObj.updated_content;
|
|
355
|
+
msg.output = JSON.stringify(newObj, null, 2);
|
|
356
|
+
}
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// edit_file_range
|
|
361
|
+
if (tool === 'edit_file_range') {
|
|
362
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ripgrep
|
|
367
|
+
if (tool === 'ripgrep') {
|
|
368
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// glob_search
|
|
373
|
+
if (tool === 'glob_search') {
|
|
374
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// fetch_web_page
|
|
379
|
+
if (tool === 'fetch_web_page') {
|
|
380
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// response_message
|
|
385
|
+
if (tool === 'response_message') {
|
|
386
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// todo_write
|
|
391
|
+
if (tool === 'todo_write') {
|
|
392
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// bash (from code_executer.js)
|
|
397
|
+
if (tool === 'bash') {
|
|
398
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// run_python_code (from code_executer.js)
|
|
403
|
+
if (tool === 'run_python_code') {
|
|
404
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Default case for unknown tools
|
|
409
|
+
msg.output = JSON.stringify(stdout, null, 2);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
309
412
|
debugLog(`[request] Payload transformation complete`);
|
|
310
413
|
let response;
|
|
311
414
|
let originalRequest;
|
|
@@ -424,37 +527,38 @@ export async function request(taskName, requestPayload) {
|
|
|
424
527
|
}
|
|
425
528
|
|
|
426
529
|
/**
|
|
427
|
-
*
|
|
530
|
+
* API 에러가 대화 trim 후 재시도로 복구 가능한지 확인합니다.
|
|
428
531
|
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
532
|
+
* 처리 대상:
|
|
533
|
+
* - context_length_exceeded: 컨텍스트 윈도우 초과
|
|
534
|
+
* - 400 error: 잘못된 요청 (대화가 너무 길어서 발생 가능)
|
|
431
535
|
*
|
|
432
536
|
* @param {Error | Object} error - 확인할 에러 객체
|
|
433
|
-
* @returns {boolean}
|
|
537
|
+
* @returns {boolean} trim 후 재시도 가능 여부
|
|
434
538
|
*
|
|
435
539
|
* @see https://platform.openai.com/docs/guides/error-codes
|
|
436
540
|
*/
|
|
437
|
-
export function
|
|
541
|
+
export function shouldRetryWithTrim(error) {
|
|
438
542
|
if (!error) return false;
|
|
439
543
|
|
|
440
544
|
// OpenAI SDK의 공식 에러 코드 확인
|
|
441
|
-
// error.code 또는 error.error.code에서 확인
|
|
442
545
|
const errorCode = error?.code || error?.error?.code;
|
|
443
546
|
|
|
444
547
|
if (errorCode === 'context_length_exceeded') {
|
|
445
|
-
debugLog('[
|
|
548
|
+
debugLog('[shouldRetryWithTrim] Detected: context_length_exceeded');
|
|
446
549
|
return true;
|
|
447
550
|
}
|
|
448
551
|
|
|
449
|
-
//
|
|
552
|
+
// 400 에러도 trim 후 재시도 대상
|
|
450
553
|
if (error?.status === 400 || error?.response?.status === 400) {
|
|
451
554
|
const errorType = error?.type || error?.error?.type;
|
|
452
555
|
const errorMessage = error?.message || error?.error?.message || '';
|
|
453
556
|
debugLog(
|
|
454
|
-
`[
|
|
455
|
-
`
|
|
557
|
+
`[shouldRetryWithTrim] Detected 400 error - ` +
|
|
558
|
+
`Type: ${errorType}, Code: ${errorCode}, ` +
|
|
456
559
|
`Message: ${errorMessage.substring(0, 200)}`
|
|
457
560
|
);
|
|
561
|
+
return true;
|
|
458
562
|
}
|
|
459
563
|
|
|
460
564
|
return false;
|