everydaycats commited on
Commit
2e99d45
·
verified ·
1 Parent(s): 2ecc8d1

Create ai_engine.js

Browse files
Files changed (1) hide show
  1. ai_engine.js +159 -0
ai_engine.js ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import OpenAI from "openai";
2
+ import { BedrockRuntimeClient, ConverseCommand, ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
3
+ import { NodeHttpHandler } from "@smithy/node-http-handler";
4
+
5
+ const CLAUDE_SYSTEM_PROMPT = "You are a pro. Provide elite, high-level technical responses.";
6
+ const GPT_SYSTEM_PROMPT = "You are a worker. Be concise, efficient, and get the job done.";
7
+
8
+ const bedrockClient = new BedrockRuntimeClient({
9
+ region: "us-east-1",
10
+ requestHandler: new NodeHttpHandler({
11
+ http2Handler: undefined,
12
+ })
13
+ });
14
+
15
+ const azureOpenAI = new OpenAI({
16
+ apiKey: process.env.AZURE_OPENAI_API_KEY || "7U3m9NRkE38ThSWTr92hMgQ4hDCUFI9MAnFNrCgRL7MhdvckfTXwJQQJ99CBACHYHv6XJ3w3AAAAACOGV22P",
17
+ baseURL: `https://hollowpad-resource.cognitiveservices.azure.com/openai/deployments/gpt-5-mini`,
18
+ defaultQuery: { "api-version": "2024-05-01-preview" },
19
+ defaultHeaders: { "api-key": process.env.AZURE_OPENAI_API_KEY || "7U3m9NRkE38ThSWTr92hMgQ4hDCUFI9MAnFNrCgRL7MhdvckfTXwJQQJ99CBACHYHv6XJ3w3AAAAACOGV22P" }
20
+ });
21
+
22
+ function getBedrockModelId(modelName) {
23
+ switch(modelName) {
24
+ case "haiku":
25
+ return "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-haiku-4-5-20251001-v1:0";
26
+ case "maverick":
27
+ return "arn:aws:bedrock:us-east-1:106774395747:inference-profile/us.meta.llama4-maverick-17b-instruct-v1:0";
28
+ case "claude":
29
+ default:
30
+ return "arn:aws:bedrock:us-east-1:106774395747:inference-profile/global.anthropic.claude-sonnet-4-6";
31
+ }
32
+ }
33
+
34
+ export const generateCompletion = async ({ model, prompt, system_prompt, images }) => {
35
+ if (model === "gpt" || model === "gpt-5-mini") {
36
+ let messagesPayload =[{ role: "system", content: system_prompt || GPT_SYSTEM_PROMPT }];
37
+
38
+ if (images && images.length > 0) {
39
+ let userContent =[{ type: "text", text: prompt }];
40
+ images.forEach(imgStr => {
41
+ userContent.push({ type: "image_url", image_url: { url: imgStr } });
42
+ });
43
+ messagesPayload.push({ role: "user", content: userContent });
44
+ } else {
45
+ messagesPayload.push({ role: "user", content: prompt });
46
+ }
47
+
48
+ const response = await azureOpenAI.chat.completions.create({
49
+ model: "gpt-5-mini",
50
+ messages: messagesPayload,
51
+ reasoning_effort: "high"
52
+ });
53
+
54
+ return { success: true, data: response.choices[0].message.content, usage: { totalTokenCount: response.usage?.total_tokens || 0 } };
55
+
56
+ } else {
57
+ const bedrockModelId = getBedrockModelId(model);
58
+ let contentBlock = [{ text: prompt }];
59
+
60
+ if (images && images.length > 0) {
61
+ const imageBlocks = images.map(imgStr => {
62
+ const base64Data = imgStr.replace(/^data:image\/\w+;base64,/, "");
63
+ return {
64
+ image: { format: 'png', source: { bytes: Buffer.from(base64Data, 'base64') } }
65
+ };
66
+ });
67
+ contentBlock = [...imageBlocks, ...contentBlock];
68
+ }
69
+
70
+ const command = new ConverseCommand({
71
+ modelId: bedrockModelId,
72
+ system:[{ text: system_prompt || CLAUDE_SYSTEM_PROMPT }],
73
+ messages: [{ role: "user", content: contentBlock }],
74
+ inferenceConfig: {
75
+ maxTokens: model.includes("haiku") ? 32000 : 4000,
76
+ temperature: 1
77
+ },
78
+ performanceConfig: model.includes("maverick") ? { latency: "standard" } : undefined,
79
+ additionalModelRequestFields: (function() {
80
+ if (model.includes("haiku")) return { reasoning_config: { type: "enabled", budget_tokens: 2048 } };
81
+ if (model.includes("claude")) return { output_config: { effort: "high" } }; // Removed adaptive thinking to prevent strict schema errors
82
+ return undefined;
83
+ })()
84
+ });
85
+
86
+ const response = await bedrockClient.send(command);
87
+ const text = response.output.message.content.find(b => b.text)?.text;
88
+ const tokenUsage = response.usage ? (response.usage.inputTokens + response.usage.outputTokens) : 0;
89
+
90
+ return { success: true, data: text, usage: { totalTokenCount: tokenUsage } };
91
+ }
92
+ };
93
+
94
+ export const streamCompletion = async ({ model, prompt, system_prompt, images, res }) => {
95
+ let totalTokenCount = 0;
96
+
97
+ if (model === "gpt" || model === "gpt-5-mini") {
98
+ let messagesPayload =[{ role: "system", content: system_prompt || GPT_SYSTEM_PROMPT }];
99
+ let userContent =[];
100
+ if (images && images.length > 0) {
101
+ userContent.push({ type: "text", text: prompt });
102
+ images.forEach(imgStr => { userContent.push({ type: "image_url", image_url: { url: imgStr } }); });
103
+ messagesPayload.push({ role: "user", content: userContent });
104
+ } else {
105
+ messagesPayload.push({ role: "user", content: prompt });
106
+ }
107
+
108
+ const stream = await azureOpenAI.chat.completions.create({
109
+ model: "gpt-5-mini",
110
+ messages: messagesPayload,
111
+ reasoning_effort: "high",
112
+ stream: true,
113
+ stream_options: { include_usage: true }
114
+ });
115
+
116
+ for await (const chunk of stream) {
117
+ const delta = chunk.choices[0]?.delta;
118
+ if (delta?.reasoning_content) res.write(`__THINK__${delta.reasoning_content}`);
119
+ else if (delta?.content) res.write(delta.content);
120
+ if (chunk.usage) totalTokenCount = chunk.usage.total_tokens;
121
+ }
122
+ res.write(`__USAGE__${JSON.stringify({ totalTokenCount })}`);
123
+ res.end();
124
+
125
+ } else {
126
+ const bedrockModelId = getBedrockModelId(model);
127
+ let contentBlock = [{ text: prompt }];
128
+
129
+ if (images && images.length > 0) {
130
+ const imageBlocks = images.map(imgStr => {
131
+ const base64Data = imgStr.replace(/^data:image\/\w+;base64,/, "");
132
+ return { image: { format: 'png', source: { bytes: Buffer.from(base64Data, 'base64') } } };
133
+ });
134
+ contentBlock = [...imageBlocks, ...contentBlock];
135
+ }
136
+
137
+ const command = new ConverseStreamCommand({
138
+ modelId: bedrockModelId,
139
+ system:[{ text: system_prompt || CLAUDE_SYSTEM_PROMPT }],
140
+ messages:[{ role: "user", content: contentBlock }],
141
+ inferenceConfig: { maxTokens: 48000, temperature: 1 },
142
+ additionalModelRequestFields: model.includes("claude") ? { output_config: { effort: "high" } } : undefined
143
+ });
144
+
145
+ const response = await bedrockClient.send(command);
146
+ for await (const chunk of response.stream) {
147
+ if (chunk.contentBlockDelta) {
148
+ const delta = chunk.contentBlockDelta.delta;
149
+ if (delta.reasoningContent && delta.reasoningContent.text) res.write(`__THINK__${delta.reasoningContent.text}`);
150
+ else if (delta.text) res.write(delta.text);
151
+ }
152
+ if (chunk.metadata && chunk.metadata.usage) {
153
+ totalTokenCount = (chunk.metadata.usage.inputTokens || 0) + (chunk.metadata.usage.outputTokens || 0);
154
+ }
155
+ }
156
+ res.write(`__USAGE__${JSON.stringify({ totalTokenCount })}`);
157
+ res.end();
158
+ }
159
+ };