cognitive-runtime 0.1.0 → 0.2.1
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 +18 -16
- package/dist/cli.js +10 -5
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/deepseek.d.ts +14 -0
- package/dist/providers/deepseek.js +66 -0
- package/dist/providers/gemini.js +1 -1
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.js +39 -5
- package/dist/providers/minimax.d.ts +14 -0
- package/dist/providers/minimax.js +64 -0
- package/dist/providers/moonshot.d.ts +14 -0
- package/dist/providers/moonshot.js +65 -0
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/providers/ollama.js +64 -0
- package/dist/providers/openai.js +1 -1
- package/dist/providers/qwen.d.ts +14 -0
- package/dist/providers/qwen.js +65 -0
- package/package.json +1 -1
- package/src/cli.ts +10 -5
- package/src/providers/anthropic.ts +1 -1
- package/src/providers/deepseek.ts +83 -0
- package/src/providers/gemini.ts +1 -1
- package/src/providers/index.ts +36 -6
- package/src/providers/minimax.ts +81 -0
- package/src/providers/moonshot.ts +82 -0
- package/src/providers/ollama.ts +83 -0
- package/src/providers/openai.ts +1 -1
- package/src/providers/qwen.ts +82 -0
package/README.md
CHANGED
|
@@ -21,7 +21,8 @@ Following the **Cognitive Runtime + Provider** architecture:
|
|
|
21
21
|
│ └────────────────────────────────┘ │
|
|
22
22
|
│ ┌────────────────────────────────┐ │
|
|
23
23
|
│ │ Provider Abstraction │ │
|
|
24
|
-
│ │ (gemini, openai, anthropic
|
|
24
|
+
│ │ (gemini, openai, anthropic, │ │
|
|
25
|
+
│ │ deepseek, minimax, qwen...) │ │
|
|
25
26
|
│ └────────────────────────────────┘ │
|
|
26
27
|
└─────────────────────────────────────┘
|
|
27
28
|
```
|
|
@@ -64,15 +65,24 @@ echo "review this code" | cog pipe --module code-reviewer
|
|
|
64
65
|
cog doctor
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
##
|
|
68
|
+
## Providers
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
| Provider | Environment Variable | Default Model |
|
|
71
|
+
|------------|------------------------|----------------------|
|
|
72
|
+
| Gemini | `GEMINI_API_KEY` | `gemini-3-flash` |
|
|
73
|
+
| OpenAI | `OPENAI_API_KEY` | `gpt-5.2` |
|
|
74
|
+
| Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4.5` |
|
|
75
|
+
| DeepSeek | `DEEPSEEK_API_KEY` | `deepseek-v3.2` |
|
|
76
|
+
| MiniMax | `MINIMAX_API_KEY` | `MiniMax-M2.1` |
|
|
77
|
+
| Moonshot | `MOONSHOT_API_KEY` | `kimi-k2.5` |
|
|
78
|
+
| Qwen | `DASHSCOPE_API_KEY` | `qwen3-max` |
|
|
79
|
+
| Ollama | `OLLAMA_HOST` | `llama4` (local) |
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
### Provider Aliases
|
|
82
|
+
|
|
83
|
+
- `kimi` → Moonshot
|
|
84
|
+
- `tongyi` / `dashscope` → Qwen
|
|
85
|
+
- `local` → Ollama
|
|
76
86
|
|
|
77
87
|
## Module Search Paths
|
|
78
88
|
|
|
@@ -98,14 +108,6 @@ if (module) {
|
|
|
98
108
|
}
|
|
99
109
|
```
|
|
100
110
|
|
|
101
|
-
## Providers
|
|
102
|
-
|
|
103
|
-
| Provider | Environment Variable | Model Default |
|
|
104
|
-
|------------|------------------------|------------------------|
|
|
105
|
-
| Gemini | `GEMINI_API_KEY` | `gemini-2.0-flash` |
|
|
106
|
-
| OpenAI | `OPENAI_API_KEY` | `gpt-4o` |
|
|
107
|
-
| Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4` |
|
|
108
|
-
|
|
109
111
|
## Development
|
|
110
112
|
|
|
111
113
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { parseArgs } from 'node:util';
|
|
11
11
|
import { getProvider, listProviders } from './providers/index.js';
|
|
12
12
|
import { run, list, pipe, init } from './commands/index.js';
|
|
13
|
-
const VERSION = '0.1
|
|
13
|
+
const VERSION = '0.2.1';
|
|
14
14
|
async function main() {
|
|
15
15
|
const args = process.argv.slice(2);
|
|
16
16
|
const command = args[0];
|
|
@@ -129,7 +129,7 @@ async function main() {
|
|
|
129
129
|
console.log('Providers:');
|
|
130
130
|
for (const p of listProviders()) {
|
|
131
131
|
const status = p.configured ? '✓ configured' : '– not configured';
|
|
132
|
-
console.log(` ${p.name}: ${status}`);
|
|
132
|
+
console.log(` ${p.name}: ${status} (${p.model})`);
|
|
133
133
|
}
|
|
134
134
|
console.log('');
|
|
135
135
|
try {
|
|
@@ -189,9 +189,14 @@ EXAMPLES:
|
|
|
189
189
|
cog doctor
|
|
190
190
|
|
|
191
191
|
ENVIRONMENT:
|
|
192
|
-
GEMINI_API_KEY Google Gemini
|
|
193
|
-
OPENAI_API_KEY OpenAI
|
|
194
|
-
ANTHROPIC_API_KEY Anthropic
|
|
192
|
+
GEMINI_API_KEY Google Gemini
|
|
193
|
+
OPENAI_API_KEY OpenAI
|
|
194
|
+
ANTHROPIC_API_KEY Anthropic Claude
|
|
195
|
+
DEEPSEEK_API_KEY DeepSeek
|
|
196
|
+
MINIMAX_API_KEY MiniMax
|
|
197
|
+
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
198
|
+
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
199
|
+
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
195
200
|
`);
|
|
196
201
|
}
|
|
197
202
|
main().catch(e => {
|
|
@@ -7,7 +7,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
7
7
|
apiKey;
|
|
8
8
|
model;
|
|
9
9
|
baseUrl = 'https://api.anthropic.com/v1';
|
|
10
|
-
constructor(apiKey, model = 'claude-sonnet-4-
|
|
10
|
+
constructor(apiKey, model = 'claude-sonnet-4-5-20250929') {
|
|
11
11
|
super();
|
|
12
12
|
this.apiKey = apiKey || process.env.ANTHROPIC_API_KEY || '';
|
|
13
13
|
this.model = model;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Provider - DeepSeek API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class DeepSeekProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Provider - DeepSeek API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class DeepSeekProvider extends BaseProvider {
|
|
6
|
+
name = 'deepseek';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://api.deepseek.com/v1';
|
|
10
|
+
constructor(apiKey, model = 'deepseek-chat') {
|
|
11
|
+
// deepseek-chat 自动映射到最新的 DeepSeek-V3.2
|
|
12
|
+
super();
|
|
13
|
+
this.apiKey = apiKey || process.env.DEEPSEEK_API_KEY || '';
|
|
14
|
+
this.model = model;
|
|
15
|
+
}
|
|
16
|
+
isConfigured() {
|
|
17
|
+
return !!this.apiKey;
|
|
18
|
+
}
|
|
19
|
+
async invoke(params) {
|
|
20
|
+
if (!this.isConfigured()) {
|
|
21
|
+
throw new Error('DeepSeek API key not configured. Set DEEPSEEK_API_KEY environment variable.');
|
|
22
|
+
}
|
|
23
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
24
|
+
const body = {
|
|
25
|
+
model: this.model,
|
|
26
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
27
|
+
temperature: params.temperature ?? 0.7,
|
|
28
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
29
|
+
};
|
|
30
|
+
// Add JSON mode if schema provided
|
|
31
|
+
if (params.jsonSchema) {
|
|
32
|
+
body.response_format = { type: 'json_object' };
|
|
33
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
34
|
+
if (lastUserIdx >= 0) {
|
|
35
|
+
const messages = [...params.messages];
|
|
36
|
+
messages[lastUserIdx] = {
|
|
37
|
+
...messages[lastUserIdx],
|
|
38
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
39
|
+
};
|
|
40
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const response = await fetch(url, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(body),
|
|
50
|
+
});
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
const error = await response.text();
|
|
53
|
+
throw new Error(`DeepSeek API error: ${response.status} - ${error}`);
|
|
54
|
+
}
|
|
55
|
+
const data = await response.json();
|
|
56
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
57
|
+
return {
|
|
58
|
+
content,
|
|
59
|
+
usage: data.usage ? {
|
|
60
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
61
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
62
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
63
|
+
} : undefined,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/providers/gemini.js
CHANGED
|
@@ -7,7 +7,7 @@ export class GeminiProvider extends BaseProvider {
|
|
|
7
7
|
apiKey;
|
|
8
8
|
model;
|
|
9
9
|
baseUrl = 'https://generativelanguage.googleapis.com/v1beta';
|
|
10
|
-
constructor(apiKey, model = 'gemini-
|
|
10
|
+
constructor(apiKey, model = 'gemini-3-flash') {
|
|
11
11
|
super();
|
|
12
12
|
this.apiKey = apiKey || process.env.GEMINI_API_KEY || '';
|
|
13
13
|
this.model = model;
|
|
@@ -6,8 +6,14 @@ export { BaseProvider } from './base.js';
|
|
|
6
6
|
export { GeminiProvider } from './gemini.js';
|
|
7
7
|
export { OpenAIProvider } from './openai.js';
|
|
8
8
|
export { AnthropicProvider } from './anthropic.js';
|
|
9
|
+
export { MiniMaxProvider } from './minimax.js';
|
|
10
|
+
export { DeepSeekProvider } from './deepseek.js';
|
|
11
|
+
export { MoonshotProvider } from './moonshot.js';
|
|
12
|
+
export { QwenProvider } from './qwen.js';
|
|
13
|
+
export { OllamaProvider } from './ollama.js';
|
|
9
14
|
export declare function getProvider(name?: string): Provider;
|
|
10
15
|
export declare function listProviders(): Array<{
|
|
11
16
|
name: string;
|
|
12
17
|
configured: boolean;
|
|
18
|
+
model: string;
|
|
13
19
|
}>;
|
package/dist/providers/index.js
CHANGED
|
@@ -4,14 +4,33 @@
|
|
|
4
4
|
import { GeminiProvider } from './gemini.js';
|
|
5
5
|
import { OpenAIProvider } from './openai.js';
|
|
6
6
|
import { AnthropicProvider } from './anthropic.js';
|
|
7
|
+
import { MiniMaxProvider } from './minimax.js';
|
|
8
|
+
import { DeepSeekProvider } from './deepseek.js';
|
|
9
|
+
import { MoonshotProvider } from './moonshot.js';
|
|
10
|
+
import { QwenProvider } from './qwen.js';
|
|
11
|
+
import { OllamaProvider } from './ollama.js';
|
|
7
12
|
export { BaseProvider } from './base.js';
|
|
8
13
|
export { GeminiProvider } from './gemini.js';
|
|
9
14
|
export { OpenAIProvider } from './openai.js';
|
|
10
15
|
export { AnthropicProvider } from './anthropic.js';
|
|
16
|
+
export { MiniMaxProvider } from './minimax.js';
|
|
17
|
+
export { DeepSeekProvider } from './deepseek.js';
|
|
18
|
+
export { MoonshotProvider } from './moonshot.js';
|
|
19
|
+
export { QwenProvider } from './qwen.js';
|
|
20
|
+
export { OllamaProvider } from './ollama.js';
|
|
11
21
|
const providers = {
|
|
12
22
|
gemini: () => new GeminiProvider(),
|
|
13
23
|
openai: () => new OpenAIProvider(),
|
|
14
24
|
anthropic: () => new AnthropicProvider(),
|
|
25
|
+
minimax: () => new MiniMaxProvider(),
|
|
26
|
+
deepseek: () => new DeepSeekProvider(),
|
|
27
|
+
moonshot: () => new MoonshotProvider(),
|
|
28
|
+
kimi: () => new MoonshotProvider(), // Alias
|
|
29
|
+
qwen: () => new QwenProvider(),
|
|
30
|
+
tongyi: () => new QwenProvider(), // Alias
|
|
31
|
+
dashscope: () => new QwenProvider(), // Alias
|
|
32
|
+
ollama: () => new OllamaProvider(),
|
|
33
|
+
local: () => new OllamaProvider(), // Alias
|
|
15
34
|
};
|
|
16
35
|
export function getProvider(name) {
|
|
17
36
|
// Auto-detect if not specified
|
|
@@ -22,7 +41,16 @@ export function getProvider(name) {
|
|
|
22
41
|
return new OpenAIProvider();
|
|
23
42
|
if (process.env.ANTHROPIC_API_KEY)
|
|
24
43
|
return new AnthropicProvider();
|
|
25
|
-
|
|
44
|
+
if (process.env.DEEPSEEK_API_KEY)
|
|
45
|
+
return new DeepSeekProvider();
|
|
46
|
+
if (process.env.MINIMAX_API_KEY)
|
|
47
|
+
return new MiniMaxProvider();
|
|
48
|
+
if (process.env.MOONSHOT_API_KEY)
|
|
49
|
+
return new MoonshotProvider();
|
|
50
|
+
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY)
|
|
51
|
+
return new QwenProvider();
|
|
52
|
+
// Ollama is always available as fallback if nothing else is configured
|
|
53
|
+
return new OllamaProvider();
|
|
26
54
|
}
|
|
27
55
|
const factory = providers[name.toLowerCase()];
|
|
28
56
|
if (!factory) {
|
|
@@ -31,8 +59,14 @@ export function getProvider(name) {
|
|
|
31
59
|
return factory();
|
|
32
60
|
}
|
|
33
61
|
export function listProviders() {
|
|
34
|
-
return
|
|
35
|
-
name,
|
|
36
|
-
configured:
|
|
37
|
-
|
|
62
|
+
return [
|
|
63
|
+
{ name: 'gemini', configured: !!process.env.GEMINI_API_KEY, model: 'gemini-3-flash' },
|
|
64
|
+
{ name: 'openai', configured: !!process.env.OPENAI_API_KEY, model: 'gpt-5.2' },
|
|
65
|
+
{ name: 'anthropic', configured: !!process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4.5' },
|
|
66
|
+
{ name: 'deepseek', configured: !!process.env.DEEPSEEK_API_KEY, model: 'deepseek-v3.2' },
|
|
67
|
+
{ name: 'minimax', configured: !!process.env.MINIMAX_API_KEY, model: 'MiniMax-M2.1' },
|
|
68
|
+
{ name: 'moonshot', configured: !!process.env.MOONSHOT_API_KEY, model: 'kimi-k2.5' },
|
|
69
|
+
{ name: 'qwen', configured: !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), model: 'qwen3-max' },
|
|
70
|
+
{ name: 'ollama', configured: true, model: 'llama4 (local)' },
|
|
71
|
+
];
|
|
38
72
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiniMax Provider - MiniMax API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class MiniMaxProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiniMax Provider - MiniMax API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class MiniMaxProvider extends BaseProvider {
|
|
6
|
+
name = 'minimax';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://api.minimax.chat/v1';
|
|
10
|
+
constructor(apiKey, model = 'MiniMax-M2.1') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.MINIMAX_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return !!this.apiKey;
|
|
17
|
+
}
|
|
18
|
+
async invoke(params) {
|
|
19
|
+
if (!this.isConfigured()) {
|
|
20
|
+
throw new Error('MiniMax API key not configured. Set MINIMAX_API_KEY environment variable.');
|
|
21
|
+
}
|
|
22
|
+
const url = `${this.baseUrl}/text/chatcompletion_v2`;
|
|
23
|
+
const body = {
|
|
24
|
+
model: this.model,
|
|
25
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
26
|
+
temperature: params.temperature ?? 0.7,
|
|
27
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
28
|
+
};
|
|
29
|
+
// Add JSON mode if schema provided
|
|
30
|
+
if (params.jsonSchema) {
|
|
31
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
32
|
+
if (lastUserIdx >= 0) {
|
|
33
|
+
const messages = [...params.messages];
|
|
34
|
+
messages[lastUserIdx] = {
|
|
35
|
+
...messages[lastUserIdx],
|
|
36
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
37
|
+
};
|
|
38
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.text();
|
|
51
|
+
throw new Error(`MiniMax API error: ${response.status} - ${error}`);
|
|
52
|
+
}
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
55
|
+
return {
|
|
56
|
+
content,
|
|
57
|
+
usage: data.usage ? {
|
|
58
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
59
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
60
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
61
|
+
} : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moonshot Provider - Moonshot AI (Kimi) API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class MoonshotProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moonshot Provider - Moonshot AI (Kimi) API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class MoonshotProvider extends BaseProvider {
|
|
6
|
+
name = 'moonshot';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://api.moonshot.cn/v1';
|
|
10
|
+
constructor(apiKey, model = 'kimi-k2.5') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.MOONSHOT_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return !!this.apiKey;
|
|
17
|
+
}
|
|
18
|
+
async invoke(params) {
|
|
19
|
+
if (!this.isConfigured()) {
|
|
20
|
+
throw new Error('Moonshot API key not configured. Set MOONSHOT_API_KEY environment variable.');
|
|
21
|
+
}
|
|
22
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
23
|
+
const body = {
|
|
24
|
+
model: this.model,
|
|
25
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
26
|
+
temperature: params.temperature ?? 0.7,
|
|
27
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
28
|
+
};
|
|
29
|
+
// Add JSON mode if schema provided
|
|
30
|
+
if (params.jsonSchema) {
|
|
31
|
+
body.response_format = { type: 'json_object' };
|
|
32
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
33
|
+
if (lastUserIdx >= 0) {
|
|
34
|
+
const messages = [...params.messages];
|
|
35
|
+
messages[lastUserIdx] = {
|
|
36
|
+
...messages[lastUserIdx],
|
|
37
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
38
|
+
};
|
|
39
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const response = await fetch(url, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(body),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const error = await response.text();
|
|
52
|
+
throw new Error(`Moonshot API error: ${response.status} - ${error}`);
|
|
53
|
+
}
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
56
|
+
return {
|
|
57
|
+
content,
|
|
58
|
+
usage: data.usage ? {
|
|
59
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
60
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
61
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
62
|
+
} : undefined,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Provider - Local LLM via Ollama
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class OllamaProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private model;
|
|
9
|
+
private baseUrl;
|
|
10
|
+
constructor(model?: string, baseUrl?: string);
|
|
11
|
+
isConfigured(): boolean;
|
|
12
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Provider - Local LLM via Ollama
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class OllamaProvider extends BaseProvider {
|
|
6
|
+
name = 'ollama';
|
|
7
|
+
model;
|
|
8
|
+
baseUrl;
|
|
9
|
+
constructor(model = 'llama4', baseUrl = 'http://localhost:11434') {
|
|
10
|
+
super();
|
|
11
|
+
this.model = process.env.OLLAMA_MODEL || model;
|
|
12
|
+
this.baseUrl = process.env.OLLAMA_HOST || baseUrl;
|
|
13
|
+
}
|
|
14
|
+
isConfigured() {
|
|
15
|
+
return true; // Ollama doesn't need API key
|
|
16
|
+
}
|
|
17
|
+
async invoke(params) {
|
|
18
|
+
const url = `${this.baseUrl}/api/chat`;
|
|
19
|
+
let messages = params.messages.map(m => ({ role: m.role, content: m.content }));
|
|
20
|
+
// Add JSON mode if schema provided
|
|
21
|
+
if (params.jsonSchema) {
|
|
22
|
+
const lastUserIdx = messages.findLastIndex(m => m.role === 'user');
|
|
23
|
+
if (lastUserIdx >= 0) {
|
|
24
|
+
messages = [...messages];
|
|
25
|
+
messages[lastUserIdx] = {
|
|
26
|
+
...messages[lastUserIdx],
|
|
27
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const body = {
|
|
32
|
+
model: this.model,
|
|
33
|
+
messages,
|
|
34
|
+
stream: false,
|
|
35
|
+
options: {
|
|
36
|
+
temperature: params.temperature ?? 0.7,
|
|
37
|
+
num_predict: params.maxTokens ?? 4096,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
// Request JSON format
|
|
41
|
+
if (params.jsonSchema) {
|
|
42
|
+
body.format = 'json';
|
|
43
|
+
}
|
|
44
|
+
const response = await fetch(url, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body: JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.text();
|
|
51
|
+
throw new Error(`Ollama API error: ${response.status} - ${error}`);
|
|
52
|
+
}
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
const content = data.message?.content || '';
|
|
55
|
+
return {
|
|
56
|
+
content,
|
|
57
|
+
usage: {
|
|
58
|
+
promptTokens: data.prompt_eval_count || 0,
|
|
59
|
+
completionTokens: data.eval_count || 0,
|
|
60
|
+
totalTokens: (data.prompt_eval_count || 0) + (data.eval_count || 0),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/providers/openai.js
CHANGED
|
@@ -7,7 +7,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
7
7
|
apiKey;
|
|
8
8
|
model;
|
|
9
9
|
baseUrl;
|
|
10
|
-
constructor(apiKey, model = 'gpt-
|
|
10
|
+
constructor(apiKey, model = 'gpt-5.2', baseUrl = 'https://api.openai.com/v1') {
|
|
11
11
|
super();
|
|
12
12
|
this.apiKey = apiKey || process.env.OPENAI_API_KEY || '';
|
|
13
13
|
this.model = model;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen Provider - Alibaba Tongyi Qianwen (通义千问) via DashScope API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class QwenProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen Provider - Alibaba Tongyi Qianwen (通义千问) via DashScope API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class QwenProvider extends BaseProvider {
|
|
6
|
+
name = 'qwen';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
10
|
+
constructor(apiKey, model = 'qwen3-max') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return !!this.apiKey;
|
|
17
|
+
}
|
|
18
|
+
async invoke(params) {
|
|
19
|
+
if (!this.isConfigured()) {
|
|
20
|
+
throw new Error('Qwen API key not configured. Set DASHSCOPE_API_KEY or QWEN_API_KEY environment variable.');
|
|
21
|
+
}
|
|
22
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
23
|
+
const body = {
|
|
24
|
+
model: this.model,
|
|
25
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
26
|
+
temperature: params.temperature ?? 0.7,
|
|
27
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
28
|
+
};
|
|
29
|
+
// Add JSON mode if schema provided
|
|
30
|
+
if (params.jsonSchema) {
|
|
31
|
+
body.response_format = { type: 'json_object' };
|
|
32
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
33
|
+
if (lastUserIdx >= 0) {
|
|
34
|
+
const messages = [...params.messages];
|
|
35
|
+
messages[lastUserIdx] = {
|
|
36
|
+
...messages[lastUserIdx],
|
|
37
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
38
|
+
};
|
|
39
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const response = await fetch(url, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(body),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const error = await response.text();
|
|
52
|
+
throw new Error(`Qwen API error: ${response.status} - ${error}`);
|
|
53
|
+
}
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
56
|
+
return {
|
|
57
|
+
content,
|
|
58
|
+
usage: data.usage ? {
|
|
59
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
60
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
61
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
62
|
+
} : undefined,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { getProvider, listProviders } from './providers/index.js';
|
|
|
13
13
|
import { run, list, pipe, init } from './commands/index.js';
|
|
14
14
|
import type { CommandContext } from './types.js';
|
|
15
15
|
|
|
16
|
-
const VERSION = '0.1
|
|
16
|
+
const VERSION = '0.2.1';
|
|
17
17
|
|
|
18
18
|
async function main() {
|
|
19
19
|
const args = process.argv.slice(2);
|
|
@@ -150,7 +150,7 @@ async function main() {
|
|
|
150
150
|
console.log('Providers:');
|
|
151
151
|
for (const p of listProviders()) {
|
|
152
152
|
const status = p.configured ? '✓ configured' : '– not configured';
|
|
153
|
-
console.log(` ${p.name}: ${status}`);
|
|
153
|
+
console.log(` ${p.name}: ${status} (${p.model})`);
|
|
154
154
|
}
|
|
155
155
|
console.log('');
|
|
156
156
|
|
|
@@ -211,9 +211,14 @@ EXAMPLES:
|
|
|
211
211
|
cog doctor
|
|
212
212
|
|
|
213
213
|
ENVIRONMENT:
|
|
214
|
-
GEMINI_API_KEY Google Gemini
|
|
215
|
-
OPENAI_API_KEY OpenAI
|
|
216
|
-
ANTHROPIC_API_KEY Anthropic
|
|
214
|
+
GEMINI_API_KEY Google Gemini
|
|
215
|
+
OPENAI_API_KEY OpenAI
|
|
216
|
+
ANTHROPIC_API_KEY Anthropic Claude
|
|
217
|
+
DEEPSEEK_API_KEY DeepSeek
|
|
218
|
+
MINIMAX_API_KEY MiniMax
|
|
219
|
+
MOONSHOT_API_KEY Moonshot (Kimi)
|
|
220
|
+
DASHSCOPE_API_KEY Alibaba Qwen (通义千问)
|
|
221
|
+
OLLAMA_HOST Ollama local (default: localhost:11434)
|
|
217
222
|
`);
|
|
218
223
|
}
|
|
219
224
|
|
|
@@ -11,7 +11,7 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
11
11
|
private model: string;
|
|
12
12
|
private baseUrl = 'https://api.anthropic.com/v1';
|
|
13
13
|
|
|
14
|
-
constructor(apiKey?: string, model = 'claude-sonnet-4-
|
|
14
|
+
constructor(apiKey?: string, model = 'claude-sonnet-4-5-20250929') {
|
|
15
15
|
super();
|
|
16
16
|
this.apiKey = apiKey || process.env.ANTHROPIC_API_KEY || '';
|
|
17
17
|
this.model = model;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek Provider - DeepSeek API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseProvider } from './base.js';
|
|
6
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
7
|
+
|
|
8
|
+
export class DeepSeekProvider extends BaseProvider {
|
|
9
|
+
name = 'deepseek';
|
|
10
|
+
private apiKey: string;
|
|
11
|
+
private model: string;
|
|
12
|
+
private baseUrl = 'https://api.deepseek.com/v1';
|
|
13
|
+
|
|
14
|
+
constructor(apiKey?: string, model = 'deepseek-chat') {
|
|
15
|
+
// deepseek-chat 自动映射到最新的 DeepSeek-V3.2
|
|
16
|
+
super();
|
|
17
|
+
this.apiKey = apiKey || process.env.DEEPSEEK_API_KEY || '';
|
|
18
|
+
this.model = model;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
isConfigured(): boolean {
|
|
22
|
+
return !!this.apiKey;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async invoke(params: InvokeParams): Promise<InvokeResult> {
|
|
26
|
+
if (!this.isConfigured()) {
|
|
27
|
+
throw new Error('DeepSeek API key not configured. Set DEEPSEEK_API_KEY environment variable.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
31
|
+
|
|
32
|
+
const body: Record<string, unknown> = {
|
|
33
|
+
model: this.model,
|
|
34
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
35
|
+
temperature: params.temperature ?? 0.7,
|
|
36
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Add JSON mode if schema provided
|
|
40
|
+
if (params.jsonSchema) {
|
|
41
|
+
body.response_format = { type: 'json_object' };
|
|
42
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
43
|
+
if (lastUserIdx >= 0) {
|
|
44
|
+
const messages = [...params.messages];
|
|
45
|
+
messages[lastUserIdx] = {
|
|
46
|
+
...messages[lastUserIdx],
|
|
47
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
48
|
+
};
|
|
49
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const error = await response.text();
|
|
64
|
+
throw new Error(`DeepSeek API error: ${response.status} - ${error}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const data = await response.json() as {
|
|
68
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
69
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
content,
|
|
76
|
+
usage: data.usage ? {
|
|
77
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
78
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
79
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
80
|
+
} : undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/providers/gemini.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class GeminiProvider extends BaseProvider {
|
|
|
11
11
|
private model: string;
|
|
12
12
|
private baseUrl = 'https://generativelanguage.googleapis.com/v1beta';
|
|
13
13
|
|
|
14
|
-
constructor(apiKey?: string, model = 'gemini-
|
|
14
|
+
constructor(apiKey?: string, model = 'gemini-3-flash') {
|
|
15
15
|
super();
|
|
16
16
|
this.apiKey = apiKey || process.env.GEMINI_API_KEY || '';
|
|
17
17
|
this.model = model;
|
package/src/providers/index.ts
CHANGED
|
@@ -6,16 +6,35 @@ import type { Provider } from '../types.js';
|
|
|
6
6
|
import { GeminiProvider } from './gemini.js';
|
|
7
7
|
import { OpenAIProvider } from './openai.js';
|
|
8
8
|
import { AnthropicProvider } from './anthropic.js';
|
|
9
|
+
import { MiniMaxProvider } from './minimax.js';
|
|
10
|
+
import { DeepSeekProvider } from './deepseek.js';
|
|
11
|
+
import { MoonshotProvider } from './moonshot.js';
|
|
12
|
+
import { QwenProvider } from './qwen.js';
|
|
13
|
+
import { OllamaProvider } from './ollama.js';
|
|
9
14
|
|
|
10
15
|
export { BaseProvider } from './base.js';
|
|
11
16
|
export { GeminiProvider } from './gemini.js';
|
|
12
17
|
export { OpenAIProvider } from './openai.js';
|
|
13
18
|
export { AnthropicProvider } from './anthropic.js';
|
|
19
|
+
export { MiniMaxProvider } from './minimax.js';
|
|
20
|
+
export { DeepSeekProvider } from './deepseek.js';
|
|
21
|
+
export { MoonshotProvider } from './moonshot.js';
|
|
22
|
+
export { QwenProvider } from './qwen.js';
|
|
23
|
+
export { OllamaProvider } from './ollama.js';
|
|
14
24
|
|
|
15
25
|
const providers: Record<string, () => Provider> = {
|
|
16
26
|
gemini: () => new GeminiProvider(),
|
|
17
27
|
openai: () => new OpenAIProvider(),
|
|
18
28
|
anthropic: () => new AnthropicProvider(),
|
|
29
|
+
minimax: () => new MiniMaxProvider(),
|
|
30
|
+
deepseek: () => new DeepSeekProvider(),
|
|
31
|
+
moonshot: () => new MoonshotProvider(),
|
|
32
|
+
kimi: () => new MoonshotProvider(), // Alias
|
|
33
|
+
qwen: () => new QwenProvider(),
|
|
34
|
+
tongyi: () => new QwenProvider(), // Alias
|
|
35
|
+
dashscope: () => new QwenProvider(), // Alias
|
|
36
|
+
ollama: () => new OllamaProvider(),
|
|
37
|
+
local: () => new OllamaProvider(), // Alias
|
|
19
38
|
};
|
|
20
39
|
|
|
21
40
|
export function getProvider(name?: string): Provider {
|
|
@@ -24,7 +43,12 @@ export function getProvider(name?: string): Provider {
|
|
|
24
43
|
if (process.env.GEMINI_API_KEY) return new GeminiProvider();
|
|
25
44
|
if (process.env.OPENAI_API_KEY) return new OpenAIProvider();
|
|
26
45
|
if (process.env.ANTHROPIC_API_KEY) return new AnthropicProvider();
|
|
27
|
-
|
|
46
|
+
if (process.env.DEEPSEEK_API_KEY) return new DeepSeekProvider();
|
|
47
|
+
if (process.env.MINIMAX_API_KEY) return new MiniMaxProvider();
|
|
48
|
+
if (process.env.MOONSHOT_API_KEY) return new MoonshotProvider();
|
|
49
|
+
if (process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY) return new QwenProvider();
|
|
50
|
+
// Ollama is always available as fallback if nothing else is configured
|
|
51
|
+
return new OllamaProvider();
|
|
28
52
|
}
|
|
29
53
|
|
|
30
54
|
const factory = providers[name.toLowerCase()];
|
|
@@ -35,9 +59,15 @@ export function getProvider(name?: string): Provider {
|
|
|
35
59
|
return factory();
|
|
36
60
|
}
|
|
37
61
|
|
|
38
|
-
export function listProviders(): Array<{ name: string; configured: boolean }> {
|
|
39
|
-
return
|
|
40
|
-
name,
|
|
41
|
-
configured:
|
|
42
|
-
|
|
62
|
+
export function listProviders(): Array<{ name: string; configured: boolean; model: string }> {
|
|
63
|
+
return [
|
|
64
|
+
{ name: 'gemini', configured: !!process.env.GEMINI_API_KEY, model: 'gemini-3-flash' },
|
|
65
|
+
{ name: 'openai', configured: !!process.env.OPENAI_API_KEY, model: 'gpt-5.2' },
|
|
66
|
+
{ name: 'anthropic', configured: !!process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4.5' },
|
|
67
|
+
{ name: 'deepseek', configured: !!process.env.DEEPSEEK_API_KEY, model: 'deepseek-v3.2' },
|
|
68
|
+
{ name: 'minimax', configured: !!process.env.MINIMAX_API_KEY, model: 'MiniMax-M2.1' },
|
|
69
|
+
{ name: 'moonshot', configured: !!process.env.MOONSHOT_API_KEY, model: 'kimi-k2.5' },
|
|
70
|
+
{ name: 'qwen', configured: !!(process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY), model: 'qwen3-max' },
|
|
71
|
+
{ name: 'ollama', configured: true, model: 'llama4 (local)' },
|
|
72
|
+
];
|
|
43
73
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MiniMax Provider - MiniMax API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseProvider } from './base.js';
|
|
6
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
7
|
+
|
|
8
|
+
export class MiniMaxProvider extends BaseProvider {
|
|
9
|
+
name = 'minimax';
|
|
10
|
+
private apiKey: string;
|
|
11
|
+
private model: string;
|
|
12
|
+
private baseUrl = 'https://api.minimax.chat/v1';
|
|
13
|
+
|
|
14
|
+
constructor(apiKey?: string, model = 'MiniMax-M2.1') {
|
|
15
|
+
super();
|
|
16
|
+
this.apiKey = apiKey || process.env.MINIMAX_API_KEY || '';
|
|
17
|
+
this.model = model;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
isConfigured(): boolean {
|
|
21
|
+
return !!this.apiKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async invoke(params: InvokeParams): Promise<InvokeResult> {
|
|
25
|
+
if (!this.isConfigured()) {
|
|
26
|
+
throw new Error('MiniMax API key not configured. Set MINIMAX_API_KEY environment variable.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const url = `${this.baseUrl}/text/chatcompletion_v2`;
|
|
30
|
+
|
|
31
|
+
const body: Record<string, unknown> = {
|
|
32
|
+
model: this.model,
|
|
33
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
34
|
+
temperature: params.temperature ?? 0.7,
|
|
35
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add JSON mode if schema provided
|
|
39
|
+
if (params.jsonSchema) {
|
|
40
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
41
|
+
if (lastUserIdx >= 0) {
|
|
42
|
+
const messages = [...params.messages];
|
|
43
|
+
messages[lastUserIdx] = {
|
|
44
|
+
...messages[lastUserIdx],
|
|
45
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
46
|
+
};
|
|
47
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify(body),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const error = await response.text();
|
|
62
|
+
throw new Error(`MiniMax API error: ${response.status} - ${error}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const data = await response.json() as {
|
|
66
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
67
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
content,
|
|
74
|
+
usage: data.usage ? {
|
|
75
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
76
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
77
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
78
|
+
} : undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moonshot Provider - Moonshot AI (Kimi) API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseProvider } from './base.js';
|
|
6
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
7
|
+
|
|
8
|
+
export class MoonshotProvider extends BaseProvider {
|
|
9
|
+
name = 'moonshot';
|
|
10
|
+
private apiKey: string;
|
|
11
|
+
private model: string;
|
|
12
|
+
private baseUrl = 'https://api.moonshot.cn/v1';
|
|
13
|
+
|
|
14
|
+
constructor(apiKey?: string, model = 'kimi-k2.5') {
|
|
15
|
+
super();
|
|
16
|
+
this.apiKey = apiKey || process.env.MOONSHOT_API_KEY || '';
|
|
17
|
+
this.model = model;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
isConfigured(): boolean {
|
|
21
|
+
return !!this.apiKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async invoke(params: InvokeParams): Promise<InvokeResult> {
|
|
25
|
+
if (!this.isConfigured()) {
|
|
26
|
+
throw new Error('Moonshot API key not configured. Set MOONSHOT_API_KEY environment variable.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
30
|
+
|
|
31
|
+
const body: Record<string, unknown> = {
|
|
32
|
+
model: this.model,
|
|
33
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
34
|
+
temperature: params.temperature ?? 0.7,
|
|
35
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add JSON mode if schema provided
|
|
39
|
+
if (params.jsonSchema) {
|
|
40
|
+
body.response_format = { type: 'json_object' };
|
|
41
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
42
|
+
if (lastUserIdx >= 0) {
|
|
43
|
+
const messages = [...params.messages];
|
|
44
|
+
messages[lastUserIdx] = {
|
|
45
|
+
...messages[lastUserIdx],
|
|
46
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
47
|
+
};
|
|
48
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const response = await fetch(url, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(body),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const error = await response.text();
|
|
63
|
+
throw new Error(`Moonshot API error: ${response.status} - ${error}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const data = await response.json() as {
|
|
67
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
68
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
content,
|
|
75
|
+
usage: data.usage ? {
|
|
76
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
77
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
78
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
79
|
+
} : undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama Provider - Local LLM via Ollama
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseProvider } from './base.js';
|
|
6
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
7
|
+
|
|
8
|
+
export class OllamaProvider extends BaseProvider {
|
|
9
|
+
name = 'ollama';
|
|
10
|
+
private model: string;
|
|
11
|
+
private baseUrl: string;
|
|
12
|
+
|
|
13
|
+
constructor(model = 'llama4', baseUrl = 'http://localhost:11434') {
|
|
14
|
+
super();
|
|
15
|
+
this.model = process.env.OLLAMA_MODEL || model;
|
|
16
|
+
this.baseUrl = process.env.OLLAMA_HOST || baseUrl;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
isConfigured(): boolean {
|
|
20
|
+
return true; // Ollama doesn't need API key
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async invoke(params: InvokeParams): Promise<InvokeResult> {
|
|
24
|
+
const url = `${this.baseUrl}/api/chat`;
|
|
25
|
+
|
|
26
|
+
let messages = params.messages.map(m => ({ role: m.role, content: m.content }));
|
|
27
|
+
|
|
28
|
+
// Add JSON mode if schema provided
|
|
29
|
+
if (params.jsonSchema) {
|
|
30
|
+
const lastUserIdx = messages.findLastIndex(m => m.role === 'user');
|
|
31
|
+
if (lastUserIdx >= 0) {
|
|
32
|
+
messages = [...messages];
|
|
33
|
+
messages[lastUserIdx] = {
|
|
34
|
+
...messages[lastUserIdx],
|
|
35
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const body: Record<string, unknown> = {
|
|
41
|
+
model: this.model,
|
|
42
|
+
messages,
|
|
43
|
+
stream: false,
|
|
44
|
+
options: {
|
|
45
|
+
temperature: params.temperature ?? 0.7,
|
|
46
|
+
num_predict: params.maxTokens ?? 4096,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Request JSON format
|
|
51
|
+
if (params.jsonSchema) {
|
|
52
|
+
body.format = 'json';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const response = await fetch(url, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
body: JSON.stringify(body),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const error = await response.text();
|
|
63
|
+
throw new Error(`Ollama API error: ${response.status} - ${error}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const data = await response.json() as {
|
|
67
|
+
message?: { content?: string };
|
|
68
|
+
prompt_eval_count?: number;
|
|
69
|
+
eval_count?: number;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const content = data.message?.content || '';
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
content,
|
|
76
|
+
usage: {
|
|
77
|
+
promptTokens: data.prompt_eval_count || 0,
|
|
78
|
+
completionTokens: data.eval_count || 0,
|
|
79
|
+
totalTokens: (data.prompt_eval_count || 0) + (data.eval_count || 0),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/providers/openai.ts
CHANGED
|
@@ -11,7 +11,7 @@ export class OpenAIProvider extends BaseProvider {
|
|
|
11
11
|
private model: string;
|
|
12
12
|
private baseUrl: string;
|
|
13
13
|
|
|
14
|
-
constructor(apiKey?: string, model = 'gpt-
|
|
14
|
+
constructor(apiKey?: string, model = 'gpt-5.2', baseUrl = 'https://api.openai.com/v1') {
|
|
15
15
|
super();
|
|
16
16
|
this.apiKey = apiKey || process.env.OPENAI_API_KEY || '';
|
|
17
17
|
this.model = model;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen Provider - Alibaba Tongyi Qianwen (通义千问) via DashScope API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseProvider } from './base.js';
|
|
6
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
7
|
+
|
|
8
|
+
export class QwenProvider extends BaseProvider {
|
|
9
|
+
name = 'qwen';
|
|
10
|
+
private apiKey: string;
|
|
11
|
+
private model: string;
|
|
12
|
+
private baseUrl = 'https://dashscope.aliyuncs.com/compatible-mode/v1';
|
|
13
|
+
|
|
14
|
+
constructor(apiKey?: string, model = 'qwen3-max') {
|
|
15
|
+
super();
|
|
16
|
+
this.apiKey = apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY || '';
|
|
17
|
+
this.model = model;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
isConfigured(): boolean {
|
|
21
|
+
return !!this.apiKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async invoke(params: InvokeParams): Promise<InvokeResult> {
|
|
25
|
+
if (!this.isConfigured()) {
|
|
26
|
+
throw new Error('Qwen API key not configured. Set DASHSCOPE_API_KEY or QWEN_API_KEY environment variable.');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
30
|
+
|
|
31
|
+
const body: Record<string, unknown> = {
|
|
32
|
+
model: this.model,
|
|
33
|
+
messages: params.messages.map(m => ({ role: m.role, content: m.content })),
|
|
34
|
+
temperature: params.temperature ?? 0.7,
|
|
35
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Add JSON mode if schema provided
|
|
39
|
+
if (params.jsonSchema) {
|
|
40
|
+
body.response_format = { type: 'json_object' };
|
|
41
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
42
|
+
if (lastUserIdx >= 0) {
|
|
43
|
+
const messages = [...params.messages];
|
|
44
|
+
messages[lastUserIdx] = {
|
|
45
|
+
...messages[lastUserIdx],
|
|
46
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
47
|
+
};
|
|
48
|
+
body.messages = messages.map(m => ({ role: m.role, content: m.content }));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const response = await fetch(url, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
'Content-Type': 'application/json',
|
|
56
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
57
|
+
},
|
|
58
|
+
body: JSON.stringify(body),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const error = await response.text();
|
|
63
|
+
throw new Error(`Qwen API error: ${response.status} - ${error}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const data = await response.json() as {
|
|
67
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
68
|
+
usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
content,
|
|
75
|
+
usage: data.usage ? {
|
|
76
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
77
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
78
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
79
|
+
} : undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|