@vybestack/llxprt-code-core 0.7.0-nightly.251218.3619c584b → 0.7.0-nightly.251218.47baadc14
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/src/auth/types.d.ts +18 -18
- package/dist/src/core/geminiChat.d.ts +0 -1
- package/dist/src/core/geminiChat.js +64 -52
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/providers/ProviderManager.js +6 -15
- package/dist/src/providers/ProviderManager.js.map +1 -1
- package/dist/src/providers/anthropic/AnthropicProvider.js +0 -11
- package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.d.ts +7 -1
- package/dist/src/providers/openai/OpenAIProvider.js +189 -49
- package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
- package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js +2 -11
- package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js.map +1 -1
- package/dist/src/providers/openai-responses/buildResponsesInputFromContent.d.ts +1 -2
- package/dist/src/providers/openai-responses/buildResponsesInputFromContent.js +3 -8
- package/dist/src/providers/openai-responses/buildResponsesInputFromContent.js.map +1 -1
- package/dist/src/providers/utils/toolResponsePayload.js +47 -15
- package/dist/src/providers/utils/toolResponsePayload.js.map +1 -1
- package/dist/src/runtime/AgentRuntimeContext.d.ts +0 -1
- package/dist/src/runtime/runtimeAdapters.js +0 -7
- package/dist/src/runtime/runtimeAdapters.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,7 +25,6 @@ import * as net from 'net';
|
|
|
25
25
|
import { isKimiModel, isMistralModel, getToolIdStrategy, } from '../../tools/ToolIdStrategy.js';
|
|
26
26
|
import { BaseProvider, } from '../BaseProvider.js';
|
|
27
27
|
import { DebugLogger } from '../../debug/index.js';
|
|
28
|
-
import { flushRuntimeAuthScope, } from '../../auth/precedence.js';
|
|
29
28
|
import { ToolFormatter } from '../../tools/ToolFormatter.js';
|
|
30
29
|
import { convertToolsToOpenAI } from './schemaConverter.js';
|
|
31
30
|
import { GemmaToolCallParser } from '../../parsers/TextToolCallParser.js';
|
|
@@ -37,12 +36,15 @@ import { resolveRuntimeAuthToken } from '../utils/authToken.js';
|
|
|
37
36
|
import { filterOpenAIRequestParams } from './openaiRequestParams.js';
|
|
38
37
|
import { ensureJsonSafe } from '../../utils/unicodeUtils.js';
|
|
39
38
|
import { ToolCallPipeline } from './ToolCallPipeline.js';
|
|
40
|
-
import { buildToolResponsePayload } from '../utils/toolResponsePayload.js';
|
|
39
|
+
import { buildToolResponsePayload, EMPTY_TOOL_RESULT_PLACEHOLDER, } from '../utils/toolResponsePayload.js';
|
|
41
40
|
import { isLocalEndpoint } from '../utils/localEndpoint.js';
|
|
42
41
|
import { filterThinkingForContext, thinkingToReasoningField, extractThinkingBlocks, } from '../reasoning/reasoningUtils.js';
|
|
43
42
|
import { shouldDumpSDKContext, dumpSDKContext, } from '../utils/dumpSDKContext.js';
|
|
44
43
|
import { extractCacheMetrics } from '../utils/cacheMetricsExtractor.js';
|
|
44
|
+
const MAX_TOOL_RESPONSE_CHARS = 1024;
|
|
45
|
+
const MAX_TOOL_RESPONSE_RETRY_CHARS = 512;
|
|
45
46
|
const TOOL_ARGS_PREVIEW_LENGTH = 500;
|
|
47
|
+
const TEXTUAL_TOOL_REPLAY_MODELS = new Set(['openrouter/polaris-alpha']);
|
|
46
48
|
export class OpenAIProvider extends BaseProvider {
|
|
47
49
|
textToolParser = new GemmaToolCallParser();
|
|
48
50
|
toolCallPipeline = new ToolCallPipeline();
|
|
@@ -50,38 +52,6 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
50
52
|
getLogger() {
|
|
51
53
|
return new DebugLogger('llxprt:provider:openai');
|
|
52
54
|
}
|
|
53
|
-
async handleBucketFailoverOnPersistent429(options, logger) {
|
|
54
|
-
const failoverHandler = options.runtime?.config?.getBucketFailoverHandler();
|
|
55
|
-
if (!failoverHandler || !failoverHandler.isEnabled()) {
|
|
56
|
-
return { result: null };
|
|
57
|
-
}
|
|
58
|
-
logger.debug(() => 'Attempting bucket failover on persistent 429');
|
|
59
|
-
const success = await failoverHandler.tryFailover();
|
|
60
|
-
if (!success) {
|
|
61
|
-
logger.debug(() => 'Bucket failover failed - no more buckets available');
|
|
62
|
-
return { result: false };
|
|
63
|
-
}
|
|
64
|
-
const previousAuthToken = options.resolved.authToken;
|
|
65
|
-
try {
|
|
66
|
-
// Clear runtime-scoped auth cache so subsequent auth resolution can pick up the new bucket.
|
|
67
|
-
if (typeof options.runtime?.runtimeId === 'string') {
|
|
68
|
-
flushRuntimeAuthScope(options.runtime.runtimeId);
|
|
69
|
-
}
|
|
70
|
-
// Force re-resolution of the auth token after bucket failover.
|
|
71
|
-
options.resolved.authToken = '';
|
|
72
|
-
const refreshedAuthToken = await this.getAuthTokenForPrompt();
|
|
73
|
-
options.resolved.authToken = refreshedAuthToken;
|
|
74
|
-
// Rebuild client with fresh credentials from new bucket
|
|
75
|
-
const client = await this.getClient(options);
|
|
76
|
-
logger.debug(() => `Bucket failover successful, new bucket: ${failoverHandler.getCurrentBucket()}`);
|
|
77
|
-
return { result: true, client };
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
options.resolved.authToken = previousAuthToken;
|
|
81
|
-
logger.debug(() => `Bucket failover auth refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
-
return { result: false };
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
55
|
/**
|
|
86
56
|
* @plan:PLAN-20251023-STATELESS-HARDENING.P08
|
|
87
57
|
* @requirement:REQ-SP4-003
|
|
@@ -857,6 +827,39 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
857
827
|
}
|
|
858
828
|
return JSON.stringify({ value: parameters });
|
|
859
829
|
}
|
|
830
|
+
determineToolReplayMode(model) {
|
|
831
|
+
if (!model) {
|
|
832
|
+
return 'native';
|
|
833
|
+
}
|
|
834
|
+
const normalized = model.toLowerCase();
|
|
835
|
+
if (TEXTUAL_TOOL_REPLAY_MODELS.has(normalized)) {
|
|
836
|
+
return 'textual';
|
|
837
|
+
}
|
|
838
|
+
return 'native';
|
|
839
|
+
}
|
|
840
|
+
describeToolCallForText(block) {
|
|
841
|
+
const normalizedArgs = this.normalizeToolCallArguments(block.parameters);
|
|
842
|
+
const preview = normalizedArgs.length > MAX_TOOL_RESPONSE_CHARS
|
|
843
|
+
? `${normalizedArgs.slice(0, MAX_TOOL_RESPONSE_CHARS)}… [truncated ${normalizedArgs.length - MAX_TOOL_RESPONSE_CHARS} chars]`
|
|
844
|
+
: normalizedArgs;
|
|
845
|
+
const callId = block.id ? ` ${this.normalizeToOpenAIToolId(block.id)}` : '';
|
|
846
|
+
return `[TOOL CALL${callId ? ` ${callId}` : ''}] ${block.name ?? 'unknown_tool'} args=${preview}`;
|
|
847
|
+
}
|
|
848
|
+
describeToolResponseForText(block, config) {
|
|
849
|
+
const payload = buildToolResponsePayload(block, config);
|
|
850
|
+
const header = `[TOOL RESULT] ${payload.toolName ?? block.toolName ?? 'unknown_tool'} (${payload.status ?? 'unknown'})`;
|
|
851
|
+
const bodyParts = [];
|
|
852
|
+
if (payload.error) {
|
|
853
|
+
bodyParts.push(`error: ${payload.error}`);
|
|
854
|
+
}
|
|
855
|
+
if (payload.result && payload.result !== EMPTY_TOOL_RESULT_PLACEHOLDER) {
|
|
856
|
+
bodyParts.push(payload.result);
|
|
857
|
+
}
|
|
858
|
+
if (payload.limitMessage) {
|
|
859
|
+
bodyParts.push(payload.limitMessage);
|
|
860
|
+
}
|
|
861
|
+
return bodyParts.length > 0 ? `${header}\n${bodyParts.join('\n')}` : header;
|
|
862
|
+
}
|
|
860
863
|
buildToolResponseContent(block, config) {
|
|
861
864
|
const payload = buildToolResponsePayload(block, config);
|
|
862
865
|
return ensureJsonSafe(JSON.stringify(payload));
|
|
@@ -908,6 +911,107 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
908
911
|
});
|
|
909
912
|
return modified;
|
|
910
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Convert IContent array to OpenAI ChatCompletionMessageParam array
|
|
916
|
+
*/
|
|
917
|
+
convertToOpenAIMessages(contents, mode = 'native', config) {
|
|
918
|
+
const messages = [];
|
|
919
|
+
for (const content of contents) {
|
|
920
|
+
if (content.speaker === 'human') {
|
|
921
|
+
// Convert human messages to user messages
|
|
922
|
+
const textBlocks = content.blocks.filter((b) => b.type === 'text');
|
|
923
|
+
const text = textBlocks.map((b) => b.text).join('\n');
|
|
924
|
+
if (text) {
|
|
925
|
+
messages.push({
|
|
926
|
+
role: 'user',
|
|
927
|
+
content: text,
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
else if (content.speaker === 'ai') {
|
|
932
|
+
// Convert AI messages
|
|
933
|
+
const textBlocks = content.blocks.filter((b) => b.type === 'text');
|
|
934
|
+
const text = textBlocks.map((b) => b.text).join('\n');
|
|
935
|
+
const toolCalls = content.blocks.filter((b) => b.type === 'tool_call');
|
|
936
|
+
if (toolCalls.length > 0) {
|
|
937
|
+
if (mode === 'textual') {
|
|
938
|
+
const segments = [];
|
|
939
|
+
if (text) {
|
|
940
|
+
segments.push(text);
|
|
941
|
+
}
|
|
942
|
+
for (const tc of toolCalls) {
|
|
943
|
+
segments.push(this.describeToolCallForText(tc));
|
|
944
|
+
}
|
|
945
|
+
const combined = segments.join('\n\n').trim();
|
|
946
|
+
if (combined) {
|
|
947
|
+
messages.push({
|
|
948
|
+
role: 'assistant',
|
|
949
|
+
content: combined,
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
else {
|
|
954
|
+
// Assistant message with tool calls
|
|
955
|
+
// CRITICAL for Mistral API compatibility (#760):
|
|
956
|
+
// When tool_calls are present, we must NOT include a content property at all
|
|
957
|
+
// (not even null). Mistral's OpenAI-compatible API requires this.
|
|
958
|
+
// See: https://docs.mistral.ai/capabilities/function_calling
|
|
959
|
+
messages.push({
|
|
960
|
+
role: 'assistant',
|
|
961
|
+
tool_calls: toolCalls.map((tc) => ({
|
|
962
|
+
id: this.normalizeToOpenAIToolId(tc.id),
|
|
963
|
+
type: 'function',
|
|
964
|
+
function: {
|
|
965
|
+
name: tc.name,
|
|
966
|
+
arguments: this.normalizeToolCallArguments(tc.parameters),
|
|
967
|
+
},
|
|
968
|
+
})),
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
else if (textBlocks.length > 0) {
|
|
973
|
+
// Plain assistant message
|
|
974
|
+
messages.push({
|
|
975
|
+
role: 'assistant',
|
|
976
|
+
content: text,
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else if (content.speaker === 'tool') {
|
|
981
|
+
// Convert tool responses
|
|
982
|
+
const toolResponses = content.blocks.filter((b) => b.type === 'tool_response');
|
|
983
|
+
if (mode === 'textual') {
|
|
984
|
+
const segments = toolResponses
|
|
985
|
+
.map((tr) => this.describeToolResponseForText(tr, config))
|
|
986
|
+
.filter(Boolean);
|
|
987
|
+
if (segments.length > 0) {
|
|
988
|
+
messages.push({
|
|
989
|
+
role: 'user',
|
|
990
|
+
content: segments.join('\n\n'),
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
else {
|
|
995
|
+
for (const tr of toolResponses) {
|
|
996
|
+
// CRITICAL for Mistral API compatibility (#760):
|
|
997
|
+
// Tool messages must include a name field matching the function name.
|
|
998
|
+
// See: https://docs.mistral.ai/capabilities/function_calling
|
|
999
|
+
// Note: The OpenAI SDK types don't include name, but Mistral requires it.
|
|
1000
|
+
// We use a type assertion to add this required field.
|
|
1001
|
+
messages.push({
|
|
1002
|
+
role: 'tool',
|
|
1003
|
+
content: this.buildToolResponseContent(tr, config),
|
|
1004
|
+
tool_call_id: this.normalizeToOpenAIToolId(tr.callId),
|
|
1005
|
+
name: tr.toolName,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
// Validate tool message sequence to prevent API errors
|
|
1012
|
+
// This ensures each tool message has a corresponding tool_calls in previous message
|
|
1013
|
+
return this.validateToolMessageSequence(messages);
|
|
1014
|
+
}
|
|
911
1015
|
/**
|
|
912
1016
|
* Build messages with optional reasoning_content based on settings.
|
|
913
1017
|
*
|
|
@@ -1162,6 +1266,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
1162
1266
|
async *generateLegacyChatCompletionImpl(options, toolFormatter, client, logger) {
|
|
1163
1267
|
const { contents, tools, metadata } = options;
|
|
1164
1268
|
const model = options.resolved.model || this.getDefaultModel();
|
|
1269
|
+
const toolReplayMode = this.determineToolReplayMode(model);
|
|
1165
1270
|
const abortSignal = metadata?.abortSignal;
|
|
1166
1271
|
const ephemeralSettings = options.invocation?.ephemerals ?? {};
|
|
1167
1272
|
if (logger.enabled) {
|
|
@@ -1189,7 +1294,12 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
1189
1294
|
// Convert IContent to OpenAI messages format
|
|
1190
1295
|
// Use buildMessagesWithReasoning for reasoning-aware message building
|
|
1191
1296
|
// Pass detectedFormat so that Kimi K2 tool IDs are generated correctly
|
|
1192
|
-
const messages =
|
|
1297
|
+
const messages = toolReplayMode === 'native'
|
|
1298
|
+
? this.buildMessagesWithReasoning(contents, options, detectedFormat)
|
|
1299
|
+
: this.convertToOpenAIMessages(contents, toolReplayMode, options.config ?? options.runtime?.config ?? this.globalConfig);
|
|
1300
|
+
if (logger.enabled && toolReplayMode !== 'native') {
|
|
1301
|
+
logger.debug(() => `[OpenAIProvider] Using textual tool replay mode for model '${model}'`);
|
|
1302
|
+
}
|
|
1193
1303
|
// Convert Gemini format tools to OpenAI format using the schema converter
|
|
1194
1304
|
// This ensures required fields are always present in tool schemas
|
|
1195
1305
|
let formattedTools = convertToolsToOpenAI(tools);
|
|
@@ -1384,11 +1494,22 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
1384
1494
|
// Bucket failover callback for 429 errors
|
|
1385
1495
|
// @plan PLAN-20251213issue686 Bucket failover integration for OpenAIProvider
|
|
1386
1496
|
const onPersistent429Callback = async () => {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1497
|
+
// Try to get the bucket failover handler from runtime context config
|
|
1498
|
+
const failoverHandler = options.runtime?.config?.getBucketFailoverHandler();
|
|
1499
|
+
if (failoverHandler && failoverHandler.isEnabled()) {
|
|
1500
|
+
logger.debug(() => 'Attempting bucket failover on persistent 429');
|
|
1501
|
+
const success = await failoverHandler.tryFailover();
|
|
1502
|
+
if (success) {
|
|
1503
|
+
// Rebuild client with fresh credentials from new bucket
|
|
1504
|
+
failoverClient = await this.getClient(options);
|
|
1505
|
+
logger.debug(() => `Bucket failover successful, new bucket: ${failoverHandler.getCurrentBucket()}`);
|
|
1506
|
+
return true; // Signal retry with new bucket
|
|
1507
|
+
}
|
|
1508
|
+
logger.debug(() => 'Bucket failover failed - no more buckets available');
|
|
1509
|
+
return false; // No more buckets, stop retrying
|
|
1510
|
+
}
|
|
1511
|
+
// No bucket failover configured
|
|
1512
|
+
return null;
|
|
1392
1513
|
};
|
|
1393
1514
|
// Use failover client if bucket failover happened, otherwise use original client
|
|
1394
1515
|
const executeRequest = () => {
|
|
@@ -1447,7 +1568,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
1447
1568
|
}
|
|
1448
1569
|
if (!compressedOnce &&
|
|
1449
1570
|
this.shouldCompressToolMessages(error, logger) &&
|
|
1450
|
-
this.compressToolMessages(requestBody.messages,
|
|
1571
|
+
this.compressToolMessages(requestBody.messages, MAX_TOOL_RESPONSE_RETRY_CHARS, logger)) {
|
|
1451
1572
|
compressedOnce = true;
|
|
1452
1573
|
logger.warn(() => `[OpenAIProvider] Retrying request after compressing tool responses due to provider 400`);
|
|
1453
1574
|
continue;
|
|
@@ -2351,6 +2472,8 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
2351
2472
|
metadataKeys: Object.keys(metadata ?? {}),
|
|
2352
2473
|
});
|
|
2353
2474
|
}
|
|
2475
|
+
// Determine tool replay mode for model compatibility (e.g., polaris-alpha)
|
|
2476
|
+
const toolReplayMode = this.determineToolReplayMode(model);
|
|
2354
2477
|
// Detect the tool format to use BEFORE building messages
|
|
2355
2478
|
// This is needed so that Kimi K2 tool IDs can be generated in the correct format
|
|
2356
2479
|
const detectedFormat = this.detectToolFormat();
|
|
@@ -2363,7 +2486,13 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
2363
2486
|
// Convert IContent to OpenAI messages format
|
|
2364
2487
|
// Use buildMessagesWithReasoning for reasoning-aware message building
|
|
2365
2488
|
// Pass detectedFormat so that Kimi K2 tool IDs are generated correctly
|
|
2366
|
-
const messages =
|
|
2489
|
+
const messages = toolReplayMode === 'native'
|
|
2490
|
+
? this.buildMessagesWithReasoning(contents, options, detectedFormat)
|
|
2491
|
+
: this.convertToOpenAIMessages(contents, toolReplayMode, options.config ?? options.runtime?.config ?? this.globalConfig);
|
|
2492
|
+
// Log tool replay mode usage for debugging
|
|
2493
|
+
if (logger.enabled && toolReplayMode !== 'native') {
|
|
2494
|
+
logger.debug(() => `[OpenAIProvider] Using textual tool replay mode for model '${model}'`);
|
|
2495
|
+
}
|
|
2367
2496
|
// Convert Gemini format tools to OpenAI format using the schema converter
|
|
2368
2497
|
// This ensures required fields are always present in tool schemas
|
|
2369
2498
|
let formattedTools = convertToolsToOpenAI(tools);
|
|
@@ -2514,11 +2643,22 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
2514
2643
|
// Bucket failover callback for 429 errors - tools mode
|
|
2515
2644
|
// @plan PLAN-20251213issue686 Bucket failover integration for OpenAIProvider
|
|
2516
2645
|
const onPersistent429CallbackTools = async () => {
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2646
|
+
// Try to get the bucket failover handler from runtime context config
|
|
2647
|
+
const failoverHandler = options.runtime?.config?.getBucketFailoverHandler();
|
|
2648
|
+
if (failoverHandler && failoverHandler.isEnabled()) {
|
|
2649
|
+
logger.debug(() => 'Attempting bucket failover on persistent 429');
|
|
2650
|
+
const success = await failoverHandler.tryFailover();
|
|
2651
|
+
if (success) {
|
|
2652
|
+
// Rebuild client with fresh credentials from new bucket
|
|
2653
|
+
failoverClientTools = await this.getClient(options);
|
|
2654
|
+
logger.debug(() => `Bucket failover successful, new bucket: ${failoverHandler.getCurrentBucket()}`);
|
|
2655
|
+
return true; // Signal retry with new bucket
|
|
2656
|
+
}
|
|
2657
|
+
logger.debug(() => 'Bucket failover failed - no more buckets available');
|
|
2658
|
+
return false; // No more buckets, stop retrying
|
|
2659
|
+
}
|
|
2660
|
+
// No bucket failover configured
|
|
2661
|
+
return null;
|
|
2522
2662
|
};
|
|
2523
2663
|
if (streamingEnabled) {
|
|
2524
2664
|
// Streaming mode - use retry loop with compression support
|
|
@@ -2567,7 +2707,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
2567
2707
|
// Tool message compression logic
|
|
2568
2708
|
if (!compressedOnce &&
|
|
2569
2709
|
this.shouldCompressToolMessages(error, logger) &&
|
|
2570
|
-
this.compressToolMessages(requestBody.messages,
|
|
2710
|
+
this.compressToolMessages(requestBody.messages, MAX_TOOL_RESPONSE_RETRY_CHARS, logger)) {
|
|
2571
2711
|
compressedOnce = true;
|
|
2572
2712
|
logger.warn(() => `[OpenAIProvider] Retrying streaming request after compressing tool responses due to provider 400`);
|
|
2573
2713
|
continue;
|
|
@@ -2650,7 +2790,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
2650
2790
|
// Tool message compression logic
|
|
2651
2791
|
if (!compressedOnce &&
|
|
2652
2792
|
this.shouldCompressToolMessages(error, logger) &&
|
|
2653
|
-
this.compressToolMessages(requestBody.messages,
|
|
2793
|
+
this.compressToolMessages(requestBody.messages, MAX_TOOL_RESPONSE_RETRY_CHARS, logger)) {
|
|
2654
2794
|
compressedOnce = true;
|
|
2655
2795
|
logger.warn(() => `[OpenAIProvider] Retrying request after compressing tool responses due to provider 400`);
|
|
2656
2796
|
continue;
|