everydaytok commited on
Commit
23a8239
·
verified ·
1 Parent(s): 84e255e

Update aiEngine.js

Browse files
Files changed (1) hide show
  1. aiEngine.js +72 -205
aiEngine.js CHANGED
@@ -1,236 +1,103 @@
1
- import { AzureOpenAI } from "openai"; // Use the dedicated Azure class
2
- import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
3
- import { NodeHttpHandler } from "@smithy/node-http-handler";
4
  import fs from 'fs';
5
  import path from 'path';
6
- import dotenv from 'dotenv';
7
  dotenv.config();
8
 
 
 
 
9
  let prompts = {};
10
  try {
11
  const promptsPath = path.resolve('./prompts.json');
12
  if (fs.existsSync(promptsPath)) prompts = JSON.parse(fs.readFileSync(promptsPath, 'utf8'));
13
  } catch (e) { console.error("Prompt Load Error:", e); }
14
 
15
- // --- BEDROCK CLIENT ---
16
- const bedrockClient = new BedrockRuntimeClient({
17
- region: "us-east-1",
18
- requestHandler: new NodeHttpHandler({
19
- http2Handler: undefined
20
- })
21
- /* credentials: {
22
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
23
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
24
- } */
25
- });
26
-
27
- // --- AZURE OPENAI CLIENT (Fixed Initialization) ---
28
- const azureOpenAI = new AzureOpenAI({
29
- apiKey: process.env.AZURE_OPENAI_API_KEY,
30
- // Note: endpoint should be: https://resource-name.cognitiveservices.azure.com
31
- endpoint: process.env.AZURE_OPENAI_ENDPOINT,
32
- deployment: process.env.AZURE_DEPLOYMENT_NAME, // e.g., "gpt-5-mini"
33
- apiVersion: "2024-05-01-preview",
34
- });
35
-
36
- // Formatters
37
- const toAzureHistory = (h) => h.map(m => ({
38
- role: m.role === 'model' ? 'assistant' : 'user',
39
- content: m.parts[0].text
40
- }));
41
-
42
- const toBedrockHistory = (h) => h.map(m => ({
43
- role: m.role === 'model' ? 'assistant' : 'user',
44
- content: [{ text: m.parts[0].text }]
45
- }));
46
 
47
  export const AIEngine = {
48
- /* callPM: async (history, input) => {
49
- const command = new ConverseCommand({
50
- modelId: "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-sonnet-4-6",
51
- system: [{ text: prompts.pm_system_prompt || "You are a pro manager." }],
52
- messages: [...toBedrockHistory(history), { role: "user", content: [{ text: input }] }],
53
- inferenceConfig: {
54
- maxTokens: 4096, // Reduced from 50k (which causes errors) to standard max output
55
- temperature: 1
56
- },
57
- additionalModelRequestFields: {
58
- thinking: { type: "adaptive" },
59
- output_config: { effort: "high" }
60
- }
61
- });
62
- const res = await bedrockClient.send(command);
63
- const text = res.output.message.content.find(b => b.text)?.text;
64
- return { text, usage: { totalTokenCount: (res.usage?.inputTokens || 0) + (res.usage?.outputTokens || 0) } };
65
- },
66
- */
67
-
68
- /* callPM: async (history, input) => {
69
- try {
70
- // 1. Filter history to ensure strict alternation (User -> Assistant)
71
- // and prevent the "Consecutive Role" error.
72
- const formattedHistory = toBedrockHistory(history).filter((msg, index, array) => {
73
- if (index === 0) return msg.role === 'user'; // First must be user
74
- return msg.role !== array[index - 1].role; // Must alternate
75
- });
76
-
77
- const command = new ConverseCommand({
78
- modelId: "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-sonnet-4-6",
79
- system: [{ text: prompts.pm_system_prompt || "You are a pro manager." }],
80
- // Only append input if it's not already the last message in history
81
- messages: [
82
- ...formattedHistory,
83
- { role: "user", content: [{ text: input }] }
84
- ],
85
- inferenceConfig: {
86
- maxTokens: 20000, // Increased to allow room for Thinking + Output
87
- temperature: 1
88
- },
89
- additionalModelRequestFields: {
90
- thinking: { type: "adaptive" },
91
- output_config: { effort: "high" }
92
- }
93
- });
94
-
95
- const res = await bedrockClient.send(command);
96
-
97
- // 2. Claude 3.7/4.6 returns multiple content blocks (thinking + text)
98
- // We specifically look for the 'text' block.
99
- const text = res.output.message.content.find(b => b.text)?.text;
100
-
101
- return {
102
- text: text || "No response text found.",
103
- usage: {
104
- totalTokenCount: (res.usage?.inputTokens || 0) + (res.usage?.outputTokens || 0)
105
- }
106
- };
107
- } catch (error) {
108
- console.error("❌ BEDROCK API ERROR:", error.name, error.message);
109
- throw error; // Rethrow so your UI knows it failed
110
- }
111
- }, */
112
-
113
- // callWorker: async (history, input, images = []) => {
114
  callPM: async (history, input) => {
115
-
116
- let userContent = input;
117
-
118
- // Handle Multimodal (Images)
119
- if (images?.length > 0) {
120
- userContent = [
121
- { type: "text", text: input },
122
- ...images.map(img => ({
123
- type: "image_url",
124
- image_url: { url: img.startsWith('data') ? img : `data:image/png;base64,${img}` }
125
- }))
126
- ];
127
- }
128
-
129
- const res = await azureOpenAI.chat.completions.create({
130
- model: process.env.AZURE_DEPLOYMENT_NAME,
131
- messages: [
132
- { role: "system", content: prompts.worker_system_prompt || "You are a worker." },
133
- ...toAzureHistory(history),
134
- { role: "user", content: userContent }
135
- ],
136
- reasoning_effort: "high"
137
  });
138
 
 
 
 
139
  return {
140
- text: res.choices[0].message.content,
141
- usage: { totalTokenCount: res.usage.total_tokens }
142
  };
143
  },
144
-
145
- /* callWorker: async (history, input, images = []) => {
146
- let userContent = input;
147
-
148
- // Handle Multimodal (Images)
149
- if (images?.length > 0) {
150
- userContent = [
151
- { type: "text", text: input },
152
- ...images.map(img => ({
153
- type: "image_url",
154
- image_url: { url: img.startsWith('data') ? img : `data:image/png;base64,${img}` }
155
- }))
156
- ];
157
- }
158
-
159
- const res = await azureOpenAI.chat.completions.create({
160
- model: process.env.AZURE_DEPLOYMENT_NAME,
161
- messages: [
162
- { role: "system", content: prompts.worker_system_prompt || "You are a worker." },
163
- ...toAzureHistory(history),
164
- { role: "user", content: userContent }
165
- ],
166
- reasoning_effort: "high"
167
  });
168
 
 
 
 
169
  return {
170
- text: res.choices[0].message.content,
171
- usage: { totalTokenCount: res.usage.total_tokens }
172
  };
173
- }, */
174
-
175
 
176
  generateEntryQuestions: async (desc) => {
177
- /*const res = await azureOpenAI.chat.completions.create({
178
- model: process.env.AZURE_DEPLOYMENT_NAME,
179
- messages: [
180
- { role: "system", content: prompts.analyst_system_prompt + " Output ONLY JSON." },
181
- { role: "user", content: `[MODE 1: QUESTIONS]\nIdea: "${desc}"` }
182
- ],
183
- response_format: { type: "json_object" }
184
  });
185
- return {
186
- ...JSON.parse(res.choices[0].message.content),
187
- usage: { totalTokenCount: res.usage.total_tokens }
188
- };*/
189
- try {
190
- let ppp = `${prompts.analyst_system_prompt} Output ONLY JSON.`;
191
-
192
- const command = new ConverseCommand({
193
- modelId: "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-sonnet-4-6",
194
- // --- OFFICIAL BEDROCK SYSTEM PROMPT ---
195
- system: [{ text: ppp }],
196
- messages: [{ role: "user", content: [{ text: `[MODE 1: QUESTIONS]\nIdea: "${desc}"` }] }],
197
- inferenceConfig: { maxTokens: 48000, temperature: 1 },
198
- additionalModelRequestFields: {
199
- thinking: { type: "adaptive" },
200
- output_config: { effort: "high" }
201
- }
202
- });
203
-
204
- const response = await bedrockClient.send(command);
205
- const text = response.output.message.content.find(b => b.text)?.text;
206
-
207
- return {
208
- text: text || "No response text found.",
209
- usage: {
210
- totalTokenCount: (response.usage?.inputTokens || 0) + (response.usage?.outputTokens || 0)
211
- }
212
- };
213
- } catch (error) {
214
- console.error("❌ BEDROCK API ERROR:", error.name, error.message);
215
- throw error; // Rethrow so your UI knows it failed
216
- }
217
-
218
  },
219
 
220
  gradeProject: async (desc, ans) => {
221
- const res = await azureOpenAI.chat.completions.create({
222
- model: process.env.AZURE_DEPLOYMENT_NAME,
223
- messages: [
224
- { role: "system", content: prompts.analyst_system_prompt + " Output ONLY JSON." },
225
- { role: "user", content: `[MODE 2: GRADING]\nIdea: ${desc}\nAns: ${JSON.stringify(ans)}` }
226
- ],
227
- response_format: { type: "json_object" }
228
  });
229
- return {
230
- ...JSON.parse(res.choices[0].message.content),
231
- usage: { totalTokenCount: res.usage.total_tokens }
232
- };
233
- },
234
 
235
- generateImage: async () => null
 
 
236
  };
 
1
+ import dotenv from 'dotenv';
 
 
2
  import fs from 'fs';
3
  import path from 'path';
4
+
5
  dotenv.config();
6
 
7
+ // Configuration for your remote "Battle Arena" server
8
+ const REMOTE_SERVER_URL = process.env.REMOTE_AI_URL || "http://localhost:7860";
9
+
10
  let prompts = {};
11
  try {
12
  const promptsPath = path.resolve('./prompts.json');
13
  if (fs.existsSync(promptsPath)) prompts = JSON.parse(fs.readFileSync(promptsPath, 'utf8'));
14
  } catch (e) { console.error("Prompt Load Error:", e); }
15
 
16
+ /**
17
+ * HELPER: Flattens the history array into a single string
18
+ * because the remote server expects a 'prompt' string.
19
+ */
20
+ const flattenHistory = (history, currentInput, systemPrompt) => {
21
+ const context = history.map(m => {
22
+ const roleName = m.role === 'model' ? 'Assistant' : 'User';
23
+ return `${roleName}: ${m.parts[0].text}`;
24
+ }).join('\n');
25
+
26
+ return `System: ${systemPrompt}\n\n${context}\nUser: ${currentInput}\nAssistant:`;
27
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  export const AIEngine = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  callPM: async (history, input) => {
31
+ const systemPrompt = prompts.pm_system_prompt || "You are a pro manager.";
32
+ const fullPrompt = flattenHistory(history, input, systemPrompt);
33
+
34
+ const response = await fetch(`${REMOTE_SERVER_URL}/api/generate`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({
38
+ model: "claude",
39
+ prompt: fullPrompt
40
+ })
 
 
 
 
 
 
 
 
 
 
 
 
41
  });
42
 
43
+ const result = await response.json();
44
+ if (!result.success) throw new Error(result.error);
45
+
46
  return {
47
+ text: result.data,
48
+ usage: { totalTokenCount: 0 } // Remote server doesn't return usage yet
49
  };
50
  },
51
+
52
+ callWorker: async (history, input, images = []) => {
53
+ // Note: The current remote server doesn't support images in its /api/generate
54
+ // We'll send it as a text-only gpt request for now.
55
+ const systemPrompt = prompts.worker_system_prompt || "You are a worker.";
56
+ const fullPrompt = flattenHistory(history, input, systemPrompt);
57
+
58
+ const response = await fetch(`${REMOTE_SERVER_URL}/api/generate`, {
59
+ method: 'POST',
60
+ headers: { 'Content-Type': 'application/json' },
61
+ body: JSON.stringify({
62
+ model: "gpt",
63
+ prompt: fullPrompt
64
+ })
 
 
 
 
 
 
 
 
 
65
  });
66
 
67
+ const result = await response.json();
68
+ if (!result.success) throw new Error(result.error);
69
+
70
  return {
71
+ text: result.data,
72
+ usage: { totalTokenCount: 0 }
73
  };
74
+ },
 
75
 
76
  generateEntryQuestions: async (desc) => {
77
+ const response = await fetch(`${REMOTE_SERVER_URL}/api/generate`, {
78
+ method: 'POST',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify({
81
+ model: "gpt",
82
+ prompt: `[OUTPUT ONLY JSON]\nGoal: Generate entry questions for this idea: ${desc}`
83
+ })
84
  });
85
+
86
+ const result = await response.json();
87
+ return JSON.parse(result.data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  },
89
 
90
  gradeProject: async (desc, ans) => {
91
+ const response = await fetch(`${REMOTE_SERVER_URL}/api/generate`, {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ model: "gpt",
96
+ prompt: `[OUTPUT ONLY JSON]\nGrade this project. Desc: ${desc}\nAnswers: ${JSON.stringify(ans)}`
97
+ })
98
  });
 
 
 
 
 
99
 
100
+ const result = await response.json();
101
+ return JSON.parse(result.data);
102
+ }
103
  };