@shareai-lab/kode 1.0.71 → 1.0.75
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/README.md +160 -1
- package/README.zh-CN.md +65 -1
- package/cli.js +5 -10
- package/package.json +6 -2
- package/src/ProjectOnboarding.tsx +47 -29
- package/src/Tool.ts +33 -4
- package/src/commands/agents.tsx +3401 -0
- package/src/commands/help.tsx +2 -2
- package/src/commands/resume.tsx +2 -1
- package/src/commands/terminalSetup.ts +4 -4
- package/src/commands.ts +3 -0
- package/src/components/ApproveApiKey.tsx +1 -1
- package/src/components/Config.tsx +10 -6
- package/src/components/ConsoleOAuthFlow.tsx +5 -4
- package/src/components/CustomSelect/select-option.tsx +28 -2
- package/src/components/CustomSelect/select.tsx +14 -5
- package/src/components/CustomSelect/theme.ts +45 -0
- package/src/components/Help.tsx +4 -4
- package/src/components/InvalidConfigDialog.tsx +1 -1
- package/src/components/LogSelector.tsx +1 -1
- package/src/components/MCPServerApprovalDialog.tsx +1 -1
- package/src/components/Message.tsx +2 -0
- package/src/components/ModelListManager.tsx +10 -6
- package/src/components/ModelSelector.tsx +201 -23
- package/src/components/ModelStatusDisplay.tsx +7 -5
- package/src/components/PromptInput.tsx +146 -96
- package/src/components/SentryErrorBoundary.ts +9 -3
- package/src/components/StickerRequestForm.tsx +16 -0
- package/src/components/StructuredDiff.tsx +36 -29
- package/src/components/TextInput.tsx +13 -0
- package/src/components/TodoItem.tsx +47 -0
- package/src/components/TrustDialog.tsx +1 -1
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +5 -1
- package/src/components/messages/AssistantToolUseMessage.tsx +14 -4
- package/src/components/messages/TaskProgressMessage.tsx +32 -0
- package/src/components/messages/TaskToolMessage.tsx +58 -0
- package/src/components/permissions/FallbackPermissionRequest.tsx +2 -4
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -1
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +5 -3
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -1
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +5 -3
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +2 -4
- package/src/components/permissions/PermissionRequest.tsx +3 -5
- package/src/constants/macros.ts +2 -0
- package/src/constants/modelCapabilities.ts +179 -0
- package/src/constants/models.ts +90 -0
- package/src/constants/product.ts +1 -1
- package/src/context.ts +7 -7
- package/src/entrypoints/cli.tsx +23 -3
- package/src/entrypoints/mcp.ts +10 -10
- package/src/hooks/useCanUseTool.ts +1 -1
- package/src/hooks/useTextInput.ts +5 -2
- package/src/hooks/useUnifiedCompletion.ts +1405 -0
- package/src/messages.ts +1 -0
- package/src/query.ts +3 -0
- package/src/screens/ConfigureNpmPrefix.tsx +1 -1
- package/src/screens/Doctor.tsx +1 -1
- package/src/screens/REPL.tsx +11 -12
- package/src/services/adapters/base.ts +38 -0
- package/src/services/adapters/chatCompletions.ts +90 -0
- package/src/services/adapters/responsesAPI.ts +170 -0
- package/src/services/claude.ts +198 -62
- package/src/services/customCommands.ts +43 -22
- package/src/services/gpt5ConnectionTest.ts +340 -0
- package/src/services/mcpClient.ts +1 -1
- package/src/services/mentionProcessor.ts +273 -0
- package/src/services/modelAdapterFactory.ts +69 -0
- package/src/services/openai.ts +534 -14
- package/src/services/responseStateManager.ts +90 -0
- package/src/services/systemReminder.ts +113 -12
- package/src/test/testAdapters.ts +96 -0
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +120 -56
- package/src/tools/BashTool/BashTool.tsx +4 -31
- package/src/tools/BashTool/BashToolResultMessage.tsx +1 -1
- package/src/tools/BashTool/OutputLine.tsx +1 -0
- package/src/tools/FileEditTool/FileEditTool.tsx +4 -5
- package/src/tools/FileReadTool/FileReadTool.tsx +43 -10
- package/src/tools/MCPTool/MCPTool.tsx +2 -1
- package/src/tools/MultiEditTool/MultiEditTool.tsx +2 -2
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +15 -23
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +1 -1
- package/src/tools/TaskTool/TaskTool.tsx +170 -86
- package/src/tools/TaskTool/prompt.ts +61 -25
- package/src/tools/ThinkTool/ThinkTool.tsx +1 -3
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +65 -41
- package/src/tools/lsTool/lsTool.tsx +5 -2
- package/src/tools.ts +16 -16
- package/src/types/conversation.ts +51 -0
- package/src/types/logs.ts +58 -0
- package/src/types/modelCapabilities.ts +64 -0
- package/src/types/notebook.ts +87 -0
- package/src/utils/advancedFuzzyMatcher.ts +290 -0
- package/src/utils/agentLoader.ts +284 -0
- package/src/utils/ask.tsx +1 -0
- package/src/utils/commands.ts +1 -1
- package/src/utils/commonUnixCommands.ts +161 -0
- package/src/utils/config.ts +173 -2
- package/src/utils/conversationRecovery.ts +1 -0
- package/src/utils/debugLogger.ts +13 -13
- package/src/utils/exampleCommands.ts +1 -0
- package/src/utils/fuzzyMatcher.ts +328 -0
- package/src/utils/messages.tsx +6 -5
- package/src/utils/model.ts +120 -42
- package/src/utils/responseState.ts +23 -0
- package/src/utils/secureFile.ts +559 -0
- package/src/utils/terminal.ts +1 -0
- package/src/utils/theme.ts +11 -0
- package/src/hooks/useSlashCommandTypeahead.ts +0 -137
package/src/services/claude.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { AnthropicBedrock } from '@anthropic-ai/bedrock-sdk'
|
|
|
4
4
|
import { AnthropicVertex } from '@anthropic-ai/vertex-sdk'
|
|
5
5
|
import type { BetaUsage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
|
|
6
6
|
import chalk from 'chalk'
|
|
7
|
-
import { createHash, randomUUID } from 'crypto'
|
|
7
|
+
import { createHash, randomUUID, UUID } from 'crypto'
|
|
8
8
|
import 'dotenv/config'
|
|
9
9
|
|
|
10
10
|
import { addToTotalCost } from '../cost-tracker'
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getAnthropicApiKey,
|
|
16
16
|
getOrCreateUserID,
|
|
17
17
|
getGlobalConfig,
|
|
18
|
+
ModelProfile,
|
|
18
19
|
} from '../utils/config'
|
|
19
20
|
import { getProjectDocs } from '../context'
|
|
20
21
|
import { logError, SESSION_ID } from '../utils/log'
|
|
@@ -41,6 +42,10 @@ import {
|
|
|
41
42
|
import { getModelManager } from '../utils/model'
|
|
42
43
|
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
43
44
|
import type { BetaMessageStream } from '@anthropic-ai/sdk/lib/BetaMessageStream.mjs'
|
|
45
|
+
import { ModelAdapterFactory } from './modelAdapterFactory'
|
|
46
|
+
import { UnifiedRequestParams } from '../types/modelCapabilities'
|
|
47
|
+
import { responseStateManager, getConversationId } from './responseStateManager'
|
|
48
|
+
import type { ToolUseContext } from '../Tool'
|
|
44
49
|
import type {
|
|
45
50
|
Message as APIMessage,
|
|
46
51
|
MessageParam,
|
|
@@ -53,10 +58,15 @@ import OpenAI from 'openai'
|
|
|
53
58
|
import type { ChatCompletionStream } from 'openai/lib/ChatCompletionStream'
|
|
54
59
|
import { ContentBlock } from '@anthropic-ai/sdk/resources/messages/messages'
|
|
55
60
|
import { nanoid } from 'nanoid'
|
|
56
|
-
import {
|
|
61
|
+
import { getCompletionWithProfile, getGPT5CompletionWithProfile } from './openai'
|
|
57
62
|
import { getReasoningEffort } from '../utils/thinking'
|
|
58
63
|
import { generateSystemReminders } from './systemReminder'
|
|
59
64
|
|
|
65
|
+
// Helper function to check if a model is GPT-5
|
|
66
|
+
function isGPT5Model(modelName: string): boolean {
|
|
67
|
+
return modelName.startsWith('gpt-5')
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
// Helper function to extract model configuration for debug logging
|
|
61
71
|
function getModelConfigForDebug(model: string): {
|
|
62
72
|
modelName: string
|
|
@@ -71,7 +81,7 @@ function getModelConfigForDebug(model: string): {
|
|
|
71
81
|
const config = getGlobalConfig()
|
|
72
82
|
const modelManager = getModelManager()
|
|
73
83
|
|
|
74
|
-
|
|
84
|
+
|
|
75
85
|
const modelProfile = modelManager.getModel('main')
|
|
76
86
|
|
|
77
87
|
let apiKeyStatus: 'configured' | 'missing' | 'invalid' = 'missing'
|
|
@@ -79,7 +89,7 @@ function getModelConfigForDebug(model: string): {
|
|
|
79
89
|
let maxTokens: number | undefined
|
|
80
90
|
let reasoningEffort: string | undefined
|
|
81
91
|
|
|
82
|
-
|
|
92
|
+
|
|
83
93
|
if (modelProfile) {
|
|
84
94
|
apiKeyStatus = modelProfile.apiKey ? 'configured' : 'missing'
|
|
85
95
|
baseURL = modelProfile.baseURL
|
|
@@ -310,7 +320,7 @@ async function withRetry<T>(
|
|
|
310
320
|
) {
|
|
311
321
|
throw error
|
|
312
322
|
}
|
|
313
|
-
|
|
323
|
+
|
|
314
324
|
if (options.signal?.aborted) {
|
|
315
325
|
throw new Error('Request cancelled by user')
|
|
316
326
|
}
|
|
@@ -429,7 +439,7 @@ export async function verifyApiKey(
|
|
|
429
439
|
'Content-Type': 'application/json',
|
|
430
440
|
}
|
|
431
441
|
|
|
432
|
-
|
|
442
|
+
|
|
433
443
|
if (!baseURL) {
|
|
434
444
|
console.warn(
|
|
435
445
|
'No baseURL provided for non-Anthropic provider verification',
|
|
@@ -637,7 +647,7 @@ function messageReducer(
|
|
|
637
647
|
}
|
|
638
648
|
async function handleMessageStream(
|
|
639
649
|
stream: ChatCompletionStream,
|
|
640
|
-
signal?: AbortSignal,
|
|
650
|
+
signal?: AbortSignal,
|
|
641
651
|
): Promise<OpenAI.ChatCompletion> {
|
|
642
652
|
const streamStartTime = Date.now()
|
|
643
653
|
let ttftMs: number | undefined
|
|
@@ -653,7 +663,7 @@ async function handleMessageStream(
|
|
|
653
663
|
let id, model, created, object, usage
|
|
654
664
|
try {
|
|
655
665
|
for await (const chunk of stream) {
|
|
656
|
-
|
|
666
|
+
|
|
657
667
|
if (signal?.aborted) {
|
|
658
668
|
debugLogger.flow('OPENAI_STREAM_ABORTED', {
|
|
659
669
|
chunkCount,
|
|
@@ -756,7 +766,7 @@ async function handleMessageStream(
|
|
|
756
766
|
}
|
|
757
767
|
}
|
|
758
768
|
|
|
759
|
-
function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
|
|
769
|
+
function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion, tools?: Tool[]) {
|
|
760
770
|
let contentBlocks: ContentBlock[] = []
|
|
761
771
|
const message = response.choices?.[0]?.message
|
|
762
772
|
if (!message) {
|
|
@@ -775,12 +785,12 @@ function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
|
|
|
775
785
|
if (message?.tool_calls) {
|
|
776
786
|
for (const toolCall of message.tool_calls) {
|
|
777
787
|
const tool = toolCall.function
|
|
778
|
-
const toolName = tool
|
|
788
|
+
const toolName = tool?.name
|
|
779
789
|
let toolArgs = {}
|
|
780
790
|
try {
|
|
781
|
-
toolArgs = JSON.parse(tool.arguments)
|
|
791
|
+
toolArgs = tool?.arguments ? JSON.parse(tool.arguments) : {}
|
|
782
792
|
} catch (e) {
|
|
783
|
-
//
|
|
793
|
+
// Invalid JSON in tool arguments
|
|
784
794
|
}
|
|
785
795
|
|
|
786
796
|
contentBlocks.push({
|
|
@@ -825,6 +835,7 @@ function convertOpenAIResponseToAnthropic(response: OpenAI.ChatCompletion) {
|
|
|
825
835
|
usage: response.usage,
|
|
826
836
|
}
|
|
827
837
|
|
|
838
|
+
|
|
828
839
|
return finalMessage
|
|
829
840
|
}
|
|
830
841
|
|
|
@@ -1047,9 +1058,10 @@ export async function queryLLM(
|
|
|
1047
1058
|
safeMode: boolean
|
|
1048
1059
|
model: string | import('../utils/config').ModelPointerType
|
|
1049
1060
|
prependCLISysprompt: boolean
|
|
1061
|
+
toolUseContext?: ToolUseContext
|
|
1050
1062
|
},
|
|
1051
1063
|
): Promise<AssistantMessage> {
|
|
1052
|
-
|
|
1064
|
+
|
|
1053
1065
|
const modelManager = getModelManager()
|
|
1054
1066
|
const modelResolution = modelManager.resolveModelWithInfo(options.model)
|
|
1055
1067
|
|
|
@@ -1062,12 +1074,25 @@ export async function queryLLM(
|
|
|
1062
1074
|
const modelProfile = modelResolution.profile
|
|
1063
1075
|
const resolvedModel = modelProfile.modelName
|
|
1064
1076
|
|
|
1077
|
+
// Initialize response state if toolUseContext is provided
|
|
1078
|
+
const toolUseContext = options.toolUseContext
|
|
1079
|
+
if (toolUseContext && !toolUseContext.responseState) {
|
|
1080
|
+
const conversationId = getConversationId(toolUseContext.agentId, toolUseContext.messageId)
|
|
1081
|
+
const previousResponseId = responseStateManager.getPreviousResponseId(conversationId)
|
|
1082
|
+
|
|
1083
|
+
toolUseContext.responseState = {
|
|
1084
|
+
previousResponseId,
|
|
1085
|
+
conversationId
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1065
1089
|
debugLogger.api('MODEL_RESOLVED', {
|
|
1066
1090
|
inputParam: options.model,
|
|
1067
|
-
resolvedModelName: modelProfile.modelName,
|
|
1068
1091
|
resolvedModelName: resolvedModel,
|
|
1069
1092
|
provider: modelProfile.provider,
|
|
1070
1093
|
isPointer: ['main', 'task', 'reasoning', 'quick'].includes(options.model),
|
|
1094
|
+
hasResponseState: !!toolUseContext?.responseState,
|
|
1095
|
+
conversationId: toolUseContext?.responseState?.conversationId,
|
|
1071
1096
|
requestId: getCurrentRequest()?.id,
|
|
1072
1097
|
})
|
|
1073
1098
|
|
|
@@ -1078,7 +1103,7 @@ export async function queryLLM(
|
|
|
1078
1103
|
toolCount: tools.length,
|
|
1079
1104
|
model: resolvedModel,
|
|
1080
1105
|
originalModelParam: options.model,
|
|
1081
|
-
requestId:
|
|
1106
|
+
requestId: getCurrentRequest()?.id,
|
|
1082
1107
|
})
|
|
1083
1108
|
|
|
1084
1109
|
markPhase('LLM_CALL')
|
|
@@ -1091,7 +1116,7 @@ export async function queryLLM(
|
|
|
1091
1116
|
maxThinkingTokens,
|
|
1092
1117
|
tools,
|
|
1093
1118
|
signal,
|
|
1094
|
-
{ ...options, model: resolvedModel, modelProfile }, // Pass resolved ModelProfile
|
|
1119
|
+
{ ...options, model: resolvedModel, modelProfile, toolUseContext }, // Pass resolved ModelProfile and toolUseContext
|
|
1095
1120
|
),
|
|
1096
1121
|
)
|
|
1097
1122
|
|
|
@@ -1099,9 +1124,23 @@ export async function queryLLM(
|
|
|
1099
1124
|
costUSD: result.costUSD,
|
|
1100
1125
|
durationMs: result.durationMs,
|
|
1101
1126
|
responseLength: result.message.content?.length || 0,
|
|
1102
|
-
requestId:
|
|
1127
|
+
requestId: getCurrentRequest()?.id,
|
|
1103
1128
|
})
|
|
1104
1129
|
|
|
1130
|
+
// Update response state for GPT-5 Responses API continuation
|
|
1131
|
+
if (toolUseContext?.responseState?.conversationId && result.responseId) {
|
|
1132
|
+
responseStateManager.setPreviousResponseId(
|
|
1133
|
+
toolUseContext.responseState.conversationId,
|
|
1134
|
+
result.responseId
|
|
1135
|
+
)
|
|
1136
|
+
|
|
1137
|
+
debugLogger.api('RESPONSE_STATE_UPDATED', {
|
|
1138
|
+
conversationId: toolUseContext.responseState.conversationId,
|
|
1139
|
+
responseId: result.responseId,
|
|
1140
|
+
requestId: getCurrentRequest()?.id,
|
|
1141
|
+
})
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1105
1144
|
return result
|
|
1106
1145
|
} catch (error) {
|
|
1107
1146
|
// 使用错误诊断系统记录 LLM 相关错误
|
|
@@ -1131,6 +1170,24 @@ export function formatSystemPromptWithContext(
|
|
|
1131
1170
|
const enhancedPrompt = [...systemPrompt]
|
|
1132
1171
|
let reminders = ''
|
|
1133
1172
|
|
|
1173
|
+
// Step 0: Add GPT-5 Agent persistence support for coding tasks
|
|
1174
|
+
const modelManager = getModelManager()
|
|
1175
|
+
const modelProfile = modelManager.getModel('main')
|
|
1176
|
+
if (modelProfile && isGPT5Model(modelProfile.modelName)) {
|
|
1177
|
+
// Add coding-specific persistence instructions based on GPT-5 documentation
|
|
1178
|
+
const persistencePrompts = [
|
|
1179
|
+
"\n# Agent Persistence for Long-Running Coding Tasks",
|
|
1180
|
+
"You are working on a coding project that may involve multiple steps and iterations. Please maintain context and continuity throughout the session:",
|
|
1181
|
+
"- Remember architectural decisions and design patterns established earlier",
|
|
1182
|
+
"- Keep track of file modifications and their relationships",
|
|
1183
|
+
"- Maintain awareness of the overall project structure and goals",
|
|
1184
|
+
"- Reference previous implementations when making related changes",
|
|
1185
|
+
"- Ensure consistency with existing code style and conventions",
|
|
1186
|
+
"- Build incrementally on previous work rather than starting from scratch"
|
|
1187
|
+
]
|
|
1188
|
+
enhancedPrompt.push(...persistencePrompts)
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1134
1191
|
// 只有当上下文存在时才处理
|
|
1135
1192
|
const hasContext = Object.entries(context).length > 0
|
|
1136
1193
|
|
|
@@ -1185,12 +1242,14 @@ async function queryLLMWithPromptCaching(
|
|
|
1185
1242
|
model: string
|
|
1186
1243
|
prependCLISysprompt: boolean
|
|
1187
1244
|
modelProfile?: ModelProfile | null
|
|
1245
|
+
toolUseContext?: ToolUseContext
|
|
1188
1246
|
},
|
|
1189
1247
|
): Promise<AssistantMessage> {
|
|
1190
1248
|
const config = getGlobalConfig()
|
|
1191
1249
|
const modelManager = getModelManager()
|
|
1250
|
+
const toolUseContext = options.toolUseContext
|
|
1251
|
+
|
|
1192
1252
|
|
|
1193
|
-
// 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
|
|
1194
1253
|
const modelProfile = options.modelProfile || modelManager.getModel('main')
|
|
1195
1254
|
let provider: string
|
|
1196
1255
|
|
|
@@ -1212,7 +1271,7 @@ async function queryLLMWithPromptCaching(
|
|
|
1212
1271
|
maxThinkingTokens,
|
|
1213
1272
|
tools,
|
|
1214
1273
|
signal,
|
|
1215
|
-
{ ...options, modelProfile },
|
|
1274
|
+
{ ...options, modelProfile, toolUseContext },
|
|
1216
1275
|
)
|
|
1217
1276
|
}
|
|
1218
1277
|
|
|
@@ -1220,6 +1279,7 @@ async function queryLLMWithPromptCaching(
|
|
|
1220
1279
|
return queryOpenAI(messages, systemPrompt, maxThinkingTokens, tools, signal, {
|
|
1221
1280
|
...options,
|
|
1222
1281
|
modelProfile,
|
|
1282
|
+
toolUseContext,
|
|
1223
1283
|
})
|
|
1224
1284
|
}
|
|
1225
1285
|
|
|
@@ -1234,12 +1294,14 @@ async function queryAnthropicNative(
|
|
|
1234
1294
|
model: string
|
|
1235
1295
|
prependCLISysprompt: boolean
|
|
1236
1296
|
modelProfile?: ModelProfile | null
|
|
1297
|
+
toolUseContext?: ToolUseContext
|
|
1237
1298
|
},
|
|
1238
1299
|
): Promise<AssistantMessage> {
|
|
1239
1300
|
const config = getGlobalConfig()
|
|
1240
1301
|
const modelManager = getModelManager()
|
|
1302
|
+
const toolUseContext = options?.toolUseContext
|
|
1303
|
+
|
|
1241
1304
|
|
|
1242
|
-
// 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
|
|
1243
1305
|
const modelProfile = options?.modelProfile || modelManager.getModel('main')
|
|
1244
1306
|
let anthropic: Anthropic | AnthropicBedrock | AnthropicVertex
|
|
1245
1307
|
let model: string
|
|
@@ -1255,7 +1317,7 @@ async function queryAnthropicNative(
|
|
|
1255
1317
|
modelProfileBaseURL: modelProfile?.baseURL,
|
|
1256
1318
|
modelProfileApiKeyExists: !!modelProfile?.apiKey,
|
|
1257
1319
|
optionsModel: options?.model,
|
|
1258
|
-
requestId:
|
|
1320
|
+
requestId: getCurrentRequest()?.id,
|
|
1259
1321
|
})
|
|
1260
1322
|
|
|
1261
1323
|
if (modelProfile) {
|
|
@@ -1296,7 +1358,7 @@ async function queryAnthropicNative(
|
|
|
1296
1358
|
modelProfileExists: !!modelProfile,
|
|
1297
1359
|
modelProfileModelName: modelProfile?.modelName,
|
|
1298
1360
|
requestedModel: options?.model,
|
|
1299
|
-
requestId:
|
|
1361
|
+
requestId: getCurrentRequest()?.id,
|
|
1300
1362
|
}
|
|
1301
1363
|
debugLogger.error('ANTHROPIC_FALLBACK_ERROR', errorDetails)
|
|
1302
1364
|
throw new Error(
|
|
@@ -1329,13 +1391,16 @@ async function queryAnthropicNative(
|
|
|
1329
1391
|
}),
|
|
1330
1392
|
)
|
|
1331
1393
|
|
|
1332
|
-
const toolSchemas =
|
|
1333
|
-
tool =>
|
|
1394
|
+
const toolSchemas = await Promise.all(
|
|
1395
|
+
tools.map(async tool =>
|
|
1334
1396
|
({
|
|
1335
1397
|
name: tool.name,
|
|
1336
|
-
description: tool.description
|
|
1398
|
+
description: typeof tool.description === 'function'
|
|
1399
|
+
? await tool.description()
|
|
1400
|
+
: tool.description,
|
|
1337
1401
|
input_schema: zodToJsonSchema(tool.inputSchema),
|
|
1338
|
-
}) as Anthropic.Beta.
|
|
1402
|
+
}) as unknown as Anthropic.Beta.Messages.BetaTool,
|
|
1403
|
+
)
|
|
1339
1404
|
)
|
|
1340
1405
|
|
|
1341
1406
|
const anthropicMessages = addCacheBreakpoints(messages)
|
|
@@ -1368,7 +1433,7 @@ async function queryAnthropicNative(
|
|
|
1368
1433
|
}
|
|
1369
1434
|
|
|
1370
1435
|
if (maxThinkingTokens > 0) {
|
|
1371
|
-
params.extra_headers = {
|
|
1436
|
+
;(params as any).extra_headers = {
|
|
1372
1437
|
'anthropic-beta': 'max-tokens-3-5-sonnet-2024-07-15',
|
|
1373
1438
|
}
|
|
1374
1439
|
;(params as any).thinking = { max_tokens: maxThinkingTokens }
|
|
@@ -1395,7 +1460,7 @@ async function queryAnthropicNative(
|
|
|
1395
1460
|
})
|
|
1396
1461
|
|
|
1397
1462
|
if (config.stream) {
|
|
1398
|
-
|
|
1463
|
+
|
|
1399
1464
|
const stream = await anthropic.beta.messages.create({
|
|
1400
1465
|
...params,
|
|
1401
1466
|
stream: true,
|
|
@@ -1403,7 +1468,7 @@ async function queryAnthropicNative(
|
|
|
1403
1468
|
signal: signal // ← CRITICAL: Connect the AbortSignal to API call
|
|
1404
1469
|
})
|
|
1405
1470
|
|
|
1406
|
-
let finalResponse:
|
|
1471
|
+
let finalResponse: any | null = null
|
|
1407
1472
|
let messageStartEvent: any = null
|
|
1408
1473
|
const contentBlocks: any[] = []
|
|
1409
1474
|
let usage: any = null
|
|
@@ -1411,7 +1476,7 @@ async function queryAnthropicNative(
|
|
|
1411
1476
|
let stopSequence: string | null = null
|
|
1412
1477
|
|
|
1413
1478
|
for await (const event of stream) {
|
|
1414
|
-
|
|
1479
|
+
|
|
1415
1480
|
if (signal.aborted) {
|
|
1416
1481
|
debugLogger.flow('STREAM_ABORTED', {
|
|
1417
1482
|
eventType: event.type,
|
|
@@ -1485,12 +1550,12 @@ async function queryAnthropicNative(
|
|
|
1485
1550
|
modelProfileName: modelProfile?.name,
|
|
1486
1551
|
})
|
|
1487
1552
|
|
|
1488
|
-
|
|
1553
|
+
|
|
1489
1554
|
return await anthropic.beta.messages.create(params, {
|
|
1490
1555
|
signal: signal // ← CRITICAL: Connect the AbortSignal to API call
|
|
1491
1556
|
})
|
|
1492
1557
|
}
|
|
1493
|
-
}, { signal })
|
|
1558
|
+
}, { signal })
|
|
1494
1559
|
|
|
1495
1560
|
const ttftMs = start - Date.now()
|
|
1496
1561
|
const durationMs = Date.now() - startIncludingRetries
|
|
@@ -1525,7 +1590,6 @@ async function queryAnthropicNative(
|
|
|
1525
1590
|
},
|
|
1526
1591
|
type: 'assistant',
|
|
1527
1592
|
uuid: nanoid() as UUID,
|
|
1528
|
-
ttftMs,
|
|
1529
1593
|
durationMs,
|
|
1530
1594
|
costUSD: 0, // Will be calculated below
|
|
1531
1595
|
}
|
|
@@ -1552,7 +1616,6 @@ async function queryAnthropicNative(
|
|
|
1552
1616
|
end: Date.now(),
|
|
1553
1617
|
},
|
|
1554
1618
|
apiFormat: 'anthropic',
|
|
1555
|
-
modelConfig: getModelConfigForDebug(model),
|
|
1556
1619
|
})
|
|
1557
1620
|
|
|
1558
1621
|
// Calculate cost using native Anthropic usage data
|
|
@@ -1571,18 +1634,18 @@ async function queryAnthropicNative(
|
|
|
1571
1634
|
(getModelInputTokenCostUSD(model) * 0.1) // Cache reads are 10% of input cost
|
|
1572
1635
|
|
|
1573
1636
|
assistantMessage.costUSD = costUSD
|
|
1574
|
-
addToTotalCost(costUSD)
|
|
1637
|
+
addToTotalCost(costUSD, durationMs)
|
|
1575
1638
|
|
|
1576
1639
|
logEvent('api_response_anthropic_native', {
|
|
1577
1640
|
model,
|
|
1578
|
-
input_tokens: inputTokens,
|
|
1579
|
-
output_tokens: outputTokens,
|
|
1580
|
-
cache_creation_input_tokens: cacheCreationInputTokens,
|
|
1581
|
-
cache_read_input_tokens: cacheReadInputTokens,
|
|
1582
|
-
cost_usd: costUSD,
|
|
1583
|
-
duration_ms: durationMs,
|
|
1584
|
-
ttft_ms: ttftMs,
|
|
1585
|
-
attempt_number: attemptNumber,
|
|
1641
|
+
input_tokens: String(inputTokens),
|
|
1642
|
+
output_tokens: String(outputTokens),
|
|
1643
|
+
cache_creation_input_tokens: String(cacheCreationInputTokens),
|
|
1644
|
+
cache_read_input_tokens: String(cacheReadInputTokens),
|
|
1645
|
+
cost_usd: String(costUSD),
|
|
1646
|
+
duration_ms: String(durationMs),
|
|
1647
|
+
ttft_ms: String(ttftMs),
|
|
1648
|
+
attempt_number: String(attemptNumber),
|
|
1586
1649
|
})
|
|
1587
1650
|
|
|
1588
1651
|
return assistantMessage
|
|
@@ -1639,12 +1702,14 @@ async function queryOpenAI(
|
|
|
1639
1702
|
model: string
|
|
1640
1703
|
prependCLISysprompt: boolean
|
|
1641
1704
|
modelProfile?: ModelProfile | null
|
|
1705
|
+
toolUseContext?: ToolUseContext
|
|
1642
1706
|
},
|
|
1643
1707
|
): Promise<AssistantMessage> {
|
|
1644
1708
|
const config = getGlobalConfig()
|
|
1645
1709
|
const modelManager = getModelManager()
|
|
1710
|
+
const toolUseContext = options?.toolUseContext
|
|
1711
|
+
|
|
1646
1712
|
|
|
1647
|
-
// 🔧 Fix: 使用传入的ModelProfile,而不是硬编码的'main'指针
|
|
1648
1713
|
const modelProfile = options?.modelProfile || modelManager.getModel('main')
|
|
1649
1714
|
let model: string
|
|
1650
1715
|
|
|
@@ -1659,7 +1724,7 @@ async function queryOpenAI(
|
|
|
1659
1724
|
modelProfileBaseURL: modelProfile?.baseURL,
|
|
1660
1725
|
modelProfileApiKeyExists: !!modelProfile?.apiKey,
|
|
1661
1726
|
optionsModel: options?.model,
|
|
1662
|
-
requestId:
|
|
1727
|
+
requestId: getCurrentRequest()?.id,
|
|
1663
1728
|
})
|
|
1664
1729
|
|
|
1665
1730
|
if (modelProfile) {
|
|
@@ -1739,11 +1804,17 @@ async function queryOpenAI(
|
|
|
1739
1804
|
response = await withRetry(async attempt => {
|
|
1740
1805
|
attemptNumber = attempt
|
|
1741
1806
|
start = Date.now()
|
|
1807
|
+
// 🔥 GPT-5 Enhanced Parameter Construction
|
|
1808
|
+
const maxTokens = getMaxTokensFromProfile(modelProfile)
|
|
1809
|
+
const isGPT5 = isGPT5Model(model)
|
|
1810
|
+
|
|
1742
1811
|
const opts: OpenAI.ChatCompletionCreateParams = {
|
|
1743
1812
|
model,
|
|
1744
|
-
|
|
1813
|
+
|
|
1814
|
+
...(isGPT5 ? { max_completion_tokens: maxTokens } : { max_tokens: maxTokens }),
|
|
1745
1815
|
messages: [...openaiSystem, ...openaiMessages],
|
|
1746
|
-
|
|
1816
|
+
|
|
1817
|
+
temperature: isGPT5 ? 1 : MAIN_QUERY_TEMPERATURE,
|
|
1747
1818
|
}
|
|
1748
1819
|
if (config.stream) {
|
|
1749
1820
|
;(opts as OpenAI.ChatCompletionCreateParams).stream = true
|
|
@@ -1764,7 +1835,7 @@ async function queryOpenAI(
|
|
|
1764
1835
|
opts.reasoning_effort = reasoningEffort
|
|
1765
1836
|
}
|
|
1766
1837
|
|
|
1767
|
-
|
|
1838
|
+
|
|
1768
1839
|
if (modelProfile && modelProfile.modelName) {
|
|
1769
1840
|
debugLogger.api('USING_MODEL_PROFILE_PATH', {
|
|
1770
1841
|
modelProfileName: modelProfile.modelName,
|
|
@@ -1772,19 +1843,85 @@ async function queryOpenAI(
|
|
|
1772
1843
|
provider: modelProfile.provider,
|
|
1773
1844
|
baseURL: modelProfile.baseURL,
|
|
1774
1845
|
apiKeyExists: !!modelProfile.apiKey,
|
|
1775
|
-
requestId:
|
|
1846
|
+
requestId: getCurrentRequest()?.id,
|
|
1776
1847
|
})
|
|
1777
1848
|
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1849
|
+
// Enable new adapter system with environment variable
|
|
1850
|
+
const USE_NEW_ADAPTER_SYSTEM = process.env.USE_NEW_ADAPTERS !== 'false'
|
|
1851
|
+
|
|
1852
|
+
if (USE_NEW_ADAPTER_SYSTEM) {
|
|
1853
|
+
// New adapter system
|
|
1854
|
+
const adapter = ModelAdapterFactory.createAdapter(modelProfile)
|
|
1855
|
+
|
|
1856
|
+
// Build unified request parameters
|
|
1857
|
+
const unifiedParams: UnifiedRequestParams = {
|
|
1858
|
+
messages: openaiMessages,
|
|
1859
|
+
systemPrompt: openaiSystem.map(s => s.content as string),
|
|
1860
|
+
tools: tools,
|
|
1861
|
+
maxTokens: getMaxTokensFromProfile(modelProfile),
|
|
1862
|
+
stream: config.stream,
|
|
1863
|
+
reasoningEffort: reasoningEffort as any,
|
|
1864
|
+
temperature: isGPT5Model(model) ? 1 : MAIN_QUERY_TEMPERATURE,
|
|
1865
|
+
previousResponseId: toolUseContext?.responseState?.previousResponseId,
|
|
1866
|
+
verbosity: 'high' // High verbosity for coding tasks
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// Create request using adapter
|
|
1870
|
+
const request = adapter.createRequest(unifiedParams)
|
|
1871
|
+
|
|
1872
|
+
// Determine which API to use
|
|
1873
|
+
if (ModelAdapterFactory.shouldUseResponsesAPI(modelProfile)) {
|
|
1874
|
+
// Use Responses API for GPT-5 and similar models
|
|
1875
|
+
const { callGPT5ResponsesAPI } = await import('./openai')
|
|
1876
|
+
const response = await callGPT5ResponsesAPI(modelProfile, request, signal)
|
|
1877
|
+
const unifiedResponse = adapter.parseResponse(response)
|
|
1878
|
+
|
|
1879
|
+
// Convert unified response back to Anthropic format
|
|
1880
|
+
const apiMessage = {
|
|
1881
|
+
role: 'assistant' as const,
|
|
1882
|
+
content: unifiedResponse.content,
|
|
1883
|
+
tool_calls: unifiedResponse.toolCalls,
|
|
1884
|
+
usage: {
|
|
1885
|
+
prompt_tokens: unifiedResponse.usage.promptTokens,
|
|
1886
|
+
completion_tokens: unifiedResponse.usage.completionTokens,
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const assistantMsg: AssistantMessage = {
|
|
1890
|
+
type: 'assistant',
|
|
1891
|
+
message: apiMessage as any,
|
|
1892
|
+
costUSD: 0, // Will be calculated later
|
|
1893
|
+
durationMs: Date.now() - start,
|
|
1894
|
+
uuid: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}` as any,
|
|
1895
|
+
responseId: unifiedResponse.responseId // For state management
|
|
1896
|
+
}
|
|
1897
|
+
return assistantMsg
|
|
1898
|
+
} else {
|
|
1899
|
+
// Use existing Chat Completions flow
|
|
1900
|
+
const s = await getCompletionWithProfile(modelProfile, request, 0, 10, signal)
|
|
1901
|
+
let finalResponse
|
|
1902
|
+
if (config.stream) {
|
|
1903
|
+
finalResponse = await handleMessageStream(s as ChatCompletionStream, signal)
|
|
1904
|
+
} else {
|
|
1905
|
+
finalResponse = s
|
|
1906
|
+
}
|
|
1907
|
+
const r = convertOpenAIResponseToAnthropic(finalResponse, tools)
|
|
1908
|
+
return r
|
|
1909
|
+
}
|
|
1782
1910
|
} else {
|
|
1783
|
-
|
|
1911
|
+
// Legacy system (preserved for fallback)
|
|
1912
|
+
const completionFunction = isGPT5Model(modelProfile.modelName)
|
|
1913
|
+
? getGPT5CompletionWithProfile
|
|
1914
|
+
: getCompletionWithProfile
|
|
1915
|
+
const s = await completionFunction(modelProfile, opts, 0, 10, signal)
|
|
1916
|
+
let finalResponse
|
|
1917
|
+
if (opts.stream) {
|
|
1918
|
+
finalResponse = await handleMessageStream(s as ChatCompletionStream, signal)
|
|
1919
|
+
} else {
|
|
1920
|
+
finalResponse = s
|
|
1921
|
+
}
|
|
1922
|
+
const r = convertOpenAIResponseToAnthropic(finalResponse, tools)
|
|
1923
|
+
return r
|
|
1784
1924
|
}
|
|
1785
|
-
|
|
1786
|
-
const r = convertOpenAIResponseToAnthropic(finalResponse)
|
|
1787
|
-
return r
|
|
1788
1925
|
} else {
|
|
1789
1926
|
// 🚨 警告:ModelProfile不可用,使用旧逻辑路径
|
|
1790
1927
|
debugLogger.api('USING_LEGACY_PATH', {
|
|
@@ -1793,7 +1930,7 @@ async function queryOpenAI(
|
|
|
1793
1930
|
modelNameExists: !!modelProfile?.modelName,
|
|
1794
1931
|
fallbackModel: 'main',
|
|
1795
1932
|
actualModel: model,
|
|
1796
|
-
requestId:
|
|
1933
|
+
requestId: getCurrentRequest()?.id,
|
|
1797
1934
|
})
|
|
1798
1935
|
|
|
1799
1936
|
// 🚨 FALLBACK: 没有有效的ModelProfile时,应该抛出错误而不是使用遗留系统
|
|
@@ -1802,14 +1939,14 @@ async function queryOpenAI(
|
|
|
1802
1939
|
modelProfileId: modelProfile?.modelName,
|
|
1803
1940
|
modelNameExists: !!modelProfile?.modelName,
|
|
1804
1941
|
requestedModel: model,
|
|
1805
|
-
requestId:
|
|
1942
|
+
requestId: getCurrentRequest()?.id,
|
|
1806
1943
|
}
|
|
1807
1944
|
debugLogger.error('NO_VALID_MODEL_PROFILE', errorDetails)
|
|
1808
1945
|
throw new Error(
|
|
1809
1946
|
`No valid ModelProfile available for model: ${model}. Please configure model through /model command. Debug: ${JSON.stringify(errorDetails)}`,
|
|
1810
1947
|
)
|
|
1811
1948
|
}
|
|
1812
|
-
}, { signal })
|
|
1949
|
+
}, { signal })
|
|
1813
1950
|
} catch (error) {
|
|
1814
1951
|
logError(error)
|
|
1815
1952
|
return getAssistantMessageFromError(error)
|
|
@@ -1847,7 +1984,6 @@ async function queryOpenAI(
|
|
|
1847
1984
|
end: Date.now(),
|
|
1848
1985
|
},
|
|
1849
1986
|
apiFormat: 'openai',
|
|
1850
|
-
modelConfig: getModelConfigForDebug(model),
|
|
1851
1987
|
})
|
|
1852
1988
|
|
|
1853
1989
|
return {
|
|
@@ -1943,5 +2079,5 @@ export async function queryQuick({
|
|
|
1943
2079
|
},
|
|
1944
2080
|
] as (UserMessage | AssistantMessage)[]
|
|
1945
2081
|
|
|
1946
|
-
return queryModel('quick', messages, systemPrompt,
|
|
2082
|
+
return queryModel('quick', messages, systemPrompt, signal)
|
|
1947
2083
|
}
|