ask-my-llm 1.0.6 → 1.0.8
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 +12 -1
- package/bin/ask-ai +88 -11
- package/index.js +63 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,10 +24,14 @@ Config saved to `~/.askairc`.
|
|
|
24
24
|
## Usage
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
|
-
const { ask } = require("ask-my-llm");
|
|
27
|
+
const { ask, askAsync } = require("ask-my-llm");
|
|
28
28
|
|
|
29
|
+
// sync
|
|
29
30
|
const response = ask("Hello");
|
|
30
31
|
console.log(response);
|
|
32
|
+
|
|
33
|
+
// async
|
|
34
|
+
askAsync("Hello").then(console.log);
|
|
31
35
|
```
|
|
32
36
|
|
|
33
37
|
## API
|
|
@@ -38,3 +42,10 @@ Sends a prompt to the configured AI and returns the response synchronously.
|
|
|
38
42
|
|
|
39
43
|
- `prompt` - The message to send
|
|
40
44
|
- Returns: AI response as string
|
|
45
|
+
|
|
46
|
+
### askAsync(prompt: string): Promise<string>
|
|
47
|
+
|
|
48
|
+
Sends a prompt to the configured AI and returns a Promise.
|
|
49
|
+
|
|
50
|
+
- `prompt` - The message to send
|
|
51
|
+
- Returns: Promise that resolves to AI response as string
|
package/bin/ask-ai
CHANGED
|
@@ -10,36 +10,113 @@ function loadConfig() {
|
|
|
10
10
|
try {
|
|
11
11
|
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
12
12
|
} catch (e) {
|
|
13
|
-
return { baseApi: 'https://api.openai.com/v1', apiKey: '', model: 'gpt-3.5-turbo' };
|
|
13
|
+
return { current: 'default', configs: { default: { baseApi: 'https://api.openai.com/v1', apiKey: '', model: 'gpt-3.5-turbo' } } };
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
function saveConfig(config) {
|
|
18
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function mainMenu() {
|
|
22
|
+
const config = loadConfig();
|
|
23
|
+
const choices = ['Add new config', 'Select config', 'Remove config'];
|
|
19
24
|
|
|
25
|
+
if (config.current) {
|
|
26
|
+
choices.unshift(`Use current (${config.current})`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { action } = await inquirer.prompt([{
|
|
30
|
+
type: 'list',
|
|
31
|
+
name: 'action',
|
|
32
|
+
message: 'Manage configs:',
|
|
33
|
+
choices
|
|
34
|
+
}]);
|
|
35
|
+
|
|
36
|
+
if (action === 'Add new config') {
|
|
37
|
+
await addConfig(config);
|
|
38
|
+
} else if (action === 'Select config') {
|
|
39
|
+
await selectConfig(config);
|
|
40
|
+
} else if (action === 'Remove config') {
|
|
41
|
+
await removeConfig(config);
|
|
42
|
+
} else {
|
|
43
|
+
console.log(`Current: ${config.current}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function addConfig(config) {
|
|
48
|
+
const { name } = await inquirer.prompt([{
|
|
49
|
+
type: 'input',
|
|
50
|
+
name: 'name',
|
|
51
|
+
message: 'Config name:'
|
|
52
|
+
}]);
|
|
53
|
+
|
|
54
|
+
if (config.configs[name]) {
|
|
55
|
+
console.log('Config already exists. Edit it instead.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
const answers = await inquirer.prompt([
|
|
21
60
|
{
|
|
22
61
|
type: 'input',
|
|
23
62
|
name: 'baseApi',
|
|
24
|
-
message: 'Base API URL
|
|
25
|
-
default:
|
|
63
|
+
message: 'Base API URL:',
|
|
64
|
+
default: 'https://api.openai.com/v1'
|
|
26
65
|
},
|
|
27
66
|
{
|
|
28
67
|
type: 'input',
|
|
29
68
|
name: 'apiKey',
|
|
30
69
|
message: 'API Key (leave empty for none):',
|
|
31
|
-
default:
|
|
70
|
+
default: ''
|
|
32
71
|
},
|
|
33
72
|
{
|
|
34
73
|
type: 'input',
|
|
35
74
|
name: 'model',
|
|
36
|
-
message: 'Model
|
|
37
|
-
default:
|
|
75
|
+
message: 'Model:',
|
|
76
|
+
default: 'gpt-3.5-turbo'
|
|
38
77
|
}
|
|
39
78
|
]);
|
|
40
79
|
|
|
41
|
-
|
|
42
|
-
|
|
80
|
+
config.configs[name] = answers;
|
|
81
|
+
config.current = name;
|
|
82
|
+
saveConfig(config);
|
|
83
|
+
console.log(`Config '${name}' saved.`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function selectConfig(config) {
|
|
87
|
+
const names = Object.keys(config.configs);
|
|
88
|
+
const { name } = await inquirer.prompt([{
|
|
89
|
+
type: 'list',
|
|
90
|
+
name: 'name',
|
|
91
|
+
message: 'Select config:',
|
|
92
|
+
choices: names
|
|
93
|
+
}]);
|
|
94
|
+
|
|
95
|
+
config.current = name;
|
|
96
|
+
saveConfig(config);
|
|
97
|
+
console.log(`Using '${name}'.`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function removeConfig(config) {
|
|
101
|
+
const names = Object.keys(config.configs);
|
|
102
|
+
if (names.length <= 1) {
|
|
103
|
+
console.log('Cannot remove last config.');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { name } = await inquirer.prompt([{
|
|
108
|
+
type: 'list',
|
|
109
|
+
name: 'name',
|
|
110
|
+
message: 'Remove config:',
|
|
111
|
+
choices: names
|
|
112
|
+
}]);
|
|
113
|
+
|
|
114
|
+
delete config.configs[name];
|
|
115
|
+
if (config.current === name) {
|
|
116
|
+
config.current = Object.keys(config.configs)[0];
|
|
117
|
+
}
|
|
118
|
+
saveConfig(config);
|
|
119
|
+
console.log(`Config '${name}' removed.`);
|
|
43
120
|
}
|
|
44
121
|
|
|
45
|
-
|
|
122
|
+
mainMenu();
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { spawnSync } = require('child_process');
|
|
1
|
+
const { spawnSync, spawn } = require('child_process');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
@@ -6,9 +6,14 @@ const CONFIG_PATH = path.join(process.env.HOME || process.env.USERPROFILE, '.ask
|
|
|
6
6
|
|
|
7
7
|
function loadConfig() {
|
|
8
8
|
try {
|
|
9
|
-
|
|
9
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
10
|
+
const current = data.current || 'default';
|
|
11
|
+
if (!data.configs || !data.configs[current]) {
|
|
12
|
+
throw new Error('No config found. Run `npx ask-my-llm` first.');
|
|
13
|
+
}
|
|
14
|
+
return data.configs[current];
|
|
10
15
|
} catch (e) {
|
|
11
|
-
throw new Error('Not configured. Run `npx ask-
|
|
16
|
+
throw new Error('Not configured. Run `npx ask-my-llm` first.');
|
|
12
17
|
}
|
|
13
18
|
}
|
|
14
19
|
|
|
@@ -48,12 +53,64 @@ function ask(prompt) {
|
|
|
48
53
|
if (result.error) throw result.error;
|
|
49
54
|
if (result.status !== 0) throw new Error('curl failed: ' + (result.stderr || result.stdout));
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
return parseResponse(result.stdout);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function askAsync(prompt) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const config = loadConfig();
|
|
62
|
+
|
|
63
|
+
const postData = JSON.stringify({
|
|
64
|
+
model: config.model,
|
|
65
|
+
messages: [{ role: 'user', content: prompt }],
|
|
66
|
+
temperature: 0.7
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
let baseUrl = config.baseApi.replace(/\/$/, '');
|
|
70
|
+
if (!baseUrl.endsWith('/v1')) {
|
|
71
|
+
baseUrl += '/v1';
|
|
72
|
+
}
|
|
73
|
+
const url = baseUrl + '/chat/completions';
|
|
74
|
+
|
|
75
|
+
const headerArgs = [
|
|
76
|
+
'-s', '-S', '-X', 'POST',
|
|
77
|
+
'-H', 'Content-Type: application/json',
|
|
78
|
+
'-H', 'HTTP-Referer: https://cows.info.gf',
|
|
79
|
+
'-H', 'X-OpenRouter-Title: ask-my-llm'
|
|
80
|
+
];
|
|
81
|
+
if (config.apiKey) {
|
|
82
|
+
headerArgs.push('-H', `Authorization: Bearer ${config.apiKey}`);
|
|
83
|
+
}
|
|
84
|
+
headerArgs.push('-d', postData, url);
|
|
85
|
+
|
|
86
|
+
const child = spawn('curl', headerArgs, { encoding: 'utf8' });
|
|
87
|
+
let data = '';
|
|
88
|
+
let error = '';
|
|
89
|
+
|
|
90
|
+
child.stdout.on('data', chunk => data += chunk);
|
|
91
|
+
child.stderr.on('data', chunk => error += chunk);
|
|
92
|
+
child.on('close', code => {
|
|
93
|
+
if (code !== 0) {
|
|
94
|
+
reject(new Error('curl failed: ' + (error || data)));
|
|
95
|
+
} else {
|
|
96
|
+
try {
|
|
97
|
+
resolve(parseResponse(data));
|
|
98
|
+
} catch (e) {
|
|
99
|
+
reject(e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
child.on('error', reject);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function parseResponse(stdout) {
|
|
108
|
+
const parsed = JSON.parse(stdout);
|
|
52
109
|
if (parsed.choices && parsed.choices[0]) {
|
|
53
110
|
return parsed.choices[0].message.content;
|
|
54
111
|
} else {
|
|
55
|
-
throw new Error('API Error: ' +
|
|
112
|
+
throw new Error('API Error: ' + stdout);
|
|
56
113
|
}
|
|
57
114
|
}
|
|
58
115
|
|
|
59
|
-
module.exports = { ask };
|
|
116
|
+
module.exports = { ask, askAsync };
|