byterover-cli 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -6
- package/dist/core/domain/cipher/errors/file-system-error.d.ts +11 -0
- package/dist/core/domain/cipher/errors/file-system-error.js +17 -0
- package/dist/core/domain/cipher/file-system/types.d.ts +40 -6
- package/dist/core/domain/cipher/process/types.d.ts +1 -1
- package/dist/core/domain/entities/agent.d.ts +1 -1
- package/dist/core/domain/entities/agent.js +5 -0
- package/dist/core/domain/entities/provider-config.d.ts +92 -0
- package/dist/core/domain/entities/provider-config.js +181 -0
- package/dist/core/domain/entities/provider-registry.d.ts +55 -0
- package/dist/core/domain/entities/provider-registry.js +74 -0
- package/dist/core/interfaces/cipher/cipher-services.d.ts +0 -3
- package/dist/core/interfaces/cipher/i-content-generator.d.ts +30 -0
- package/dist/core/interfaces/cipher/i-content-generator.js +12 -1
- package/dist/core/interfaces/cipher/index.d.ts +0 -2
- package/dist/core/interfaces/cipher/message-factory.d.ts +4 -1
- package/dist/core/interfaces/cipher/message-factory.js +5 -0
- package/dist/core/interfaces/cipher/message-types.d.ts +19 -1
- package/dist/core/interfaces/i-provider-config-store.d.ts +88 -0
- package/dist/core/interfaces/i-provider-keychain-store.d.ts +33 -0
- package/dist/infra/cipher/file-system/binary-utils.d.ts +15 -2
- package/dist/infra/cipher/file-system/binary-utils.js +26 -3
- package/dist/infra/cipher/file-system/file-system-service.d.ts +9 -0
- package/dist/infra/cipher/file-system/file-system-service.js +96 -13
- package/dist/infra/cipher/file-system/pdf-extractor.d.ts +100 -0
- package/dist/infra/cipher/file-system/pdf-extractor.js +226 -0
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +40 -0
- package/dist/infra/cipher/http/internal-llm-http-service.js +152 -2
- package/dist/infra/cipher/llm/formatters/gemini-formatter.js +8 -1
- package/dist/infra/cipher/llm/generators/byterover-content-generator.d.ts +2 -3
- package/dist/infra/cipher/llm/generators/byterover-content-generator.js +20 -11
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.d.ts +1 -0
- package/dist/infra/cipher/llm/generators/openrouter-content-generator.js +26 -0
- package/dist/infra/cipher/llm/internal-llm-service.d.ts +13 -0
- package/dist/infra/cipher/llm/internal-llm-service.js +75 -4
- package/dist/infra/cipher/llm/model-capabilities.d.ts +74 -0
- package/dist/infra/cipher/llm/model-capabilities.js +157 -0
- package/dist/infra/cipher/llm/openrouter-llm-service.d.ts +35 -1
- package/dist/infra/cipher/llm/openrouter-llm-service.js +216 -28
- package/dist/infra/cipher/llm/stream-processor.d.ts +22 -2
- package/dist/infra/cipher/llm/stream-processor.js +78 -4
- package/dist/infra/cipher/llm/thought-parser.d.ts +1 -1
- package/dist/infra/cipher/llm/thought-parser.js +5 -5
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.d.ts +49 -0
- package/dist/infra/cipher/llm/transformers/openrouter-stream-transformer.js +272 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.d.ts +71 -0
- package/dist/infra/cipher/llm/transformers/reasoning-extractor.js +253 -0
- package/dist/infra/cipher/process/process-service.js +1 -1
- package/dist/infra/cipher/session/chat-session.d.ts +2 -0
- package/dist/infra/cipher/session/chat-session.js +13 -2
- package/dist/infra/cipher/storage/message-storage-service.js +4 -0
- package/dist/infra/cipher/tools/implementations/bash-exec-tool.js +3 -3
- package/dist/infra/cipher/tools/implementations/read-file-tool.js +24 -4
- package/dist/infra/cipher/tools/implementations/task-tool.js +1 -1
- package/dist/infra/connectors/rules/rules-connector-config.d.ts +4 -0
- package/dist/infra/connectors/rules/rules-connector-config.js +4 -0
- package/dist/infra/http/openrouter-api-client.d.ts +148 -0
- package/dist/infra/http/openrouter-api-client.js +161 -0
- package/dist/infra/mcp/tools/brv-curate-tool.d.ts +10 -4
- package/dist/infra/mcp/tools/brv-curate-tool.js +9 -4
- package/dist/infra/mcp/tools/task-result-waiter.js +9 -1
- package/dist/infra/process/agent-worker.js +178 -70
- package/dist/infra/process/transport-handlers.d.ts +25 -4
- package/dist/infra/process/transport-handlers.js +57 -10
- package/dist/infra/repl/commands/connectors-command.js +2 -2
- package/dist/infra/repl/commands/index.js +5 -0
- package/dist/infra/repl/commands/model-command.d.ts +13 -0
- package/dist/infra/repl/commands/model-command.js +212 -0
- package/dist/infra/repl/commands/provider-command.d.ts +13 -0
- package/dist/infra/repl/commands/provider-command.js +181 -0
- package/dist/infra/repl/commands/space/switch-command.js +0 -2
- package/dist/infra/repl/transport-client-helper.js +6 -2
- package/dist/infra/storage/file-provider-config-store.d.ts +83 -0
- package/dist/infra/storage/file-provider-config-store.js +157 -0
- package/dist/infra/storage/provider-keychain-store.d.ts +37 -0
- package/dist/infra/storage/provider-keychain-store.js +75 -0
- package/dist/infra/transport/socket-io-transport-client.d.ts +20 -0
- package/dist/infra/transport/socket-io-transport-client.js +88 -1
- package/dist/infra/usecase/curate-use-case.js +10 -4
- package/dist/infra/usecase/space-switch-use-case.d.ts +0 -10
- package/dist/infra/usecase/space-switch-use-case.js +7 -37
- package/dist/oclif/hooks/init/welcome.js +4 -17
- package/dist/resources/prompts/curate.yml +1 -0
- package/dist/resources/tools/bash_exec.txt +1 -1
- package/dist/resources/tools/read_file.txt +5 -2
- package/dist/tui/components/api-key-dialog.d.ts +39 -0
- package/dist/tui/components/api-key-dialog.js +94 -0
- package/dist/tui/components/execution/execution-changes.d.ts +3 -1
- package/dist/tui/components/execution/execution-changes.js +4 -4
- package/dist/tui/components/execution/execution-content.d.ts +1 -1
- package/dist/tui/components/execution/execution-content.js +4 -12
- package/dist/tui/components/execution/execution-input.js +1 -1
- package/dist/tui/components/execution/execution-progress.d.ts +10 -13
- package/dist/tui/components/execution/execution-progress.js +70 -17
- package/dist/tui/components/execution/execution-reasoning.d.ts +16 -0
- package/dist/tui/components/execution/execution-reasoning.js +34 -0
- package/dist/tui/components/execution/execution-tool.d.ts +23 -0
- package/dist/tui/components/execution/execution-tool.js +125 -0
- package/dist/tui/components/execution/expanded-log-view.js +3 -3
- package/dist/tui/components/execution/log-item.d.ts +2 -0
- package/dist/tui/components/execution/log-item.js +6 -4
- package/dist/tui/components/index.d.ts +2 -0
- package/dist/tui/components/index.js +2 -0
- package/dist/tui/components/inline-prompts/inline-select.js +3 -2
- package/dist/tui/components/model-dialog.d.ts +63 -0
- package/dist/tui/components/model-dialog.js +89 -0
- package/dist/tui/components/onboarding/onboarding-flow.js +8 -2
- package/dist/tui/components/provider-dialog.d.ts +27 -0
- package/dist/tui/components/provider-dialog.js +31 -0
- package/dist/tui/components/reasoning-text.d.ts +26 -0
- package/dist/tui/components/reasoning-text.js +49 -0
- package/dist/tui/components/selectable-list.d.ts +54 -0
- package/dist/tui/components/selectable-list.js +180 -0
- package/dist/tui/components/streaming-text.d.ts +30 -0
- package/dist/tui/components/streaming-text.js +52 -0
- package/dist/tui/contexts/tasks-context.d.ts +15 -0
- package/dist/tui/contexts/tasks-context.js +224 -40
- package/dist/tui/contexts/theme-context.d.ts +1 -0
- package/dist/tui/contexts/theme-context.js +3 -2
- package/dist/tui/hooks/use-activity-logs.js +7 -1
- package/dist/tui/types/messages.d.ts +32 -5
- package/dist/tui/utils/index.d.ts +1 -1
- package/dist/tui/utils/index.js +1 -1
- package/dist/tui/utils/log.d.ts +0 -9
- package/dist/tui/utils/log.js +2 -53
- package/dist/tui/views/command-view.js +4 -1
- package/dist/utils/file-validator.js +8 -4
- package/oclif.manifest.json +1 -54
- package/package.json +4 -2
- package/dist/core/interfaces/cipher/i-coding-agent-log-parser.d.ts +0 -20
- package/dist/core/interfaces/cipher/i-coding-agent-log-watcher.d.ts +0 -31
- package/dist/core/interfaces/i-file-watcher-service.d.ts +0 -41
- package/dist/core/interfaces/i-file-watcher-service.js +0 -1
- package/dist/core/interfaces/parser/i-clean-parser-service.d.ts +0 -18
- package/dist/core/interfaces/parser/i-clean-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-raw-parser-service.d.ts +0 -17
- package/dist/core/interfaces/parser/i-raw-parser-service.js +0 -1
- package/dist/core/interfaces/parser/i-session-normalizer.d.ts +0 -56
- package/dist/core/interfaces/parser/i-session-normalizer.js +0 -1
- package/dist/infra/cipher/parsers/coding-agent-log-parser.d.ts +0 -24
- package/dist/infra/cipher/parsers/coding-agent-log-parser.js +0 -51
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.d.ts +0 -14
- package/dist/infra/cipher/watcher/coding-agent-log-watcher.js +0 -55
- package/dist/infra/parsers/clean/clean-claude-service.d.ts +0 -111
- package/dist/infra/parsers/clean/clean-claude-service.js +0 -271
- package/dist/infra/parsers/clean/clean-codex-service.d.ts +0 -231
- package/dist/infra/parsers/clean/clean-codex-service.js +0 -534
- package/dist/infra/parsers/clean/clean-copilot-service.d.ts +0 -255
- package/dist/infra/parsers/clean/clean-copilot-service.js +0 -729
- package/dist/infra/parsers/clean/clean-cursor-service.d.ts +0 -161
- package/dist/infra/parsers/clean/clean-cursor-service.js +0 -432
- package/dist/infra/parsers/clean/clean-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/clean/clean-parser-service-factory.js +0 -80
- package/dist/infra/parsers/clean/shared.d.ts +0 -84
- package/dist/infra/parsers/clean/shared.js +0 -273
- package/dist/infra/parsers/raw/raw-claude-service.d.ts +0 -195
- package/dist/infra/parsers/raw/raw-claude-service.js +0 -548
- package/dist/infra/parsers/raw/raw-codex-service.d.ts +0 -313
- package/dist/infra/parsers/raw/raw-codex-service.js +0 -782
- package/dist/infra/parsers/raw/raw-copilot-service.d.ts +0 -196
- package/dist/infra/parsers/raw/raw-copilot-service.js +0 -558
- package/dist/infra/parsers/raw/raw-cursor-service.d.ts +0 -316
- package/dist/infra/parsers/raw/raw-cursor-service.js +0 -818
- package/dist/infra/parsers/raw/raw-parser-service-factory.d.ts +0 -54
- package/dist/infra/parsers/raw/raw-parser-service-factory.js +0 -81
- package/dist/infra/watcher/file-watcher-service.d.ts +0 -10
- package/dist/infra/watcher/file-watcher-service.js +0 -81
- package/dist/oclif/commands/watch.d.ts +0 -25
- package/dist/oclif/commands/watch.js +0 -175
- /package/dist/core/interfaces/{cipher/i-coding-agent-log-parser.js → i-provider-config-store.js} +0 -0
- /package/dist/core/interfaces/{cipher/i-coding-agent-log-watcher.js → i-provider-keychain-store.js} +0 -0
|
@@ -4,7 +4,10 @@ import { NoOpLogger } from '../../../core/interfaces/cipher/i-logger.js';
|
|
|
4
4
|
import { getErrorMessage } from '../../../utils/error-helpers.js';
|
|
5
5
|
import { ContextManager } from './context/context-manager.js';
|
|
6
6
|
import { OpenRouterMessageFormatter } from './formatters/openrouter-formatter.js';
|
|
7
|
+
import { OpenRouterContentGenerator } from './generators/openrouter-content-generator.js';
|
|
8
|
+
import { createIdGenerator, StreamProcessor } from './stream-processor.js';
|
|
7
9
|
import { OpenRouterTokenizer } from './tokenizers/openrouter-tokenizer.js';
|
|
10
|
+
import { transformGenerateContentChunksToStreamEvents } from './transformers/openrouter-stream-transformer.js';
|
|
8
11
|
/**
|
|
9
12
|
* OpenRouter LLM Service.
|
|
10
13
|
*
|
|
@@ -25,11 +28,14 @@ import { OpenRouterTokenizer } from './tokenizers/openrouter-tokenizer.js';
|
|
|
25
28
|
export class OpenRouterLLMService {
|
|
26
29
|
client;
|
|
27
30
|
config;
|
|
31
|
+
contentGenerator;
|
|
28
32
|
contextManager;
|
|
29
33
|
formatter;
|
|
30
34
|
logger;
|
|
31
35
|
memoryManager;
|
|
32
36
|
sessionEventBus;
|
|
37
|
+
sessionId;
|
|
38
|
+
streamProcessor;
|
|
33
39
|
systemPromptManager;
|
|
34
40
|
tokenizer;
|
|
35
41
|
toolManager;
|
|
@@ -65,6 +71,8 @@ export class OpenRouterLLMService {
|
|
|
65
71
|
timeout: config.timeout,
|
|
66
72
|
verbose: config.verbose,
|
|
67
73
|
};
|
|
74
|
+
// Store sessionId for streaming context
|
|
75
|
+
this.sessionId = sessionId;
|
|
68
76
|
// Initialize OpenAI client with OpenRouter base URL
|
|
69
77
|
this.client = new OpenAI({
|
|
70
78
|
apiKey: this.config.apiKey,
|
|
@@ -78,6 +86,19 @@ export class OpenRouterLLMService {
|
|
|
78
86
|
// Initialize formatter and tokenizer
|
|
79
87
|
this.formatter = new OpenRouterMessageFormatter();
|
|
80
88
|
this.tokenizer = new OpenRouterTokenizer();
|
|
89
|
+
// Initialize content generator for streaming support
|
|
90
|
+
this.contentGenerator = new OpenRouterContentGenerator({
|
|
91
|
+
apiKey: this.config.apiKey,
|
|
92
|
+
baseUrl: this.config.baseUrl,
|
|
93
|
+
httpReferer: this.config.httpReferer,
|
|
94
|
+
maxTokens: this.config.maxTokens,
|
|
95
|
+
model: this.config.model,
|
|
96
|
+
siteName: this.config.siteName,
|
|
97
|
+
temperature: this.config.temperature,
|
|
98
|
+
timeout: this.config.timeout,
|
|
99
|
+
});
|
|
100
|
+
// Initialize stream processor for handling streaming events
|
|
101
|
+
this.streamProcessor = new StreamProcessor();
|
|
81
102
|
// Initialize context manager with optional history storage
|
|
82
103
|
this.contextManager = new ContextManager({
|
|
83
104
|
formatter: this.formatter,
|
|
@@ -101,18 +122,23 @@ export class OpenRouterLLMService {
|
|
|
101
122
|
* @param options.signal - Optional abort signal for cancellation
|
|
102
123
|
* @param options.imageData - Optional image data
|
|
103
124
|
* @param options.fileData - Optional file data
|
|
104
|
-
* @param options.stream - Whether to stream response (
|
|
125
|
+
* @param options.stream - Whether to stream response (emits llmservice:chunk events)
|
|
105
126
|
* @param options.executionContext - Optional execution context (for JSON input mode, etc.)
|
|
106
127
|
* @param options.taskId - Task ID for billing tracking
|
|
107
128
|
* @returns Final assistant response
|
|
108
129
|
*/
|
|
109
130
|
async completeTask(textInput, options) {
|
|
110
|
-
// Extract options with defaults
|
|
111
|
-
const { executionContext, fileData, imageData, signal } = options ?? {};
|
|
131
|
+
// Extract options with defaults - include taskId for concurrent task isolation
|
|
132
|
+
const { executionContext, fileData, imageData, signal, stream, taskId } = options ?? {};
|
|
112
133
|
// Add user message to context
|
|
113
134
|
await this.contextManager.addUserMessage(textInput, imageData, fileData);
|
|
114
135
|
// Get filtered tools based on command type (e.g., only read-only tools for 'query')
|
|
115
136
|
const toolSet = this.toolManager.getToolsForCommand(executionContext?.commandType);
|
|
137
|
+
// Route to streaming or non-streaming execution
|
|
138
|
+
if (stream) {
|
|
139
|
+
return this.completeTaskStreaming(toolSet, executionContext, signal, taskId);
|
|
140
|
+
}
|
|
141
|
+
// Non-streaming path: Build tools array for OpenAI format
|
|
116
142
|
const tools = Object.entries(toolSet).map(([name, schema]) => ({
|
|
117
143
|
function: {
|
|
118
144
|
description: schema.description ?? '',
|
|
@@ -130,14 +156,14 @@ export class OpenRouterLLMService {
|
|
|
130
156
|
}
|
|
131
157
|
try {
|
|
132
158
|
// eslint-disable-next-line no-await-in-loop -- Sequential iterations required for agentic loop
|
|
133
|
-
const result = await this.executeAgenticIteration(iterationCount, tools, executionContext);
|
|
159
|
+
const result = await this.executeAgenticIteration(iterationCount, tools, executionContext, taskId);
|
|
134
160
|
if (result !== null) {
|
|
135
161
|
return result;
|
|
136
162
|
}
|
|
137
163
|
iterationCount++;
|
|
138
164
|
}
|
|
139
165
|
catch (error) {
|
|
140
|
-
this.handleLLMError(error);
|
|
166
|
+
this.handleLLMError(error, taskId);
|
|
141
167
|
}
|
|
142
168
|
}
|
|
143
169
|
// Max iterations exceeded
|
|
@@ -204,15 +230,65 @@ export class OpenRouterLLMService {
|
|
|
204
230
|
}
|
|
205
231
|
return lastMessage;
|
|
206
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Complete a task using streaming mode.
|
|
235
|
+
*
|
|
236
|
+
* Emits real-time llmservice:chunk events as tokens arrive.
|
|
237
|
+
* Follows the OpenCode pattern of delta-based streaming.
|
|
238
|
+
*
|
|
239
|
+
* @param toolSet - Available tools for the task
|
|
240
|
+
* @param executionContext - Optional execution context
|
|
241
|
+
* @param signal - Optional abort signal for cancellation
|
|
242
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
243
|
+
* @returns Final accumulated response
|
|
244
|
+
*/
|
|
245
|
+
async completeTaskStreaming(toolSet, executionContext, signal, taskId) {
|
|
246
|
+
let iterationCount = 0;
|
|
247
|
+
let finalResponse = '';
|
|
248
|
+
// Streaming agentic loop
|
|
249
|
+
while (iterationCount < this.config.maxIterations) {
|
|
250
|
+
// Check if aborted
|
|
251
|
+
if (signal?.aborted) {
|
|
252
|
+
throw new Error('Operation aborted');
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential iterations required for agentic loop
|
|
256
|
+
const result = await this.executeAgenticIterationStreaming(iterationCount, toolSet, executionContext, taskId);
|
|
257
|
+
// If no tool calls, we're done - emit final response
|
|
258
|
+
if (!result.hasToolCalls) {
|
|
259
|
+
finalResponse = result.response;
|
|
260
|
+
// Emit response event
|
|
261
|
+
this.sessionEventBus.emit('llmservice:response', {
|
|
262
|
+
content: finalResponse,
|
|
263
|
+
model: this.config.model,
|
|
264
|
+
provider: 'openrouter',
|
|
265
|
+
taskId: taskId || undefined,
|
|
266
|
+
});
|
|
267
|
+
// Add assistant message to context
|
|
268
|
+
// eslint-disable-next-line no-await-in-loop -- Must complete before returning
|
|
269
|
+
await this.contextManager.addAssistantMessage(finalResponse);
|
|
270
|
+
return finalResponse;
|
|
271
|
+
}
|
|
272
|
+
// Has tool calls - continue the loop
|
|
273
|
+
iterationCount++;
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
this.handleLLMError(error, taskId);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Max iterations exceeded
|
|
280
|
+
throw new LlmMaxIterationsError(this.config.maxIterations, 'openrouter', this.config.model);
|
|
281
|
+
}
|
|
207
282
|
/**
|
|
208
283
|
* Execute a single iteration of the agentic loop.
|
|
209
284
|
*
|
|
210
285
|
* @param iterationCount - Current iteration number
|
|
211
286
|
* @param tools - Available tools for this iteration
|
|
212
287
|
* @param executionContext - Optional execution context
|
|
288
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
213
289
|
* @returns Final response string if complete, null if more iterations needed
|
|
214
290
|
*/
|
|
215
|
-
async executeAgenticIteration(iterationCount, tools, executionContext) {
|
|
291
|
+
async executeAgenticIteration(iterationCount, tools, executionContext, taskId) {
|
|
216
292
|
// Build system prompt using SystemPromptManager (before compression for correct token accounting)
|
|
217
293
|
// Use filtered tool names based on command type (e.g., only read-only tools for 'query')
|
|
218
294
|
const availableTools = this.toolManager.getToolNamesForCommand(executionContext?.commandType);
|
|
@@ -250,53 +326,162 @@ export class OpenRouterLLMService {
|
|
|
250
326
|
tokensUsed,
|
|
251
327
|
});
|
|
252
328
|
}
|
|
253
|
-
// Emit thinking event
|
|
254
|
-
this.sessionEventBus.emit('llmservice:thinking');
|
|
329
|
+
// Emit thinking event with taskId for concurrent task isolation
|
|
330
|
+
this.sessionEventBus.emit('llmservice:thinking', { taskId });
|
|
255
331
|
// Call LLM and parse response
|
|
256
332
|
const lastMessage = await this.callLLMAndParseResponse(tools, formattedMessages);
|
|
257
333
|
// Check if there are tool calls
|
|
258
334
|
if (!lastMessage.toolCalls || lastMessage.toolCalls.length === 0) {
|
|
259
|
-
return this.handleFinalResponse(lastMessage);
|
|
335
|
+
return this.handleFinalResponse(lastMessage, taskId);
|
|
260
336
|
}
|
|
261
337
|
// Has tool calls - handle them
|
|
262
|
-
await this.handleToolCalls(lastMessage);
|
|
338
|
+
await this.handleToolCalls(lastMessage, taskId);
|
|
263
339
|
return null;
|
|
264
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Execute a single iteration of the agentic loop with streaming.
|
|
343
|
+
*
|
|
344
|
+
* This method uses the ContentGenerator's streaming API to provide
|
|
345
|
+
* real-time token-by-token output via the SessionEventBus.
|
|
346
|
+
*
|
|
347
|
+
* @param iterationCount - Current iteration number
|
|
348
|
+
* @param toolSet - Available tools for this iteration
|
|
349
|
+
* @param executionContext - Optional execution context
|
|
350
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
351
|
+
* @returns Object with response text and whether tool calls were made
|
|
352
|
+
*/
|
|
353
|
+
async executeAgenticIterationStreaming(iterationCount, toolSet, executionContext, taskId) {
|
|
354
|
+
// Build system prompt using SystemPromptManager
|
|
355
|
+
const availableTools = this.toolManager.getToolNamesForCommand(executionContext?.commandType);
|
|
356
|
+
const markersSet = this.toolManager.getAvailableMarkers();
|
|
357
|
+
const availableMarkers = {};
|
|
358
|
+
for (const marker of markersSet) {
|
|
359
|
+
availableMarkers[marker] = marker;
|
|
360
|
+
}
|
|
361
|
+
const systemPrompt = await this.systemPromptManager.build({
|
|
362
|
+
availableMarkers,
|
|
363
|
+
availableTools,
|
|
364
|
+
commandType: executionContext?.commandType,
|
|
365
|
+
conversationMetadata: executionContext?.conversationMetadata,
|
|
366
|
+
memoryManager: this.memoryManager,
|
|
367
|
+
});
|
|
368
|
+
// Get messages from context with compression
|
|
369
|
+
const { tokensUsed } = await this.contextManager.getFormattedMessagesWithCompression(systemPrompt);
|
|
370
|
+
if (this.config.verbose) {
|
|
371
|
+
this.logger.debug('Streaming iteration', {
|
|
372
|
+
iteration: `${iterationCount + 1}/${this.config.maxIterations}`,
|
|
373
|
+
maxInputTokens: this.config.maxInputTokens,
|
|
374
|
+
tokensUsed,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
// Emit thinking event
|
|
378
|
+
this.sessionEventBus.emit('llmservice:thinking', { taskId });
|
|
379
|
+
// Get internal messages for content generator
|
|
380
|
+
const contents = this.contextManager.getMessages();
|
|
381
|
+
// Generate streaming response using ContentGenerator
|
|
382
|
+
const streamGenerator = this.contentGenerator.generateContentStream({
|
|
383
|
+
config: {
|
|
384
|
+
maxTokens: this.config.maxTokens,
|
|
385
|
+
temperature: this.config.temperature,
|
|
386
|
+
},
|
|
387
|
+
contents,
|
|
388
|
+
executionContext,
|
|
389
|
+
model: this.config.model,
|
|
390
|
+
systemPrompt,
|
|
391
|
+
taskId: taskId ?? `task-${Date.now()}`,
|
|
392
|
+
tools: toolSet,
|
|
393
|
+
});
|
|
394
|
+
// Transform chunks to StreamEvents and process
|
|
395
|
+
// Pass modelId for native reasoning extraction (OpenAI, Grok, Gemini)
|
|
396
|
+
const streamEvents = transformGenerateContentChunksToStreamEvents(streamGenerator, {
|
|
397
|
+
modelId: this.config.model,
|
|
398
|
+
stepIndex: iterationCount,
|
|
399
|
+
});
|
|
400
|
+
// Process stream and accumulate state
|
|
401
|
+
const generateId = createIdGenerator();
|
|
402
|
+
const processorState = await this.streamProcessor.process(streamEvents, {
|
|
403
|
+
eventBus: this.sessionEventBus,
|
|
404
|
+
generateId,
|
|
405
|
+
sessionId: this.sessionId,
|
|
406
|
+
taskId,
|
|
407
|
+
});
|
|
408
|
+
// Extract accumulated text and tool calls
|
|
409
|
+
const accumulatedText = processorState.textContent;
|
|
410
|
+
const toolParts = [...processorState.toolParts.values()];
|
|
411
|
+
const hasToolCalls = toolParts.length > 0;
|
|
412
|
+
// If there are tool calls, extract and execute them
|
|
413
|
+
if (hasToolCalls) {
|
|
414
|
+
// Convert tool parts to ToolCall format
|
|
415
|
+
const toolCalls = toolParts.map((part) => ({
|
|
416
|
+
function: {
|
|
417
|
+
arguments: JSON.stringify(part.state.status === 'pending' ? part.state.input : {}),
|
|
418
|
+
name: part.toolName,
|
|
419
|
+
},
|
|
420
|
+
id: part.callId,
|
|
421
|
+
type: 'function',
|
|
422
|
+
}));
|
|
423
|
+
// Add assistant message with tool calls to context
|
|
424
|
+
await this.contextManager.addAssistantMessage(accumulatedText, toolCalls);
|
|
425
|
+
// Execute tool calls in parallel (matching internal service behavior)
|
|
426
|
+
// This prevents long-running tools (e.g., subagent Tasks) from blocking others
|
|
427
|
+
await Promise.allSettled(toolCalls.map((toolCall) => this.executeToolCall(toolCall, taskId)));
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
hasToolCalls,
|
|
431
|
+
response: accumulatedText,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
265
434
|
/**
|
|
266
435
|
* Execute a single tool call.
|
|
267
436
|
*
|
|
268
437
|
* @param toolCall - Tool call to execute
|
|
438
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
269
439
|
*/
|
|
270
|
-
async executeToolCall(toolCall) {
|
|
440
|
+
async executeToolCall(toolCall, taskId) {
|
|
271
441
|
try {
|
|
272
442
|
const toolName = toolCall.function.name;
|
|
273
443
|
const toolArgs = JSON.parse(toolCall.function.arguments);
|
|
274
|
-
// Emit tool call event
|
|
444
|
+
// Emit tool call event with taskId for concurrent task isolation
|
|
275
445
|
this.sessionEventBus.emit('llmservice:toolCall', {
|
|
276
446
|
args: toolArgs,
|
|
277
447
|
callId: toolCall.id,
|
|
448
|
+
taskId: taskId || undefined,
|
|
278
449
|
toolName,
|
|
279
450
|
});
|
|
280
451
|
// Execute tool via ToolManager (handles approval, routing, etc.)
|
|
281
|
-
|
|
282
|
-
|
|
452
|
+
// Pass sessionId and taskId context for sub-agent event routing
|
|
453
|
+
const result = await this.toolManager.executeTool(toolName, toolArgs, this.sessionId, {
|
|
454
|
+
sessionId: this.sessionId,
|
|
455
|
+
taskId,
|
|
456
|
+
});
|
|
457
|
+
// Extract content from ToolExecutionResult - the LLM needs the content string,
|
|
458
|
+
// not the full result object (which would be JSON-stringified and confuse the model)
|
|
459
|
+
const resultContent = result.content;
|
|
460
|
+
const isSuccess = result.success;
|
|
461
|
+
// Emit tool result event with taskId
|
|
283
462
|
this.sessionEventBus.emit('llmservice:toolResult', {
|
|
284
463
|
callId: toolCall.id,
|
|
285
|
-
result,
|
|
286
|
-
|
|
464
|
+
...(isSuccess ? { result: resultContent } : { error: result.errorMessage ?? String(resultContent) }),
|
|
465
|
+
errorType: result.errorType,
|
|
466
|
+
success: isSuccess,
|
|
467
|
+
taskId: taskId || undefined,
|
|
287
468
|
toolName,
|
|
288
469
|
});
|
|
289
470
|
// Add tool result to context
|
|
290
|
-
await this.contextManager.addToolResult(toolCall.id, toolName,
|
|
471
|
+
await this.contextManager.addToolResult(toolCall.id, toolName, resultContent, {
|
|
472
|
+
errorType: result.errorType,
|
|
473
|
+
success: isSuccess,
|
|
474
|
+
});
|
|
291
475
|
}
|
|
292
476
|
catch (error) {
|
|
293
477
|
// Add error result to context
|
|
294
478
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
295
|
-
// Emit tool result event (error)
|
|
479
|
+
// Emit tool result event (error) with taskId
|
|
296
480
|
this.sessionEventBus.emit('llmservice:toolResult', {
|
|
297
481
|
callId: toolCall.id,
|
|
298
482
|
error: errorMessage,
|
|
299
483
|
success: false,
|
|
484
|
+
taskId: taskId || undefined,
|
|
300
485
|
toolName: toolCall.function.name,
|
|
301
486
|
});
|
|
302
487
|
await this.contextManager.addToolResult(toolCall.id, toolCall.function.name, `Error: ${errorMessage}`, {
|
|
@@ -326,15 +511,17 @@ export class OpenRouterLLMService {
|
|
|
326
511
|
* Handle final response when there are no tool calls.
|
|
327
512
|
*
|
|
328
513
|
* @param lastMessage - Last message from LLM
|
|
514
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
329
515
|
* @returns Final response content
|
|
330
516
|
*/
|
|
331
|
-
async handleFinalResponse(lastMessage) {
|
|
517
|
+
async handleFinalResponse(lastMessage, taskId) {
|
|
332
518
|
const content = this.extractTextContent(lastMessage);
|
|
333
|
-
// Emit response event
|
|
519
|
+
// Emit response event with taskId for concurrent task isolation
|
|
334
520
|
this.sessionEventBus.emit('llmservice:response', {
|
|
335
521
|
content,
|
|
336
522
|
model: this.config.model,
|
|
337
523
|
provider: 'openrouter',
|
|
524
|
+
taskId: taskId || undefined,
|
|
338
525
|
});
|
|
339
526
|
// Add assistant message to context
|
|
340
527
|
await this.contextManager.addAssistantMessage(content);
|
|
@@ -344,12 +531,14 @@ export class OpenRouterLLMService {
|
|
|
344
531
|
* Handle LLM errors and re-throw or wrap appropriately.
|
|
345
532
|
*
|
|
346
533
|
* @param error - Error to handle
|
|
534
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
347
535
|
*/
|
|
348
|
-
handleLLMError(error) {
|
|
349
|
-
// Emit error event
|
|
536
|
+
handleLLMError(error, taskId) {
|
|
537
|
+
// Emit error event with taskId for concurrent task isolation
|
|
350
538
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
351
539
|
this.sessionEventBus.emit('llmservice:error', {
|
|
352
540
|
error: errorMessage,
|
|
541
|
+
taskId: taskId || undefined,
|
|
353
542
|
});
|
|
354
543
|
// Re-throw LLM errors as-is
|
|
355
544
|
if (error instanceof LlmResponseParsingError ||
|
|
@@ -367,18 +556,17 @@ export class OpenRouterLLMService {
|
|
|
367
556
|
* Handle tool calls from LLM response.
|
|
368
557
|
*
|
|
369
558
|
* @param lastMessage - Last message containing tool calls
|
|
559
|
+
* @param taskId - Optional task ID for concurrent task isolation
|
|
370
560
|
*/
|
|
371
|
-
async handleToolCalls(lastMessage) {
|
|
561
|
+
async handleToolCalls(lastMessage, taskId) {
|
|
372
562
|
if (!lastMessage.toolCalls || lastMessage.toolCalls.length === 0) {
|
|
373
563
|
return;
|
|
374
564
|
}
|
|
375
565
|
// Has tool calls - add assistant message with tool calls
|
|
376
566
|
const assistantContent = this.extractTextContent(lastMessage);
|
|
377
567
|
await this.contextManager.addAssistantMessage(assistantContent, lastMessage.toolCalls);
|
|
378
|
-
// Execute tool calls
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
await this.executeToolCall(toolCall);
|
|
382
|
-
}
|
|
568
|
+
// Execute tool calls in parallel (matching internal service behavior)
|
|
569
|
+
// This prevents long-running tools (e.g., subagent Tasks) from blocking others
|
|
570
|
+
await Promise.allSettled(lastMessage.toolCalls.map((toolCall) => this.executeToolCall(toolCall, taskId)));
|
|
383
571
|
}
|
|
384
572
|
}
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
* - Part creation and updates with unique IDs
|
|
12
12
|
*/
|
|
13
13
|
import type { StepTokenUsage } from '../../../core/domain/cipher/agent-events/types.js';
|
|
14
|
-
import type { CompactionPart, PatchPart, RetryPart, SnapshotPart, StepFinishPart, StepStartPart, TextPart, ToolPart } from '../../../core/interfaces/cipher/message-types.js';
|
|
14
|
+
import type { CompactionPart, PatchPart, ReasoningPart, RetryPart, SnapshotPart, StepFinishPart, StepStartPart, TextPart, ToolPart } from '../../../core/interfaces/cipher/message-types.js';
|
|
15
15
|
import type { SessionEventBus } from '../events/event-emitter.js';
|
|
16
16
|
/**
|
|
17
17
|
* Stream event types that the processor can handle.
|
|
18
|
+
* Following OpenCode's pattern with reasoning-start/delta/end lifecycle.
|
|
18
19
|
*/
|
|
19
20
|
export type StreamEvent = {
|
|
20
21
|
callId: string;
|
|
@@ -41,12 +42,25 @@ export type StreamEvent = {
|
|
|
41
42
|
stepIndex: number;
|
|
42
43
|
tokens: StepTokenUsage;
|
|
43
44
|
type: 'step-finish';
|
|
45
|
+
} | {
|
|
46
|
+
delta: string;
|
|
47
|
+
id: string;
|
|
48
|
+
providerMetadata?: Record<string, unknown>;
|
|
49
|
+
type: 'reasoning-delta-v2';
|
|
44
50
|
} | {
|
|
45
51
|
delta: string;
|
|
46
52
|
type: 'reasoning-delta';
|
|
47
53
|
} | {
|
|
48
54
|
delta: string;
|
|
49
55
|
type: 'text-delta';
|
|
56
|
+
} | {
|
|
57
|
+
id: string;
|
|
58
|
+
providerMetadata?: Record<string, unknown>;
|
|
59
|
+
type: 'reasoning-end';
|
|
60
|
+
} | {
|
|
61
|
+
id: string;
|
|
62
|
+
providerMetadata?: Record<string, unknown>;
|
|
63
|
+
type: 'reasoning-start';
|
|
50
64
|
} | {
|
|
51
65
|
stepIndex: number;
|
|
52
66
|
type: 'step-start';
|
|
@@ -63,6 +77,8 @@ export interface ProcessorContext {
|
|
|
63
77
|
generateId: () => string;
|
|
64
78
|
/** Session ID for event context */
|
|
65
79
|
sessionId: string;
|
|
80
|
+
/** Task ID for event routing (required for chunk events to reach TUI) */
|
|
81
|
+
taskId?: string;
|
|
66
82
|
}
|
|
67
83
|
/**
|
|
68
84
|
* Accumulated state during stream processing.
|
|
@@ -71,7 +87,11 @@ export interface ProcessorState {
|
|
|
71
87
|
/** Current step index */
|
|
72
88
|
currentStepIndex: number;
|
|
73
89
|
/** Parts created during processing */
|
|
74
|
-
parts: Array<CompactionPart | PatchPart | RetryPart | SnapshotPart | StepFinishPart | StepStartPart | TextPart | ToolPart>;
|
|
90
|
+
parts: Array<CompactionPart | PatchPart | ReasoningPart | RetryPart | SnapshotPart | StepFinishPart | StepStartPart | TextPart | ToolPart>;
|
|
91
|
+
/** Accumulated reasoning content (for legacy reasoning-delta events) */
|
|
92
|
+
reasoningContent: string;
|
|
93
|
+
/** Reasoning parts indexed by ID (for v2 reasoning events) */
|
|
94
|
+
reasoningParts: Map<string, ReasoningPart>;
|
|
75
95
|
/** Accumulated text content */
|
|
76
96
|
textContent: string;
|
|
77
97
|
/** Tool parts indexed by call ID */
|
|
@@ -43,12 +43,24 @@ export class StreamProcessor {
|
|
|
43
43
|
const state = {
|
|
44
44
|
currentStepIndex: 0,
|
|
45
45
|
parts: [],
|
|
46
|
+
reasoningContent: '',
|
|
47
|
+
reasoningParts: new Map(),
|
|
46
48
|
textContent: '',
|
|
47
49
|
toolParts: new Map(),
|
|
48
50
|
};
|
|
51
|
+
let receivedFinish = false;
|
|
49
52
|
for await (const event of stream) {
|
|
53
|
+
if (event.type === 'finish') {
|
|
54
|
+
receivedFinish = true;
|
|
55
|
+
}
|
|
50
56
|
await this.handleEvent(event, state, context);
|
|
51
57
|
}
|
|
58
|
+
// Safety net: if the stream ended without a 'finish' event (e.g., OpenRouter
|
|
59
|
+
// stream closed without setting finish_reason), finalize any pending text part
|
|
60
|
+
// so the TUI receives isComplete: true and stops showing a loading spinner.
|
|
61
|
+
if (!receivedFinish) {
|
|
62
|
+
this.finalizeTextPart(state, context);
|
|
63
|
+
}
|
|
52
64
|
return state;
|
|
53
65
|
}
|
|
54
66
|
/**
|
|
@@ -65,6 +77,7 @@ export class StreamProcessor {
|
|
|
65
77
|
context.eventBus.emit('llmservice:chunk', {
|
|
66
78
|
content: '',
|
|
67
79
|
isComplete: true,
|
|
80
|
+
taskId: context.taskId,
|
|
68
81
|
type: 'text',
|
|
69
82
|
});
|
|
70
83
|
}
|
|
@@ -80,13 +93,65 @@ export class StreamProcessor {
|
|
|
80
93
|
break;
|
|
81
94
|
}
|
|
82
95
|
case 'reasoning-delta': {
|
|
83
|
-
// Emit reasoning chunk for UI streaming
|
|
96
|
+
// Legacy: Emit reasoning chunk for UI streaming (simple delta without ID tracking)
|
|
97
|
+
state.reasoningContent += event.delta;
|
|
84
98
|
context.eventBus.emit('llmservice:chunk', {
|
|
85
99
|
content: event.delta,
|
|
100
|
+
taskId: context.taskId,
|
|
86
101
|
type: 'reasoning',
|
|
87
102
|
});
|
|
88
103
|
break;
|
|
89
104
|
}
|
|
105
|
+
case 'reasoning-delta-v2': {
|
|
106
|
+
// V2: Emit reasoning chunk with ID tracking (following OpenCode pattern)
|
|
107
|
+
const reasoningPart = state.reasoningParts.get(event.id);
|
|
108
|
+
if (reasoningPart) {
|
|
109
|
+
reasoningPart.text += event.delta;
|
|
110
|
+
if (event.providerMetadata) {
|
|
111
|
+
reasoningPart.providerMetadata = event.providerMetadata;
|
|
112
|
+
}
|
|
113
|
+
context.eventBus.emit('llmservice:chunk', {
|
|
114
|
+
content: event.delta,
|
|
115
|
+
taskId: context.taskId,
|
|
116
|
+
type: 'reasoning',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case 'reasoning-end': {
|
|
122
|
+
// Finalize reasoning part with end timestamp
|
|
123
|
+
const reasoningPart = state.reasoningParts.get(event.id);
|
|
124
|
+
if (reasoningPart) {
|
|
125
|
+
reasoningPart.text = reasoningPart.text.trimEnd();
|
|
126
|
+
reasoningPart.time.end = Date.now();
|
|
127
|
+
if (event.providerMetadata) {
|
|
128
|
+
reasoningPart.providerMetadata = event.providerMetadata;
|
|
129
|
+
}
|
|
130
|
+
// Emit completion signal
|
|
131
|
+
context.eventBus.emit('llmservice:chunk', {
|
|
132
|
+
content: '',
|
|
133
|
+
isComplete: true,
|
|
134
|
+
taskId: context.taskId,
|
|
135
|
+
type: 'reasoning',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case 'reasoning-start': {
|
|
141
|
+
// Create new reasoning part and track it
|
|
142
|
+
const reasoningPart = {
|
|
143
|
+
id: event.id,
|
|
144
|
+
providerMetadata: event.providerMetadata,
|
|
145
|
+
text: '',
|
|
146
|
+
time: {
|
|
147
|
+
start: Date.now(),
|
|
148
|
+
},
|
|
149
|
+
type: 'reasoning',
|
|
150
|
+
};
|
|
151
|
+
state.reasoningParts.set(event.id, reasoningPart);
|
|
152
|
+
state.parts.push(reasoningPart);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
90
155
|
case 'step-finish': {
|
|
91
156
|
this.handleStepFinish({
|
|
92
157
|
cost: event.cost,
|
|
@@ -113,7 +178,7 @@ export class StreamProcessor {
|
|
|
113
178
|
break;
|
|
114
179
|
}
|
|
115
180
|
case 'tool-call-input': {
|
|
116
|
-
this.handleToolCallInput(event.callId, event.input, state);
|
|
181
|
+
this.handleToolCallInput(event.callId, event.input, state, context);
|
|
117
182
|
break;
|
|
118
183
|
}
|
|
119
184
|
case 'tool-call-running': {
|
|
@@ -173,6 +238,7 @@ export class StreamProcessor {
|
|
|
173
238
|
// Emit chunk with delta for real-time UI update
|
|
174
239
|
context.eventBus.emit('llmservice:chunk', {
|
|
175
240
|
content: delta,
|
|
241
|
+
taskId: context.taskId,
|
|
176
242
|
type: 'text',
|
|
177
243
|
});
|
|
178
244
|
}
|
|
@@ -215,13 +281,20 @@ export class StreamProcessor {
|
|
|
215
281
|
/**
|
|
216
282
|
* Handle tool call input received.
|
|
217
283
|
*/
|
|
218
|
-
handleToolCallInput(callId, input, state) {
|
|
284
|
+
handleToolCallInput(callId, input, state, context) {
|
|
219
285
|
const toolPart = state.toolParts.get(callId);
|
|
220
286
|
if (toolPart && toolPart.state.status === 'pending') {
|
|
221
287
|
toolPart.state = {
|
|
222
288
|
input,
|
|
223
289
|
status: 'pending',
|
|
224
290
|
};
|
|
291
|
+
// Emit updated tool call event with args so TUI can display them
|
|
292
|
+
context.eventBus.emit('llmservice:toolCall', {
|
|
293
|
+
args: input,
|
|
294
|
+
callId,
|
|
295
|
+
taskId: context.taskId,
|
|
296
|
+
toolName: toolPart.toolName,
|
|
297
|
+
});
|
|
225
298
|
}
|
|
226
299
|
}
|
|
227
300
|
/**
|
|
@@ -255,10 +328,11 @@ export class StreamProcessor {
|
|
|
255
328
|
};
|
|
256
329
|
state.toolParts.set(callId, toolPart);
|
|
257
330
|
state.parts.push(toolPart);
|
|
258
|
-
// Emit tool call event
|
|
331
|
+
// Emit tool call event with taskId for TUI routing
|
|
259
332
|
context.eventBus.emit('llmservice:toolCall', {
|
|
260
333
|
args: {},
|
|
261
334
|
callId,
|
|
335
|
+
taskId: context.taskId,
|
|
262
336
|
toolName,
|
|
263
337
|
});
|
|
264
338
|
}
|
|
@@ -70,7 +70,7 @@ export declare function supportsMultimodalFunctionResponse(model: string): boole
|
|
|
70
70
|
/**
|
|
71
71
|
* Default thinking mode token budget
|
|
72
72
|
*/
|
|
73
|
-
export declare const DEFAULT_THINKING_BUDGET =
|
|
73
|
+
export declare const DEFAULT_THINKING_BUDGET = 8192;
|
|
74
74
|
/**
|
|
75
75
|
* Synthetic thought signature used for Preview models
|
|
76
76
|
*/
|
|
@@ -44,7 +44,7 @@ export function supportsMultimodalFunctionResponse(model) {
|
|
|
44
44
|
/**
|
|
45
45
|
* Default thinking mode token budget
|
|
46
46
|
*/
|
|
47
|
-
export const DEFAULT_THINKING_BUDGET =
|
|
47
|
+
export const DEFAULT_THINKING_BUDGET = 8192;
|
|
48
48
|
/**
|
|
49
49
|
* Synthetic thought signature used for Preview models
|
|
50
50
|
*/
|
|
@@ -159,20 +159,20 @@ export const ThinkingConfigManager = {
|
|
|
159
159
|
// Gemini 3.x models
|
|
160
160
|
if (lowerModel.startsWith('gemini-3') || lowerModel.includes('gemini-3')) {
|
|
161
161
|
return {
|
|
162
|
-
includeThoughts:
|
|
163
|
-
thinkingLevel: ThinkingLevel.
|
|
162
|
+
includeThoughts: true,
|
|
163
|
+
thinkingLevel: ThinkingLevel.LOW,
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
166
|
// Gemini 2.x models
|
|
167
167
|
if (lowerModel.startsWith('gemini-2') || lowerModel.includes('gemini-2')) {
|
|
168
168
|
return {
|
|
169
|
-
includeThoughts:
|
|
169
|
+
includeThoughts: true,
|
|
170
170
|
thinkingBudget: DEFAULT_THINKING_BUDGET,
|
|
171
171
|
};
|
|
172
172
|
}
|
|
173
173
|
// Other Gemini models - use budget as default
|
|
174
174
|
return {
|
|
175
|
-
includeThoughts:
|
|
175
|
+
includeThoughts: true,
|
|
176
176
|
thinkingBudget: DEFAULT_THINKING_BUDGET,
|
|
177
177
|
};
|
|
178
178
|
},
|