askimo 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/index.mjs +5 -1
- package/lib/providers.mjs +34 -75
- package/package.json +15 -14
- package/test/providers.mjs +9 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
A CLI tool for communicating with AI providers.
|
|
9
9
|
|
|
10
|
-
**Supported providers:** Perplexity · OpenAI · Anthropic · xAI (Grok)
|
|
10
|
+
**Supported providers:** Perplexity · OpenAI · Anthropic · xAI (Grok) · Google Gemini
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -27,6 +27,7 @@ PERPLEXITY_API_KEY=your-perplexity-key
|
|
|
27
27
|
OPENAI_API_KEY=your-openai-key
|
|
28
28
|
ANTHROPIC_API_KEY=your-anthropic-key
|
|
29
29
|
XAI_API_KEY=your-xai-key
|
|
30
|
+
GOOGLE_GENERATIVE_AI_API_KEY=your-google-key
|
|
30
31
|
|
|
31
32
|
# Optional settings
|
|
32
33
|
DEFAULT_PROVIDER=perplexity
|
|
@@ -34,6 +35,7 @@ PERPLEXITY_MODEL=sonar
|
|
|
34
35
|
OPENAI_MODEL=gpt-4o
|
|
35
36
|
ANTHROPIC_MODEL=claude-sonnet-4-20250514
|
|
36
37
|
XAI_MODEL=grok-4
|
|
38
|
+
GEMINI_MODEL=gemini-3-pro-preview
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
---
|
|
@@ -54,11 +56,13 @@ askimo "What is the capital of France?"
|
|
|
54
56
|
| `-o` | OpenAI |
|
|
55
57
|
| `-a` | Anthropic |
|
|
56
58
|
| `-x` | xAI (Grok) |
|
|
59
|
+
| `-g` | Google Gemini |
|
|
57
60
|
|
|
58
61
|
```bash
|
|
59
62
|
askimo "explain quantum computing" -o # Use OpenAI
|
|
60
63
|
askimo "write a haiku" -a # Use Anthropic
|
|
61
64
|
askimo "what's happening today?" -x # Use xAI Grok
|
|
65
|
+
askimo "summarize this topic" -g # Use Google Gemini
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
### Continue a conversation
|
|
@@ -94,9 +98,9 @@ askimo -f error.log "find the bug"
|
|
|
94
98
|
```bash
|
|
95
99
|
askimo chat # Start new chat
|
|
96
100
|
askimo chat -o # Chat with OpenAI
|
|
97
|
-
askimo chat -
|
|
101
|
+
askimo chat -g # Chat with Gemini
|
|
98
102
|
askimo chat -c 1 # Continue last conversation
|
|
99
|
-
askimo chat --cid abc123
|
|
103
|
+
askimo chat --cid abc123 # Continue by conversation ID
|
|
100
104
|
```
|
|
101
105
|
|
|
102
106
|
Type `exit` or `Ctrl+C` to quit.
|
|
@@ -106,7 +110,7 @@ Type `exit` or `Ctrl+C` to quit.
|
|
|
106
110
|
```bash
|
|
107
111
|
askimo models # All providers
|
|
108
112
|
askimo models -p # Perplexity only
|
|
109
|
-
askimo models -
|
|
113
|
+
askimo models -g # Gemini only
|
|
110
114
|
```
|
|
111
115
|
|
|
112
116
|
### View conversation history
|
package/index.mjs
CHANGED
|
@@ -22,6 +22,7 @@ program
|
|
|
22
22
|
.option('-o, --openai', 'Use OpenAI')
|
|
23
23
|
.option('-a, --anthropic', 'Use Anthropic Claude')
|
|
24
24
|
.option('-x, --xai', 'Use xAI Grok')
|
|
25
|
+
.option('-g, --gemini', 'Use Google Gemini')
|
|
25
26
|
.option('-j, --json', 'Output as JSON instead of streaming')
|
|
26
27
|
.option('-c, --continue <n>', 'Continue conversation N (1=last, 2=second-to-last)', Number.parseInt)
|
|
27
28
|
.option('--cid <id>', 'Continue conversation by ID')
|
|
@@ -115,6 +116,7 @@ program
|
|
|
115
116
|
.option('-o, --openai', 'Use OpenAI')
|
|
116
117
|
.option('-a, --anthropic', 'Use Anthropic Claude')
|
|
117
118
|
.option('-x, --xai', 'Use xAI Grok')
|
|
119
|
+
.option('-g, --gemini', 'Use Google Gemini')
|
|
118
120
|
.option('-c, --continue <n>', 'Continue conversation N (1=last, 2=second-to-last)', Number.parseInt)
|
|
119
121
|
.option('--cid <id>', 'Continue conversation by ID')
|
|
120
122
|
.action(async (options) => {
|
|
@@ -144,6 +146,7 @@ program
|
|
|
144
146
|
.option('-o, --openai', 'Show only OpenAI models')
|
|
145
147
|
.option('-a, --anthropic', 'Show only Anthropic models')
|
|
146
148
|
.option('-x, --xai', 'Show only xAI models')
|
|
149
|
+
.option('-g, --gemini', 'Show only Gemini models')
|
|
147
150
|
.action(async (options) => {
|
|
148
151
|
try {
|
|
149
152
|
const config = await loadConfig()
|
|
@@ -153,8 +156,9 @@ program
|
|
|
153
156
|
if (options.openai) providers.push('openai')
|
|
154
157
|
if (options.anthropic) providers.push('anthropic')
|
|
155
158
|
if (options.xai) providers.push('xai')
|
|
159
|
+
if (options.gemini) providers.push('gemini')
|
|
156
160
|
|
|
157
|
-
const toShow = providers.length === 0 ? ['perplexity', 'openai', 'anthropic', 'xai'] : providers
|
|
161
|
+
const toShow = providers.length === 0 ? ['perplexity', 'openai', 'anthropic', 'xai', 'gemini'] : providers
|
|
158
162
|
|
|
159
163
|
const results = await Promise.all(
|
|
160
164
|
toShow.map(async (provider) => ({
|
package/lib/providers.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAnthropic } from '@ai-sdk/anthropic'
|
|
2
|
+
import { createGoogleGenerativeAI } from '@ai-sdk/google'
|
|
2
3
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
3
4
|
import { createPerplexity } from '@ai-sdk/perplexity'
|
|
4
5
|
import { createXai } from '@ai-sdk/xai'
|
|
@@ -7,94 +8,38 @@ const DEFAULT_MODELS = {
|
|
|
7
8
|
perplexity: 'sonar',
|
|
8
9
|
openai: 'gpt-4o',
|
|
9
10
|
anthropic: 'claude-sonnet-4-20250514',
|
|
10
|
-
xai: 'grok-4'
|
|
11
|
+
xai: 'grok-4',
|
|
12
|
+
gemini: 'gemini-3-pro-preview'
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
//
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{ id: 'sonar-reasoning', description: 'Chain-of-thought problem solving' },
|
|
18
|
-
{ id: 'sonar-reasoning-pro', description: 'Advanced reasoning (DeepSeek-R1)' },
|
|
19
|
-
{ id: 'sonar-deep-research', description: 'Deep research sessions' }
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
// xAI doesn't have a public models list API, so we hardcode these
|
|
23
|
-
const XAI_MODELS = [
|
|
24
|
-
{ id: 'grok-4-1-fast-reasoning', description: 'Grok 4.1 fast with reasoning' },
|
|
25
|
-
{ id: 'grok-4-1-fast-non-reasoning', description: 'Grok 4.1 fast without reasoning' },
|
|
26
|
-
{ id: 'grok-code-fast-1', description: 'Grok optimized for code' },
|
|
27
|
-
{ id: 'grok-4-fast-reasoning', description: 'Grok 4 fast with reasoning' },
|
|
28
|
-
{ id: 'grok-4-fast-non-reasoning', description: 'Grok 4 fast without reasoning' },
|
|
29
|
-
{ id: 'grok-4-0709', description: 'Grok 4 flagship model' },
|
|
30
|
-
{ id: 'grok-3-mini', description: 'Lightweight Grok 3 model' },
|
|
31
|
-
{ id: 'grok-3', description: 'Grok 3 base model' },
|
|
32
|
-
{ id: 'grok-2-vision-1212', description: 'Grok 2 with vision capabilities' },
|
|
33
|
-
{ id: 'grok-2-image-1212', description: 'Image generation model' }
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
async function fetchOpenAiModels(apiKey) {
|
|
37
|
-
const response = await fetch('https://api.openai.com/v1/models', {
|
|
38
|
-
// biome-ignore lint/style/useNamingConvention: headers use standard capitalization
|
|
39
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
40
|
-
})
|
|
15
|
+
// Provider name mapping (askimo name → models.dev name)
|
|
16
|
+
const PROVIDER_MAP = {
|
|
17
|
+
gemini: 'google'
|
|
18
|
+
}
|
|
41
19
|
|
|
20
|
+
async function fetchModelsFromApi() {
|
|
21
|
+
const response = await fetch('https://models.dev/api.json')
|
|
42
22
|
if (!response.ok) {
|
|
43
|
-
throw new Error(`
|
|
23
|
+
throw new Error(`models.dev API error: ${response.status}`)
|
|
44
24
|
}
|
|
45
|
-
|
|
46
|
-
const data = await response.json()
|
|
47
|
-
return data.data.map((m) => ({ id: m.id, created: m.created })).sort((a, b) => b.created - a.created)
|
|
25
|
+
return response.json()
|
|
48
26
|
}
|
|
49
27
|
|
|
50
|
-
async function
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
'anthropic-version': '2023-06-01'
|
|
55
|
-
}
|
|
56
|
-
})
|
|
28
|
+
async function listModels(provider) {
|
|
29
|
+
const apiData = await fetchModelsFromApi()
|
|
30
|
+
const providerKey = PROVIDER_MAP[provider] || provider
|
|
31
|
+
const providerData = apiData[providerKey]
|
|
57
32
|
|
|
58
|
-
if (!
|
|
59
|
-
throw new Error(`
|
|
33
|
+
if (!providerData) {
|
|
34
|
+
throw new Error(`Unknown provider: ${provider}`)
|
|
60
35
|
}
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
return data.data.map((m) => ({
|
|
37
|
+
return Object.values(providerData.models).map((m) => ({
|
|
64
38
|
id: m.id,
|
|
65
|
-
|
|
39
|
+
description: m.name
|
|
66
40
|
}))
|
|
67
41
|
}
|
|
68
42
|
|
|
69
|
-
async function listModels(provider, config) {
|
|
70
|
-
switch (provider) {
|
|
71
|
-
case 'perplexity':
|
|
72
|
-
return PERPLEXITY_MODELS
|
|
73
|
-
|
|
74
|
-
case 'openai': {
|
|
75
|
-
const apiKey = config.OPENAI_API_KEY
|
|
76
|
-
if (!apiKey) {
|
|
77
|
-
throw new Error('OPENAI_API_KEY not found in config')
|
|
78
|
-
}
|
|
79
|
-
return fetchOpenAiModels(apiKey)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
case 'anthropic': {
|
|
83
|
-
const apiKey = config.ANTHROPIC_API_KEY
|
|
84
|
-
if (!apiKey) {
|
|
85
|
-
throw new Error('ANTHROPIC_API_KEY not found in config')
|
|
86
|
-
}
|
|
87
|
-
return fetchAnthropicModels(apiKey)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
case 'xai':
|
|
91
|
-
return XAI_MODELS
|
|
92
|
-
|
|
93
|
-
default:
|
|
94
|
-
throw new Error(`Unknown provider: ${provider}`)
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
43
|
function getProvider(providerName, config) {
|
|
99
44
|
switch (providerName) {
|
|
100
45
|
case 'perplexity': {
|
|
@@ -149,6 +94,19 @@ function getProvider(providerName, config) {
|
|
|
149
94
|
modelName
|
|
150
95
|
}
|
|
151
96
|
}
|
|
97
|
+
case 'gemini': {
|
|
98
|
+
const apiKey = config.GOOGLE_GENERATIVE_AI_API_KEY
|
|
99
|
+
if (!apiKey) {
|
|
100
|
+
throw new Error('GOOGLE_GENERATIVE_AI_API_KEY not found in config')
|
|
101
|
+
}
|
|
102
|
+
const modelName = config.GEMINI_MODEL || DEFAULT_MODELS.gemini
|
|
103
|
+
const google = createGoogleGenerativeAI({ apiKey })
|
|
104
|
+
return {
|
|
105
|
+
model: google(modelName),
|
|
106
|
+
name: 'gemini',
|
|
107
|
+
modelName
|
|
108
|
+
}
|
|
109
|
+
}
|
|
152
110
|
default:
|
|
153
111
|
throw new Error(`Unknown provider: ${providerName}`)
|
|
154
112
|
}
|
|
@@ -159,9 +117,10 @@ function determineProvider(options, config = {}) {
|
|
|
159
117
|
if (options.anthropic) return 'anthropic'
|
|
160
118
|
if (options.perplexity) return 'perplexity'
|
|
161
119
|
if (options.xai) return 'xai'
|
|
120
|
+
if (options.gemini) return 'gemini'
|
|
162
121
|
|
|
163
122
|
const defaultProvider = config.DEFAULT_PROVIDER?.toLowerCase()
|
|
164
|
-
if (defaultProvider && ['perplexity', 'openai', 'anthropic', 'xai'].includes(defaultProvider)) {
|
|
123
|
+
if (defaultProvider && ['perplexity', 'openai', 'anthropic', 'xai', 'gemini'].includes(defaultProvider)) {
|
|
165
124
|
return defaultProvider
|
|
166
125
|
}
|
|
167
126
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "askimo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A CLI tool for communicating with AI providers (Perplexity, OpenAI, Anthropic)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Amit Tal",
|
|
@@ -20,10 +20,6 @@
|
|
|
20
20
|
"bin": {
|
|
21
21
|
"askimo": "./index.mjs"
|
|
22
22
|
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"test": "ava",
|
|
25
|
-
"lint": "biome check --write"
|
|
26
|
-
},
|
|
27
23
|
"ava": {
|
|
28
24
|
"files": [
|
|
29
25
|
"test/**/*.mjs"
|
|
@@ -32,17 +28,22 @@
|
|
|
32
28
|
"repository": "https://github.com/amitosdev/askimo.git",
|
|
33
29
|
"homepage": "https://github.com/amitosdev/askimo#readme",
|
|
34
30
|
"devDependencies": {
|
|
35
|
-
"@biomejs/biome": "^2.3.
|
|
31
|
+
"@biomejs/biome": "^2.3.14",
|
|
36
32
|
"ava": "^6.4.1"
|
|
37
33
|
},
|
|
38
34
|
"dependencies": {
|
|
39
|
-
"@ai-sdk/anthropic": "^3.0.
|
|
40
|
-
"@ai-sdk/
|
|
41
|
-
"@ai-sdk/
|
|
42
|
-
"@ai-sdk/
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"
|
|
35
|
+
"@ai-sdk/anthropic": "^3.0.35",
|
|
36
|
+
"@ai-sdk/google": "^3.0.20",
|
|
37
|
+
"@ai-sdk/openai": "^3.0.25",
|
|
38
|
+
"@ai-sdk/perplexity": "^3.0.17",
|
|
39
|
+
"@ai-sdk/xai": "^3.0.46",
|
|
40
|
+
"@inquirer/input": "^5.0.4",
|
|
41
|
+
"ai": "^6.0.69",
|
|
42
|
+
"commander": "^14.0.3",
|
|
46
43
|
"hcat": "^2.2.1"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"test": "ava",
|
|
47
|
+
"lint": "biome check --write"
|
|
47
48
|
}
|
|
48
|
-
}
|
|
49
|
+
}
|
package/test/providers.mjs
CHANGED
|
@@ -19,6 +19,11 @@ test('determineProvider returns anthropic when --anthropic flag is set', (t) =>
|
|
|
19
19
|
t.is(result, 'anthropic')
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
+
test('determineProvider returns gemini when --gemini flag is set', (t) => {
|
|
23
|
+
const result = determineProvider({ gemini: true })
|
|
24
|
+
t.is(result, 'gemini')
|
|
25
|
+
})
|
|
26
|
+
|
|
22
27
|
test('determineProvider prefers flag over config', (t) => {
|
|
23
28
|
const result = determineProvider({ openai: true }, { DEFAULT_PROVIDER: 'anthropic' })
|
|
24
29
|
t.is(result, 'openai')
|
|
@@ -57,3 +62,7 @@ test('DEFAULT_MODELS contains openai model', (t) => {
|
|
|
57
62
|
test('DEFAULT_MODELS contains anthropic model', (t) => {
|
|
58
63
|
t.is(DEFAULT_MODELS.anthropic, 'claude-sonnet-4-20250514')
|
|
59
64
|
})
|
|
65
|
+
|
|
66
|
+
test('DEFAULT_MODELS contains gemini model', (t) => {
|
|
67
|
+
t.is(DEFAULT_MODELS.gemini, 'gemini-3-pro-preview')
|
|
68
|
+
})
|