converse-mcp-server 3.7.1 → 4.1.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/.env.example +22 -3
- package/README.md +86 -49
- package/docs/API.md +160 -18
- package/docs/EXAMPLES.md +38 -0
- package/docs/PROVIDERS.md +92 -52
- package/package.json +1 -1
- package/src/config.js +36 -6
- package/src/decisionProviders/index.js +174 -0
- package/src/decisionProviders/systemOne.js +199 -0
- package/src/prompts/helpPrompt.js +50 -3
- package/src/providers/anthropic.js +5 -1
- package/src/providers/claude.js +39 -88
- package/src/providers/codex.js +73 -144
- package/src/providers/copilot.js +42 -149
- package/src/providers/deepseek.js +1 -0
- package/src/providers/gemini-cli.js +64 -97
- package/src/providers/google.js +5 -1
- package/src/providers/mistral.js +5 -1
- package/src/providers/openai-compatible.js +4 -1
- package/src/providers/openai.js +5 -1
- package/src/providers/openrouter.js +2 -1
- package/src/providers/xai.js +5 -1
- package/src/services/summarizationService.js +45 -49
- package/src/tools/chat.js +56 -52
- package/src/tools/decide.js +312 -0
- package/src/tools/index.js +2 -0
- package/src/tools/modes/roundtable.js +60 -49
- package/src/utils/localProviderAuth.js +63 -0
- package/src/utils/modelCatalog.js +38 -0
- package/src/utils/modelRouting.js +580 -343
|
@@ -18,11 +18,10 @@
|
|
|
18
18
|
* interactively (`agy`) via Google OAuth. The first interactive login also
|
|
19
19
|
* establishes workspace trust for the user's home directory.
|
|
20
20
|
*
|
|
21
|
-
* The provider registry key remains 'gemini-cli'
|
|
22
|
-
*
|
|
23
|
-
* model
|
|
24
|
-
*
|
|
25
|
-
* lists first, while 3.1 Pro remains the only Pro tier Antigravity offers.
|
|
21
|
+
* The provider registry key remains 'gemini-cli'; its namespaces are `gemini:`
|
|
22
|
+
* and `agy:`. Flash is the default: Gemini 3.8 Flash is the current-generation
|
|
23
|
+
* model agy lists first, while 3.1 Pro remains the only Pro tier Antigravity
|
|
24
|
+
* offers.
|
|
26
25
|
*/
|
|
27
26
|
|
|
28
27
|
import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
@@ -32,6 +31,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
32
31
|
import { debugLog, debugError } from '../utils/console.js';
|
|
33
32
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
34
33
|
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
34
|
+
import { findCatalogEntry } from '../utils/modelCatalog.js';
|
|
35
35
|
|
|
36
36
|
// Prompts at or below this length pass directly as the -p argv value (fast
|
|
37
37
|
// path). Larger prompts are written to a file and -p carries a bootstrap
|
|
@@ -53,13 +53,15 @@ const POST_KILL_GRACE_MS = 5000;
|
|
|
53
53
|
const PTY_COLS = 1000;
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Supported Gemini models
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Supported Gemini models, keyed by the same model IDs the Google API provider
|
|
57
|
+
* uses so a bare name resolves to one model whichever provider serves it. Each
|
|
58
|
+
* maps to an agy display-name base that gets a reasoning-effort suffix
|
|
59
|
+
* appended at spawn time. All are text-only (print mode has no image input
|
|
60
|
+
* channel).
|
|
59
61
|
*/
|
|
60
62
|
const SUPPORTED_MODELS = {
|
|
61
|
-
gemini: {
|
|
62
|
-
modelName: 'gemini',
|
|
63
|
+
'gemini-3.8-flash': {
|
|
64
|
+
modelName: 'gemini-3.8-flash',
|
|
63
65
|
friendlyName: 'Gemini 3.8 Flash (via Antigravity CLI)',
|
|
64
66
|
contextWindow: 1048576,
|
|
65
67
|
maxOutputTokens: 65536,
|
|
@@ -69,28 +71,23 @@ const SUPPORTED_MODELS = {
|
|
|
69
71
|
supportsThinking: true,
|
|
70
72
|
timeout: DEFAULT_TIMEOUT_MS,
|
|
71
73
|
description:
|
|
72
|
-
'Gemini 3.8 Flash via Antigravity CLI (agy) - requires Antigravity Google OAuth login',
|
|
73
|
-
aliases: [
|
|
74
|
+
'Gemini 3.8 Flash via Antigravity CLI (agy, default) - requires Antigravity Google OAuth login',
|
|
75
|
+
aliases: [
|
|
76
|
+
'flash',
|
|
77
|
+
'gemini-3.8',
|
|
78
|
+
'gemini3.8',
|
|
79
|
+
'gemini-3.8-flash-latest',
|
|
80
|
+
'flash-3.8',
|
|
81
|
+
'flash3.8',
|
|
82
|
+
'gemini-flash-3.8',
|
|
83
|
+
'gemini flash 3.8',
|
|
84
|
+
'3.8-flash',
|
|
85
|
+
],
|
|
74
86
|
// agy display-name base; reasoning_effort selects the parenthesized variant
|
|
75
87
|
agyModelBase: 'Gemini 3.8 Flash',
|
|
76
88
|
},
|
|
77
|
-
'gemini
|
|
78
|
-
modelName: 'gemini
|
|
79
|
-
friendlyName: 'Gemini 3.8 Flash (via Antigravity CLI)',
|
|
80
|
-
contextWindow: 1048576,
|
|
81
|
-
maxOutputTokens: 65536,
|
|
82
|
-
supportsStreaming: true,
|
|
83
|
-
supportsImages: false,
|
|
84
|
-
supportsWebSearch: false,
|
|
85
|
-
supportsThinking: true,
|
|
86
|
-
timeout: DEFAULT_TIMEOUT_MS,
|
|
87
|
-
description:
|
|
88
|
-
'Gemini 3.8 Flash via Antigravity CLI (agy) - explicit alias of `gemini`',
|
|
89
|
-
aliases: ['flash'],
|
|
90
|
-
agyModelBase: 'Gemini 3.8 Flash',
|
|
91
|
-
},
|
|
92
|
-
'gemini:pro': {
|
|
93
|
-
modelName: 'gemini:pro',
|
|
89
|
+
'gemini-3.1-pro-preview': {
|
|
90
|
+
modelName: 'gemini-3.1-pro-preview',
|
|
94
91
|
friendlyName: 'Gemini 3.1 Pro (via Antigravity CLI)',
|
|
95
92
|
contextWindow: 1048576,
|
|
96
93
|
maxOutputTokens: 65536,
|
|
@@ -101,11 +98,26 @@ const SUPPORTED_MODELS = {
|
|
|
101
98
|
timeout: DEFAULT_TIMEOUT_MS,
|
|
102
99
|
description:
|
|
103
100
|
'Gemini 3.1 Pro via Antigravity CLI (agy) - requires Antigravity Google OAuth login',
|
|
104
|
-
aliases: [
|
|
101
|
+
aliases: [
|
|
102
|
+
'pro',
|
|
103
|
+
'gemini-pro',
|
|
104
|
+
'gemini pro',
|
|
105
|
+
'gemini-3.1-pro',
|
|
106
|
+
'gemini-3.1',
|
|
107
|
+
'gemini3.1',
|
|
108
|
+
'3.1-pro',
|
|
109
|
+
'gemini-3',
|
|
110
|
+
'gemini3',
|
|
111
|
+
'gemini-3-pro',
|
|
112
|
+
'gemini-3-pro-preview',
|
|
113
|
+
'3-pro',
|
|
114
|
+
],
|
|
105
115
|
agyModelBase: 'Gemini 3.1 Pro',
|
|
106
116
|
},
|
|
107
117
|
};
|
|
108
118
|
|
|
119
|
+
const DEFAULT_MODEL = 'gemini-3.8-flash';
|
|
120
|
+
|
|
109
121
|
/**
|
|
110
122
|
* Custom error class for Gemini CLI (agy) provider errors
|
|
111
123
|
*/
|
|
@@ -197,45 +209,24 @@ function effortSuffix(base, reasoningEffort) {
|
|
|
197
209
|
}
|
|
198
210
|
|
|
199
211
|
/**
|
|
200
|
-
* Resolve a
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* @param {string} model - e.g. 'gemini', 'gemini:flash', or a full agy name
|
|
212
|
+
* Resolve a model name + reasoning_effort to the agy display name passed via
|
|
213
|
+
* --model. The router hands over canonical catalog IDs; aliases are accepted
|
|
214
|
+
* for direct callers.
|
|
215
|
+
* @param {string} [model] - Catalog ID or alias; defaults to DEFAULT_MODEL
|
|
205
216
|
* @param {string} [reasoningEffort]
|
|
206
217
|
* @returns {string} agy --model value, e.g. 'Gemini 3.8 Flash (High)'
|
|
218
|
+
* @throws {GeminiCliProviderError} When the name is not in the catalog
|
|
207
219
|
*/
|
|
208
220
|
export function resolveAgyModel(model, reasoningEffort) {
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
let name = raw;
|
|
217
|
-
if (name.toLowerCase().startsWith('gemini:')) {
|
|
218
|
-
name = name.slice('gemini:'.length).trim();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const nameLower = name.toLowerCase();
|
|
222
|
-
|
|
223
|
-
// Determine the agy base
|
|
224
|
-
let base;
|
|
225
|
-
if (
|
|
226
|
-
!nameLower ||
|
|
227
|
-
nameLower === 'gemini' ||
|
|
228
|
-
nameLower === 'gemini-cli' ||
|
|
229
|
-
nameLower === 'flash'
|
|
230
|
-
) {
|
|
231
|
-
base = SUPPORTED_MODELS.gemini.agyModelBase;
|
|
232
|
-
} else if (nameLower === 'pro') {
|
|
233
|
-
base = SUPPORTED_MODELS['gemini:pro'].agyModelBase;
|
|
234
|
-
} else {
|
|
235
|
-
// Unknown suffix: pass through verbatim (power-user agy display name)
|
|
236
|
-
return raw;
|
|
221
|
+
const name = typeof model === 'string' && model.trim() ? model : DEFAULT_MODEL;
|
|
222
|
+
const entry = findCatalogEntry(SUPPORTED_MODELS, name);
|
|
223
|
+
if (!entry) {
|
|
224
|
+
throw new GeminiCliProviderError(
|
|
225
|
+
`Unknown Antigravity model "${model}"`,
|
|
226
|
+
ErrorCodes.MODEL_NOT_FOUND,
|
|
227
|
+
);
|
|
237
228
|
}
|
|
238
|
-
|
|
229
|
+
const base = entry.agyModelBase;
|
|
239
230
|
return `${base} ${effortSuffix(base, reasoningEffort)}`;
|
|
240
231
|
}
|
|
241
232
|
|
|
@@ -654,7 +645,7 @@ async function* createStreamingGenerator(fullText, userFacingModel) {
|
|
|
654
645
|
* provider errors.
|
|
655
646
|
*/
|
|
656
647
|
async function executeAgy(messages, options) {
|
|
657
|
-
const { model =
|
|
648
|
+
const { model = DEFAULT_MODEL, reasoning_effort, signal, timeout } = options;
|
|
658
649
|
|
|
659
650
|
const prompt = buildPrompt(messages);
|
|
660
651
|
const agyModel = resolveAgyModel(model, reasoning_effort);
|
|
@@ -699,7 +690,7 @@ export const geminiCliProvider = {
|
|
|
699
690
|
* @returns {Promise<Object>|AsyncGenerator} Response or stream generator
|
|
700
691
|
*/
|
|
701
692
|
async invoke(messages, options = {}) {
|
|
702
|
-
const { model =
|
|
693
|
+
const { model = DEFAULT_MODEL, stream = false, signal } = options;
|
|
703
694
|
|
|
704
695
|
if (signal?.aborted) {
|
|
705
696
|
throw new GeminiCliProviderError('Request cancelled', 'CANCELLED');
|
|
@@ -729,9 +720,13 @@ export const geminiCliProvider = {
|
|
|
729
720
|
};
|
|
730
721
|
},
|
|
731
722
|
|
|
723
|
+
defaultModel: DEFAULT_MODEL,
|
|
724
|
+
|
|
732
725
|
/**
|
|
733
726
|
* Validate configuration. agy uses OAuth (no env keys); always true.
|
|
734
|
-
* Availability is determined by isAvailable (binary presence).
|
|
727
|
+
* Availability is determined by isAvailable (binary presence). agy keeps
|
|
728
|
+
* its login outside any file converse can check, so a logged-out agy
|
|
729
|
+
* surfaces at invoke time and bare-name/auto routing fails over.
|
|
735
730
|
*/
|
|
736
731
|
validateConfig(_config) {
|
|
737
732
|
return true;
|
|
@@ -752,37 +747,9 @@ export const geminiCliProvider = {
|
|
|
752
747
|
},
|
|
753
748
|
|
|
754
749
|
/**
|
|
755
|
-
* Get model configuration for a specific model (alias-aware
|
|
750
|
+
* Get model configuration for a specific model (alias-aware).
|
|
756
751
|
*/
|
|
757
752
|
getModelConfig(modelName) {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const name = modelName.toLowerCase().trim();
|
|
761
|
-
|
|
762
|
-
// Full agy display-name passthrough → matching tier config. Any 3.x Flash
|
|
763
|
-
// (agy also lists 3.6/3.7) shares the Flash config; any 3.x Pro the Pro one.
|
|
764
|
-
if (/gemini 3\.\d+ flash/i.test(modelName)) {
|
|
765
|
-
return SUPPORTED_MODELS['gemini:flash'];
|
|
766
|
-
}
|
|
767
|
-
if (/gemini 3\.\d+ pro/i.test(modelName)) {
|
|
768
|
-
return SUPPORTED_MODELS['gemini:pro'];
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// Exact key match (gemini, gemini:flash, gemini:pro)
|
|
772
|
-
if (SUPPORTED_MODELS[name]) {
|
|
773
|
-
return SUPPORTED_MODELS[name];
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// Alias match
|
|
777
|
-
for (const config of Object.values(SUPPORTED_MODELS)) {
|
|
778
|
-
if (
|
|
779
|
-
config.aliases &&
|
|
780
|
-
config.aliases.some((alias) => alias.toLowerCase() === name)
|
|
781
|
-
) {
|
|
782
|
-
return config;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
return null;
|
|
753
|
+
return findCatalogEntry(SUPPORTED_MODELS, modelName);
|
|
787
754
|
},
|
|
788
755
|
};
|
package/src/providers/google.js
CHANGED
|
@@ -434,7 +434,11 @@ async function retryWithBackoff(fn, maxRetries = 4) {
|
|
|
434
434
|
/**
|
|
435
435
|
* Main Google provider implementation
|
|
436
436
|
*/
|
|
437
|
+
const DEFAULT_MODEL = 'gemini-3.1-pro-preview';
|
|
438
|
+
|
|
437
439
|
export const googleProvider = {
|
|
440
|
+
defaultModel: DEFAULT_MODEL,
|
|
441
|
+
|
|
438
442
|
/**
|
|
439
443
|
* Unified provider interface: invoke messages with options
|
|
440
444
|
* @param {Array} messages - Array of message objects with role and content
|
|
@@ -443,7 +447,7 @@ export const googleProvider = {
|
|
|
443
447
|
*/
|
|
444
448
|
async invoke(messages, options = {}) {
|
|
445
449
|
const {
|
|
446
|
-
model =
|
|
450
|
+
model = DEFAULT_MODEL,
|
|
447
451
|
maxTokens = null,
|
|
448
452
|
stream = false,
|
|
449
453
|
reasoning_effort = 'medium',
|
package/src/providers/mistral.js
CHANGED
|
@@ -319,7 +319,11 @@ function extractRateLimitInfo(headers) {
|
|
|
319
319
|
/**
|
|
320
320
|
* Main Mistral provider implementation
|
|
321
321
|
*/
|
|
322
|
+
const DEFAULT_MODEL = 'mistral-medium-3-5';
|
|
323
|
+
|
|
322
324
|
export const mistralProvider = {
|
|
325
|
+
defaultModel: DEFAULT_MODEL,
|
|
326
|
+
|
|
323
327
|
/**
|
|
324
328
|
* Unified provider interface: invoke messages with options
|
|
325
329
|
* @param {Array} messages - Array of message objects with role and content
|
|
@@ -328,7 +332,7 @@ export const mistralProvider = {
|
|
|
328
332
|
*/
|
|
329
333
|
async invoke(messages, options = {}) {
|
|
330
334
|
const {
|
|
331
|
-
model =
|
|
335
|
+
model = DEFAULT_MODEL,
|
|
332
336
|
maxTokens = null,
|
|
333
337
|
stream = false,
|
|
334
338
|
reasoning_effort = 'medium',
|
|
@@ -273,6 +273,7 @@ export function createOpenAICompatibleProvider(providerConfig) {
|
|
|
273
273
|
transformStreamChunk,
|
|
274
274
|
resolveModelConfig,
|
|
275
275
|
defaultParams = {},
|
|
276
|
+
defaultModel = Object.keys(supportedModels)[0],
|
|
276
277
|
} = providerConfig;
|
|
277
278
|
|
|
278
279
|
// Create custom error class for this provider
|
|
@@ -284,12 +285,14 @@ export function createOpenAICompatibleProvider(providerConfig) {
|
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
return {
|
|
288
|
+
defaultModel,
|
|
289
|
+
|
|
287
290
|
/**
|
|
288
291
|
* Unified provider interface: invoke messages with options
|
|
289
292
|
*/
|
|
290
293
|
async invoke(messages, options = {}) {
|
|
291
294
|
const {
|
|
292
|
-
model =
|
|
295
|
+
model = defaultModel,
|
|
293
296
|
maxTokens = null,
|
|
294
297
|
stream = false,
|
|
295
298
|
reasoning_effort = 'medium',
|
package/src/providers/openai.js
CHANGED
|
@@ -547,7 +547,11 @@ function convertMessages(messages, useResponsesAPI = false) {
|
|
|
547
547
|
/**
|
|
548
548
|
* Main OpenAI provider implementation
|
|
549
549
|
*/
|
|
550
|
+
const DEFAULT_MODEL = 'gpt-6-sol';
|
|
551
|
+
|
|
550
552
|
export const openaiProvider = {
|
|
553
|
+
defaultModel: DEFAULT_MODEL,
|
|
554
|
+
|
|
551
555
|
/**
|
|
552
556
|
* Unified provider interface: invoke messages with options
|
|
553
557
|
* @param {Array} messages - Array of message objects with role and content
|
|
@@ -556,7 +560,7 @@ export const openaiProvider = {
|
|
|
556
560
|
*/
|
|
557
561
|
async invoke(messages, options = {}) {
|
|
558
562
|
const {
|
|
559
|
-
model =
|
|
563
|
+
model = DEFAULT_MODEL,
|
|
560
564
|
maxTokens = null,
|
|
561
565
|
stream = false,
|
|
562
566
|
reasoning_effort = 'medium',
|
|
@@ -203,7 +203,7 @@ function validateApiKey(apiKey) {
|
|
|
203
203
|
* `X-Title`), while still accepting the legacy `openrouterreferer`/
|
|
204
204
|
* `openroutertitle` config-key spellings as input.
|
|
205
205
|
*/
|
|
206
|
-
function getCustomHeaders(config) {
|
|
206
|
+
export function getCustomHeaders(config) {
|
|
207
207
|
const headers = {};
|
|
208
208
|
|
|
209
209
|
const referer =
|
|
@@ -550,6 +550,7 @@ export const openrouterProvider = createOpenAICompatibleProvider({
|
|
|
550
550
|
baseURL: 'https://openrouter.ai/api/v1',
|
|
551
551
|
providerName: 'OpenRouter',
|
|
552
552
|
supportedModels: SUPPORTED_MODELS,
|
|
553
|
+
defaultModel: 'z-ai/glm-5.2',
|
|
553
554
|
validateApiKey,
|
|
554
555
|
transformRequest,
|
|
555
556
|
transformResponse,
|
package/src/providers/xai.js
CHANGED
|
@@ -212,7 +212,11 @@ function extractCitations(response) {
|
|
|
212
212
|
/**
|
|
213
213
|
* Main XAI provider implementation
|
|
214
214
|
*/
|
|
215
|
+
const DEFAULT_MODEL = 'grok-4.5';
|
|
216
|
+
|
|
215
217
|
export const xaiProvider = {
|
|
218
|
+
defaultModel: DEFAULT_MODEL,
|
|
219
|
+
|
|
216
220
|
/**
|
|
217
221
|
* Unified provider interface: invoke messages with options
|
|
218
222
|
* @param {Array} messages - Array of message objects with role and content
|
|
@@ -221,7 +225,7 @@ export const xaiProvider = {
|
|
|
221
225
|
*/
|
|
222
226
|
async invoke(messages, options = {}) {
|
|
223
227
|
const {
|
|
224
|
-
model =
|
|
228
|
+
model = DEFAULT_MODEL,
|
|
225
229
|
maxTokens = null,
|
|
226
230
|
stream = false,
|
|
227
231
|
reasoning_effort = 'medium',
|
|
@@ -9,20 +9,21 @@
|
|
|
9
9
|
import { createLogger } from '../utils/logger.js';
|
|
10
10
|
import { debugLog, debugError } from '../utils/console.js';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { resolveModelSpec } from '../utils/modelRouting.js';
|
|
13
13
|
|
|
14
14
|
const logger = createLogger('summarization');
|
|
15
15
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
// Fast models for summarization tasks, tried in order (GPT-5-nano first for
|
|
17
|
+
// speed). Namespaced so each names exactly one provider.
|
|
18
|
+
export const FAST_MODELS = [
|
|
19
|
+
'openai:gpt-5-nano',
|
|
20
|
+
'google:gemini-2.5-flash',
|
|
21
|
+
'xai:grok-4.5',
|
|
22
|
+
'anthropic:claude-haiku-4-5-20251001',
|
|
23
|
+
'mistral:mistral-small-2603',
|
|
24
|
+
'deepseek:deepseek-v4-flash',
|
|
25
|
+
'openrouter:z-ai/glm-5.2',
|
|
26
|
+
];
|
|
26
27
|
|
|
27
28
|
export class SummarizationService {
|
|
28
29
|
constructor(providers, config) {
|
|
@@ -51,16 +52,12 @@ export class SummarizationService {
|
|
|
51
52
|
|
|
52
53
|
try {
|
|
53
54
|
// Select fast model if not specified
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!provider || !provider.isAvailable(this.config)) {
|
|
59
|
-
debugLog(
|
|
60
|
-
`Summarization: Provider ${providerName} not available for title generation`,
|
|
61
|
-
);
|
|
55
|
+
const selected = this._resolve(model || this._selectFastModel());
|
|
56
|
+
if (!selected) {
|
|
57
|
+
debugLog('Summarization: No available model for title generation');
|
|
62
58
|
return this._fallbackTitle(prompt);
|
|
63
59
|
}
|
|
60
|
+
const { provider, resolvedModel: selectedModel } = selected;
|
|
64
61
|
|
|
65
62
|
// Create messages for title generation
|
|
66
63
|
const messages = [
|
|
@@ -112,16 +109,12 @@ export class SummarizationService {
|
|
|
112
109
|
|
|
113
110
|
try {
|
|
114
111
|
// Select fast model if not specified
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!provider || !provider.isAvailable(this.config)) {
|
|
120
|
-
debugLog(
|
|
121
|
-
`Summarization: Provider ${providerName} not available for streaming summary`,
|
|
122
|
-
);
|
|
112
|
+
const selected = this._resolve(model || this._selectFastModel());
|
|
113
|
+
if (!selected) {
|
|
114
|
+
debugLog('Summarization: No available model for streaming summary');
|
|
123
115
|
return this._fallbackStreamingSummary(content, currentFocus);
|
|
124
116
|
}
|
|
117
|
+
const { provider, resolvedModel: selectedModel } = selected;
|
|
125
118
|
|
|
126
119
|
// Create messages for streaming summary
|
|
127
120
|
const messages = [
|
|
@@ -175,16 +168,12 @@ export class SummarizationService {
|
|
|
175
168
|
|
|
176
169
|
try {
|
|
177
170
|
// Select fast model if not specified
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (!provider || !provider.isAvailable(this.config)) {
|
|
183
|
-
debugLog(
|
|
184
|
-
`Summarization: Provider ${providerName} not available for final summary`,
|
|
185
|
-
);
|
|
171
|
+
const selected = this._resolve(model || this._selectFastModel());
|
|
172
|
+
if (!selected) {
|
|
173
|
+
debugLog('Summarization: No available model for final summary');
|
|
186
174
|
return this._fallbackFinalSummary(content);
|
|
187
175
|
}
|
|
176
|
+
const { provider, resolvedModel: selectedModel } = selected;
|
|
188
177
|
|
|
189
178
|
// Create messages for final summary
|
|
190
179
|
const messages = [
|
|
@@ -221,6 +210,20 @@ export class SummarizationService {
|
|
|
221
210
|
}
|
|
222
211
|
}
|
|
223
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Route a model spec to its first available provider.
|
|
215
|
+
* @private
|
|
216
|
+
* @returns {{ provider: object, providerName: string, resolvedModel: string }|null}
|
|
217
|
+
*/
|
|
218
|
+
_resolve(spec) {
|
|
219
|
+
const resolution = resolveModelSpec(spec, this.providers, this.config);
|
|
220
|
+
if (resolution.status !== 'ok') {
|
|
221
|
+
debugLog(`Summarization: ${resolution.error}`);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
return resolution;
|
|
225
|
+
}
|
|
226
|
+
|
|
224
227
|
/**
|
|
225
228
|
* Select the best available fast model
|
|
226
229
|
* @private
|
|
@@ -228,14 +231,10 @@ export class SummarizationService {
|
|
|
228
231
|
_selectFastModel() {
|
|
229
232
|
// If a model is configured, try to use it first
|
|
230
233
|
if (this.configuredModel) {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
this.providers,
|
|
234
|
-
);
|
|
235
|
-
const provider = this.providers[providerName];
|
|
236
|
-
if (provider && provider.isAvailable(this.config)) {
|
|
234
|
+
const resolution = resolveModelSpec(this.configuredModel, this.providers, this.config);
|
|
235
|
+
if (resolution.status === 'ok') {
|
|
237
236
|
debugLog(
|
|
238
|
-
`Summarization: Using configured model ${this.configuredModel} from ${providerName}`,
|
|
237
|
+
`Summarization: Using configured model ${this.configuredModel} from ${resolution.providerName}`,
|
|
239
238
|
);
|
|
240
239
|
return this.configuredModel;
|
|
241
240
|
}
|
|
@@ -244,13 +243,10 @@ export class SummarizationService {
|
|
|
244
243
|
);
|
|
245
244
|
}
|
|
246
245
|
|
|
247
|
-
//
|
|
248
|
-
for (const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
debugLog(
|
|
252
|
-
`Summarization: Selected fast model ${fastModel} from ${providerName}`,
|
|
253
|
-
);
|
|
246
|
+
// Return the first fast model whose provider is available
|
|
247
|
+
for (const fastModel of FAST_MODELS) {
|
|
248
|
+
if (resolveModelSpec(fastModel, this.providers, this.config).status === 'ok') {
|
|
249
|
+
debugLog(`Summarization: Selected fast model ${fastModel}`);
|
|
254
250
|
return fastModel;
|
|
255
251
|
}
|
|
256
252
|
}
|