cloving 0.1.21 → 0.1.22
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/dist/cloving_gpt/adapters/claude.js +29 -2
- package/dist/cloving_gpt/adapters/gemini.js +34 -4
- package/dist/cloving_gpt/adapters/mistral.js +45 -2
- package/dist/cloving_gpt/adapters/ollama.js +34 -2
- package/dist/cloving_gpt/adapters/openai.js +46 -2
- package/dist/cloving_gpt/index.js +52 -6
- package/dist/commands/chat.js +14 -0
- package/dist/commands/generate/code.js +8 -316
- package/dist/commands/generate/commit.js +2 -20
- package/dist/commands/proxy.js +11 -0
- package/dist/commands/tokens.js +43 -0
- package/dist/index.js +23 -0
- package/dist/managers/ChatManager.js +283 -0
- package/dist/managers/ChunkManager.js +20 -0
- package/dist/managers/CodeManager.js +319 -0
- package/dist/managers/ProxyManager.js +163 -0
- package/dist/managers/src/managers/ProxyManager.js +1 -0
- package/dist/utils/command_utils.js +1 -1
- package/dist/utils/git_utils.js +21 -1
- package/dist/utils/string_utils.js +21 -2
- package/package.json +4 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const body_parser_1 = __importDefault(require("body-parser"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
|
|
11
|
+
const ChunkManager_1 = __importDefault(require("./ChunkManager"));
|
|
12
|
+
const config_utils_1 = require("../utils/config_utils");
|
|
13
|
+
const command_utils_1 = require("../utils/command_utils");
|
|
14
|
+
class ProxyManager {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
this.contextFiles = {};
|
|
18
|
+
this.options.silent = true;
|
|
19
|
+
this.options.stream = true;
|
|
20
|
+
this.gpt = new cloving_gpt_1.default(this.options);
|
|
21
|
+
this.app = (0, express_1.default)();
|
|
22
|
+
this.app.use(body_parser_1.default.json());
|
|
23
|
+
this.chunkManager = new ChunkManager_1.default();
|
|
24
|
+
}
|
|
25
|
+
async initialize() {
|
|
26
|
+
await this.setupRoutes();
|
|
27
|
+
await this.loadContextFiles();
|
|
28
|
+
this.startServer();
|
|
29
|
+
}
|
|
30
|
+
async setupRoutes() {
|
|
31
|
+
this.app.get('/', (_req, res) => {
|
|
32
|
+
res.json({ message: 'ClovingGPT proxy server running' });
|
|
33
|
+
});
|
|
34
|
+
this.app.post('/v1/chat/completions', this.handleChatCompletions.bind(this));
|
|
35
|
+
}
|
|
36
|
+
async loadContextFiles() {
|
|
37
|
+
let files = this.options.files || '.';
|
|
38
|
+
let expandedFiles = [];
|
|
39
|
+
for (const file of files) {
|
|
40
|
+
const filePath = path_1.default.resolve(file);
|
|
41
|
+
if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
|
|
42
|
+
const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
|
|
43
|
+
expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
files = expandedFiles;
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
const filePath = path_1.default.resolve(file);
|
|
52
|
+
if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
|
|
53
|
+
const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
|
|
54
|
+
this.contextFiles[file] = content;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async handleChatCompletions(req, res) {
|
|
59
|
+
try {
|
|
60
|
+
const { messages, stream } = req.body;
|
|
61
|
+
const content = messages.map((message) => message.content).join('\n\n');
|
|
62
|
+
const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
|
|
63
|
+
const prompt = `### Task
|
|
64
|
+
|
|
65
|
+
${content}
|
|
66
|
+
|
|
67
|
+
### Description of App
|
|
68
|
+
|
|
69
|
+
${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
|
|
70
|
+
|
|
71
|
+
${contextFileContents}
|
|
72
|
+
|
|
73
|
+
### Task
|
|
74
|
+
|
|
75
|
+
${content}`;
|
|
76
|
+
if (stream) {
|
|
77
|
+
await this.handleStreamResponse(prompt, res);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
await this.handleNonStreamResponse(prompt, res);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error('Error processing request:', error);
|
|
85
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async handleStreamResponse(prompt, res) {
|
|
89
|
+
const shortPrompt = prompt.replace('### Task', '').trim().slice(0, 50).replace(/\n/g, ' ');
|
|
90
|
+
const promptTokens = Math.ceil(prompt.length / 4).toLocaleString();
|
|
91
|
+
console.log(`> sending ${promptTokens} token prompt:`, shortPrompt);
|
|
92
|
+
const responseStream = await this.gpt.streamText({ prompt });
|
|
93
|
+
res.setHeader('Content-Type', 'application/json');
|
|
94
|
+
res.setHeader('Transfer-Encoding', 'chunked');
|
|
95
|
+
let convertedStreams = [];
|
|
96
|
+
// Add initial stream to indicate the start of the response
|
|
97
|
+
convertedStreams.push('{"id":"chatcmpl-9tLvSuRQaYzQIiKRAwI5a7WqdJspq","object":"chat.completion.chunk","created":1722979349,"model":"cloving","system_fingerprint":"fp_3aa7262c27","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}]}');
|
|
98
|
+
this.chunkManager.on('content', (buffer) => {
|
|
99
|
+
let convertedStream = this.gpt.convertStream(buffer);
|
|
100
|
+
while (convertedStream !== null) {
|
|
101
|
+
const { output, lastChar } = convertedStream;
|
|
102
|
+
const content = JSON.stringify({ id: "chatcmpl-9tLvSuRQaYzQIiKRAwI5a7WqdJspq", object: "chat.completion.chunk", created: 1722979350, model: "cloving", system_fingerprint: "fp_3aa7262c27", choices: [{ index: 0, delta: { content: output }, logprobs: null, finish_reason: null }] });
|
|
103
|
+
convertedStreams.push(content);
|
|
104
|
+
if (convertedStreams.length >= 2) {
|
|
105
|
+
const dataToSend = convertedStreams.map(c => `data: ${c}`).join('\n\n');
|
|
106
|
+
res.write(`${dataToSend}\n\n`);
|
|
107
|
+
convertedStreams = [];
|
|
108
|
+
}
|
|
109
|
+
this.chunkManager.clearBuffer(lastChar);
|
|
110
|
+
buffer = buffer.slice(lastChar);
|
|
111
|
+
convertedStream = this.gpt.convertStream(buffer);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
responseStream.data.on('data', (chunk) => {
|
|
115
|
+
const chunkString = chunk.toString();
|
|
116
|
+
this.chunkManager.addChunk(chunkString);
|
|
117
|
+
});
|
|
118
|
+
responseStream.data.on('end', () => {
|
|
119
|
+
// Send any remaining converted streams
|
|
120
|
+
const dataToSend = convertedStreams.map(c => `data: ${c}`).join('\n\n');
|
|
121
|
+
res.write(`${dataToSend}\n\ndata: [DONE]\n\n`);
|
|
122
|
+
res.end();
|
|
123
|
+
});
|
|
124
|
+
responseStream.data.on('error', (error) => {
|
|
125
|
+
console.error('Error streaming response:', error);
|
|
126
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async handleNonStreamResponse(prompt, res) {
|
|
130
|
+
const content = await this.gpt.generateText({ prompt });
|
|
131
|
+
const response = {
|
|
132
|
+
id: 'chatcmpl-' + Date.now(),
|
|
133
|
+
object: 'chat.completion',
|
|
134
|
+
created: Math.floor(Date.now() / 1000),
|
|
135
|
+
model: 'cloving',
|
|
136
|
+
choices: [
|
|
137
|
+
{
|
|
138
|
+
index: 0,
|
|
139
|
+
message: {
|
|
140
|
+
role: 'assistant',
|
|
141
|
+
content,
|
|
142
|
+
},
|
|
143
|
+
logprobs: null,
|
|
144
|
+
finish_reason: 'stop',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
usage: {
|
|
148
|
+
prompt_tokens: Math.ceil(prompt.length / 4),
|
|
149
|
+
completion_tokens: Math.ceil(content.length / 4),
|
|
150
|
+
total_tokens: Math.ceil(prompt.length / 4) + Math.ceil(content.length / 4),
|
|
151
|
+
},
|
|
152
|
+
system_fingerprint: 'fp_3cd8b62c3b',
|
|
153
|
+
};
|
|
154
|
+
res.json(response);
|
|
155
|
+
}
|
|
156
|
+
startServer() {
|
|
157
|
+
const port = parseInt(`${process.env.PORT || this.options.port || 3000}`, 10);
|
|
158
|
+
this.app.listen(port, () => {
|
|
159
|
+
console.log(`ClovingGPT proxy server running on port ${port}`);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.default = ProxyManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -14,7 +14,7 @@ exports.SPECIAL_FILES = [
|
|
|
14
14
|
'.csproj', 'packages.config', 'composer.json', 'CMakeLists.txt', 'conanfile.txt', 'conanfile.py',
|
|
15
15
|
'go.mod', 'Cargo.toml', 'Package.swift', 'build.gradle.kts', 'Podfile', 'Cartfile', 'cpanfile',
|
|
16
16
|
'DESCRIPTION', 'mix.exs', 'build.sbt', 'pubspec.yaml', 'stack.yaml', 'cabal.project', 'Project.toml',
|
|
17
|
-
'rockspec', 'rebar.config', 'project.clj'
|
|
17
|
+
'rockspec', 'rebar.config', 'project.clj', 'tsconfig.json'
|
|
18
18
|
];
|
|
19
19
|
const collectSpecialFileContents = () => {
|
|
20
20
|
const specialFileContents = {};
|
package/dist/utils/git_utils.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getCurrentBranchName = exports.getDefaultBranchName = exports.getGitDiff = void 0;
|
|
6
|
+
exports.generateCommitMessagePrompt = exports.getCurrentBranchName = exports.getDefaultBranchName = exports.getGitDiff = void 0;
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const readline_1 = __importDefault(require("readline"));
|
|
9
9
|
const askQuestion = (question) => {
|
|
@@ -61,3 +61,23 @@ const getCurrentBranchName = () => {
|
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
exports.getCurrentBranchName = getCurrentBranchName;
|
|
64
|
+
const generateCommitMessagePrompt = (diff) => {
|
|
65
|
+
return `Generate a concise and meaningful commit message based on a diff.
|
|
66
|
+
|
|
67
|
+
Do not add any commentary or context to the message other than the commit message itself.
|
|
68
|
+
|
|
69
|
+
An example of the output for this should look like the following:
|
|
70
|
+
|
|
71
|
+
\`\`\`plaintext
|
|
72
|
+
Update dependencies and package versions
|
|
73
|
+
|
|
74
|
+
- Upgrade Ruby gems including aws-sdk, honeybadger, irb, and rubocop
|
|
75
|
+
- Update Node.js packages including esbuild, tinymce, and trix
|
|
76
|
+
- Bump TypeScript and ESLint related packages to latest versions
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
Here is the diff to help you write the commit message:
|
|
80
|
+
|
|
81
|
+
${diff}`;
|
|
82
|
+
};
|
|
83
|
+
exports.generateCommitMessagePrompt = generateCommitMessagePrompt;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractFilesAndContent = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
6
|
+
exports.saveGeneratedFiles = exports.extractFilesAndContent = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
4
9
|
// Function to estimate token count
|
|
5
10
|
const estimateTokens = async (text) => {
|
|
6
11
|
const charCount = text.length;
|
|
@@ -102,7 +107,7 @@ const extractFilesAndContent = (rawCodeCommand) => {
|
|
|
102
107
|
for (const match of matches) {
|
|
103
108
|
const fileName = match.replace(/\*{2}/g, '').trim();
|
|
104
109
|
const escapedFileName = fileName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
105
|
-
const regex = new RegExp(`\\*\\*${escapedFileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}`, 'g');
|
|
110
|
+
const regex = new RegExp(`\\*\\*${escapedFileName}\\*\\*\\n\\n\\\`{3}([\\s\\S]+?)\\\`{3}(\n|$)`, 'g');
|
|
106
111
|
const contentMatch = regex.exec(rawCodeCommand);
|
|
107
112
|
if (contentMatch) {
|
|
108
113
|
files.push(fileName);
|
|
@@ -115,3 +120,17 @@ const extractFilesAndContent = (rawCodeCommand) => {
|
|
|
115
120
|
return [files, fileContents];
|
|
116
121
|
};
|
|
117
122
|
exports.extractFilesAndContent = extractFilesAndContent;
|
|
123
|
+
const saveGeneratedFiles = async (files, fileContents) => {
|
|
124
|
+
for (const file of files) {
|
|
125
|
+
if (fileContents[file]) {
|
|
126
|
+
const filePath = path_1.default.resolve(file);
|
|
127
|
+
await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
128
|
+
await fs_1.default.promises.writeFile(filePath, fileContents[file]);
|
|
129
|
+
console.log(`${file} has been saved.`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log(`File content not found for ${file}.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
exports.saveGeneratedFiles = saveGeneratedFiles;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloving",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"packageManager": "yarn@1.22.22",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"eslint-plugin-jsdoc": "^48.11.0",
|
|
50
50
|
"eslint-plugin-n": "^17.10.1",
|
|
51
51
|
"eslint-plugin-promise": "^7.0.0",
|
|
52
|
+
"express": "^4.19.2",
|
|
52
53
|
"ignore": "^5.3.1",
|
|
53
54
|
"ini": "^4.1.3",
|
|
54
55
|
"isbinaryfile": "^5.0.2",
|
|
@@ -57,7 +58,9 @@
|
|
|
57
58
|
"vite-node": "^2.0.5"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
61
|
+
"@types/body-parser": "^1.19.5",
|
|
60
62
|
"@types/copy-paste": "^1.1.33",
|
|
63
|
+
"@types/express": "^4.17.21",
|
|
61
64
|
"@types/ini": "^4.1.1",
|
|
62
65
|
"@types/inquirer": "^9.0.7",
|
|
63
66
|
"@types/jest": "^29.5.12",
|