genaicode 0.0.31
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/LICENSE +202 -0
- package/README.md +130 -0
- package/bin/genaicode.cjs +8 -0
- package/bin/genaicode.js +5 -0
- package/bin/vertex-monkey-patch.cjs +33 -0
- package/media/demo-for-readme.gif +0 -0
- package/media/logo-dark.png +0 -0
- package/media/logo.png +0 -0
- package/package.json +58 -0
- package/src/ai-service/anthropic.js +91 -0
- package/src/ai-service/chat-gpt.js +96 -0
- package/src/ai-service/common.js +40 -0
- package/src/ai-service/dall-e.js +29 -0
- package/src/ai-service/function-calling.js +320 -0
- package/src/ai-service/vertex-ai-claude.js +95 -0
- package/src/ai-service/vertex-ai.js +155 -0
- package/src/cli/cli-options.js +106 -0
- package/src/cli/cli-options.test.js +25 -0
- package/src/cli/cli-params.js +84 -0
- package/src/cli/cli-params.test.js +43 -0
- package/src/cli/service-autodetect.js +11 -0
- package/src/cli/service-autodetect.test.js +43 -0
- package/src/cli/validate-cli-params.js +90 -0
- package/src/cli/validate-cli-params.test.js +103 -0
- package/src/files/find-files.js +148 -0
- package/src/files/read-files.js +41 -0
- package/src/files/update-files.js +118 -0
- package/src/main/codegen.js +113 -0
- package/src/main/codegen.test.js +186 -0
- package/src/prompt/limits.js +23 -0
- package/src/prompt/limits.test.js +40 -0
- package/src/prompt/prompt-codegen.js +106 -0
- package/src/prompt/prompt-codegen.test.js +116 -0
- package/src/prompt/prompt-consts.js +1 -0
- package/src/prompt/prompt-service.js +211 -0
- package/src/prompt/prompt-service.test.js +486 -0
- package/src/prompt/systemprompt.js +32 -0
- package/src/prompt/systemprompt.test.js +60 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate an image using OpenAI's DALL-E model and save it to a file
|
|
5
|
+
* @param {string} prompt - The description of the image to generate
|
|
6
|
+
* @param {string} size - The size of the image to generate ('256x256', '512x512', or '1024x1024')
|
|
7
|
+
* @returns {Promise<string>} - The url of the image
|
|
8
|
+
*/
|
|
9
|
+
export async function generateImage(prompt, size = '1024x1024') {
|
|
10
|
+
const openai = new OpenAI();
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const response = await openai.images.generate({
|
|
14
|
+
model: 'dall-e-3',
|
|
15
|
+
prompt: prompt,
|
|
16
|
+
n: 1,
|
|
17
|
+
size: size,
|
|
18
|
+
response_format: 'url',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const imageUrl = response.data[0].url;
|
|
22
|
+
|
|
23
|
+
console.log(`Image generated, url: ${imageUrl}`);
|
|
24
|
+
return imageUrl;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Error generating image:', error);
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { requireExplanations, temperature } from '../cli/cli-params.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Function definitions for function calling feature
|
|
5
|
+
*/
|
|
6
|
+
export const functionDefs = [
|
|
7
|
+
{
|
|
8
|
+
name: 'getSourceCode',
|
|
9
|
+
description:
|
|
10
|
+
'This function returns source code of the application in Map format, where absolute file path is the key, and file content is the value. This function can be called only once during the conversation, and only if suggested by the user.',
|
|
11
|
+
parameters: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
filePaths: {
|
|
15
|
+
type: 'array',
|
|
16
|
+
description: 'An array of absolute paths of files that should be used to provided context.',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
required: [],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'getImageAssets',
|
|
27
|
+
description:
|
|
28
|
+
'This function returns a map of application image assets. This map contains absolute file path, and basic metadata information. It does not contain contents. Contents must be requested using dedicated tool.',
|
|
29
|
+
parameters: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
filePaths: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
description: 'An array of absolute paths of files that should be used to provided context.',
|
|
35
|
+
items: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: [],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'codegenSummary',
|
|
45
|
+
description:
|
|
46
|
+
'This function is called with a summary of proposed updates. It contains a list of file paths that will be subject to code generation request, and also a list of file paths that make sense to use as a context for code generation requests.',
|
|
47
|
+
parameters: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
files: {
|
|
51
|
+
type: 'array',
|
|
52
|
+
description: 'An array of proposed file updates.',
|
|
53
|
+
items: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
description: 'Proposed update of a file, the path, and the method of update',
|
|
56
|
+
properties: {
|
|
57
|
+
path: { type: 'string', description: 'An absolute path of the project file that will be updated' },
|
|
58
|
+
updateToolName: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
enum: [
|
|
61
|
+
'updateFile',
|
|
62
|
+
'patchFile',
|
|
63
|
+
'createFile',
|
|
64
|
+
'deleteFile',
|
|
65
|
+
'createDirectory',
|
|
66
|
+
'moveFile',
|
|
67
|
+
'generateImage',
|
|
68
|
+
'downloadFile',
|
|
69
|
+
],
|
|
70
|
+
description: 'A name of the tool that will be used to perform the update.',
|
|
71
|
+
},
|
|
72
|
+
temperature: {
|
|
73
|
+
type: 'number',
|
|
74
|
+
description:
|
|
75
|
+
'Temperature parameter that will be used for LLM request. The value is adjusted to the characteristic of the update. If there is a need for more creative solution, the value should be lower, but stil within [0.0, 2.0] range. The default value is: ' +
|
|
76
|
+
temperature,
|
|
77
|
+
},
|
|
78
|
+
prompt: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description:
|
|
81
|
+
'Prompt that will be added to the LLM request together with the tool request. It summarizes the planned changes for this particular file.',
|
|
82
|
+
},
|
|
83
|
+
contextImageAssets: {
|
|
84
|
+
type: 'array',
|
|
85
|
+
description:
|
|
86
|
+
'A list of of absolute image asset paths that should be included to the context of LLm request',
|
|
87
|
+
items: { type: 'string' },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: ['path', 'updateToolName', 'temperature', 'prompt', 'contextImageAssets'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
contextPaths: {
|
|
94
|
+
type: 'array',
|
|
95
|
+
description:
|
|
96
|
+
'An array of absolute paths of files that should be used to provided context. Context files could be for example the dependencies, or files that depend on one of the files that we want to update in the next step.',
|
|
97
|
+
items: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
explanation: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
description: 'Explanation of planned changes or explanation of reasoning for no code changes',
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ['files', 'contextPaths', 'explanation'],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'updateFile',
|
|
111
|
+
description:
|
|
112
|
+
'Update a file with new content. The file must already exists in the application source code. The function should be called only if there is a need to actually change something.',
|
|
113
|
+
parameters: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
filePath: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'The file path to update.',
|
|
119
|
+
},
|
|
120
|
+
newContent: {
|
|
121
|
+
type: 'string',
|
|
122
|
+
description: 'The content to update the file with. Must not be empty.',
|
|
123
|
+
},
|
|
124
|
+
explanation: {
|
|
125
|
+
type: 'string',
|
|
126
|
+
description: 'The explanation of the reasoning behind the suggested code changes for this file',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ['filePath', 'newContent'],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'patchFile',
|
|
134
|
+
description:
|
|
135
|
+
'Partially update a file content. The file must already exists in the application source code. The function should be called only if there is a need to actually change something.',
|
|
136
|
+
parameters: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
filePath: {
|
|
140
|
+
type: 'string',
|
|
141
|
+
description: 'The file path to patch.',
|
|
142
|
+
},
|
|
143
|
+
patch: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: `Modification to the file expressed in patch format. Example patch:
|
|
146
|
+
|
|
147
|
+
\`\`\`
|
|
148
|
+
Index: filename.js
|
|
149
|
+
===================================================================
|
|
150
|
+
--- filename.js
|
|
151
|
+
+++ filename.js
|
|
152
|
+
@@ -1,2 +1,3 @@
|
|
153
|
+
line1
|
|
154
|
+
+line3
|
|
155
|
+
line2
|
|
156
|
+
\
|
|
157
|
+
\`\`\`
|
|
158
|
+
`,
|
|
159
|
+
},
|
|
160
|
+
explanation: {
|
|
161
|
+
type: 'string',
|
|
162
|
+
description: 'The explanation of the reasoning behind the suggested code changes for this file',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
required: ['filePath', 'patch'],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'createFile',
|
|
170
|
+
description:
|
|
171
|
+
'Create a new file with specified content. The file will be created inside of project folder structure. This tool should not be used of creation if image files.',
|
|
172
|
+
parameters: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
filePath: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: 'The file path to create.',
|
|
178
|
+
},
|
|
179
|
+
newContent: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'The content for the new file.',
|
|
182
|
+
},
|
|
183
|
+
explanation: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
description: 'The explanation of the reasoning behind creating this file',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
required: ['filePath', 'newContent'],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'deleteFile',
|
|
193
|
+
description: 'Delete a specified file from the application source code.',
|
|
194
|
+
parameters: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
filePath: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
description: 'The file path to delete.',
|
|
200
|
+
},
|
|
201
|
+
explanation: {
|
|
202
|
+
type: 'string',
|
|
203
|
+
description: 'The explanation of the reasoning behind deleting this file',
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
required: ['filePath'],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'explanation',
|
|
211
|
+
description: 'Explain the reasoning behind the suggested code changes or reasoning for lack of code changes',
|
|
212
|
+
parameters: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
text: {
|
|
216
|
+
type: 'string',
|
|
217
|
+
description: 'The explanation text',
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
required: ['text'],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'createDirectory',
|
|
225
|
+
description: 'Create a new directory',
|
|
226
|
+
parameters: {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
filePath: {
|
|
230
|
+
type: 'string',
|
|
231
|
+
description: 'The directory path to create.',
|
|
232
|
+
},
|
|
233
|
+
explanation: {
|
|
234
|
+
type: 'string',
|
|
235
|
+
description: 'The explanation of the reasoning behind creating this directory',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
required: ['filePath'],
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'moveFile',
|
|
243
|
+
description: 'Move a file from one location to another',
|
|
244
|
+
parameters: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
source: {
|
|
248
|
+
type: 'string',
|
|
249
|
+
description: 'The current file path.',
|
|
250
|
+
},
|
|
251
|
+
destination: {
|
|
252
|
+
type: 'string',
|
|
253
|
+
description: 'The new file path.',
|
|
254
|
+
},
|
|
255
|
+
explanation: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'The explanation of the reasoning behind moving this file',
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
required: ['source', 'destination'],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
name: 'generateImage',
|
|
265
|
+
description: 'Generate an image using AI service and save it as a file.',
|
|
266
|
+
parameters: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
prompt: {
|
|
270
|
+
type: 'string',
|
|
271
|
+
description: 'The prompt to generate the image.',
|
|
272
|
+
},
|
|
273
|
+
filePath: {
|
|
274
|
+
type: 'string',
|
|
275
|
+
description: 'The file path to save the generated image.',
|
|
276
|
+
},
|
|
277
|
+
size: {
|
|
278
|
+
type: 'string',
|
|
279
|
+
enum: ['256x256', '512x512', '1024x1024'],
|
|
280
|
+
description: 'The size of the image to generate.',
|
|
281
|
+
},
|
|
282
|
+
explanation: {
|
|
283
|
+
type: 'string',
|
|
284
|
+
description: 'The explanation of the reasoning behind generating this image',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
required: ['prompt', 'filePath', 'size'],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'downloadFile',
|
|
292
|
+
description: 'Download file from url, and save to file',
|
|
293
|
+
parameters: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {
|
|
296
|
+
filePath: {
|
|
297
|
+
type: 'string',
|
|
298
|
+
description: 'The file path to save the downloaded file.',
|
|
299
|
+
},
|
|
300
|
+
downloadUrl: {
|
|
301
|
+
type: 'string',
|
|
302
|
+
description: 'The url of the file that will be used for downloading.',
|
|
303
|
+
},
|
|
304
|
+
explanation: {
|
|
305
|
+
type: 'string',
|
|
306
|
+
description: 'The reasoning behind downloading this image.',
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
required: ['filePath', 'downloadUrl'],
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
].map((fd) => {
|
|
313
|
+
if (requireExplanations && fd.parameters.properties.explanation && !fd.parameters.required.includes('explanation')) {
|
|
314
|
+
fd.parameters.required.push('explanation');
|
|
315
|
+
} else if (!requireExplanations) {
|
|
316
|
+
delete fd.parameters.properties.explanation;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return fd;
|
|
320
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { AnthropicVertex } from '@anthropic-ai/vertex-sdk';
|
|
2
|
+
import { printTokenUsageAndCost, processFunctionCalls } from './common.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This function generates content using the Anthropic Claude model via Vertex AI.
|
|
6
|
+
*/
|
|
7
|
+
export async function generateContent(prompt, functionDefs, requiredFunctionName, temperature) {
|
|
8
|
+
const projectId = process.env.GOOGLE_CLOUD_PROJECT;
|
|
9
|
+
const region = process.env.GOOGLE_CLOUD_REGION;
|
|
10
|
+
|
|
11
|
+
if (!projectId || !region) {
|
|
12
|
+
throw new Error('GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_REGION environment variables must be set');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const client = new AnthropicVertex({
|
|
16
|
+
projectId,
|
|
17
|
+
region,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const messages = prompt
|
|
21
|
+
.filter((item) => item.type !== 'systemPrompt')
|
|
22
|
+
.map((item) => {
|
|
23
|
+
if (item.type === 'user') {
|
|
24
|
+
return {
|
|
25
|
+
role: 'user',
|
|
26
|
+
content: [
|
|
27
|
+
...(item.functionResponses ?? []).map((response) => ({
|
|
28
|
+
type: 'tool_result',
|
|
29
|
+
tool_use_id: response.call_id ?? response.name,
|
|
30
|
+
content: response.content,
|
|
31
|
+
})),
|
|
32
|
+
...(item.images ?? []).map((image) => ({
|
|
33
|
+
type: 'image',
|
|
34
|
+
source: {
|
|
35
|
+
type: 'base64',
|
|
36
|
+
media_type: image.mediaType,
|
|
37
|
+
data: image.base64url,
|
|
38
|
+
},
|
|
39
|
+
})),
|
|
40
|
+
{ type: 'text', text: item.text },
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
} else if (item.type === 'assistant') {
|
|
44
|
+
return {
|
|
45
|
+
role: 'assistant',
|
|
46
|
+
content: [
|
|
47
|
+
...(item.text ? [{ type: 'text', text: item.text }] : []),
|
|
48
|
+
...item.functionCalls.map((call) => ({
|
|
49
|
+
type: 'tool_use',
|
|
50
|
+
id: call.id ?? call.name,
|
|
51
|
+
name: call.name,
|
|
52
|
+
input: call.args ?? {},
|
|
53
|
+
})),
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const response = await client.messages.create({
|
|
60
|
+
model: 'claude-3-5-sonnet@20240620',
|
|
61
|
+
max_tokens: 4096,
|
|
62
|
+
temperature: temperature,
|
|
63
|
+
system: prompt.find((item) => item.type === 'systemPrompt').systemPrompt,
|
|
64
|
+
messages,
|
|
65
|
+
tools: functionDefs.map((fd) => ({
|
|
66
|
+
name: fd.name,
|
|
67
|
+
description: fd.description,
|
|
68
|
+
input_schema: fd.parameters,
|
|
69
|
+
})),
|
|
70
|
+
tool_choice: requiredFunctionName ? { type: 'tool', name: requiredFunctionName } : { type: 'any' },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Print token usage for Anthropic Vertex AI
|
|
74
|
+
const usage = {
|
|
75
|
+
inputTokens: response.usage.input_tokens,
|
|
76
|
+
outputTokens: response.usage.output_tokens,
|
|
77
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
78
|
+
};
|
|
79
|
+
printTokenUsageAndCost(usage, 3 / 1000 / 1000, 15 / 1000 / 1000);
|
|
80
|
+
|
|
81
|
+
const responseMessages = response.content.filter((item) => item.type !== 'tool_use');
|
|
82
|
+
if (responseMessages.length > 0) {
|
|
83
|
+
console.log('Response messages', responseMessages);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const functionCalls = response.content
|
|
87
|
+
.filter((item) => item.type === 'tool_use')
|
|
88
|
+
.map((item) => ({
|
|
89
|
+
id: item.id,
|
|
90
|
+
name: item.name,
|
|
91
|
+
args: item.input,
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
return processFunctionCalls(functionCalls);
|
|
95
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { VertexAI } from '@google-cloud/vertexai';
|
|
3
|
+
import { printTokenUsageAndCost, processFunctionCalls } from './common.js';
|
|
4
|
+
import { geminiBlockNone } from '../cli/cli-params.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This function generates content using the Gemini Pro model.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export async function generateContent(prompt, functionDefs, requiredFunctionName, temperature) {
|
|
11
|
+
const messages = prompt
|
|
12
|
+
.filter((item) => item.type !== 'systemPrompt')
|
|
13
|
+
.map((item) => {
|
|
14
|
+
if (item.type === 'user') {
|
|
15
|
+
return {
|
|
16
|
+
role: 'user',
|
|
17
|
+
parts: [
|
|
18
|
+
...(item.functionResponses ?? []).map((response) => ({
|
|
19
|
+
functionResponse: {
|
|
20
|
+
name: response.name,
|
|
21
|
+
response: { name: response.name, content: response.content },
|
|
22
|
+
},
|
|
23
|
+
})),
|
|
24
|
+
...(item.images ?? []).map((image) => ({
|
|
25
|
+
inlineData: {
|
|
26
|
+
mimeType: image.mediaType,
|
|
27
|
+
data: image.base64url,
|
|
28
|
+
},
|
|
29
|
+
})),
|
|
30
|
+
{ text: item.text },
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
} else if (item.type === 'assistant') {
|
|
34
|
+
return {
|
|
35
|
+
role: 'model',
|
|
36
|
+
parts: [
|
|
37
|
+
...(item.text ? [{ text: item.text }] : []),
|
|
38
|
+
...item.functionCalls.map((call) => ({
|
|
39
|
+
functionCall: {
|
|
40
|
+
name: call.name,
|
|
41
|
+
args: call.args ?? {},
|
|
42
|
+
},
|
|
43
|
+
})),
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const req = {
|
|
50
|
+
contents: messages,
|
|
51
|
+
tools: [
|
|
52
|
+
{
|
|
53
|
+
functionDeclarations: functionDefs,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
// TODO: add tool_config once [it is supported](https://github.com/googleapis/nodejs-vertexai/issues/331)
|
|
57
|
+
toolConfig: {
|
|
58
|
+
functionCallingConfig: {
|
|
59
|
+
mode: 'ANY',
|
|
60
|
+
...(requiredFunctionName ? { allowedFunctionNames: [requiredFunctionName] } : {}),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const model = await getGenModel(prompt.find((item) => item.type === 'systemPrompt').systemPrompt, temperature);
|
|
66
|
+
|
|
67
|
+
assert(await verifyVertexMonkeyPatch(), 'Vertex AI Tool Config was not monkey patched');
|
|
68
|
+
|
|
69
|
+
const result = await model.generateContent(req);
|
|
70
|
+
|
|
71
|
+
// Print token usage
|
|
72
|
+
const usageMetadata = result.response.usageMetadata;
|
|
73
|
+
const usage = {
|
|
74
|
+
inputTokens: usageMetadata.promptTokenCount,
|
|
75
|
+
outputTokens: usageMetadata.candidatesTokenCount,
|
|
76
|
+
totalTokens: usageMetadata.totalTokenCount,
|
|
77
|
+
};
|
|
78
|
+
printTokenUsageAndCost(usage, 0.000125 / 1000, 0.000375 / 1000);
|
|
79
|
+
|
|
80
|
+
if (result.response.promptFeedback) {
|
|
81
|
+
console.log('Prompt feedback:');
|
|
82
|
+
console.log(JSON.stringify(result.response.promptFeedback, null, 2));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!result.response.candidates?.length > 0) {
|
|
86
|
+
console.log('Response:', result);
|
|
87
|
+
throw new Error('No candidates found');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const functionCalls = result.response.candidates
|
|
91
|
+
.map((candidate) => candidate.content.parts?.map((part) => part.functionCall))
|
|
92
|
+
.flat()
|
|
93
|
+
.filter((functionCall) => !!functionCall);
|
|
94
|
+
|
|
95
|
+
if (functionCalls.length === 0) {
|
|
96
|
+
const textResponse = result.response.candidates
|
|
97
|
+
.map((candidate) => candidate.content.parts?.map((part) => part.text))
|
|
98
|
+
.flat()
|
|
99
|
+
.filter((text) => !!text)
|
|
100
|
+
.join('\n');
|
|
101
|
+
console.log('No function calls, output text response if it exists:', textResponse);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return processFunctionCalls(functionCalls);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// A function to get the generative model
|
|
108
|
+
// Modified to accept temperature parameter
|
|
109
|
+
export function getGenModel(systemPrompt, temperature) {
|
|
110
|
+
// Initialize Vertex with your Cloud project and location
|
|
111
|
+
const vertex_ai = new VertexAI({});
|
|
112
|
+
const model = 'gemini-1.5-pro-001';
|
|
113
|
+
|
|
114
|
+
// Instantiate the models
|
|
115
|
+
return vertex_ai.preview.getGenerativeModel({
|
|
116
|
+
model: model,
|
|
117
|
+
generationConfig: {
|
|
118
|
+
maxOutputTokens: 8192,
|
|
119
|
+
temperature: temperature,
|
|
120
|
+
topP: 0.95,
|
|
121
|
+
},
|
|
122
|
+
safetySettings: [
|
|
123
|
+
{
|
|
124
|
+
category: 'HARM_CATEGORY_HATE_SPEECH',
|
|
125
|
+
threshold: geminiBlockNone ? 'BLOCK_NONE' : 'BLOCK_LOW_AND_ABOVE',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
129
|
+
threshold: geminiBlockNone ? 'BLOCK_NONE' : 'BLOCK_LOW_AND_ABOVE',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
133
|
+
threshold: geminiBlockNone ? 'BLOCK_NONE' : 'BLOCK_LOW_AND_ABOVE',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
category: 'HARM_CATEGORY_HARASSMENT',
|
|
137
|
+
threshold: geminiBlockNone ? 'BLOCK_NONE' : 'BLOCK_LOW_AND_ABOVE',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
systemInstruction: {
|
|
141
|
+
role: 'system',
|
|
142
|
+
parts: [
|
|
143
|
+
{
|
|
144
|
+
text: systemPrompt,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function verifyVertexMonkeyPatch() {
|
|
152
|
+
return (await import('@google-cloud/vertexai/build/src/functions/generate_content.js')).generateContent
|
|
153
|
+
.toString()
|
|
154
|
+
.includes('// MONKEY PATCH TOOL_CONFIG');
|
|
155
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// CLI options and their descriptions
|
|
2
|
+
const cliOptions = [
|
|
3
|
+
{
|
|
4
|
+
name: '--help',
|
|
5
|
+
description: 'Display this help message.',
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: '--dry-run',
|
|
9
|
+
description: 'Run the codegen script without updating the source code.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: '--consider-all-files',
|
|
13
|
+
description: "Consider all files for code generation, even if they don't contain the @" + 'CODEGEN comments.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: '--allow-file-create',
|
|
17
|
+
description: 'Allow the codegen script to create new files.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: '--allow-file-delete',
|
|
21
|
+
description: 'Allow the codegen script to delete files.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: '--allow-directory-create',
|
|
25
|
+
description: 'Allow codegen script to create directories.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: '--allow-file-move',
|
|
29
|
+
description: 'Allow the codegen script to move files.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: '--chat-gpt',
|
|
33
|
+
description: "Use the OpenAI model for code generation instead of Vertex AI with Google's Gemini Pro model.",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: '--anthropic',
|
|
37
|
+
description: "Use Anthropic's Claude model for code generation.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: '--vertex-ai',
|
|
41
|
+
description:
|
|
42
|
+
"Use Vertex AI with Google's Gemini Pro model for code generation (default if no AI model is specified).",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: '--vertex-ai-claude',
|
|
46
|
+
description: 'Use Claude via Vertex AI for code generation.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: '--explicit-prompt=<prompt>',
|
|
50
|
+
description: 'An explicit prompt to use for code generation.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: '--task-file=<file>',
|
|
54
|
+
description: 'Specifies a file with a task description for code generation.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: '--dependency-tree',
|
|
58
|
+
description: 'Limit the scope of codegen only to files marked with @' + 'CODEGEN and their dependencies.',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: '--verbose-prompt',
|
|
62
|
+
description: 'Print the prompt used for code generation.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: '--require-explanations',
|
|
66
|
+
description: 'Require explanations for all code generation operations.',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: '--disable-context-optimization',
|
|
70
|
+
description: 'Disable the optimization that uses context paths for more efficient code generation.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: '--gemini-block-none',
|
|
74
|
+
description: 'Disable safety settings for Gemini Pro model (requires whitelisted Cloud project).',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: '--disable-initial-lint',
|
|
78
|
+
description: 'Skip the initial lint check before running the code generation process.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: '--temperature=<value>',
|
|
82
|
+
description: 'Set the temperature parameter for the AI model (default: 0.7).',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: '--vision',
|
|
86
|
+
description:
|
|
87
|
+
'Enable vision capabilities for processing image inputs. This option allows the tool to analyze and generate code based on image content when used with compatible AI models.',
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Print the help message with all available CLI options
|
|
93
|
+
*/
|
|
94
|
+
export function printHelpMessage() {
|
|
95
|
+
console.log('GenAIcode - AI-powered code generation tool');
|
|
96
|
+
console.log('\nUsage: npx genaicode [options]');
|
|
97
|
+
console.log('\nOptions:');
|
|
98
|
+
|
|
99
|
+
cliOptions.forEach((option) => {
|
|
100
|
+
console.log(` ${option.name.padEnd(30)} ${option.description}`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
console.log('\nFor more information, please refer to the project home page: https://github.com/gtanczyk/genaicode/');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { cliOptions };
|