viho 0.0.3 → 0.0.4
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 +203 -1
- package/bin/util.js +17 -0
- package/bin/viho-chat.js +97 -0
- package/bin/viho-model.js +185 -0
- package/bin/viho.js +2 -0
- package/package.json +19 -6
package/README.md
CHANGED
|
@@ -1 +1,203 @@
|
|
|
1
|
-
|
|
1
|
+
# viho
|
|
2
|
+
|
|
3
|
+
A lightweight CLI tool for managing and chatting with AI models.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/viho)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Multiple AI model management
|
|
11
|
+
- Interactive chat with streaming responses
|
|
12
|
+
- Support for thinking mode (enabled/disabled/auto)
|
|
13
|
+
- Configurable API endpoints (OpenAI, Anthropic, custom providers)
|
|
14
|
+
- Default model configuration
|
|
15
|
+
- Simple and intuitive CLI interface
|
|
16
|
+
- Persistent configuration storage
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install globally via npm:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g viho
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Node.js >= 18.0.0
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
1. Add your first AI model:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
viho model add
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Set it as default:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
viho model default
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. Start chatting:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
viho chat
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
### Model Management
|
|
53
|
+
|
|
54
|
+
#### `viho model add`
|
|
55
|
+
|
|
56
|
+
Add a new AI model configuration interactively.
|
|
57
|
+
|
|
58
|
+
You'll be prompted to enter:
|
|
59
|
+
|
|
60
|
+
- Model name (a custom identifier)
|
|
61
|
+
- API key
|
|
62
|
+
- Base URL (e.g., https://api.openai.com/v1)
|
|
63
|
+
- Model ID (e.g., gpt-4, claude-3-5-sonnet-20241022)
|
|
64
|
+
- Thinking mode (enabled/disabled/auto)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
viho model add
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### `viho model list`
|
|
71
|
+
|
|
72
|
+
List all configured models:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
viho model list
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### `viho model remove`
|
|
79
|
+
|
|
80
|
+
Remove a model configuration:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
viho model remove
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `viho model default`
|
|
87
|
+
|
|
88
|
+
Set a default model for chat sessions:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
viho model default
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Chat
|
|
95
|
+
|
|
96
|
+
#### `viho chat [modelName]`
|
|
97
|
+
|
|
98
|
+
Start an interactive chat session with an AI model.
|
|
99
|
+
|
|
100
|
+
If no model name is provided, uses the default model:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
viho chat
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or specify a model explicitly:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
viho chat mymodel
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The chat interface includes:
|
|
113
|
+
|
|
114
|
+
- Editor-based question input
|
|
115
|
+
- Streaming responses
|
|
116
|
+
- Visual thinking process (when enabled)
|
|
117
|
+
- Colored output for better readability
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
Configuration is stored in `~/viho.json`. You can manage all settings through the CLI commands.
|
|
122
|
+
|
|
123
|
+
### Example Configuration Structure
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"mymodel": {
|
|
128
|
+
"modelName": "mymodel",
|
|
129
|
+
"apiKey": "your-api-key",
|
|
130
|
+
"baseURL": "https://api.openai.com/v1",
|
|
131
|
+
"modelID": "gpt-4",
|
|
132
|
+
"modelThinking": "auto"
|
|
133
|
+
},
|
|
134
|
+
"default": "mymodel"
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Supported Providers
|
|
139
|
+
|
|
140
|
+
viho works with any OpenAI-compatible API, including:
|
|
141
|
+
|
|
142
|
+
- OpenAI (GPT-4, GPT-3.5, etc.)
|
|
143
|
+
- Anthropic Claude (via compatible endpoints)
|
|
144
|
+
- Custom LLM providers with OpenAI-compatible APIs
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
### Adding an OpenAI Model
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
viho model add
|
|
152
|
+
# Enter model name: gpt4
|
|
153
|
+
# Enter API key: sk-...
|
|
154
|
+
# Enter base URL: https://api.openai.com/v1
|
|
155
|
+
# Enter model ID: gpt-4
|
|
156
|
+
# Thinking mode: disabled
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Adding a Claude Model
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
viho model add
|
|
163
|
+
# Enter model name: claude
|
|
164
|
+
# Enter API key: your-anthropic-key
|
|
165
|
+
# Enter base URL: https://api.anthropic.com
|
|
166
|
+
# Enter model ID: claude-3-5-sonnet-20241022
|
|
167
|
+
# Thinking mode: auto
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Setting Up for First Use
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Add a model
|
|
174
|
+
viho model add
|
|
175
|
+
|
|
176
|
+
# Set it as default
|
|
177
|
+
viho model default
|
|
178
|
+
|
|
179
|
+
# Start chatting
|
|
180
|
+
viho chat
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Dependencies
|
|
184
|
+
|
|
185
|
+
- [qiao-cli](https://www.npmjs.com/package/qiao-cli) - CLI utilities
|
|
186
|
+
- [qiao-config](https://www.npmjs.com/package/qiao-config) - Configuration management
|
|
187
|
+
- [qiao-llm](https://www.npmjs.com/package/qiao-llm) - LLM integration
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
|
192
|
+
|
|
193
|
+
## Author
|
|
194
|
+
|
|
195
|
+
uikoo9 <uikoo9@qq.com>
|
|
196
|
+
|
|
197
|
+
## Issues
|
|
198
|
+
|
|
199
|
+
Report issues at: https://github.com/uikoo9/viho/issues
|
|
200
|
+
|
|
201
|
+
## Homepage
|
|
202
|
+
|
|
203
|
+
https://github.com/uikoo9/viho
|
package/bin/util.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// os
|
|
2
|
+
const os = require('os');
|
|
3
|
+
|
|
4
|
+
// path
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
// db
|
|
8
|
+
const DB = require('qiao-config');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* getDB
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
exports.getDB = () => {
|
|
15
|
+
const dbPath = path.resolve(os.homedir(), './viho.json');
|
|
16
|
+
return DB(dbPath);
|
|
17
|
+
};
|
package/bin/viho-chat.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// qiao
|
|
2
|
+
const cli = require('qiao-cli');
|
|
3
|
+
|
|
4
|
+
// llm
|
|
5
|
+
const LLM = require('qiao-llm');
|
|
6
|
+
|
|
7
|
+
// db
|
|
8
|
+
const { getDB } = require('./util.js');
|
|
9
|
+
const db = getDB();
|
|
10
|
+
|
|
11
|
+
// cmd
|
|
12
|
+
cli.cmd
|
|
13
|
+
.command('chat [modelName]')
|
|
14
|
+
.description('Chat with an AI model')
|
|
15
|
+
.action(async (modelName) => {
|
|
16
|
+
if (!modelName) {
|
|
17
|
+
const defaultModel = await db.config('default');
|
|
18
|
+
if (!defaultModel) {
|
|
19
|
+
console.log(cli.colors.red('No default model set. Use: viho model default'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
modelName = defaultModel;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// check
|
|
27
|
+
const model = await db.config(modelName);
|
|
28
|
+
if (!model) {
|
|
29
|
+
console.log(cli.colors.red(`Model not found: ${modelName}`));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// init
|
|
34
|
+
const llm = LLM({
|
|
35
|
+
apiKey: model.apiKey,
|
|
36
|
+
baseURL: model.baseURL,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ask
|
|
40
|
+
const questions = [
|
|
41
|
+
{
|
|
42
|
+
type: 'editor',
|
|
43
|
+
name: 'content',
|
|
44
|
+
message: 'Your question:',
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
const answers = await cli.ask(questions);
|
|
48
|
+
|
|
49
|
+
// answers
|
|
50
|
+
console.log();
|
|
51
|
+
console.log(cli.colors.gray('Question:'));
|
|
52
|
+
console.log(cli.colors.gray(answers.content));
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
// chat
|
|
56
|
+
const chatOptions = {
|
|
57
|
+
model: model.modelID,
|
|
58
|
+
messages: [
|
|
59
|
+
{ role: 'system', content: 'You are a helpful AI assistant' },
|
|
60
|
+
{ role: 'user', content: answers.content },
|
|
61
|
+
],
|
|
62
|
+
thinking: {
|
|
63
|
+
type: model.modelThinking,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// callback options
|
|
68
|
+
const callbackOptions = {
|
|
69
|
+
firstThinkingCallback: () => {
|
|
70
|
+
console.log();
|
|
71
|
+
console.log(cli.colors.gray('[Thinking...]'));
|
|
72
|
+
console.log();
|
|
73
|
+
},
|
|
74
|
+
thinkingCallback: (msg) => {
|
|
75
|
+
process.stdout.write(cli.colors.gray(msg));
|
|
76
|
+
},
|
|
77
|
+
firstContentCallback: () => {
|
|
78
|
+
console.log();
|
|
79
|
+
console.log(cli.colors.cyan('[Response]'));
|
|
80
|
+
console.log();
|
|
81
|
+
},
|
|
82
|
+
contentCallback: (msg) => {
|
|
83
|
+
process.stdout.write(msg);
|
|
84
|
+
},
|
|
85
|
+
endCallback: () => {
|
|
86
|
+
console.log();
|
|
87
|
+
},
|
|
88
|
+
errorCallback: (error) => {
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(cli.colors.red('Error:'));
|
|
91
|
+
console.log(error);
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// go
|
|
96
|
+
await llm.chatWithStreaming(chatOptions, callbackOptions);
|
|
97
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// qiao
|
|
2
|
+
const cli = require('qiao-cli');
|
|
3
|
+
|
|
4
|
+
// db
|
|
5
|
+
const { getDB } = require('./util.js');
|
|
6
|
+
const db = getDB();
|
|
7
|
+
|
|
8
|
+
// actions
|
|
9
|
+
const actions = ['add', 'list', 'remove', 'default'];
|
|
10
|
+
|
|
11
|
+
// model
|
|
12
|
+
cli.cmd
|
|
13
|
+
.command('model <action>')
|
|
14
|
+
.description('Manage AI models')
|
|
15
|
+
.action((action) => {
|
|
16
|
+
if (!actions.includes(action)) {
|
|
17
|
+
console.log(cli.colors.red('Invalid action. Use: add, list, remove, default'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// actions
|
|
22
|
+
if (action === 'add') modelAdd();
|
|
23
|
+
if (action === 'list') modelList();
|
|
24
|
+
if (action === 'remove') modelRemove();
|
|
25
|
+
if (action === 'default') modelDefault();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* modelAdd
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
async function modelAdd() {
|
|
33
|
+
try {
|
|
34
|
+
// q a
|
|
35
|
+
const questions = [
|
|
36
|
+
{
|
|
37
|
+
type: 'input',
|
|
38
|
+
name: 'modelName',
|
|
39
|
+
message: 'Enter model name:',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'input',
|
|
43
|
+
name: 'apiKey',
|
|
44
|
+
message: 'Enter API key:',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: 'input',
|
|
48
|
+
name: 'baseURL',
|
|
49
|
+
message: 'Enter base URL:',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'input',
|
|
53
|
+
name: 'modelID',
|
|
54
|
+
message: 'Enter model ID:',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'modelThinking',
|
|
59
|
+
message: 'Thinking mode:',
|
|
60
|
+
choices: ['enabled', 'disabled', 'auto'],
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const answers = await cli.ask(questions);
|
|
64
|
+
console.log();
|
|
65
|
+
|
|
66
|
+
// check
|
|
67
|
+
const dbKey = answers.modelName;
|
|
68
|
+
const dbValue = await db.config(dbKey);
|
|
69
|
+
if (dbValue) {
|
|
70
|
+
console.log(cli.colors.red('Model name already exists'));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// set
|
|
75
|
+
await db.config(dbKey, answers);
|
|
76
|
+
console.log(cli.colors.green('Model added'));
|
|
77
|
+
console.log();
|
|
78
|
+
|
|
79
|
+
// list
|
|
80
|
+
const all = await db.all();
|
|
81
|
+
console.log(all);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.log(cli.colors.red('Error: Failed to add model'));
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* modelList
|
|
91
|
+
*/
|
|
92
|
+
async function modelList() {
|
|
93
|
+
try {
|
|
94
|
+
// list
|
|
95
|
+
const all = await db.all();
|
|
96
|
+
console.log(cli.colors.cyan('Configured models:'));
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(all);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.log(cli.colors.red('Error: Failed to list models'));
|
|
101
|
+
console.log();
|
|
102
|
+
|
|
103
|
+
console.log(e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* modelRemove
|
|
109
|
+
*/
|
|
110
|
+
async function modelRemove() {
|
|
111
|
+
try {
|
|
112
|
+
// q a
|
|
113
|
+
const questions = [
|
|
114
|
+
{
|
|
115
|
+
type: 'input',
|
|
116
|
+
name: 'modelName',
|
|
117
|
+
message: 'Enter model name to remove:',
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
const answers = await cli.ask(questions);
|
|
121
|
+
console.log();
|
|
122
|
+
|
|
123
|
+
// del
|
|
124
|
+
const dbKey = answers.modelName;
|
|
125
|
+
await db.config(dbKey, null);
|
|
126
|
+
console.log(cli.colors.green('Model removed'));
|
|
127
|
+
console.log();
|
|
128
|
+
|
|
129
|
+
// list
|
|
130
|
+
const all = await db.all();
|
|
131
|
+
console.log(all);
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.log(cli.colors.red('Error: Failed to remove model'));
|
|
134
|
+
console.log();
|
|
135
|
+
|
|
136
|
+
console.log(e);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* modelDefault
|
|
142
|
+
* @returns
|
|
143
|
+
*/
|
|
144
|
+
async function modelDefault() {
|
|
145
|
+
try {
|
|
146
|
+
// q a
|
|
147
|
+
const questions = [
|
|
148
|
+
{
|
|
149
|
+
type: 'input',
|
|
150
|
+
name: 'modelName',
|
|
151
|
+
message: 'Enter default model name:',
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
const answers = await cli.ask(questions);
|
|
155
|
+
console.log();
|
|
156
|
+
|
|
157
|
+
// get keys
|
|
158
|
+
const all = await db.all();
|
|
159
|
+
const keys = Object.keys(all);
|
|
160
|
+
|
|
161
|
+
// check keys
|
|
162
|
+
if (!keys || !keys.length) {
|
|
163
|
+
console.log(cli.colors.red('No models found. Add one first: viho model add'));
|
|
164
|
+
console.log();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// check model
|
|
169
|
+
if (!keys.includes(answers.modelName)) {
|
|
170
|
+
console.log(cli.colors.red('Model not found. Available models:'));
|
|
171
|
+
console.log();
|
|
172
|
+
console.log(all);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// set
|
|
177
|
+
await db.config('default', answers.modelName);
|
|
178
|
+
console.log(cli.colors.green(`Default model: ${answers.modelName}`));
|
|
179
|
+
console.log();
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.log(cli.colors.red('Error: Failed to set default model'));
|
|
182
|
+
console.log();
|
|
183
|
+
console.log(e);
|
|
184
|
+
}
|
|
185
|
+
}
|
package/bin/viho.js
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viho",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"keywords": [
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "A lightweight CLI tool for managing and chatting with AI models",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"llm",
|
|
8
|
+
"chat",
|
|
9
|
+
"cli",
|
|
10
|
+
"chatgpt",
|
|
11
|
+
"anthropic",
|
|
12
|
+
"openai",
|
|
13
|
+
"ai-assistant",
|
|
14
|
+
"command-line",
|
|
15
|
+
"terminal"
|
|
16
|
+
],
|
|
6
17
|
"author": "uikoo9 <uikoo9@qq.com>",
|
|
7
18
|
"license": "MIT",
|
|
8
19
|
"homepage": "https://github.com/uikoo9/viho#readme",
|
|
@@ -22,10 +33,12 @@
|
|
|
22
33
|
"README.md"
|
|
23
34
|
],
|
|
24
35
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
36
|
+
"node": ">=18.0.0"
|
|
26
37
|
},
|
|
27
38
|
"dependencies": {
|
|
28
|
-
"qiao-cli": "^5.0.0"
|
|
39
|
+
"qiao-cli": "^5.0.0",
|
|
40
|
+
"qiao-config": "^5.0.1",
|
|
41
|
+
"qiao-llm": "^0.2.5"
|
|
29
42
|
},
|
|
30
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "08434f296e9d87e8ae6dcb2720989edbc414841e"
|
|
31
44
|
}
|