Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/msa-ez/platform
Browse files Browse the repository at this point in the history
  • Loading branch information
Kimsanghoon committed Feb 25, 2025
2 parents fddc155 + d4dc436 commit 3e6a56b
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 34 deletions.
4 changes: 2 additions & 2 deletions public/static/env.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
VUE_APP_MODE=onprem
VUE_APP_MODE=dev
VUE_APP_DB_HOST=localhost
VUE_APP_DB_PORT=5757
VUE_APP_DB_NAME=mydb
VUE_APP_GIT=gitea
VUE_APP_GIT=github
64 changes: 47 additions & 17 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ app.post('/api/ollama/chat', async (req, res) => {
buildFetchOptions: (req) => ({
method: 'POST',
headers: {
'Content-Type': 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify(req.body)
}),
Expand All @@ -86,7 +86,7 @@ app.post('/api/anthropic/chat', async (req, res) => {
"content-type": req.headers["content-type"] || "application/json",
"anthropic-version": req.headers["anthropic-version"] || "2023-06-01",
"x-api-key": req.headers["x-api-key"],
"User-Agent": req.headers["user-agent"] || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
"user-agent": req.headers["user-agent"] || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
},
body: JSON.stringify(req.body),
agent: Util.createHttpsAgent(false)
Expand All @@ -96,6 +96,25 @@ app.post('/api/anthropic/chat', async (req, res) => {
});
});

app.post('/api/openai-compatibility/chat', async (req, res) => {
await Util.makeProxyStream(req, res, {
targetUrl: req.headers["ai-param-url"],
buildFetchOptions: (req) => {
return {
method: 'POST',
headers: {
"content-type": req.headers["content-type"] || "application/json",
"user-agent": req.headers["user-agent"] || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
"authorization": req.headers["authorization"]
},
body: JSON.stringify(req.body),
agent: Util.createHttpsAgent(false)
};
},
errorLabel: 'OpenAI Compatibility'
});
});


class Util {
/**
Expand All @@ -110,7 +129,10 @@ class Util {
*/
static async makeProxyStream(req, res, options) {
try {
console.log(`Received ${options.errorLabel} request:`, req.body);
console.log(`Received ${options.errorLabel} request:`, {
body: JSON.stringify(req.body),
headers: JSON.stringify(req.headers)
});

if (options.healthCheckUrl) {
const checkResponse = await fetch(options.healthCheckUrl);
Expand All @@ -125,6 +147,12 @@ class Util {
? options.buildFetchOptions(req)
: options.buildFetchOptions;

console.log(`Maked ${options.errorLabel} request:`, {
body: JSON.stringify(fetchOptions.body),
headers: JSON.stringify(fetchOptions.headers)
});


const response = await fetch(options.targetUrl, fetchOptions);
console.log(`${options.errorLabel} API status:`, response.status);

Expand All @@ -137,11 +165,11 @@ class Util {


const defaultHeaders = {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': 'https://www.msaez.io:8081',
'Access-Control-Allow-Credentials': 'true'
'content-type': 'text/event-stream',
'cache-control': 'no-cache',
'connection': 'keep-alive',
'access-control-allow-origin': 'https://www.msaez.io:8081',
'access-control-allow-credentials': 'true'
};

const streamHeaders = options.streamHeaders ? { ...defaultHeaders, ...options.streamHeaders } : defaultHeaders;
Expand All @@ -163,18 +191,20 @@ class Util {

static setCorsHeaders(res) {
const headersToAllow = [
"Origin",
"X-Requested-With",
"Content-Type",
"Accept",
"origin",
"x-requested-with",
"content-type",
"accept",
"anthropic-version",
"x-api-key"
"x-api-key",
"ai-param-url",
"authorization"
]

res.header('Access-Control-Allow-Origin', 'https://www.msaez.io:8081');
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.header('Access-Control-Allow-Headers', headersToAllow.join(', '));
res.header('Access-Control-Allow-Credentials', 'true');
res.header('access-control-allow-origin', 'https://www.msaez.io:8081');
res.header('access-control-allow-methods', 'GET, POST, OPTIONS');
res.header('access-control-allow-headers', headersToAllow.join(', '));
res.header('access-control-allow-credentials', 'true');
}

static createHttpsAgent(rejectUnauthorized = false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,15 +530,6 @@ ${JSON.stringify(exampleOutputs)}
}
}
}

if(returnObj.isStopped)
return returnObj

if(!text) {
console.log("[*] 별도의 처리할 텍스트가 없음", { ...this._makeDebugObject(returnObj) })
return returnObj
}


if(this.isFirstResponse) {
this.prevPartialAiOutput = {}
Expand All @@ -550,7 +541,16 @@ ${JSON.stringify(exampleOutputs)}
else
returnObj.isFirstResponse = this.isFirstResponse


if(returnObj.isStopped)
return returnObj

if(!text) {
console.log("[*] 별도의 처리할 텍스트가 없음", { ...this._makeDebugObject(returnObj) })
return returnObj
}


if(this.progressCheckStrings.length > 0)
returnObj.progress = this._getProcessPercentage(text)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const OpenAIClient = require('./OpenAIClient');
const OllamaClient = require('./OllamaClient');
const AnthropicClient = require('./AnthropicClient');
const RunpodClient = require('./RunpodClient');
const { ModelInfoHelper } = require('../helpers');
const getDefaultOptions = require('./getDefaultOptions');

Expand All @@ -23,7 +24,8 @@ class APIClientFactory {
APIClientFactory.vendorApiClientMap = {
"openai": OpenAIClient,
"ollama": OllamaClient,
"anthropic": AnthropicClient
"anthropic": AnthropicClient,
"runpod": RunpodClient
}

module.exports = APIClientFactory;
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AnthropicClient extends BaseAPIClient {
requestUrl: "http://localhost:4000/api/anthropic/chat",
requestData: JSON.stringify(requestData),
requestHeaders: {
"Content-type": "application/json",
"content-type": "application/json",
"anthropic-version": "2023-06-01",
"x-api-key": token
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class OllamaClient extends BaseAPIClient {
requestUrl: "http://localhost:4000/api/ollama/chat",
requestData: JSON.stringify(requestData),
requestHeaders: {
"Content-Type": "application/json"
"content-type": "application/json"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class OpenAIClient extends BaseAPIClient {
requestUrl: "https://api.openai.com/v1/chat/completions",
requestData: JSON.stringify(requestData),
requestHeaders: {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
"content-type": "application/json",
"authorization": "Bearer " + token
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const BaseAPIClient = require('./BaseAPIClient');
const { TextParseHelper } = require('../helpers');

class RunpodClient extends BaseAPIClient {
constructor(client, options, model, aiGenerator) {
super(client, options, model, aiGenerator)
}

_makeRequestParams(messages, modelInfo, token){
let requestData = {
model: modelInfo.requestModelName,
messages: messages,
stream: true
}

return {
requestUrl: "http://localhost:4000/api/openai-compatibility/chat",
requestData: JSON.stringify(requestData),
requestHeaders: {
"content-type": "application/json",
"authorization": "Bearer " + token,
"ai-param-url": localStorage.getItem("runpodUrl") || "https://dkkzpbvvh17k7v-8000.proxy.runpod.net/v1/chat/completions",
}
}
}

_parseResponseText(responseText){
const result = TextParseHelper.parseResponseText(responseText, {
splitFunction: (text) => text.replace("data: [DONE]", "")
.trim()
.split("data: ")
.filter(Boolean),

extractFunction: (parsed) => {
if(parsed.choices && parsed.choices[0]){
return {
content: parsed.choices[0].delta.content || "",
id: parsed.id,
finish_reason: parsed.choices[0].finish_reason === 'length' ? 'length' : null,
error: parsed.error || null
}
}
return { content: "", id: parsed.id, finish_reason: null, error: parsed.error || null }
}
})

if(this.aiGenerator.modelInfo.requestModelName.toLowerCase().includes("deepseek-r1")) {
// 이유는 알 수 없으나, 몇몇 모델들은 <think> 태그로 시작하지 않으면서, </think> 태그로 끝나는 응답을 보내는 경우가 있음
if(!result.joinedText.startsWith("<think>")) {
result.joinedText = "<think>" + result.joinedText
}

const tagParsedContents = TextParseHelper.parseFrontTagContents(result.joinedText, "think");
result.joinedText = tagParsedContents.restText;
this.aiGenerator.parsedTexts.think = tagParsedContents.tagContents;
}

return result;
}
}

module.exports = RunpodClient;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { openaiModelInfos } = require("./openaiModelInfos");
const { ollamaModelInfos } = require("./ollamaModelInfos");
const { anthropicModelInfos } = require("./anthropicModelInfos");
const { runpodModelInfos } = require("./runpodModelInfos");

/**
* @description 특정 모델에 대한 API 요청시에 활용되는 기반 정보
Expand Down Expand Up @@ -33,7 +34,8 @@ const { anthropicModelInfos } = require("./anthropicModelInfos");
const modelInfos = {
...openaiModelInfos,
...ollamaModelInfos,
...anthropicModelInfos
...anthropicModelInfos,
...runpodModelInfos
}

const defaultModelInfos = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const runpodModelInfos = {
// 가능한 설정 옵션들: neuralmagic/DeepSeek-R1-Distill-Llama-70B-quantized.w8a8
"neuralmagic/DeepSeek-R1-Distill-Llama-70B-quantized.w8a8": {
vendor: "runpod",
contextWindowTokenLimit: 16000,
outputTokenLimit: 2048,
requestArgs: {
maxTokens: 2048
}
},

"deepseek-ai/DeepSeek-R1-Distill-Qwen-32B": {
vendor: "runpod",
contextWindowTokenLimit: 131072,
outputTokenLimit: 8192,
requestArgs: {
maxTokens: 8192
}
}
}

0 comments on commit 3e6a56b

Please sign in to comment.