cloving 0.3.8 → 0.3.10

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.
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AzureOpenAIAdapter = void 0;
4
+ class AzureOpenAIAdapter {
5
+ static listSupportedModels() {
6
+ AzureOpenAIAdapter.supportedModels.forEach((model) => {
7
+ console.log(model);
8
+ });
9
+ }
10
+ constructor(model, endpoint) {
11
+ this.model = model;
12
+ this.endpoint = endpoint;
13
+ }
14
+ getEndpoint() {
15
+ return this.endpoint;
16
+ }
17
+ getHeaders(apiKey) {
18
+ return {
19
+ 'api-key': apiKey,
20
+ 'Content-Type': 'application/json',
21
+ };
22
+ }
23
+ getPayload(request, stream = false) {
24
+ return {
25
+ model: this.model.replace('azureopenai:', '').replace(':', '-'),
26
+ messages: request.messages && request.messages.length > 0
27
+ ? request.messages
28
+ : [{ role: 'user', content: request.prompt }],
29
+ max_tokens: request.maxTokens,
30
+ temperature: request.temperature || 0.2,
31
+ stream,
32
+ };
33
+ }
34
+ extractResponse(data) {
35
+ return data.choices[0].message.content;
36
+ }
37
+ convertStream(data) {
38
+ let beginningChar = 0;
39
+ let lastChar = 0;
40
+ while (lastChar < data.length) {
41
+ try {
42
+ let remainingString = data.slice(beginningChar, lastChar);
43
+ const firstBraceIndex = remainingString.indexOf('{');
44
+ if (firstBraceIndex > -1) {
45
+ remainingString = remainingString.slice(firstBraceIndex);
46
+ }
47
+ const parsedObject = JSON.parse(remainingString);
48
+ const output = parsedObject?.choices[0]?.delta?.content || '';
49
+ return {
50
+ output,
51
+ lastChar,
52
+ };
53
+ }
54
+ catch (error) {
55
+ lastChar += 1;
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ }
61
+ exports.AzureOpenAIAdapter = AzureOpenAIAdapter;
62
+ AzureOpenAIAdapter.supportedModels = [
63
+ 'azureopenai:gpt:4o-2024-08-06',
64
+ 'azureopenai:gpt:4o',
65
+ 'azureopenai:gpt:4o-mini',
66
+ 'azureopenai:gpt:4-turbo',
67
+ 'azureopenai:gpt:3.5-turbo',
68
+ // Add more supported models as needed
69
+ ];
@@ -1,19 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ClaudeAdapter = void 0;
4
+ /**
5
+ * ClaudeAdapter class implements the Adapter interface for Claude AI models.
6
+ */
4
7
  class ClaudeAdapter {
8
+ /**
9
+ * Initializes a new ClaudeAdapter instance.
10
+ * @param model - The Claude model identifier.
11
+ */
5
12
  constructor(model) {
6
13
  this.model = model;
7
14
  this.anthropicVersion = '2023-06-01';
8
15
  }
16
+ /**
17
+ * Logs all supported Claude models to the console.
18
+ */
9
19
  static listSupportedModels() {
10
20
  ClaudeAdapter.supportedModels.forEach((model) => {
11
21
  console.log(model);
12
22
  });
13
23
  }
24
+ /**
25
+ * Returns the API endpoint for Claude.
26
+ * @returns The API endpoint URL.
27
+ */
14
28
  getEndpoint() {
15
29
  return `https://api.anthropic.com/v1/messages`;
16
30
  }
31
+ /**
32
+ * Generates headers for API requests.
33
+ * @param apiKey - The API key for authentication.
34
+ * @returns An object containing the required headers.
35
+ */
17
36
  getHeaders(apiKey) {
18
37
  return {
19
38
  'x-api-key': apiKey,
@@ -21,6 +40,12 @@ class ClaudeAdapter {
21
40
  'anthropic-version': this.anthropicVersion,
22
41
  };
23
42
  }
43
+ /**
44
+ * Prepares the payload for API requests.
45
+ * @param request - The GPTRequest object containing request details.
46
+ * @param stream - Whether to enable streaming (default: false).
47
+ * @returns An object containing the prepared payload.
48
+ */
24
49
  getPayload(request, stream = false) {
25
50
  let system = 'You are a computer programmer giving advice on how to write better code.';
26
51
  if (request.messages && request.messages.length > 0 && request.messages[0].role === 'system') {
@@ -38,6 +63,12 @@ class ClaudeAdapter {
38
63
  stream,
39
64
  };
40
65
  }
66
+ /**
67
+ * Extracts the response text from the API response data.
68
+ * @param data - The API response data.
69
+ * @returns The extracted response text.
70
+ * @throws Error if the response structure is invalid.
71
+ */
41
72
  extractResponse(data) {
42
73
  try {
43
74
  if (data && data.content && Array.isArray(data.content) && data.content.length > 0) {
@@ -53,7 +84,11 @@ class ClaudeAdapter {
53
84
  throw error;
54
85
  }
55
86
  }
56
- // data example: data: { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'Hello, how are you?' } }
87
+ /**
88
+ * Converts streaming data to OpenAIStreamChunk format.
89
+ * @param data - The streaming data string.
90
+ * @returns An OpenAIStreamChunk object or null if parsing fails.
91
+ */
57
92
  convertStream(data) {
58
93
  let beginningChar = 0;
59
94
  let lastChar = 0;
@@ -81,6 +116,9 @@ class ClaudeAdapter {
81
116
  }
82
117
  }
83
118
  exports.ClaudeAdapter = ClaudeAdapter;
119
+ /**
120
+ * Array of supported Claude model identifiers.
121
+ */
84
122
  ClaudeAdapter.supportedModels = [
85
123
  'claude:claude-3-5:sonnet-20240620',
86
124
  'claude:claude-3:opus-20240229',
@@ -1,15 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GeminiAdapter = void 0;
4
+ /**
5
+ * GeminiAdapter class implements the Adapter interface for Google's Gemini AI model.
6
+ */
4
7
  class GeminiAdapter {
8
+ /**
9
+ * Creates a new GeminiAdapter instance.
10
+ * @param model - The Gemini model to use.
11
+ */
5
12
  constructor(model) {
6
13
  this.model = model;
7
14
  }
15
+ /**
16
+ * Lists all supported Gemini models to the console.
17
+ */
8
18
  static async listSupportedModels() {
9
19
  GeminiAdapter.supportedModels.forEach((model) => {
10
20
  console.log(model);
11
21
  });
12
22
  }
23
+ /**
24
+ * Gets the appropriate API endpoint URL based on whether streaming is required.
25
+ * @param stream - Whether to use streaming endpoint.
26
+ * @returns The API endpoint URL.
27
+ */
13
28
  getEndpoint(stream = false) {
14
29
  if (stream) {
15
30
  return `https://generativelanguage.googleapis.com/v1beta/models/${this.model.replace(':', '-')}:streamGenerateContent`;
@@ -18,12 +33,23 @@ class GeminiAdapter {
18
33
  return `https://generativelanguage.googleapis.com/v1beta/models/${this.model.replace(':', '-')}:generateContent`;
19
34
  }
20
35
  }
36
+ /**
37
+ * Generates headers for the API request.
38
+ * @param apiKey - The API key for authentication.
39
+ * @returns An object containing the necessary headers.
40
+ */
21
41
  getHeaders(apiKey) {
22
42
  return {
23
43
  'Content-Type': 'application/json',
24
44
  'x-goog-api-key': apiKey,
25
45
  };
26
46
  }
47
+ /**
48
+ * Prepares the payload for the API request.
49
+ * @param request - The GPTRequest object containing prompt and other parameters.
50
+ * @param stream - Whether the request is for streaming.
51
+ * @returns The formatted payload object.
52
+ */
27
53
  getPayload(request, stream = false) {
28
54
  return {
29
55
  contents: [
@@ -44,6 +70,12 @@ class GeminiAdapter {
44
70
  },
45
71
  };
46
72
  }
73
+ /**
74
+ * Extracts the response text from the API response data.
75
+ * @param data - The raw API response data.
76
+ * @returns The extracted response text.
77
+ * @throws Error if the response structure is invalid.
78
+ */
47
79
  extractResponse(data) {
48
80
  try {
49
81
  if (data &&
@@ -64,6 +96,11 @@ class GeminiAdapter {
64
96
  throw error;
65
97
  }
66
98
  }
99
+ /**
100
+ * Converts a stream chunk to an OpenAIStreamChunk format.
101
+ * @param data - The raw stream chunk data.
102
+ * @returns An OpenAIStreamChunk object or null if parsing fails.
103
+ */
67
104
  convertStream(data) {
68
105
  let beginningChar = 0;
69
106
  let lastChar = 0;
@@ -1,24 +1,48 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MistralAdapter = void 0;
4
+ /**
5
+ * MistralAdapter class
6
+ * Implements the Adapter interface for Mistral AI models.
7
+ */
4
8
  class MistralAdapter {
9
+ /**
10
+ * Prints all supported models to the console
11
+ */
5
12
  static listSupportedModels() {
6
13
  MistralAdapter.supportedModels.forEach((model) => {
7
14
  console.log(model);
8
15
  });
9
16
  }
17
+ /**
18
+ * Constructor
19
+ * @param model - The Mistral AI model to use
20
+ */
10
21
  constructor(model) {
11
22
  this.model = model;
12
23
  }
24
+ /**
25
+ * @returns The Mistral AI API endpoint
26
+ */
13
27
  getEndpoint() {
14
28
  return 'https://api.mistral.ai/v1/chat/completions';
15
29
  }
30
+ /**
31
+ * @param apiKey - The API key for authentication
32
+ * @returns Headers for the API request
33
+ */
16
34
  getHeaders(apiKey) {
17
35
  return {
18
36
  Authorization: `Bearer ${apiKey}`,
19
37
  'Content-Type': 'application/json',
20
38
  };
21
39
  }
40
+ /**
41
+ * Prepares the payload for the API request
42
+ * @param request - The GPT request object
43
+ * @param stream - Whether to use streaming mode
44
+ * @returns The formatted payload
45
+ */
22
46
  getPayload(request, stream = false) {
23
47
  return {
24
48
  model: this.model.replace(':', '-'),
@@ -30,6 +54,11 @@ class MistralAdapter {
30
54
  stream,
31
55
  };
32
56
  }
57
+ /**
58
+ * Extracts the response content from the API data
59
+ * @param data - The API response data
60
+ * @returns The extracted response content
61
+ */
33
62
  extractResponse(data) {
34
63
  return data.choices[0].message.content;
35
64
  }
@@ -51,6 +80,11 @@ class MistralAdapter {
51
80
  // }
52
81
  // ]
53
82
  // }
83
+ /**
84
+ * Converts streaming data to OpenAIStreamChunk format
85
+ * @param data - The streaming data string
86
+ * @returns Parsed OpenAIStreamChunk or null if parsing fails
87
+ */
54
88
  convertStream(data) {
55
89
  let beginningChar = 0;
56
90
  let lastChar = 0;
@@ -78,6 +112,9 @@ class MistralAdapter {
78
112
  }
79
113
  }
80
114
  exports.MistralAdapter = MistralAdapter;
115
+ /**
116
+ * List of supported Mistral AI models
117
+ */
81
118
  MistralAdapter.supportedModels = [
82
119
  'mistral:mistral:embed',
83
120
  'mistral:mistral:tiny-latest',
@@ -5,10 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.OllamaAdapter = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
+ /**
9
+ * Adapter for interacting with the Ollama API.
10
+ */
8
11
  class OllamaAdapter {
12
+ /**
13
+ * Constructor to initialize the OllamaAdapter with a specific model.
14
+ * @param model - The model to be used by the adapter.
15
+ */
9
16
  constructor(model) {
10
17
  this.model = model;
11
18
  }
19
+ /**
20
+ * Fetches and lists supported models from the Ollama API.
21
+ */
12
22
  static async listSupportedModels() {
13
23
  try {
14
24
  const endpoint = 'http://localhost:11434/api/tags';
@@ -31,15 +41,30 @@ class OllamaAdapter {
31
41
  // do nothing, no ollama server running
32
42
  }
33
43
  }
44
+ /**
45
+ * Returns the endpoint URL for generating responses.
46
+ * @returns The endpoint URL as a string.
47
+ */
34
48
  getEndpoint() {
35
49
  return `http://localhost:11434/api/generate`;
36
50
  }
51
+ /**
52
+ * Returns the headers required for making API requests.
53
+ * @param apiKey - The API key for authorization.
54
+ * @returns An object containing the headers.
55
+ */
37
56
  getHeaders(apiKey) {
38
57
  return {
39
58
  Authorization: `Bearer ${apiKey}`,
40
59
  'Content-Type': 'application/json',
41
60
  };
42
61
  }
62
+ /**
63
+ * Constructs the payload for the API request.
64
+ * @param request - The GPT request object containing the prompt.
65
+ * @param stream - Boolean indicating if the response should be streamed.
66
+ * @returns An object containing the payload.
67
+ */
43
68
  getPayload(request, stream = false) {
44
69
  return {
45
70
  model: this.model,
@@ -47,6 +72,12 @@ class OllamaAdapter {
47
72
  stream,
48
73
  };
49
74
  }
75
+ /**
76
+ * Extracts the response from the API response data.
77
+ * @param data - The response data from the API.
78
+ * @returns The extracted response as a string.
79
+ * @throws Will throw an error if the response structure is invalid.
80
+ */
50
81
  extractResponse(data) {
51
82
  try {
52
83
  if (data && typeof data === 'object' && 'response' in data) {
@@ -62,13 +93,11 @@ class OllamaAdapter {
62
93
  throw error;
63
94
  }
64
95
  }
65
- // data example:
66
- // {
67
- // model: 'dolphin-llama3:latest',
68
- // created_at: '2024-08-06T21:04:34.616355Z',
69
- // response: 'The',
70
- // done: false
71
- // }
96
+ /**
97
+ * Converts a stream of data into an OpenAIStreamChunk.
98
+ * @param data - The string data to be converted.
99
+ * @returns An OpenAIStreamChunk object or null if conversion fails.
100
+ */
72
101
  convertStream(data) {
73
102
  let beginningChar = 0;
74
103
  let lastChar = 0;
@@ -1,24 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenAIAdapter = void 0;
4
+ // OpenAIAdapter class implements the Adapter interface
4
5
  class OpenAIAdapter {
6
+ // Method to list supported models
5
7
  static listSupportedModels() {
6
8
  OpenAIAdapter.supportedModels.forEach((model) => {
7
9
  console.log(model);
8
10
  });
9
11
  }
12
+ // Constructor to initialize the model
10
13
  constructor(model) {
11
14
  this.model = model;
12
15
  }
16
+ // Method to get the API endpoint
13
17
  getEndpoint() {
14
18
  return `https://api.openai.com/v1/chat/completions`;
15
19
  }
20
+ // Method to get the headers for the API request
16
21
  getHeaders(apiKey) {
17
22
  return {
18
23
  Authorization: `Bearer ${apiKey}`,
19
24
  'Content-Type': 'application/json',
20
25
  };
21
26
  }
27
+ // Method to get the payload for the API request
22
28
  getPayload(request, stream = false) {
23
29
  return {
24
30
  model: this.model.replace(':', '-'),
@@ -30,9 +36,11 @@ class OpenAIAdapter {
30
36
  stream,
31
37
  };
32
38
  }
39
+ // Method to extract the response from the API response data
33
40
  extractResponse(data) {
34
41
  return data.choices[0].message.content;
35
42
  }
43
+ // Method to convert the stream data into OpenAIStreamChunk format
36
44
  // data example:
37
45
  // {
38
46
  // id: 'chatcmpl-9tL477A98d54qvVQqJT010bm8BLJl',
@@ -78,6 +86,7 @@ class OpenAIAdapter {
78
86
  }
79
87
  }
80
88
  exports.OpenAIAdapter = OpenAIAdapter;
89
+ // List of supported models
81
90
  OpenAIAdapter.supportedModels = [
82
91
  'openai:gpt:4o-2024-08-06',
83
92
  'openai:gpt:4o',
@@ -16,6 +16,7 @@ const openai_1 = require("./adapters/openai");
16
16
  const mistral_1 = require("./adapters/mistral");
17
17
  const ollama_1 = require("./adapters/ollama");
18
18
  const gemini_1 = require("./adapters/gemini");
19
+ const azureopenai_1 = require("./adapters/azureopenai");
19
20
  const config_utils_1 = require("../utils/config_utils");
20
21
  class ClovingGPT {
21
22
  constructor(options) {
@@ -52,6 +53,12 @@ class ClovingGPT {
52
53
  case 'gemini':
53
54
  this.adapter = new gemini_1.GeminiAdapter(model);
54
55
  break;
56
+ case 'azureopenai':
57
+ if (!modelConfig.endpoint) {
58
+ throw new Error('Azure OpenAI endpoint not configured');
59
+ }
60
+ this.adapter = new azureopenai_1.AzureOpenAIAdapter(model, modelConfig.endpoint);
61
+ break;
55
62
  default:
56
63
  throw new Error(`Unsupported provider: ${provider}`);
57
64
  }
@@ -165,12 +172,24 @@ class ClovingGPT {
165
172
  }
166
173
  catch (err) {
167
174
  const error = err;
168
- let errorMessage = error instanceof Error ? error.message : 'connection error';
169
- if (errorMessage === '') {
170
- errorMessage = 'connection error';
175
+ let errorMessage = 'An error occurred while communicating with the AI server';
176
+ if (error.response) {
177
+ // The request was made and the server responded with a status code
178
+ // that falls out of the range of 2xx
179
+ errorMessage = `Server responded with status ${error.response.status}: ${error.response.statusText}`;
180
+ }
181
+ else if (error.request) {
182
+ // The request was made but no response was received
183
+ errorMessage = 'No response received from the server';
184
+ }
185
+ else {
186
+ // Something happened in setting up the request that triggered an Error
187
+ errorMessage = error.message || 'Unknown error occurred';
171
188
  }
172
- console.error(`Error communicating with the GPT server (${this.adapter.getEndpoint(this.stream)})\n\n${colors_1.default.red.bold(JSON.stringify(error.response?.data || "''", null, 2))}\n`);
173
- throw error;
189
+ console.error(`${colors_1.default.red.bold('ERROR')} ${errorMessage}\n` +
190
+ `Endpoint: ${this.adapter.getEndpoint(this.stream)}\n` +
191
+ `Details: ${colors_1.default.yellow(JSON.stringify(error.response?.data || {}, null, 2))}\n`);
192
+ throw new Error(errorMessage);
174
193
  }
175
194
  }
176
195
  convertStream(data) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.config = void 0;
4
4
  const config_utils_1 = require("../utils/config_utils");
5
5
  const prompt_utils_1 = require("../utils/prompt_utils");
6
+ // Add this type definition at the top of the file
6
7
  const prompts_1 = require("@inquirer/prompts");
7
8
  const config = async () => {
8
9
  let currentConfig = (0, config_utils_1.getConfig)({});
@@ -78,7 +79,7 @@ const config = async () => {
78
79
  },
79
80
  });
80
81
  const parsedTemperature = parseFloat(temperature);
81
- const modelConfig = {
82
+ let modelConfig = {
82
83
  apiKey,
83
84
  primary,
84
85
  priority: parseInt(priority, 10),
@@ -86,6 +87,18 @@ const config = async () => {
86
87
  trust,
87
88
  temperature: parsedTemperature,
88
89
  };
90
+ if (provider === 'azureopenai') {
91
+ const endpoint = await (0, prompts_1.input)({
92
+ message: 'Enter the Azure OpenAI endpoint URL:',
93
+ validate: (value) => {
94
+ if (!value.startsWith('https://')) {
95
+ return 'Please enter a valid HTTPS URL';
96
+ }
97
+ return true;
98
+ },
99
+ });
100
+ modelConfig.endpoint = endpoint;
101
+ }
89
102
  currentConfig.models[provider][model] = modelConfig;
90
103
  if (primary) {
91
104
  // Ensure only one primary model
@@ -5,6 +5,7 @@ const openai_1 = require("../cloving_gpt/adapters/openai");
5
5
  const ollama_1 = require("../cloving_gpt/adapters/ollama");
6
6
  const gemini_1 = require("../cloving_gpt/adapters/gemini");
7
7
  const mistral_1 = require("../cloving_gpt/adapters/mistral");
8
+ const azureopenai_1 = require("../cloving_gpt/adapters/azureopenai");
8
9
  const listModels = async () => {
9
10
  claude_1.ClaudeAdapter.listSupportedModels();
10
11
  gemini_1.GeminiAdapter.listSupportedModels();
@@ -16,5 +17,6 @@ const listModels = async () => {
16
17
  // do nothing, no ollama server running
17
18
  }
18
19
  openai_1.OpenAIAdapter.listSupportedModels();
20
+ azureopenai_1.AzureOpenAIAdapter.listSupportedModels();
19
21
  };
20
22
  exports.default = listModels;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -226,22 +226,25 @@ exports.extractBlock = extractBlock;
226
226
  * Each block contains information about file paths, current content, and new content.
227
227
  *
228
228
  * The blocks are defined by specific delimiters:
229
+ * - Setup: '```lang'
229
230
  * - Start: '<<<<<<< CURRENT'
230
231
  * - Divider: '======='
231
232
  * - End: '>>>>>>> NEW'
233
+ * - Finish: '```'
232
234
  *
233
235
  * @param {string} [input] - The input string containing CURRENT/NEW blocks.
234
236
  * @returns {CurrentNewBlock[]} An array of CurrentNewBlock objects, each representing a parsed block.
235
237
  * If the input is undefined or empty, an empty array is returned.
236
238
  *
237
239
  * @example
238
- * const input = `
240
+ * const input = "
241
+ * ```typescript
239
242
  * <<<<<<< CURRENT path/to/file.ts
240
243
  * const oldCode = 'old'
241
244
  * =======
242
245
  * const newCode = 'new'
243
246
  * >>>>>>> NEW
244
- * `
247
+ * ```"
245
248
  * const blocks = extractCurrentNewBlocks(input)
246
249
  * // blocks will contain one CurrentNewBlock object with the parsed information
247
250
  */
@@ -312,6 +315,10 @@ const searchReplaceString = (currentContent, block) => {
312
315
  if (block.currentContent.trim() === '') {
313
316
  return block.newContent;
314
317
  }
318
+ // check if you can replace the entire block without changing indentation
319
+ if (currentContent.includes(block.currentContent)) {
320
+ return currentContent.replace(block.currentContent, block.newContent);
321
+ }
315
322
  // 2) split currentContent and block.currentContent into new lines and trim() all the lines, then put back the new lines
316
323
  const currentLines = currentContent.split('\n').map((line) => line.trim());
317
324
  const blockLines = block.currentContent.split('\n').map((line) => line.trim());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",