Skip to content

Commit 511b1d6

Browse files
authored
Merge pull request #34 from CommandAI/openAI
Open ai
2 parents 1077647 + 4a1071e commit 511b1d6

4 files changed

Lines changed: 123 additions & 52 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ This tool is still in its alpha stage, so expect some hiccups as we fine-tune ou
5454

5555
## 🔥 Features
5656

57-
- **AI Service Selection**: Pick between Ollama and ChatGPT to generate scripts for you.
57+
- **AI Service Selection**: Pick between Ollama, ChatGPT, and any OpenAI compatable server.
5858
- **Easy Configuration**: Set everything up quickly with a few prompts.
59-
- **Execution Plans**: See what’s going to happen before it happens.
60-
- **Optional Logging**: Keep track of everything for debugging or just for fun.
59+
- **Command Line Power**: Ask the AI to do any task(s) you would want to do at the command line.
60+
- **DB Power**: Ask the AI for any information from your DB in plain language.
61+
- **Conversational AI**: Start a conversation with the AI.
6162

6263
## 📋 Requirements
6364

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import OpenAI from "openai";
22
import BaseAdapter from "./BaseAdapter.js";
33

4-
class ChatGPTAdapter extends BaseAdapter {
4+
class OpenAIAdapter extends BaseAdapter {
55
constructor(config) {
66
super(config);
7-
this.apiKey = config.chatgptApiKey;
8-
this.model = config.chatgptModel;
7+
this.apiKey = config.openAIApiKey;
8+
this.model = config.openAIModel;
9+
this.apiUrl = config.openAIApiUrl || "https://api.openai.com/v1";
910
}
1011

1112
async generateResponse(command) {
1213
try {
13-
const openai = new OpenAI({ apiKey: this.apiKey });
14+
const openai = new OpenAI({
15+
apiKey: this.apiKey,
16+
baseURL: this.apiUrl,
17+
});
1418

1519
this.messages.push({
1620
role: "user",
@@ -33,4 +37,4 @@ class ChatGPTAdapter extends BaseAdapter {
3337
}
3438
}
3539

36-
export default ChatGPTAdapter;
40+
export default OpenAIAdapter;

lib/aiClient/aiClient.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import OllamaAdapter from "./adapters/OllamaAdapter.js";
2-
import ChatGPTAdapter from "./adapters/ChatGPTAdapter.js";
2+
import OpenAIAdapter from "./adapters/OpenAIAdapter.js";
33

44
class AIClient {
55
constructor(config) {
66
this.adapter =
7-
config.aiService === "ChatGPT"
8-
? new ChatGPTAdapter(config)
7+
config.aiService === "ChatGPT" || "OpenAI"
8+
? new OpenAIAdapter(config)
99
: new OllamaAdapter(config);
1010
}
1111

lib/config.js

Lines changed: 107 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import { Ollama } from "ollama";
77
import gradient from "gradient-string";
88

99
const configPath = path.join(os.homedir(), ".commandai", "config.json");
10-
const defaultChatGPTModel = "gpt-4o";
10+
const defaultOpenAIModel = "gpt-4o";
11+
const defaultOpenAIApiUrl = "https://api.openai.com/v1";
1112
const defaultOllamaUrl = "http://127.0.0.1:11434";
1213

1314
async function checkOllamaActive(url = defaultOllamaUrl) {
@@ -25,8 +26,32 @@ function loadConfig() {
2526
if (fs.existsSync(configPath)) {
2627
const configFile = fs.readFileSync(configPath);
2728
const config = JSON.parse(configFile);
29+
30+
if (!config.openAIApiUrl) {
31+
config.openAIApiUrl = config.chatgptApiUrl;
32+
33+
delete config.chatgptApiUrl;
34+
}
35+
36+
if (!config.openAIModel) {
37+
config.openAIModel = config.chatgptModel;
38+
39+
delete config.chatgptModel;
40+
}
41+
42+
if (!config.openAIApiKey) {
43+
config.openAIApiKey = config.chatgptApiKey;
44+
45+
delete config.chatgptApiKey;
46+
}
47+
2848
config.model =
29-
config.aiService === "Ollama" ? config.ollamaModel : config.chatgptModel;
49+
config.aiService === "Ollama" ? config.ollamaModel : config.openAIModel;
50+
51+
if (config.aiService === "ChatGPT") {
52+
config.openAIApiUrl = defaultOpenAIApiUrl;
53+
}
54+
3055
return config;
3156
} else {
3257
return null;
@@ -35,7 +60,7 @@ function loadConfig() {
3560

3661
async function saveConfig(config) {
3762
config.model =
38-
config.aiService === "Ollama" ? config.ollamaModel : config.chatgptModel;
63+
config.aiService === "Ollama" ? config.ollamaModel : config.openAIModel;
3964
const dir = path.dirname(configPath);
4065
if (!fs.existsSync(dir)) {
4166
fs.mkdirSync(dir, { recursive: true });
@@ -46,9 +71,9 @@ async function saveConfig(config) {
4671
console.log();
4772
}
4873

49-
async function validateApiKey(apiKey) {
74+
async function validateApiKey(apiKey, openAIApiUrl) {
5075
try {
51-
const openai = new OpenAI({ apiKey: apiKey });
76+
const openai = new OpenAI({ baseURL: openAIApiUrl, apiKey: apiKey });
5277
await openai.models.list();
5378
return true;
5479
} catch (error) {
@@ -57,9 +82,9 @@ async function validateApiKey(apiKey) {
5782
}
5883
}
5984

60-
async function fetchChatGPTModels(apiKey) {
85+
async function fetchOpenAIModels(openAIApiUrl, apiKey) {
6186
try {
62-
const openai = new OpenAI({ apiKey: apiKey });
87+
const openai = new OpenAI({ baseURL: openAIApiUrl, apiKey: apiKey });
6388
const response = await openai.models.list();
6489
return response.data.map((model) => model.id);
6590
} catch (error) {
@@ -161,67 +186,100 @@ async function promptOllamaConfig(defaultUrl, defaultModel) {
161186
};
162187
}
163188

164-
async function promptChatGPTAPIKey(defaultApiKey) {
165-
const chatgptConfig = await inquirer.prompt([
189+
async function promptOpenAIAPIKey(defaultApiKey, openAIApiUrl) {
190+
const openAIConfig = await inquirer.prompt([
166191
{
167192
type: "input",
168-
name: "chatgptApiKey",
193+
name: "openAIApiKey",
169194
message: "Enter your OpenAI API key:",
170195
default: defaultApiKey,
171196
},
172197
]);
173198

174-
const isValidApiKey = await validateApiKey(chatgptConfig.chatgptApiKey);
199+
const isValidApiKey = await validateApiKey(openAIConfig.openAIApiKey, openAIApiUrl);
175200

176201
if (!isValidApiKey) {
177202
return null;
178203
}
179204

180-
return chatgptConfig;
205+
return openAIConfig;
181206
}
182207

183-
async function promptChatGPTModels(defaultModel, defaultApiKey) {
184-
const models = await fetchChatGPTModels(defaultApiKey);
208+
209+
async function promptOpenAIModels(defaultModel, openAIApiUrl, openAIApiKey) {
210+
const models = await fetchOpenAIModels(openAIApiUrl, openAIApiKey);
211+
185212
if (models.length === 0) {
186213
console.error(
187214
"No models found for the provided API key. Please try again.",
188215
);
189216
return null;
190217
}
191218

192-
const chatgptModel = await inquirer.prompt([
219+
const openAIModel = await inquirer.prompt([
193220
{
194221
type: "list",
195-
name: "chatgptModel",
196-
message: "Select the ChatGPT model:",
222+
name: "openAIModel",
223+
message: "Select the OpenAI model:",
197224
choices: models,
198225
default: defaultModel,
199226
},
200227
]);
201228

202-
return chatgptModel;
229+
return openAIModel;
230+
}
231+
232+
async function promptOpenAIURL(defaultUrl) {
233+
const openAIApiUrl = await inquirer.prompt([
234+
{
235+
type: "input",
236+
name: "openAIApiUrl",
237+
message: "Enter the URL for the OpenAI server:",
238+
default: defaultUrl,
239+
},
240+
]);
241+
242+
return openAIApiUrl;
203243
}
204244

205-
async function promptChatGPTConfig(defaultApiKey, defaultModel) {
206-
const chatgptConfig = await promptChatGPTAPIKey(defaultApiKey);
245+
async function promptOpenAIConfig(config) {
246+
let openAIApiUrl = { openAIApiUrl: config.openAIApiUrl || defaultOpenAIApiUrl };
207247

208-
if (!chatgptConfig) return null;
248+
if (config.aiService === "OpenAI") {
249+
openAIApiUrl = await promptOpenAIURL(config.openAIApiUrl);
250+
if (!openAIApiUrl) return null;
251+
} else {
252+
openAIApiUrl = { openAIApiUrl: defaultOpenAIApiUrl };
253+
}
209254

210-
const chatgptModel = await promptChatGPTModels(defaultModel, defaultApiKey);
255+
const openAIConfig = await promptOpenAIAPIKey(config.openAIApiKey, openAIApiUrl.openAIApiUrl);
211256

212-
if (!chatgptModel) return null;
257+
if (!openAIConfig) return null;
213258

214-
return {
215-
chatgptApiKey: chatgptConfig.chatgptApiKey,
216-
chatgptModel: chatgptModel.chatgptModel,
217-
};
259+
const openAIModel = await promptOpenAIModels(config.openAIModel, openAIApiUrl.openAIApiUrl, config.openAIApiKey);
260+
261+
if (!openAIModel) return null;
262+
263+
if (openAIApiUrl) {
264+
return {
265+
openAIApiKey: openAIConfig.openAIApiKey,
266+
openAIModel: openAIModel.openAIModel,
267+
openAIApiUrl: openAIApiUrl.openAIApiUrl,
268+
};
269+
} else {
270+
return {
271+
openAIApiKey: openAIConfig.openAIApiKey,
272+
openAIModel: openAIModel.openAIModel,
273+
};
274+
}
218275
}
219276

220277
async function getAIServiceChoices() {
221278
const isOllamaActive = await checkOllamaActive();
222279
return [
223280
{ name: isOllamaActive ? "Ollama (Running)" : "Ollama", value: "Ollama" },
224281
{ name: "ChatGPT", value: "ChatGPT" },
282+
{ name: "OpenAI", value: "OpenAI" },
225283
];
226284
}
227285

@@ -230,8 +288,9 @@ async function configure(
230288
aiService: "",
231289
ollamaUrl: defaultOllamaUrl,
232290
ollamaModel: "",
233-
chatgptApiKey: "",
234-
chatgptModel: defaultChatGPTModel,
291+
openAIApiKey: "",
292+
openAIModel: defaultOpenAIModel,
293+
openAIApiUrl: defaultOpenAIApiUrl,
235294
showExecutionDescription: true,
236295
showExecutionPlan: true,
237296
enableLogging: false,
@@ -266,7 +325,7 @@ async function promptAndConfigureAIService(finalConfig, aiServiceChoices) {
266325
finalConfig.model =
267326
finalConfig.aiService === "Ollama"
268327
? finalConfig.ollamaModel
269-
: finalConfig.chatgptModel;
328+
: finalConfig.openAIModel;
270329

271330
validConfig = true;
272331
}
@@ -278,7 +337,9 @@ async function configureSelectedAIService(finalConfig) {
278337
if (finalConfig.aiService === "Ollama") {
279338
return await configureOllamaService(finalConfig);
280339
} else if (finalConfig.aiService === "ChatGPT") {
281-
return await configureChatGPTService(finalConfig);
340+
return await configureOpenAIService(finalConfig);
341+
} else if (finalConfig.aiService === "OpenAI") {
342+
return await configureOpenAIService(finalConfig);
282343
}
283344
return null;
284345
}
@@ -296,17 +357,22 @@ async function configureOllamaService(finalConfig) {
296357
};
297358
}
298359

299-
async function configureChatGPTService(finalConfig) {
300-
const chatgptConfig = await promptChatGPTConfig(
301-
finalConfig.chatgptApiKey,
302-
finalConfig.chatgptModel,
303-
);
304-
if (!chatgptConfig) return null;
360+
async function configureOpenAIService(finalConfig) {
361+
const openAIConfig = await promptOpenAIConfig(finalConfig);
362+
if (!openAIConfig) return null;
305363

306-
return {
307-
chatgptApiKey: chatgptConfig.chatgptApiKey,
308-
chatgptModel: chatgptConfig.chatgptModel,
309-
};
364+
if (finalConfig.aiService === "OpenAI") {
365+
return {
366+
openAIApiKey: openAIConfig.openAIApiKey,
367+
openAIModel: openAIConfig.openAIModel,
368+
openAIApiUrl: openAIConfig.openAIApiUrl,
369+
};
370+
} else {
371+
return {
372+
openAIApiKey: openAIConfig.openAIApiKey,
373+
openAIModel: openAIConfig.openAIModel,
374+
};
375+
}
310376
}
311377

312378
export { loadConfig, saveConfig, configure };

0 commit comments

Comments
 (0)