@google/gemini-cli-core 0.6.0-nightly.20250910.a31830a3 → 0.6.0-preview.10
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/LICENSE +2 -2
- package/README.md +12 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/converter.d.ts +1 -0
- package/dist/src/code_assist/converter.js +1 -0
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/converter.test.js +10 -0
- package/dist/src/code_assist/converter.test.js.map +1 -1
- package/dist/src/code_assist/oauth-credential-storage.d.ts +5 -7
- package/dist/src/code_assist/oauth-credential-storage.js +5 -8
- package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
- package/dist/src/code_assist/oauth-credential-storage.test.js +35 -33
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
- package/dist/src/code_assist/oauth2.js +28 -2
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +674 -536
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/config/config.d.ts +37 -1
- package/dist/src/config/config.js +74 -17
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +104 -16
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/models.d.ts +15 -0
- package/dist/src/config/models.js +27 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.d.ts +6 -0
- package/dist/src/config/models.test.js +55 -0
- package/dist/src/config/models.test.js.map +1 -0
- package/dist/src/confirmation-bus/index.d.ts +7 -0
- package/dist/src/confirmation-bus/index.js +8 -0
- package/dist/src/confirmation-bus/index.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.d.ts +17 -0
- package/dist/src/confirmation-bus/message-bus.js +81 -0
- package/dist/src/confirmation-bus/message-bus.js.map +1 -0
- package/dist/src/confirmation-bus/message-bus.test.d.ts +6 -0
- package/dist/src/confirmation-bus/message-bus.test.js +164 -0
- package/dist/src/confirmation-bus/message-bus.test.js.map +1 -0
- package/dist/src/confirmation-bus/types.d.ts +38 -0
- package/dist/src/confirmation-bus/types.js +15 -0
- package/dist/src/confirmation-bus/types.js.map +1 -0
- package/dist/src/core/baseLlmClient.d.ts +1 -0
- package/dist/src/core/baseLlmClient.js +24 -0
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +63 -0
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.d.ts +4 -3
- package/dist/src/core/client.js +76 -141
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +218 -220
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +0 -1
- package/dist/src/core/contentGenerator.js +0 -4
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +0 -3
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +4 -3
- package/dist/src/core/coreToolScheduler.js +42 -5
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +43 -0
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +13 -30
- package/dist/src/core/geminiChat.js +88 -230
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +268 -489
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +5 -5
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +49 -0
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/subagent.js +1 -1
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/subagent.test.js +9 -8
- package/dist/src/core/subagent.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +2 -1
- package/dist/src/core/turn.js +2 -2
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +18 -18
- package/dist/src/core/turn.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/ide/constants.d.ts +1 -0
- package/dist/src/ide/constants.js +1 -0
- package/dist/src/ide/constants.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +51 -13
- package/dist/src/ide/ide-client.js +184 -37
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +93 -3
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ide-installer.js +8 -2
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +13 -2
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/ide/ideContext.d.ts +34 -113
- package/dist/src/ide/ideContext.js +20 -78
- package/dist/src/ide/ideContext.js.map +1 -1
- package/dist/src/ide/ideContext.test.js +37 -39
- package/dist/src/ide/ideContext.test.js.map +1 -1
- package/dist/src/ide/types.d.ts +141 -0
- package/dist/src/ide/types.js +73 -0
- package/dist/src/ide/types.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.d.ts +2 -0
- package/dist/src/mcp/oauth-token-storage.js +25 -0
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.test.js +251 -160
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
- package/dist/src/mcp/token-storage/index.d.ts +11 -0
- package/dist/src/mcp/token-storage/index.js +12 -0
- package/dist/src/mcp/token-storage/index.js.map +1 -0
- package/dist/src/output/json-formatter.d.ts +11 -0
- package/dist/src/output/json-formatter.js +30 -0
- package/dist/src/output/json-formatter.js.map +1 -0
- package/dist/src/output/json-formatter.test.d.ts +6 -0
- package/dist/src/output/json-formatter.test.js +266 -0
- package/dist/src/output/json-formatter.test.js.map +1 -0
- package/dist/src/output/types.d.ts +20 -0
- package/dist/src/output/types.js +11 -0
- package/dist/src/output/types.js.map +1 -0
- package/dist/src/policy/index.d.ts +7 -0
- package/dist/src/policy/index.js +8 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/policy/policy-engine.d.ts +30 -0
- package/dist/src/policy/policy-engine.js +83 -0
- package/dist/src/policy/policy-engine.js.map +1 -0
- package/dist/src/policy/policy-engine.test.d.ts +6 -0
- package/dist/src/policy/policy-engine.test.js +470 -0
- package/dist/src/policy/policy-engine.test.js.map +1 -0
- package/dist/src/policy/stable-stringify.d.ts +58 -0
- package/dist/src/policy/stable-stringify.js +122 -0
- package/dist/src/policy/stable-stringify.js.map +1 -0
- package/dist/src/policy/types.d.ts +47 -0
- package/dist/src/policy/types.js +12 -0
- package/dist/src/policy/types.js.map +1 -0
- package/dist/src/routing/modelRouterService.d.ts +23 -0
- package/dist/src/routing/modelRouterService.js +70 -0
- package/dist/src/routing/modelRouterService.js.map +1 -0
- package/dist/src/routing/modelRouterService.test.d.ts +6 -0
- package/dist/src/routing/modelRouterService.test.js +98 -0
- package/dist/src/routing/modelRouterService.test.js.map +1 -0
- package/dist/src/routing/routingStrategy.d.ts +62 -0
- package/dist/src/routing/routingStrategy.js +7 -0
- package/dist/src/routing/routingStrategy.js.map +1 -0
- package/dist/src/routing/strategies/classifierStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/classifierStrategy.js +173 -0
- package/dist/src/routing/strategies/classifierStrategy.js.map +1 -0
- package/dist/src/routing/strategies/classifierStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/classifierStrategy.test.js +192 -0
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.d.ts +26 -0
- package/dist/src/routing/strategies/compositeStrategy.js +67 -0
- package/dist/src/routing/strategies/compositeStrategy.js.map +1 -0
- package/dist/src/routing/strategies/compositeStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js +123 -0
- package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/defaultStrategy.js +20 -0
- package/dist/src/routing/strategies/defaultStrategy.js.map +1 -0
- package/dist/src/routing/strategies/defaultStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js +26 -0
- package/dist/src/routing/strategies/defaultStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.d.ts +12 -0
- package/dist/src/routing/strategies/fallbackStrategy.js +25 -0
- package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js +55 -0
- package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.d.ts +15 -0
- package/dist/src/routing/strategies/overrideStrategy.js +28 -0
- package/dist/src/routing/strategies/overrideStrategy.js.map +1 -0
- package/dist/src/routing/strategies/overrideStrategy.test.d.ts +6 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js +42 -0
- package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -0
- package/dist/src/services/chatRecordingService.d.ts +2 -1
- package/dist/src/services/chatRecordingService.js +3 -3
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingService.test.js +8 -3
- package/dist/src/services/chatRecordingService.test.js.map +1 -1
- package/dist/src/services/fileDiscoveryService.d.ts +10 -0
- package/dist/src/services/fileDiscoveryService.js +31 -17
- package/dist/src/services/fileDiscoveryService.js.map +1 -1
- package/dist/src/services/gitService.js +9 -12
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/gitService.test.js +10 -20
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/loopDetectionService.d.ts +5 -0
- package/dist/src/services/loopDetectionService.js +36 -20
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/loopDetectionService.test.js +41 -12
- package/dist/src/services/loopDetectionService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +34 -2
- package/dist/src/services/shellExecutionService.js +192 -43
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +184 -55
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +12 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +100 -5
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +63 -5
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +13 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +33 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +6 -0
- package/dist/src/telemetry/constants.js +6 -0
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/gcp-exporters.d.ts +34 -0
- package/dist/src/telemetry/gcp-exporters.js +117 -0
- package/dist/src/telemetry/gcp-exporters.js.map +1 -0
- package/dist/src/telemetry/gcp-exporters.test.d.ts +6 -0
- package/dist/src/telemetry/gcp-exporters.test.js +318 -0
- package/dist/src/telemetry/gcp-exporters.test.js.map +1 -0
- package/dist/src/telemetry/index.d.ts +3 -2
- package/dist/src/telemetry/index.js +3 -2
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +7 -1
- package/dist/src/telemetry/loggers.js +97 -7
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +171 -37
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +3 -1
- package/dist/src/telemetry/metrics.js +32 -3
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +42 -0
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/sdk.js +19 -1
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.js +95 -0
- package/dist/src/telemetry/sdk.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +54 -3
- package/dist/src/telemetry/types.js +79 -3
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/tools/edit.js +6 -5
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/edit.test.js +79 -9
- package/dist/src/tools/edit.test.js.map +1 -1
- package/dist/src/tools/glob.d.ts +5 -1
- package/dist/src/tools/glob.js +24 -17
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/glob.test.js +51 -0
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/ls.js +19 -32
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/ls.test.js +140 -280
- package/dist/src/tools/ls.test.js.map +1 -1
- package/dist/src/tools/read-many-files.d.ts +1 -1
- package/dist/src/tools/read-many-files.js +17 -49
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +4 -0
- package/dist/src/tools/ripGrep.js +11 -1
- package/dist/src/tools/ripGrep.js.map +1 -1
- package/dist/src/tools/ripGrep.test.js +51 -1
- package/dist/src/tools/ripGrep.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +12 -2
- package/dist/src/tools/shell.js +20 -27
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +33 -68
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/smart-edit.d.ts +0 -1
- package/dist/src/tools/smart-edit.js +5 -18
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js +18 -9
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tool-registry.js +1 -0
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tools.d.ts +8 -5
- package/dist/src/tools/tools.js +9 -2
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-file.js +4 -5
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-file.test.js +94 -10
- package/dist/src/tools/write-file.test.js.map +1 -1
- package/dist/src/utils/bfsFileSearch.js +11 -5
- package/dist/src/utils/bfsFileSearch.js.map +1 -1
- package/dist/src/utils/editCorrector.d.ts +7 -6
- package/dist/src/utils/editCorrector.js +61 -18
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editCorrector.test.js +30 -79
- package/dist/src/utils/editCorrector.test.js.map +1 -1
- package/dist/src/utils/editor.js +31 -44
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +61 -75
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/errorParsing.js +2 -2
- package/dist/src/utils/errorParsing.js.map +1 -1
- package/dist/src/utils/errorParsing.test.js +7 -7
- package/dist/src/utils/errorParsing.test.js.map +1 -1
- package/dist/src/utils/errors.d.ts +6 -0
- package/dist/src/utils/errors.js +10 -0
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/fileUtils.test.js +17 -8
- package/dist/src/utils/fileUtils.test.js.map +1 -1
- package/dist/src/utils/geminiIgnoreParser.d.ts +18 -0
- package/dist/src/utils/geminiIgnoreParser.js +61 -0
- package/dist/src/utils/geminiIgnoreParser.js.map +1 -0
- package/dist/src/utils/geminiIgnoreParser.test.d.ts +6 -0
- package/dist/src/utils/geminiIgnoreParser.test.js +50 -0
- package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -0
- package/dist/src/utils/gitIgnoreParser.d.ts +3 -9
- package/dist/src/utils/gitIgnoreParser.js +60 -69
- package/dist/src/utils/gitIgnoreParser.js.map +1 -1
- package/dist/src/utils/gitIgnoreParser.test.js +18 -53
- package/dist/src/utils/gitIgnoreParser.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +12 -6
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.d.ts +2 -2
- package/dist/src/utils/nextSpeakerChecker.js +8 -2
- package/dist/src/utils/nextSpeakerChecker.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +40 -33
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +5 -0
- package/dist/src/utils/shell-utils.js +23 -0
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/terminalSerializer.d.ts +28 -0
- package/dist/src/utils/terminalSerializer.js +432 -0
- package/dist/src/utils/terminalSerializer.js.map +1 -0
- package/dist/src/utils/terminalSerializer.test.d.ts +6 -0
- package/dist/src/utils/terminalSerializer.test.js +176 -0
- package/dist/src/utils/terminalSerializer.test.js.map +1 -0
- package/dist/src/utils/textUtils.d.ts +5 -0
- package/dist/src/utils/textUtils.js +14 -0
- package/dist/src/utils/textUtils.js.map +1 -1
- package/dist/src/utils/textUtils.test.d.ts +6 -0
- package/dist/src/utils/textUtils.test.js +59 -0
- package/dist/src/utils/textUtils.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -1
- package/dist/src/utils/ide-trust.d.ts +0 -10
- package/dist/src/utils/ide-trust.js +0 -14
- package/dist/src/utils/ide-trust.js.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { promptIdContext } from '../../utils/promptIdContext.js';
|
|
8
|
+
import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_FLASH_LITE_MODEL, DEFAULT_GEMINI_MODEL, } from '../../config/models.js';
|
|
9
|
+
import { createUserContent, Type, } from '@google/genai';
|
|
10
|
+
import { isFunctionCall, isFunctionResponse, } from '../../utils/messageInspectors.js';
|
|
11
|
+
const CLASSIFIER_GENERATION_CONFIG = {
|
|
12
|
+
temperature: 0,
|
|
13
|
+
maxOutputTokens: 1024,
|
|
14
|
+
thinkingConfig: {
|
|
15
|
+
thinkingBudget: 512, // This counts towards output max, so we don't want -1.
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
// The number of recent history turns to provide to the router for context.
|
|
19
|
+
const HISTORY_TURNS_FOR_CONTEXT = 4;
|
|
20
|
+
const HISTORY_SEARCH_WINDOW = 20;
|
|
21
|
+
const FLASH_MODEL = 'flash';
|
|
22
|
+
const PRO_MODEL = 'pro';
|
|
23
|
+
const CLASSIFIER_SYSTEM_PROMPT = `
|
|
24
|
+
You are a specialized Task Routing AI. Your sole function is to analyze the user's request and classify its complexity. Choose between \`${FLASH_MODEL}\` (SIMPLE) or \`${PRO_MODEL}\` (COMPLEX).
|
|
25
|
+
1. \`${FLASH_MODEL}\`: A fast, efficient model for simple, well-defined tasks.
|
|
26
|
+
2. \`${PRO_MODEL}\`: A powerful, advanced model for complex, open-ended, or multi-step tasks.
|
|
27
|
+
<complexity_rubric>
|
|
28
|
+
A task is COMPLEX (Choose \`${PRO_MODEL}\`) if it meets ONE OR MORE of the following criteria:
|
|
29
|
+
1. **High Operational Complexity (Est. 4+ Steps/Tool Calls):** Requires dependent actions, significant planning, or multiple coordinated changes.
|
|
30
|
+
2. **Strategic Planning & Conceptual Design:** Asking "how" or "why." Requires advice, architecture, or high-level strategy.
|
|
31
|
+
3. **High Ambiguity or Large Scope (Extensive Investigation):** Broadly defined requests requiring extensive investigation.
|
|
32
|
+
4. **Deep Debugging & Root Cause Analysis:** Diagnosing unknown or complex problems from symptoms.
|
|
33
|
+
A task is SIMPLE (Choose \`${FLASH_MODEL}\`) if it is highly specific, bounded, and has Low Operational Complexity (Est. 1-3 tool calls). Operational simplicity overrides strategic phrasing.
|
|
34
|
+
</complexity_rubric>
|
|
35
|
+
**Output Format:**
|
|
36
|
+
Respond *only* in JSON format according to the following schema. Do not include any text outside the JSON structure.
|
|
37
|
+
{
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"reasoning": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "A brief, step-by-step explanation for the model choice, referencing the rubric."
|
|
43
|
+
},
|
|
44
|
+
"model_choice": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"enum": ["${FLASH_MODEL}", "${PRO_MODEL}"]
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"required": ["reasoning", "model_choice"]
|
|
50
|
+
}
|
|
51
|
+
--- EXAMPLES ---
|
|
52
|
+
**Example 1 (Strategic Planning):**
|
|
53
|
+
*User Prompt:* "How should I architect the data pipeline for this new analytics service?"
|
|
54
|
+
*Your JSON Output:*
|
|
55
|
+
{
|
|
56
|
+
"reasoning": "The user is asking for high-level architectural design and strategy. This falls under 'Strategic Planning & Conceptual Design'.",
|
|
57
|
+
"model_choice": "${PRO_MODEL}"
|
|
58
|
+
}
|
|
59
|
+
**Example 2 (Simple Tool Use):**
|
|
60
|
+
*User Prompt:* "list the files in the current directory"
|
|
61
|
+
*Your JSON Output:*
|
|
62
|
+
{
|
|
63
|
+
"reasoning": "This is a direct command requiring a single tool call (ls). It has Low Operational Complexity (1 step).",
|
|
64
|
+
"model_choice": "${FLASH_MODEL}"
|
|
65
|
+
}
|
|
66
|
+
**Example 3 (High Operational Complexity):**
|
|
67
|
+
*User Prompt:* "I need to add a new 'email' field to the User schema in 'src/models/user.ts', migrate the database, and update the registration endpoint."
|
|
68
|
+
*Your JSON Output:*
|
|
69
|
+
{
|
|
70
|
+
"reasoning": "This request involves multiple coordinated steps across different files and systems. This meets the criteria for High Operational Complexity (4+ steps).",
|
|
71
|
+
"model_choice": "${PRO_MODEL}"
|
|
72
|
+
}
|
|
73
|
+
**Example 4 (Simple Read):**
|
|
74
|
+
*User Prompt:* "Read the contents of 'package.json'."
|
|
75
|
+
*Your JSON Output:*
|
|
76
|
+
{
|
|
77
|
+
"reasoning": "This is a direct command requiring a single read. It has Low Operational Complexity (1 step).",
|
|
78
|
+
"model_choice": "${FLASH_MODEL}"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
**Example 5 (Deep Debugging):**
|
|
82
|
+
*User Prompt:* "I'm getting an error 'Cannot read property 'map' of undefined' when I click the save button. Can you fix it?"
|
|
83
|
+
*Your JSON Output:*
|
|
84
|
+
{
|
|
85
|
+
"reasoning": "The user is reporting an error symptom without a known cause. This requires investigation and falls under 'Deep Debugging'.",
|
|
86
|
+
"model_choice": "${PRO_MODEL}"
|
|
87
|
+
}
|
|
88
|
+
**Example 6 (Simple Edit despite Phrasing):**
|
|
89
|
+
*User Prompt:* "What is the best way to rename the variable 'data' to 'userData' in 'src/utils.js'?"
|
|
90
|
+
*Your JSON Output:*
|
|
91
|
+
{
|
|
92
|
+
"reasoning": "Although the user uses strategic language ('best way'), the underlying task is a localized edit. The operational complexity is low (1-2 steps).",
|
|
93
|
+
"model_choice": "${FLASH_MODEL}"
|
|
94
|
+
}
|
|
95
|
+
`;
|
|
96
|
+
const RESPONSE_SCHEMA = {
|
|
97
|
+
type: Type.OBJECT,
|
|
98
|
+
properties: {
|
|
99
|
+
reasoning: {
|
|
100
|
+
type: Type.STRING,
|
|
101
|
+
description: 'A brief, step-by-step explanation for the model choice, referencing the rubric.',
|
|
102
|
+
},
|
|
103
|
+
model_choice: {
|
|
104
|
+
type: Type.STRING,
|
|
105
|
+
enum: [FLASH_MODEL, PRO_MODEL],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
required: ['reasoning', 'model_choice'],
|
|
109
|
+
};
|
|
110
|
+
const ClassifierResponseSchema = z.object({
|
|
111
|
+
reasoning: z.string(),
|
|
112
|
+
model_choice: z.enum([FLASH_MODEL, PRO_MODEL]),
|
|
113
|
+
});
|
|
114
|
+
export class ClassifierStrategy {
|
|
115
|
+
name = 'classifier';
|
|
116
|
+
async route(context, _config, baseLlmClient) {
|
|
117
|
+
const startTime = Date.now();
|
|
118
|
+
try {
|
|
119
|
+
let promptId = promptIdContext.getStore();
|
|
120
|
+
if (!promptId) {
|
|
121
|
+
promptId = `classifier-router-fallback-${Date.now()}-${Math.random()
|
|
122
|
+
.toString(16)
|
|
123
|
+
.slice(2)}`;
|
|
124
|
+
console.warn(`Could not find promptId in context. This is unexpected. Using a fallback ID: ${promptId}`);
|
|
125
|
+
}
|
|
126
|
+
const historySlice = context.history.slice(-HISTORY_SEARCH_WINDOW);
|
|
127
|
+
// Filter out tool-related turns.
|
|
128
|
+
// TODO - Consider using function req/res if they help accuracy.
|
|
129
|
+
const cleanHistory = historySlice.filter((content) => !isFunctionCall(content) && !isFunctionResponse(content));
|
|
130
|
+
// Take the last N turns from the *cleaned* history.
|
|
131
|
+
const finalHistory = cleanHistory.slice(-HISTORY_TURNS_FOR_CONTEXT);
|
|
132
|
+
const jsonResponse = await baseLlmClient.generateJson({
|
|
133
|
+
contents: [...finalHistory, createUserContent(context.request)],
|
|
134
|
+
schema: RESPONSE_SCHEMA,
|
|
135
|
+
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
|
136
|
+
systemInstruction: CLASSIFIER_SYSTEM_PROMPT,
|
|
137
|
+
config: CLASSIFIER_GENERATION_CONFIG,
|
|
138
|
+
abortSignal: context.signal,
|
|
139
|
+
promptId,
|
|
140
|
+
});
|
|
141
|
+
const routerResponse = ClassifierResponseSchema.parse(jsonResponse);
|
|
142
|
+
const reasoning = routerResponse.reasoning;
|
|
143
|
+
const latencyMs = Date.now() - startTime;
|
|
144
|
+
if (routerResponse.model_choice === FLASH_MODEL) {
|
|
145
|
+
return {
|
|
146
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
147
|
+
metadata: {
|
|
148
|
+
source: 'Classifier',
|
|
149
|
+
latencyMs,
|
|
150
|
+
reasoning,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return {
|
|
156
|
+
model: DEFAULT_GEMINI_MODEL,
|
|
157
|
+
metadata: {
|
|
158
|
+
source: 'Classifier',
|
|
159
|
+
reasoning,
|
|
160
|
+
latencyMs,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// If the classifier fails for any reason (API error, parsing error, etc.),
|
|
167
|
+
// we log it and return null to allow the composite strategy to proceed.
|
|
168
|
+
console.warn(`[Routing] ClassifierStrategy failed:`, error);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=classifierStrategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifierStrategy.js","sourceRoot":"","sources":["../../../../src/routing/strategies/classifierStrategy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { ClassifierStrategy } from './classifierStrategy.js';
|
|
8
|
+
import { isFunctionCall, isFunctionResponse, } from '../../utils/messageInspectors.js';
|
|
9
|
+
import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_FLASH_LITE_MODEL, DEFAULT_GEMINI_MODEL, } from '../../config/models.js';
|
|
10
|
+
import { promptIdContext } from '../../utils/promptIdContext.js';
|
|
11
|
+
vi.mock('../../core/baseLlmClient.js');
|
|
12
|
+
vi.mock('../../utils/promptIdContext.js');
|
|
13
|
+
describe('ClassifierStrategy', () => {
|
|
14
|
+
let strategy;
|
|
15
|
+
let mockContext;
|
|
16
|
+
let mockConfig;
|
|
17
|
+
let mockBaseLlmClient;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.clearAllMocks();
|
|
20
|
+
strategy = new ClassifierStrategy();
|
|
21
|
+
mockContext = {
|
|
22
|
+
history: [],
|
|
23
|
+
request: [{ text: 'simple task' }],
|
|
24
|
+
signal: new AbortController().signal,
|
|
25
|
+
};
|
|
26
|
+
mockConfig = {};
|
|
27
|
+
mockBaseLlmClient = {
|
|
28
|
+
generateJson: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
vi.mocked(promptIdContext.getStore).mockReturnValue('test-prompt-id');
|
|
31
|
+
});
|
|
32
|
+
it('should call generateJson with the correct parameters', async () => {
|
|
33
|
+
const mockApiResponse = {
|
|
34
|
+
reasoning: 'Simple task',
|
|
35
|
+
model_choice: 'flash',
|
|
36
|
+
};
|
|
37
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
38
|
+
await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
39
|
+
expect(mockBaseLlmClient.generateJson).toHaveBeenCalledWith(expect.objectContaining({
|
|
40
|
+
model: DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
|
41
|
+
config: expect.objectContaining({
|
|
42
|
+
temperature: 0,
|
|
43
|
+
maxOutputTokens: 1024,
|
|
44
|
+
thinkingConfig: {
|
|
45
|
+
thinkingBudget: 512,
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
promptId: 'test-prompt-id',
|
|
49
|
+
}));
|
|
50
|
+
});
|
|
51
|
+
it('should route to FLASH model for a simple task', async () => {
|
|
52
|
+
const mockApiResponse = {
|
|
53
|
+
reasoning: 'This is a simple task.',
|
|
54
|
+
model_choice: 'flash',
|
|
55
|
+
};
|
|
56
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
57
|
+
const decision = await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
58
|
+
expect(mockBaseLlmClient.generateJson).toHaveBeenCalledOnce();
|
|
59
|
+
expect(decision).toEqual({
|
|
60
|
+
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
61
|
+
metadata: {
|
|
62
|
+
source: 'Classifier',
|
|
63
|
+
latencyMs: expect.any(Number),
|
|
64
|
+
reasoning: mockApiResponse.reasoning,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
it('should route to PRO model for a complex task', async () => {
|
|
69
|
+
const mockApiResponse = {
|
|
70
|
+
reasoning: 'This is a complex task.',
|
|
71
|
+
model_choice: 'pro',
|
|
72
|
+
};
|
|
73
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
74
|
+
mockContext.request = [{ text: 'how do I build a spaceship?' }];
|
|
75
|
+
const decision = await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
76
|
+
expect(mockBaseLlmClient.generateJson).toHaveBeenCalledOnce();
|
|
77
|
+
expect(decision).toEqual({
|
|
78
|
+
model: DEFAULT_GEMINI_MODEL,
|
|
79
|
+
metadata: {
|
|
80
|
+
source: 'Classifier',
|
|
81
|
+
latencyMs: expect.any(Number),
|
|
82
|
+
reasoning: mockApiResponse.reasoning,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it('should return null if the classifier API call fails', async () => {
|
|
87
|
+
const consoleWarnSpy = vi
|
|
88
|
+
.spyOn(console, 'warn')
|
|
89
|
+
.mockImplementation(() => { });
|
|
90
|
+
const testError = new Error('API Failure');
|
|
91
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockRejectedValue(testError);
|
|
92
|
+
const decision = await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
93
|
+
expect(decision).toBeNull();
|
|
94
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
95
|
+
consoleWarnSpy.mockRestore();
|
|
96
|
+
});
|
|
97
|
+
it('should return null if the classifier returns a malformed JSON object', async () => {
|
|
98
|
+
const consoleWarnSpy = vi
|
|
99
|
+
.spyOn(console, 'warn')
|
|
100
|
+
.mockImplementation(() => { });
|
|
101
|
+
const malformedApiResponse = {
|
|
102
|
+
reasoning: 'This is a simple task.',
|
|
103
|
+
// model_choice is missing, which will cause a Zod parsing error.
|
|
104
|
+
};
|
|
105
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(malformedApiResponse);
|
|
106
|
+
const decision = await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
107
|
+
expect(decision).toBeNull();
|
|
108
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
109
|
+
consoleWarnSpy.mockRestore();
|
|
110
|
+
});
|
|
111
|
+
it('should filter out tool-related history before sending to classifier', async () => {
|
|
112
|
+
mockContext.history = [
|
|
113
|
+
{ role: 'user', parts: [{ text: 'call a tool' }] },
|
|
114
|
+
{ role: 'model', parts: [{ functionCall: { name: 'test_tool' } }] },
|
|
115
|
+
{
|
|
116
|
+
role: 'user',
|
|
117
|
+
parts: [
|
|
118
|
+
{ functionResponse: { name: 'test_tool', response: { ok: true } } },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{ role: 'user', parts: [{ text: 'another user turn' }] },
|
|
122
|
+
];
|
|
123
|
+
const mockApiResponse = {
|
|
124
|
+
reasoning: 'Simple.',
|
|
125
|
+
model_choice: 'flash',
|
|
126
|
+
};
|
|
127
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
128
|
+
await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
129
|
+
const generateJsonCall = vi.mocked(mockBaseLlmClient.generateJson).mock
|
|
130
|
+
.calls[0][0];
|
|
131
|
+
const contents = generateJsonCall.contents;
|
|
132
|
+
const expectedContents = [
|
|
133
|
+
{ role: 'user', parts: [{ text: 'call a tool' }] },
|
|
134
|
+
{ role: 'user', parts: [{ text: 'another user turn' }] },
|
|
135
|
+
{ role: 'user', parts: [{ text: 'simple task' }] },
|
|
136
|
+
];
|
|
137
|
+
expect(contents).toEqual(expectedContents);
|
|
138
|
+
});
|
|
139
|
+
it('should respect HISTORY_SEARCH_WINDOW and HISTORY_TURNS_FOR_CONTEXT', async () => {
|
|
140
|
+
const longHistory = [];
|
|
141
|
+
for (let i = 0; i < 30; i++) {
|
|
142
|
+
longHistory.push({ role: 'user', parts: [{ text: `Message ${i}` }] });
|
|
143
|
+
// Add noise that should be filtered
|
|
144
|
+
if (i % 2 === 0) {
|
|
145
|
+
longHistory.push({
|
|
146
|
+
role: 'model',
|
|
147
|
+
parts: [{ functionCall: { name: 'noise', args: {} } }],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
mockContext.history = longHistory;
|
|
152
|
+
const mockApiResponse = {
|
|
153
|
+
reasoning: 'Simple.',
|
|
154
|
+
model_choice: 'flash',
|
|
155
|
+
};
|
|
156
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
157
|
+
await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
158
|
+
const generateJsonCall = vi.mocked(mockBaseLlmClient.generateJson).mock
|
|
159
|
+
.calls[0][0];
|
|
160
|
+
const contents = generateJsonCall.contents;
|
|
161
|
+
// Manually calculate what the history should be
|
|
162
|
+
const HISTORY_SEARCH_WINDOW = 20;
|
|
163
|
+
const HISTORY_TURNS_FOR_CONTEXT = 4;
|
|
164
|
+
const historySlice = longHistory.slice(-HISTORY_SEARCH_WINDOW);
|
|
165
|
+
const cleanHistory = historySlice.filter((content) => !isFunctionCall(content) && !isFunctionResponse(content));
|
|
166
|
+
const finalHistory = cleanHistory.slice(-HISTORY_TURNS_FOR_CONTEXT);
|
|
167
|
+
expect(contents).toEqual([
|
|
168
|
+
...finalHistory,
|
|
169
|
+
{ role: 'user', parts: mockContext.request },
|
|
170
|
+
]);
|
|
171
|
+
// There should be 4 history items + the current request
|
|
172
|
+
expect(contents).toHaveLength(5);
|
|
173
|
+
});
|
|
174
|
+
it('should use a fallback promptId if not found in context', async () => {
|
|
175
|
+
const consoleWarnSpy = vi
|
|
176
|
+
.spyOn(console, 'warn')
|
|
177
|
+
.mockImplementation(() => { });
|
|
178
|
+
vi.mocked(promptIdContext.getStore).mockReturnValue(undefined);
|
|
179
|
+
const mockApiResponse = {
|
|
180
|
+
reasoning: 'Simple.',
|
|
181
|
+
model_choice: 'flash',
|
|
182
|
+
};
|
|
183
|
+
vi.mocked(mockBaseLlmClient.generateJson).mockResolvedValue(mockApiResponse);
|
|
184
|
+
await strategy.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
185
|
+
const generateJsonCall = vi.mocked(mockBaseLlmClient.generateJson).mock
|
|
186
|
+
.calls[0][0];
|
|
187
|
+
expect(generateJsonCall.promptId).toMatch(/^classifier-router-fallback-\d+-\w+$/);
|
|
188
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Could not find promptId in context. This is unexpected. Using a fallback ID:'));
|
|
189
|
+
consoleWarnSpy.mockRestore();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
//# sourceMappingURL=classifierStrategy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifierStrategy.test.js","sourceRoot":"","sources":["../../../../src/routing/strategies/classifierStrategy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { Config } from '../../config/config.js';
|
|
7
|
+
import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
|
8
|
+
import type { RoutingContext, RoutingDecision, RoutingStrategy, TerminalStrategy } from '../routingStrategy.js';
|
|
9
|
+
/**
|
|
10
|
+
* A strategy that attempts a list of child strategies in order (Chain of Responsibility).
|
|
11
|
+
*/
|
|
12
|
+
export declare class CompositeStrategy implements TerminalStrategy {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
private strategies;
|
|
15
|
+
/**
|
|
16
|
+
* Initializes the CompositeStrategy.
|
|
17
|
+
* @param strategies The strategies to try, in order of priority. The last strategy must be terminal.
|
|
18
|
+
* @param name The name of this composite configuration (e.g., 'router' or 'composite').
|
|
19
|
+
*/
|
|
20
|
+
constructor(strategies: [...RoutingStrategy[], TerminalStrategy], name?: string);
|
|
21
|
+
route(context: RoutingContext, config: Config, baseLlmClient: BaseLlmClient): Promise<RoutingDecision>;
|
|
22
|
+
/**
|
|
23
|
+
* Helper function to enhance the decision metadata with composite information.
|
|
24
|
+
*/
|
|
25
|
+
private finalizeDecision;
|
|
26
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* A strategy that attempts a list of child strategies in order (Chain of Responsibility).
|
|
8
|
+
*/
|
|
9
|
+
export class CompositeStrategy {
|
|
10
|
+
name;
|
|
11
|
+
strategies;
|
|
12
|
+
/**
|
|
13
|
+
* Initializes the CompositeStrategy.
|
|
14
|
+
* @param strategies The strategies to try, in order of priority. The last strategy must be terminal.
|
|
15
|
+
* @param name The name of this composite configuration (e.g., 'router' or 'composite').
|
|
16
|
+
*/
|
|
17
|
+
constructor(strategies, name = 'composite') {
|
|
18
|
+
this.strategies = strategies;
|
|
19
|
+
this.name = name;
|
|
20
|
+
}
|
|
21
|
+
async route(context, config, baseLlmClient) {
|
|
22
|
+
const startTime = performance.now();
|
|
23
|
+
// Separate non-terminal strategies from the terminal one.
|
|
24
|
+
// This separation allows TypeScript to understand the control flow guarantees.
|
|
25
|
+
const nonTerminalStrategies = this.strategies.slice(0, -1);
|
|
26
|
+
const terminalStrategy = this.strategies[this.strategies.length - 1];
|
|
27
|
+
// Try non-terminal strategies, allowing them to fail gracefully.
|
|
28
|
+
for (const strategy of nonTerminalStrategies) {
|
|
29
|
+
try {
|
|
30
|
+
const decision = await strategy.route(context, config, baseLlmClient);
|
|
31
|
+
if (decision) {
|
|
32
|
+
return this.finalizeDecision(decision, startTime);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(`[Routing] Strategy '${strategy.name}' failed. Continuing to next strategy. Error:`, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// If no other strategy matched, execute the terminal strategy.
|
|
40
|
+
try {
|
|
41
|
+
const decision = await terminalStrategy.route(context, config, baseLlmClient);
|
|
42
|
+
return this.finalizeDecision(decision, startTime);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`[Routing] Critical Error: Terminal strategy '${terminalStrategy.name}' failed. Routing cannot proceed. Error:`, error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Helper function to enhance the decision metadata with composite information.
|
|
51
|
+
*/
|
|
52
|
+
finalizeDecision(decision, startTime) {
|
|
53
|
+
const endTime = performance.now();
|
|
54
|
+
const totalLatency = endTime - startTime;
|
|
55
|
+
// Combine the source paths: composite_name/child_source (e.g. 'router/default')
|
|
56
|
+
const compositeSource = `${this.name}/${decision.metadata.source}`;
|
|
57
|
+
return {
|
|
58
|
+
...decision,
|
|
59
|
+
metadata: {
|
|
60
|
+
...decision.metadata,
|
|
61
|
+
source: compositeSource,
|
|
62
|
+
latencyMs: decision.metadata.latencyMs || totalLatency,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=compositeStrategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositeStrategy.js","sourceRoot":"","sources":["../../../../src/routing/strategies/compositeStrategy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACnB,IAAI,CAAS;IAEd,UAAU,CAA2C;IAE7D;;;;OAIG;IACH,YACE,UAAoD,EACpD,OAAe,WAAW;QAE1B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,KAAK,CACT,OAAuB,EACvB,MAAc,EACd,aAA4B;QAE5B,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,0DAA0D;QAC1D,+EAA+E;QAC/E,MAAM,qBAAqB,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CACjD,CAAC,EACD,CAAC,CAAC,CACkB,CAAC;QACvB,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU,CACtC,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CACP,CAAC;QAEtB,iEAAiE;QACjE,KAAK,MAAM,QAAQ,IAAI,qBAAqB,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;gBACtE,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CACX,uBAAuB,QAAQ,CAAC,IAAI,+CAA+C,EACnF,KAAK,CACN,CAAC;YACJ,CAAC;QACH,CAAC;QAED,+DAA+D;QAC/D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAC3C,OAAO,EACP,MAAM,EACN,aAAa,CACd,CAAC;YAEF,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CACX,gDAAgD,gBAAgB,CAAC,IAAI,0CAA0C,EAC/G,KAAK,CACN,CAAC;YACF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACK,gBAAgB,CACtB,QAAyB,EACzB,SAAiB;QAEjB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,CAAC;QAEzC,gFAAgF;QAChF,MAAM,eAAe,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAEnE,OAAO;YACL,GAAG,QAAQ;YACX,QAAQ,EAAE;gBACR,GAAG,QAAQ,CAAC,QAAQ;gBACpB,MAAM,EAAE,eAAe;gBACvB,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,SAAS,IAAI,YAAY;aACvD;SACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
import { CompositeStrategy } from './compositeStrategy.js';
|
|
8
|
+
describe('CompositeStrategy', () => {
|
|
9
|
+
let mockContext;
|
|
10
|
+
let mockConfig;
|
|
11
|
+
let mockBaseLlmClient;
|
|
12
|
+
let mockStrategy1;
|
|
13
|
+
let mockStrategy2;
|
|
14
|
+
let mockTerminalStrategy;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
mockContext = {};
|
|
18
|
+
mockConfig = {};
|
|
19
|
+
mockBaseLlmClient = {};
|
|
20
|
+
mockStrategy1 = {
|
|
21
|
+
name: 'strategy1',
|
|
22
|
+
route: vi.fn().mockResolvedValue(null),
|
|
23
|
+
};
|
|
24
|
+
mockStrategy2 = {
|
|
25
|
+
name: 'strategy2',
|
|
26
|
+
route: vi.fn().mockResolvedValue(null),
|
|
27
|
+
};
|
|
28
|
+
mockTerminalStrategy = {
|
|
29
|
+
name: 'terminal',
|
|
30
|
+
route: vi.fn().mockResolvedValue({
|
|
31
|
+
model: 'terminal-model',
|
|
32
|
+
metadata: {
|
|
33
|
+
source: 'terminal',
|
|
34
|
+
latencyMs: 10,
|
|
35
|
+
reasoning: 'Terminal decision',
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
it('should try strategies in order and return the first successful decision', async () => {
|
|
41
|
+
const decision = {
|
|
42
|
+
model: 'strategy2-model',
|
|
43
|
+
metadata: {
|
|
44
|
+
source: 'strategy2',
|
|
45
|
+
latencyMs: 20,
|
|
46
|
+
reasoning: 'Strategy 2 decided',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
vi.spyOn(mockStrategy2, 'route').mockResolvedValue(decision);
|
|
50
|
+
const composite = new CompositeStrategy([mockStrategy1, mockStrategy2, mockTerminalStrategy], 'test-router');
|
|
51
|
+
const result = await composite.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
52
|
+
expect(mockStrategy1.route).toHaveBeenCalledWith(mockContext, mockConfig, mockBaseLlmClient);
|
|
53
|
+
expect(mockStrategy2.route).toHaveBeenCalledWith(mockContext, mockConfig, mockBaseLlmClient);
|
|
54
|
+
expect(mockTerminalStrategy.route).not.toHaveBeenCalled();
|
|
55
|
+
expect(result.model).toBe('strategy2-model');
|
|
56
|
+
expect(result.metadata.source).toBe('test-router/strategy2');
|
|
57
|
+
});
|
|
58
|
+
it('should fall back to the terminal strategy if no other strategy provides a decision', async () => {
|
|
59
|
+
const composite = new CompositeStrategy([mockStrategy1, mockStrategy2, mockTerminalStrategy], 'test-router');
|
|
60
|
+
const result = await composite.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
61
|
+
expect(mockStrategy1.route).toHaveBeenCalledTimes(1);
|
|
62
|
+
expect(mockStrategy2.route).toHaveBeenCalledTimes(1);
|
|
63
|
+
expect(mockTerminalStrategy.route).toHaveBeenCalledTimes(1);
|
|
64
|
+
expect(result.model).toBe('terminal-model');
|
|
65
|
+
expect(result.metadata.source).toBe('test-router/terminal');
|
|
66
|
+
});
|
|
67
|
+
it('should handle errors in non-terminal strategies and continue', async () => {
|
|
68
|
+
const consoleErrorSpy = vi
|
|
69
|
+
.spyOn(console, 'error')
|
|
70
|
+
.mockImplementation(() => { });
|
|
71
|
+
vi.spyOn(mockStrategy1, 'route').mockRejectedValue(new Error('Strategy 1 failed'));
|
|
72
|
+
const composite = new CompositeStrategy([mockStrategy1, mockTerminalStrategy], 'test-router');
|
|
73
|
+
const result = await composite.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
74
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith("[Routing] Strategy 'strategy1' failed. Continuing to next strategy. Error:", expect.any(Error));
|
|
75
|
+
expect(result.model).toBe('terminal-model');
|
|
76
|
+
consoleErrorSpy.mockRestore();
|
|
77
|
+
});
|
|
78
|
+
it('should re-throw an error from the terminal strategy', async () => {
|
|
79
|
+
const consoleErrorSpy = vi
|
|
80
|
+
.spyOn(console, 'error')
|
|
81
|
+
.mockImplementation(() => { });
|
|
82
|
+
const terminalError = new Error('Terminal strategy failed');
|
|
83
|
+
vi.spyOn(mockTerminalStrategy, 'route').mockRejectedValue(terminalError);
|
|
84
|
+
const composite = new CompositeStrategy([mockTerminalStrategy]);
|
|
85
|
+
await expect(composite.route(mockContext, mockConfig, mockBaseLlmClient)).rejects.toThrow(terminalError);
|
|
86
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith("[Routing] Critical Error: Terminal strategy 'terminal' failed. Routing cannot proceed. Error:", terminalError);
|
|
87
|
+
consoleErrorSpy.mockRestore();
|
|
88
|
+
});
|
|
89
|
+
it('should correctly finalize the decision metadata', async () => {
|
|
90
|
+
const decision = {
|
|
91
|
+
model: 'some-model',
|
|
92
|
+
metadata: {
|
|
93
|
+
source: 'child-source',
|
|
94
|
+
latencyMs: 50,
|
|
95
|
+
reasoning: 'Child reasoning',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
vi.spyOn(mockStrategy1, 'route').mockResolvedValue(decision);
|
|
99
|
+
const composite = new CompositeStrategy([mockStrategy1, mockTerminalStrategy], 'my-composite');
|
|
100
|
+
const result = await composite.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
101
|
+
expect(result.model).toBe('some-model');
|
|
102
|
+
expect(result.metadata.source).toBe('my-composite/child-source');
|
|
103
|
+
expect(result.metadata.reasoning).toBe('Child reasoning');
|
|
104
|
+
// It should keep the child's latency
|
|
105
|
+
expect(result.metadata.latencyMs).toBe(50);
|
|
106
|
+
});
|
|
107
|
+
it('should calculate total latency if child latency is not provided', async () => {
|
|
108
|
+
const decision = {
|
|
109
|
+
model: 'some-model',
|
|
110
|
+
metadata: {
|
|
111
|
+
source: 'child-source',
|
|
112
|
+
// No latencyMs here
|
|
113
|
+
latencyMs: 0,
|
|
114
|
+
reasoning: 'Child reasoning',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
vi.spyOn(mockStrategy1, 'route').mockResolvedValue(decision);
|
|
118
|
+
const composite = new CompositeStrategy([mockStrategy1, mockTerminalStrategy], 'my-composite');
|
|
119
|
+
const result = await composite.route(mockContext, mockConfig, mockBaseLlmClient);
|
|
120
|
+
expect(result.metadata.latencyMs).toBeGreaterThanOrEqual(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=compositeStrategy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositeStrategy.test.js","sourceRoot":"","sources":["../../../../src/routing/strategies/compositeStrategy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { Config } from '../../config/config.js';
|
|
7
|
+
import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
|
8
|
+
import type { RoutingContext, RoutingDecision, TerminalStrategy } from '../routingStrategy.js';
|
|
9
|
+
export declare class DefaultStrategy implements TerminalStrategy {
|
|
10
|
+
readonly name = "default";
|
|
11
|
+
route(_context: RoutingContext, _config: Config, _baseLlmClient: BaseLlmClient): Promise<RoutingDecision>;
|
|
12
|
+
}
|