everydaytok commited on
Commit
53fe6b9
·
verified ·
1 Parent(s): 8c1cb30

Update aiEngine.js

Browse files
Files changed (1) hide show
  1. aiEngine.js +92 -237
aiEngine.js CHANGED
@@ -1,249 +1,104 @@
1
- //aiEngine.js
2
- import { GoogleGenAI } from '@google/genai';
 
3
  import fs from 'fs';
4
- import path from 'path';
5
- import mime from 'mime';
6
 
7
- // Load prompts safely
8
- const promptsPath = path.resolve('./prompts.json');
9
- const prompts = JSON.parse(fs.readFileSync(promptsPath, 'utf8'));
10
 
11
- // Initialize SDK
12
- // Make sure process.env.GEMINI_API_KEY is set in your environment
13
- const genAI = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
14
-
15
- export const AIEngine = {
16
- /**
17
- * 1. PROJECT MANAGER (Reasoning & Delegation)
18
- * Uses High-Reasoning Model
19
- */
20
- callPM: async (history, input) => {
21
- const modelId = 'gemini-3-pro-preview';
22
-
23
- const config = {
24
- thinkingConfig: { thinkingLevel: 'HIGH' },
25
- tools: [{ googleSearch: {} }],
26
- systemInstruction: {
27
- parts: [{ text: prompts.pm_system_prompt }]
28
- }
29
- };
30
-
31
- const contents = [
32
- ...history,
33
- { role: 'user', parts: [{ text: input }] }
34
- ];
35
-
36
- try {
37
- const response = await genAI.models.generateContent({
38
- model: modelId,
39
- config,
40
- contents,
41
- });
42
-
43
- console.log(response.usageMetadata.total_token_count);
44
-
45
- // Return both text and usage metadata
46
- return {
47
- text: response.text,
48
- usage: response.usageMetadata
49
- };
50
-
51
- } catch (error) {
52
- console.error("PM AI Error:", error);
53
- throw error;
54
  }
55
- },
56
-
57
- /**
58
- * 2. WORKER (Coding & Execution)
59
- * Uses Flash Model (Fast) + Image Support
60
- */
61
- callWorker: async (history, input, images = []) => {
62
- const modelId = "gemini-3-flash-preview"; // 'gemini-3-pro-preview';
63
- const config = {
64
- thinkingConfig: { thinkingLevel: 'HIGH' },
65
- // tools: [{ googleSearch: {} }],
66
- systemInstruction: {
67
- parts: [{ text: prompts.worker_system_prompt }]
68
- }
69
- };
70
-
71
-
72
- const currentParts = [{ text: input }];
73
 
74
- // Handle Image Injection (Base64)
75
- if (images && images.length > 0) {
76
- images.forEach(base64String => {
77
- // Strip prefix if present
78
- const cleanData = base64String.replace(/^data:image\/\w+;base64,/, "");
79
- currentParts.push({
80
- inlineData: {
81
- mimeType: "image/png",
82
- data: cleanData
 
 
 
83
  }
84
  });
85
- });
86
- }
87
-
88
- const contents = [
89
- ...history,
90
- { role: 'user', parts: currentParts }
91
- ];
92
-
93
- try {
94
- const response = await genAI.models.generateContent({
95
- model: modelId,
96
- config,
97
- contents,
98
- });
99
-
100
- console.log(response.usageMetadata.total_token_count);
101
-
102
- // Return both text and usage metadata
103
- return {
104
- text: response.text,
105
- usage: response.usageMetadata
106
- };
107
-
108
- } catch (error) {
109
- console.error("Worker AI Error:", error);
110
- throw error;
111
- }
112
- },
113
-
114
- /**
115
- * 3. ONBOARDING ANALYST (Question Generation)
116
- * Returns STRICT JSON for the Frontend
117
- */
118
- generateEntryQuestions: async (description) => {
119
- const modelId = "gemini-3-flash-preview"; // 'gemini-2.5-flash';
120
- // Using the updated prompt which handles REJECTED/ACCEPTED logic
121
- const input = `[MODE 1: QUESTIONS]\nAnalyze this game idea: "${description}". Check for TOS violations or nonsense. If good, ask 3 questions. Output ONLY raw JSON.`;
122
-
123
- try {
124
- const response = await genAI.models.generateContent({
125
- model: modelId,
126
- config: {
127
- responseMimeType: "application/json",
128
- systemInstruction: { parts: [{ text: prompts.analyst_system_prompt }] }
129
- },
130
- contents: [{ role: 'user', parts: [{ text: input }] }]
131
- });
132
-
133
- const text = response.text;
134
- const parsed = JSON.parse(text);
135
 
136
- console.log(response.usageMetadata.total_token_count)
137
-
138
- // Attach usage to the JSON object
139
- return {
140
- ...parsed,
141
- usage: response.usageMetadata
142
- };
143
-
144
- } catch (e) {
145
- console.error("Analyst Error:", e);
146
- // On failure, we don't return usage, so no charge applies
147
- // return { status: "ACCEPTED", questions: [{ id: "fallback", label: "Please describe the core gameplay loop in detail.", type: "textarea" }] };
148
- throw e;
149
- }
150
- },
151
-
152
- /**
153
- * 4. PROJECT GRADER (Feasibility Check)
154
- * Returns STRICT JSON
155
- */
156
- gradeProject: async (description, answers) => {
157
- const modelId = "gemini-3-flash-preview"; // 'gemini-2.5-flash';
158
- // Using the updated prompt to respect Title and relaxed Grading
159
- const input = `[MODE 2: GRADING]\nIdea: "${description}"\nUser Answers: ${JSON.stringify(answers)}\n\nAssess feasibility. Output JSON with title and rating.`;
160
-
161
- try {
162
- const response = await genAI.models.generateContent({
163
- model: modelId,
164
- config: {
165
- responseMimeType: "application/json",
166
- systemInstruction: { parts: [{ text: prompts.analyst_system_prompt }] }
167
- },
168
- contents: [{ role: 'user', parts: [{ text: input }] }]
169
- });
170
-
171
- const parsed = JSON.parse(response.text);
172
- console.log(response.usageMetadata.total_token_count);
173
-
174
- // Attach usage to the JSON object
175
- return {
176
- ...parsed,
177
- usage: response.usageMetadata
178
- };
179
-
180
- } catch (e) {
181
- console.error("Grading Error:", e);
182
- // On failure, no usage returned
183
- // return { feasibility: 80, rating: "B", title: "Untitled Project", summary: "Standard project structure detected." };
184
- throw e;
185
- }
186
- },
187
-
188
- /**
189
- * 5. IMAGE GENERATOR (Visual Assets)
190
- * Uses Gemini 2.5 Flash Image with Stream (Correct Implementation)
191
- */
192
- generateImage: async (prompt) => {
193
- // Inject the prompt template from JSON to ensure adherence to instructions
194
- const finalPrompt = prompts.image_gen_prompt.replace('{{DESCRIPTION}}', prompt);
195
-
196
- const config = {
197
- responseModalities: ['IMAGE', 'TEXT'],
198
- };
199
- const model = 'gemini-2.5-flash-image';
200
- const contents = [
201
- {
202
- role: 'user',
203
- parts: [{ text: finalPrompt }],
204
- },
205
- ];
206
-
207
- try {
208
- const response = await genAI.models.generateContentStream({
209
- model,
210
- config,
211
- contents,
212
- });
213
-
214
- let finalDataUrl = null;
215
-
216
- for await (const chunk of response) {
217
- if (!chunk.candidates || !chunk.candidates[0].content || !chunk.candidates[0].content.parts) {
218
- continue;
219
- }
220
 
221
- // Capture image data if present
222
- if (chunk.candidates?.[0]?.content?.parts?.[0]?.inlineData) {
223
- const inlineData = chunk.candidates[0].content.parts[0].inlineData;
224
- const rawB64 = (inlineData.data || "").replace(/\s+/g, "");
225
- const mimeType = inlineData.mimeType || "image/png";
226
- const buffer = Buffer.from(rawB64, "base64");
227
- const base64 = buffer.toString("base64");
228
-
229
- finalDataUrl = `data:${mimeType};base64,${base64}`;
230
- // We do NOT return here immediately, we continue to allow the stream to finish
231
- // so we can access the aggregated usage metadata at the end.
232
- }
233
- }
234
-
235
- // Retrieve the full response object to get usage metadata
236
- const aggregatedResponse = await response.response;
237
- // console.log(aggregatedResponse.usageMetadata.total_token_count);
238
- return {
239
- image: finalDataUrl,
240
- usage: 2000// aggregatedResponse.usageMetadata
241
- };
242
 
243
- } catch (error) {
244
- console.error("Image Gen Error:", error);
245
- // On failure, return null (logic in backend handles null as no-op)
246
- return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
- }
249
  };
 
1
+ import OpenAI from "openai";
2
+ import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
3
+ import { NodeHttpHandler } from "@smithy/node-http-handler";
4
  import fs from 'fs';
 
 
5
 
6
+ // Load prompts
7
+ const prompts = JSON.parse(fs.readFileSync('./prompts.json', 'utf8'));
 
8
 
9
+ // --- CLIENT INITIALIZATION ---
10
+ const bedrockClient = new BedrockRuntimeClient({
11
+ region: process.env.AWS_REGION || "us-east-1",
12
+ requestHandler: new NodeHttpHandler({ http2Handler: undefined }),
13
+ credentials: {
14
+ accessKey_id: process.env.AWS_ACCESS_KEY_ID,
15
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
+ });
18
+
19
+ const azureOpenAI = new OpenAI({
20
+ apiKey: process.env.AZURE_OPENAI_API_KEY,
21
+ baseURL: `${process.env.AZURE_OPENAI_ENDPOINT}/openai/deployments/${process.env.AZURE_DEPLOYMENT_NAME}`,
22
+ defaultQuery: { "api-version": "2024-05-01-preview" },
23
+ defaultHeaders: { "api-key": process.env.AZURE_OPENAI_API_KEY }
24
+ });
25
+
26
+ // Helper to convert Google history format to Standard Chat format
27
+ function transformHistory(history) {
28
+ return history.map(h => ({
29
+ role: h.role === 'model' ? 'assistant' : h.role,
30
+ content: h.parts[0].text
31
+ }));
32
+ }
 
 
33
 
34
+ export const AIEngine = {
35
+ // PM -> Claude Sonnet 4.6 (via Bedrock)
36
+ callPM: async (history, input) => {
37
+ const chatHistory = transformHistory(history);
38
+ const command = new ConverseCommand({
39
+ modelId: "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-sonnet-4-6",
40
+ system: [{ text: prompts.pm_system_prompt }],
41
+ messages: [...chatHistory, { role: "user", content: [{ text: input }] }],
42
+ inferenceConfig: { maxTokens: 50000, temperature: 0.7 },
43
+ additionalModelRequestFields: {
44
+ thinking: { type: "adaptive" },
45
+ output_config: { effort: "high" }
46
  }
47
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ const response = await bedrockClient.send(command);
50
+ const text = response.output.message.content.find(b => b.text)?.text;
51
+
52
+ return {
53
+ text,
54
+ usage: { totalTokenCount: response.usage?.totalTokens || 0 }
55
+ };
56
+ },
57
+
58
+ // Worker -> GPT-5 Mini (via Azure)
59
+ callWorker: async (history, input, images = []) => {
60
+ const chatHistory = transformHistory(history);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ // Note: GPT-5 Mini handles images via the content array if needed
63
+ const response = await azureOpenAI.chat.completions.create({
64
+ model: process.env.AZURE_DEPLOYMENT_NAME,
65
+ messages: [
66
+ { role: "system", content: prompts.worker_system_prompt },
67
+ ...chatHistory,
68
+ { role: "user", content: input }
69
+ ],
70
+ reasoning_effort: "high"
71
+ });
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ return {
74
+ text: response.choices[0].message.content,
75
+ usage: { totalTokenCount: response.usage?.total_tokens || 0 }
76
+ };
77
+ },
78
+
79
+ // Standard tasks use the faster Worker model
80
+ generateEntryQuestions: async (description) => {
81
+ const response = await azureOpenAI.chat.completions.create({
82
+ model: process.env.AZURE_DEPLOYMENT_NAME,
83
+ messages: [
84
+ { role: "system", content: prompts.analyst_system_prompt },
85
+ { role: "user", content: `[MODE 1: QUESTIONS]\nIdea: "${description}"` }
86
+ ],
87
+ response_format: { type: "json_object" }
88
+ });
89
+ const parsed = JSON.parse(response.choices[0].message.content);
90
+ return { ...parsed, usage: { totalTokenCount: response.usage.total_tokens } };
91
+ },
92
+
93
+ gradeProject: async (description, answers) => {
94
+ const response = await azureOpenAI.chat.completions.create({
95
+ model: process.env.AZURE_DEPLOYMENT_NAME,
96
+ messages: [
97
+ { role: "system", content: prompts.analyst_system_prompt },
98
+ { role: "user", content: `[MODE 2: GRADING]\nIdea: ${description}\nAnswers: ${JSON.stringify(answers)}` }
99
+ ],
100
+ response_format: { type: "json_object" }
101
+ });
102
+ return { ...JSON.parse(response.choices[0].message.content), usage: { totalTokenCount: response.usage.total_tokens } };
103
  }
 
104
  };