converse-mcp-server 2.4.2 → 2.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/docs/API.md +39 -3
- package/docs/PROVIDERS.md +69 -2
- package/package.json +3 -1
- package/src/providers/gemini-cli.js +388 -0
- package/src/providers/index.js +2 -0
- package/src/tools/chat.js +5 -0
- package/src/tools/consensus.js +10 -0
package/docs/API.md
CHANGED
|
@@ -331,16 +331,16 @@ MCP_TRANSPORT=stdio npm start
|
|
|
331
331
|
| `gpt-4o` | 128K | 16K | Multimodal | Vision, general chat |
|
|
332
332
|
| `gpt-4o-mini` | 128K | 16K | Fast multimodal | Quick responses, images |
|
|
333
333
|
|
|
334
|
-
### Google/Gemini Models
|
|
334
|
+
### Google/Gemini Models (API-based)
|
|
335
335
|
|
|
336
336
|
| Model | Alias | Context | Tokens | Features | Use Cases |
|
|
337
337
|
|-------|-------|---------|--------|----------|-----------|
|
|
338
|
-
| `gemini-3-pro-preview` | `pro
|
|
338
|
+
| `gemini-3-pro-preview` | `pro` | 1M | 64K | Thinking levels, enhanced reasoning | Complex problems, deep analysis |
|
|
339
339
|
| `gemini-2.5-flash` | `flash` | 1M | 65K | Ultra-fast | Quick analysis, simple queries |
|
|
340
340
|
| `gemini-2.5-pro` | `pro 2.5` | 1M | 65K | Thinking mode | Deep reasoning, architecture |
|
|
341
341
|
| `gemini-2.0-flash` | `flash2` | 1M | 65K | Latest | Experimental thinking |
|
|
342
342
|
|
|
343
|
-
**Note:**
|
|
343
|
+
**Note:** The short model name `gemini` now routes to **Gemini CLI** (OAuth-based). For Google API access, use specific model names like `gemini-2.5-pro` or `gemini-2.0-flash`.
|
|
344
344
|
|
|
345
345
|
### X.AI/Grok Models
|
|
346
346
|
|
|
@@ -398,6 +398,42 @@ MCP_TRANSPORT=stdio npm start
|
|
|
398
398
|
- **Response times**: 6-20 seconds typical (complex tasks may take minutes)
|
|
399
399
|
- **Authentication**: Requires ChatGPT login OR `CODEX_API_KEY` environment variable
|
|
400
400
|
|
|
401
|
+
### Gemini CLI Models (OAuth-based)
|
|
402
|
+
|
|
403
|
+
**Gemini CLI** provides subscription-based access to Gemini models through OAuth:
|
|
404
|
+
|
|
405
|
+
- **Model**: `gemini` (routes to gemini-3-pro-preview)
|
|
406
|
+
- **Authentication**: OAuth via Gemini CLI (requires one-time setup)
|
|
407
|
+
- **Setup**: Install `@google/gemini-cli` globally and run `gemini` to authenticate
|
|
408
|
+
- **Billing**: Uses Google subscription (Google One AI Premium or Gemini Advanced) instead of API credits
|
|
409
|
+
- **Credentials**: Stored in `~/.gemini/oauth_creds.json`
|
|
410
|
+
- **Features**: Access to enhanced agentic features available through CLI
|
|
411
|
+
- **Context**: 1M tokens (inherited from gemini-3-pro-preview)
|
|
412
|
+
- **Output**: 64K tokens
|
|
413
|
+
|
|
414
|
+
**Authentication Setup:**
|
|
415
|
+
```bash
|
|
416
|
+
# Install Gemini CLI globally
|
|
417
|
+
npm install -g @google/gemini-cli
|
|
418
|
+
|
|
419
|
+
# Run interactive authentication
|
|
420
|
+
gemini
|
|
421
|
+
|
|
422
|
+
# Follow prompts to authenticate via browser
|
|
423
|
+
# Credentials are saved to ~/.gemini/oauth_creds.json
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Usage Example:**
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"name": "chat",
|
|
430
|
+
"arguments": {
|
|
431
|
+
"prompt": "Explain the event loop in JavaScript",
|
|
432
|
+
"model": "gemini"
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
401
437
|
**Codex-Specific Behavior:**
|
|
402
438
|
- `continuation_id` - Required for thread continuation (maintains full conversation history)
|
|
403
439
|
- `files` parameter - Files accessed directly from working directory, not passed as message content
|
package/docs/PROVIDERS.md
CHANGED
|
@@ -20,11 +20,11 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
20
20
|
- **Get Key**: [makersuite.google.com/app/apikey](https://makersuite.google.com/app/apikey)
|
|
21
21
|
- **Environment Variable**: `GOOGLE_API_KEY`
|
|
22
22
|
- **Supported Models**:
|
|
23
|
-
- `gemini-3-pro-preview` (
|
|
23
|
+
- `gemini-3-pro-preview` (alias: `pro`) - Enhanced reasoning with thinking levels (1M context, 64K output)
|
|
24
24
|
- `gemini-2.5-pro` (alias: `pro 2.5`) - Deep reasoning with thinking budget (1M context, 65K output)
|
|
25
25
|
- `gemini-2.5-flash` (alias: `flash`) - Ultra-fast model with thinking budget (1M context, 65K output)
|
|
26
26
|
- `gemini-2.0-flash`, `gemini-2.0-flash-lite` - Latest generation (1M context, 65K output)
|
|
27
|
-
- **Note**:
|
|
27
|
+
- **Note**: The short model name `gemini` now routes to **Gemini CLI** (OAuth-based access). For Google API access, use specific model names like `gemini-2.5-pro` or `gemini-2.0-flash`.
|
|
28
28
|
|
|
29
29
|
### X.AI (Grok)
|
|
30
30
|
- **API Key Format**: `xai-...` (starts with `xai-`)
|
|
@@ -114,6 +114,73 @@ This guide documents all supported AI providers in the Converse MCP Server and t
|
|
|
114
114
|
- Use `CODEX_APPROVAL_POLICY=never` for headless server deployments
|
|
115
115
|
- Always use `continuation_id` for thread continuation
|
|
116
116
|
|
|
117
|
+
### Gemini CLI
|
|
118
|
+
- **Authentication**: OAuth via Gemini CLI (no API key needed)
|
|
119
|
+
- **Setup Required**:
|
|
120
|
+
1. Install Gemini CLI globally: `npm install -g @google/gemini-cli`
|
|
121
|
+
2. Authenticate: Run `gemini` command and follow interactive prompts
|
|
122
|
+
3. Credentials stored in `~/.gemini/oauth_creds.json`
|
|
123
|
+
- **Environment Variables**: None (uses OAuth credentials file)
|
|
124
|
+
- **Supported Models**:
|
|
125
|
+
- `gemini` - Routes to gemini-3-pro-preview via CLI
|
|
126
|
+
- Provides access to Gemini 3.0 Pro Preview through Google subscription (Google One AI Premium or Gemini Advanced)
|
|
127
|
+
|
|
128
|
+
**Key Features:**
|
|
129
|
+
- **OAuth Authentication**: Uses Google account login instead of API keys
|
|
130
|
+
- **Subscription Access**: Leverage Google subscription instead of paying per API call
|
|
131
|
+
- **Enhanced Features**: Access to agentic features available through CLI that aren't in standard API
|
|
132
|
+
- **Model Support**: Currently supports gemini-3-pro-preview only
|
|
133
|
+
|
|
134
|
+
**Authentication Setup:**
|
|
135
|
+
```bash
|
|
136
|
+
# Install Gemini CLI globally
|
|
137
|
+
npm install -g @google/gemini-cli
|
|
138
|
+
|
|
139
|
+
# Run interactive authentication (one-time setup)
|
|
140
|
+
gemini
|
|
141
|
+
|
|
142
|
+
# Follow prompts to:
|
|
143
|
+
# 1. Select authentication method (Personal OAuth recommended)
|
|
144
|
+
# 2. Authorize via browser
|
|
145
|
+
# 3. Credentials are saved to ~/.gemini/oauth_creds.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Usage Examples:**
|
|
149
|
+
|
|
150
|
+
*Chat Tool:*
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"name": "chat",
|
|
154
|
+
"arguments": {
|
|
155
|
+
"prompt": "Explain async/await in JavaScript",
|
|
156
|
+
"model": "gemini"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
*Consensus Tool:*
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"name": "consensus",
|
|
165
|
+
"arguments": {
|
|
166
|
+
"prompt": "Should we use TypeScript for this component?",
|
|
167
|
+
"models": ["gemini", "gpt-5", "claude-sonnet-4"]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Best Practices:**
|
|
173
|
+
- Authenticate before first use (run `gemini` CLI command)
|
|
174
|
+
- Use specific model names for Google API access (e.g., `gemini-2.5-pro`)
|
|
175
|
+
- Model name `gemini` is reserved for CLI-based access
|
|
176
|
+
- Check credentials file exists at `~/.gemini/oauth_creds.json` if authentication fails
|
|
177
|
+
|
|
178
|
+
**Differences from Google API Provider:**
|
|
179
|
+
- **Authentication**: OAuth (CLI) vs API Key (Google API)
|
|
180
|
+
- **Billing**: Google subscription vs pay-per-use API
|
|
181
|
+
- **Model Routing**: `gemini` → CLI provider, specific names (e.g., `gemini-2.5-pro`) → API provider
|
|
182
|
+
- **Models**: Only gemini-3-pro-preview vs full Gemini model family
|
|
183
|
+
|
|
117
184
|
## Configuration Examples
|
|
118
185
|
|
|
119
186
|
### Basic Configuration (.env file)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "converse-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Converse MCP Server - Converse with other LLMs with chat and consensus tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
"@mistralai/mistralai": "^1.10.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
47
47
|
"@openai/codex-sdk": "^0.58.0",
|
|
48
|
+
"ai": "^5.0.101",
|
|
49
|
+
"ai-sdk-provider-gemini-cli": "^1.4.0",
|
|
48
50
|
"cors": "^2.8.5",
|
|
49
51
|
"dotenv": "^17.2.3",
|
|
50
52
|
"express": "^5.1.0",
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI Provider
|
|
3
|
+
*
|
|
4
|
+
* Provider implementation for Google's Gemini models using the ai-sdk-provider-gemini-cli package.
|
|
5
|
+
* Implements the unified interface: async invoke(messages, options) => { content, stop_reason, rawResponse }
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Uses OAuth authentication from Gemini CLI (no API keys needed)
|
|
9
|
+
* - Supports gemini-3-pro-preview model via Google Cloud Code endpoints
|
|
10
|
+
* - Uses AI SDK v5 standard interfaces (generateText/streamText)
|
|
11
|
+
* - Compatible with both chat and consensus tools
|
|
12
|
+
*
|
|
13
|
+
* Authentication:
|
|
14
|
+
* - Requires global Gemini CLI installation: npm install -g @google/gemini-cli
|
|
15
|
+
* - User must authenticate once via: gemini (interactive CLI)
|
|
16
|
+
* - Credentials stored in ~/.gemini/oauth_creds.json
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { debugLog, debugError } from '../utils/console.js';
|
|
23
|
+
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
24
|
+
|
|
25
|
+
// Supported Gemini CLI models with their configurations
|
|
26
|
+
const SUPPORTED_MODELS = {
|
|
27
|
+
'gemini-3-pro-preview': {
|
|
28
|
+
modelName: 'gemini-3-pro-preview',
|
|
29
|
+
friendlyName: 'Gemini 3.0 Pro Preview (via CLI)',
|
|
30
|
+
contextWindow: 1048576, // 1M tokens
|
|
31
|
+
maxOutputTokens: 64000,
|
|
32
|
+
supportsStreaming: true,
|
|
33
|
+
supportsImages: true, // Base64 only (no URLs)
|
|
34
|
+
supportsTemperature: true,
|
|
35
|
+
supportsThinking: true,
|
|
36
|
+
supportsWebSearch: true,
|
|
37
|
+
timeout: 300000, // 5 minutes
|
|
38
|
+
description:
|
|
39
|
+
'Gemini 3.0 Pro Preview via OAuth - requires Gemini CLI authentication',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Custom error class for Gemini CLI provider errors
|
|
45
|
+
*/
|
|
46
|
+
class GeminiCliProviderError extends ProviderError {
|
|
47
|
+
constructor(message, code, originalError = null) {
|
|
48
|
+
super(message, code, originalError);
|
|
49
|
+
this.name = 'GeminiCliProviderError';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if OAuth credentials file exists
|
|
55
|
+
* @returns {boolean} True if credentials file exists
|
|
56
|
+
*/
|
|
57
|
+
function hasOAuthCredentials() {
|
|
58
|
+
try {
|
|
59
|
+
const credsPath = join(homedir(), '.gemini', 'oauth_creds.json');
|
|
60
|
+
return existsSync(credsPath);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
debugError('[Gemini CLI] Error checking OAuth credentials', error);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Dynamically import Gemini CLI SDK (lazy loading)
|
|
69
|
+
* This keeps the SDK as an optional dependency
|
|
70
|
+
*/
|
|
71
|
+
async function getGeminiCliSDK() {
|
|
72
|
+
try {
|
|
73
|
+
// Use dynamic import to load SDK only when needed
|
|
74
|
+
const { createGeminiProvider } = await import('ai-sdk-provider-gemini-cli');
|
|
75
|
+
return createGeminiProvider;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new GeminiCliProviderError(
|
|
78
|
+
'Gemini CLI SDK not installed. Install with: npm install ai-sdk-provider-gemini-cli',
|
|
79
|
+
'GEMINI_CLI_NOT_INSTALLED',
|
|
80
|
+
error,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Dynamically import AI SDK (lazy loading)
|
|
87
|
+
*/
|
|
88
|
+
async function getAISDK() {
|
|
89
|
+
try {
|
|
90
|
+
const { generateText, streamText } = await import('ai');
|
|
91
|
+
return { generateText, streamText };
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new GeminiCliProviderError(
|
|
94
|
+
'AI SDK not installed. Install with: npm install ai',
|
|
95
|
+
'AI_SDK_NOT_INSTALLED',
|
|
96
|
+
error,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create stream generator for Gemini CLI streaming responses
|
|
103
|
+
* Yields normalized events compatible with ProviderStreamNormalizer
|
|
104
|
+
*/
|
|
105
|
+
async function* createStreamingGenerator(
|
|
106
|
+
model,
|
|
107
|
+
messages,
|
|
108
|
+
options,
|
|
109
|
+
signal,
|
|
110
|
+
) {
|
|
111
|
+
const { streamText } = await getAISDK();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const streamOptions = {
|
|
115
|
+
model,
|
|
116
|
+
messages,
|
|
117
|
+
...options,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (signal) {
|
|
121
|
+
streamOptions.abortSignal = signal;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const result = await streamText(streamOptions);
|
|
125
|
+
|
|
126
|
+
// Yield start event
|
|
127
|
+
yield {
|
|
128
|
+
type: 'start',
|
|
129
|
+
provider: 'gemini-cli',
|
|
130
|
+
model: options.model || 'gemini-3-pro-preview',
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Stream text chunks
|
|
134
|
+
for await (const chunk of result.textStream) {
|
|
135
|
+
// Check for cancellation
|
|
136
|
+
if (signal?.aborted) {
|
|
137
|
+
throw new GeminiCliProviderError('Request cancelled', 'CANCELLED');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Yield delta event with content chunk
|
|
141
|
+
yield {
|
|
142
|
+
type: 'delta',
|
|
143
|
+
content: chunk,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Get final usage stats and metadata
|
|
148
|
+
const usage = await result.usage;
|
|
149
|
+
const finishReason = await result.finishReason;
|
|
150
|
+
|
|
151
|
+
// Yield usage event
|
|
152
|
+
if (usage) {
|
|
153
|
+
yield {
|
|
154
|
+
type: 'usage',
|
|
155
|
+
usage: {
|
|
156
|
+
input_tokens: usage.promptTokens || 0,
|
|
157
|
+
output_tokens: usage.completionTokens || 0,
|
|
158
|
+
total_tokens: usage.totalTokens || 0,
|
|
159
|
+
cached_input_tokens: 0,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Yield end event
|
|
165
|
+
yield {
|
|
166
|
+
type: 'end',
|
|
167
|
+
stop_reason: mapFinishReason(finishReason),
|
|
168
|
+
finish_reason: finishReason,
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (signal?.aborted) {
|
|
172
|
+
throw new GeminiCliProviderError('Request cancelled', 'CANCELLED');
|
|
173
|
+
}
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Map AI SDK finish reasons to our StopReasons enum
|
|
180
|
+
*/
|
|
181
|
+
function mapFinishReason(finishReason) {
|
|
182
|
+
switch (finishReason) {
|
|
183
|
+
case 'stop':
|
|
184
|
+
return StopReasons.STOP;
|
|
185
|
+
case 'length':
|
|
186
|
+
case 'max-tokens':
|
|
187
|
+
return StopReasons.LENGTH;
|
|
188
|
+
case 'content-filter':
|
|
189
|
+
return StopReasons.CONTENT_FILTER;
|
|
190
|
+
case 'tool-calls':
|
|
191
|
+
return StopReasons.TOOL_USE;
|
|
192
|
+
case 'error':
|
|
193
|
+
return StopReasons.ERROR;
|
|
194
|
+
default:
|
|
195
|
+
return StopReasons.OTHER;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Gemini CLI Provider Implementation
|
|
201
|
+
*/
|
|
202
|
+
export const geminiCliProvider = {
|
|
203
|
+
/**
|
|
204
|
+
* Invoke Gemini CLI with messages and options
|
|
205
|
+
* @param {Array} messages - Message array (Converse format)
|
|
206
|
+
* @param {Object} options - Invocation options
|
|
207
|
+
* @returns {Promise<Object>|AsyncGenerator} Response or stream generator
|
|
208
|
+
*/
|
|
209
|
+
async invoke(messages, options = {}) {
|
|
210
|
+
const {
|
|
211
|
+
model = 'gemini-3-pro-preview',
|
|
212
|
+
config,
|
|
213
|
+
stream = false,
|
|
214
|
+
signal,
|
|
215
|
+
reasoning_effort,
|
|
216
|
+
temperature,
|
|
217
|
+
use_websearch,
|
|
218
|
+
} = options;
|
|
219
|
+
|
|
220
|
+
// Validate configuration
|
|
221
|
+
if (!config) {
|
|
222
|
+
throw new GeminiCliProviderError(
|
|
223
|
+
'Configuration is required',
|
|
224
|
+
ErrorCodes.MISSING_API_KEY,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check OAuth credentials
|
|
229
|
+
if (!hasOAuthCredentials()) {
|
|
230
|
+
throw new GeminiCliProviderError(
|
|
231
|
+
'Gemini CLI authentication required. Run: gemini (interactive CLI) to authenticate',
|
|
232
|
+
ErrorCodes.INVALID_API_KEY,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// Get SDKs
|
|
238
|
+
const createGeminiProvider = await getGeminiCliSDK();
|
|
239
|
+
const { generateText } = await getAISDK();
|
|
240
|
+
|
|
241
|
+
// Create provider instance with OAuth authentication
|
|
242
|
+
const gemini = createGeminiProvider({
|
|
243
|
+
authType: 'oauth-personal',
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Create model instance
|
|
247
|
+
const modelInstance = gemini(model);
|
|
248
|
+
|
|
249
|
+
// Build AI SDK options
|
|
250
|
+
const aiOptions = {
|
|
251
|
+
messages,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// Add optional parameters
|
|
255
|
+
if (temperature !== undefined) {
|
|
256
|
+
aiOptions.temperature = temperature;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Note: reasoning_effort and use_websearch are not directly supported by AI SDK
|
|
260
|
+
// These would need to be handled at the API level if the provider supports them
|
|
261
|
+
if (reasoning_effort !== undefined) {
|
|
262
|
+
debugLog(
|
|
263
|
+
'[Gemini CLI] Parameter "reasoning_effort" not directly supported (ignored)',
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (use_websearch) {
|
|
267
|
+
debugLog(
|
|
268
|
+
'[Gemini CLI] Parameter "use_websearch" not directly supported (ignored)',
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Streaming mode
|
|
273
|
+
if (stream) {
|
|
274
|
+
return createStreamingGenerator(modelInstance, messages, aiOptions, signal);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Synchronous mode
|
|
278
|
+
const startTime = Date.now();
|
|
279
|
+
|
|
280
|
+
const result = await generateText({
|
|
281
|
+
model: modelInstance,
|
|
282
|
+
...aiOptions,
|
|
283
|
+
...(signal && { abortSignal: signal }),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const responseTime = Date.now() - startTime;
|
|
287
|
+
|
|
288
|
+
// Extract content from AI SDK v5 response format
|
|
289
|
+
const content = result.content?.[0]?.text || result.text || '';
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
content,
|
|
293
|
+
stop_reason: mapFinishReason(result.finishReason),
|
|
294
|
+
rawResponse: result,
|
|
295
|
+
metadata: {
|
|
296
|
+
provider: 'gemini-cli',
|
|
297
|
+
model,
|
|
298
|
+
usage: result.usage
|
|
299
|
+
? {
|
|
300
|
+
input_tokens: result.usage.promptTokens || 0,
|
|
301
|
+
output_tokens: result.usage.completionTokens || 0,
|
|
302
|
+
total_tokens: result.usage.totalTokens || 0,
|
|
303
|
+
cached_input_tokens: 0,
|
|
304
|
+
}
|
|
305
|
+
: null,
|
|
306
|
+
response_time_ms: responseTime,
|
|
307
|
+
finish_reason: result.finishReason || 'stop',
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
} catch (error) {
|
|
311
|
+
debugError('[Gemini CLI] Execution error', error);
|
|
312
|
+
|
|
313
|
+
// Map common errors to standard error codes
|
|
314
|
+
if (
|
|
315
|
+
error.message?.includes('authentication') ||
|
|
316
|
+
error.message?.includes('oauth') ||
|
|
317
|
+
error.message?.includes('credentials')
|
|
318
|
+
) {
|
|
319
|
+
throw new GeminiCliProviderError(
|
|
320
|
+
'Gemini CLI authentication failed. Run: gemini (interactive CLI) to authenticate',
|
|
321
|
+
ErrorCodes.INVALID_API_KEY,
|
|
322
|
+
error,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (error.message?.includes('rate limit')) {
|
|
327
|
+
throw new GeminiCliProviderError(
|
|
328
|
+
'Rate limit exceeded',
|
|
329
|
+
ErrorCodes.RATE_LIMIT_EXCEEDED,
|
|
330
|
+
error,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (error.message?.includes('timeout')) {
|
|
335
|
+
throw new GeminiCliProviderError(
|
|
336
|
+
'Request timeout',
|
|
337
|
+
ErrorCodes.TIMEOUT_ERROR,
|
|
338
|
+
error,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Re-throw as Gemini CLI error
|
|
343
|
+
throw new GeminiCliProviderError(
|
|
344
|
+
error.message || 'Gemini CLI execution failed',
|
|
345
|
+
ErrorCodes.API_ERROR,
|
|
346
|
+
error,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Validate Gemini CLI configuration
|
|
353
|
+
* Gemini CLI uses OAuth authentication (no API keys needed)
|
|
354
|
+
*/
|
|
355
|
+
validateConfig(_config) {
|
|
356
|
+
// Check if OAuth credentials file exists
|
|
357
|
+
return hasOAuthCredentials();
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Check if Gemini CLI provider is available
|
|
362
|
+
*/
|
|
363
|
+
isAvailable(config) {
|
|
364
|
+
return this.validateConfig(config);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get supported Gemini CLI models
|
|
369
|
+
*/
|
|
370
|
+
getSupportedModels() {
|
|
371
|
+
return SUPPORTED_MODELS;
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get model configuration for specific model
|
|
376
|
+
*/
|
|
377
|
+
getModelConfig(modelName) {
|
|
378
|
+
const modelNameLower = modelName.toLowerCase();
|
|
379
|
+
|
|
380
|
+
// Check exact match
|
|
381
|
+
if (SUPPORTED_MODELS[modelNameLower]) {
|
|
382
|
+
return SUPPORTED_MODELS[modelNameLower];
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// No aliases for Gemini CLI models currently
|
|
386
|
+
return null;
|
|
387
|
+
},
|
|
388
|
+
};
|
package/src/providers/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { mistralProvider } from './mistral.js';
|
|
|
14
14
|
import { deepseekProvider } from './deepseek.js';
|
|
15
15
|
import { openrouterProvider } from './openrouter.js';
|
|
16
16
|
import { codexProvider } from './codex.js';
|
|
17
|
+
import { geminiCliProvider } from './gemini-cli.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Provider registry map
|
|
@@ -31,6 +32,7 @@ const providers = {
|
|
|
31
32
|
deepseek: deepseekProvider,
|
|
32
33
|
openrouter: openrouterProvider,
|
|
33
34
|
codex: codexProvider,
|
|
35
|
+
'gemini-cli': geminiCliProvider,
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
/**
|
package/src/tools/chat.js
CHANGED
|
@@ -441,6 +441,11 @@ export function mapModelToProvider(model, providers) {
|
|
|
441
441
|
return 'codex';
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
// Check Gemini CLI (exact match only - routes to CLI provider instead of Google API)
|
|
445
|
+
if (modelLower === 'gemini') {
|
|
446
|
+
return 'gemini-cli';
|
|
447
|
+
}
|
|
448
|
+
|
|
444
449
|
// Check OpenRouter-specific patterns first
|
|
445
450
|
if (
|
|
446
451
|
modelLower === 'openrouter auto' ||
|
package/src/tools/consensus.js
CHANGED
|
@@ -684,6 +684,16 @@ function mapModelToProvider(model, providers) {
|
|
|
684
684
|
return 'openai';
|
|
685
685
|
}
|
|
686
686
|
|
|
687
|
+
// Check Codex (exact match only - don't route "gpt-5-codex" etc to Codex provider)
|
|
688
|
+
if (modelLower === 'codex') {
|
|
689
|
+
return 'codex';
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Check Gemini CLI (exact match only - routes to CLI provider instead of Google API)
|
|
693
|
+
if (modelLower === 'gemini') {
|
|
694
|
+
return 'gemini-cli';
|
|
695
|
+
}
|
|
696
|
+
|
|
687
697
|
// Check OpenRouter-specific patterns first
|
|
688
698
|
if (
|
|
689
699
|
modelLower === 'openrouter auto' ||
|