cloving 0.1.21 → 0.1.23

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,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 = {};
@@ -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.21",
3
+ "version": "0.1.23",
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",