@synapseia-network/node 0.8.5
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 +105 -0
- package/README.md +232 -0
- package/dist/bid-responder-Q725ZIUC.js +86 -0
- package/dist/bootstrap.js +22 -0
- package/dist/chain-info-lightweight-2UWAQZBF.js +303 -0
- package/dist/chat-stream-handler-BSHSGMFF.js +127 -0
- package/dist/chunk-2X7MSWD4.js +270 -0
- package/dist/chunk-3BHRQWSM.js +531 -0
- package/dist/chunk-5QFTU52A.js +442 -0
- package/dist/chunk-5ZAJBIAV.js +25 -0
- package/dist/chunk-7FLDR5NT.js +186 -0
- package/dist/chunk-C5XRYLYP.js +137 -0
- package/dist/chunk-D7ADMHK2.js +36 -0
- package/dist/chunk-DXUYWRO7.js +23 -0
- package/dist/chunk-F5UDK56Z.js +289 -0
- package/dist/chunk-NEHR6XY7.js +111 -0
- package/dist/chunk-NMJVODKH.js +453 -0
- package/dist/chunk-PRVT22SM.js +324 -0
- package/dist/chunk-T2ZRG5CX.js +1380 -0
- package/dist/chunk-V2L5SXTL.js +88 -0
- package/dist/chunk-XL2NJWFY.js +702 -0
- package/dist/embedding-C6GE3WVM.js +16 -0
- package/dist/hardware-ITQQJ5YI.js +37 -0
- package/dist/index.js +16836 -0
- package/dist/inference-server-CIGRJ36H.js +25 -0
- package/dist/local-cors-J6RWNMMD.js +44 -0
- package/dist/model-catalog-C53SDFMG.js +15 -0
- package/dist/model-discovery-LA6YMT3I.js +10 -0
- package/dist/ollama-XVXA3A37.js +9 -0
- package/dist/rewards-vault-cli-HW7H4EMD.js +147 -0
- package/dist/scripts/create_nodes.sh +6 -0
- package/dist/scripts/diloco_train.py +319 -0
- package/dist/scripts/train_lora.py +237 -0
- package/dist/scripts/train_micro.py +586 -0
- package/dist/trainer-HQMV2ZAR.js +21 -0
- package/package.json +128 -0
- package/scripts/create_nodes.sh +6 -0
- package/scripts/diloco_train.py +319 -0
- package/scripts/train_lora.py +237 -0
- package/scripts/train_micro.py +586 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
|
|
2
|
+
import {
|
|
3
|
+
__esm,
|
|
4
|
+
__name
|
|
5
|
+
} from "./chunk-D7ADMHK2.js";
|
|
6
|
+
|
|
7
|
+
// src/shared/embedding.ts
|
|
8
|
+
import { Injectable } from "@nestjs/common";
|
|
9
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
10
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
11
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
12
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
13
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
14
|
+
}
|
|
15
|
+
function _ts_metadata(k, v) {
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
17
|
+
}
|
|
18
|
+
var DEFAULT_OLLAMA_URL, EmbeddingHelper, generateEmbedding, cosineSimilarity, similaritySearch;
|
|
19
|
+
var init_embedding = __esm({
|
|
20
|
+
"src/shared/embedding.ts"() {
|
|
21
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
22
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
23
|
+
DEFAULT_OLLAMA_URL = "http://localhost:11434";
|
|
24
|
+
EmbeddingHelper = class {
|
|
25
|
+
static {
|
|
26
|
+
__name(this, "EmbeddingHelper");
|
|
27
|
+
}
|
|
28
|
+
ollamaBaseUrl;
|
|
29
|
+
constructor(ollamaUrl) {
|
|
30
|
+
this.ollamaBaseUrl = ollamaUrl ?? process.env.OLLAMA_URL ?? DEFAULT_OLLAMA_URL;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate embedding vector from text via Ollama (real vectors, no mocks).
|
|
34
|
+
* Requires Ollama running locally with the target model pulled.
|
|
35
|
+
* Throws clearly if Ollama is unavailable — no silent fallbacks.
|
|
36
|
+
*/
|
|
37
|
+
async generateEmbedding(text, model = "locusai/all-minilm-l6-v2") {
|
|
38
|
+
const url = `${this.ollamaBaseUrl}/api/embeddings`;
|
|
39
|
+
const payload = {
|
|
40
|
+
model,
|
|
41
|
+
prompt: text
|
|
42
|
+
};
|
|
43
|
+
let response;
|
|
44
|
+
try {
|
|
45
|
+
response = await fetch(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json"
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(payload)
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
throw new Error(`Cannot connect to Ollama at ${this.ollamaBaseUrl}. Is Ollama running? Start with: ollama serve. Then pull the model: ollama pull ${model}. Original error: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const body = await response.text().catch(() => "");
|
|
57
|
+
throw new Error(`Ollama embeddings API error ${response.status} ${response.statusText} \u2014 model="${model}", url="${url}". ` + (body.includes("model not found") || response.status === 404 ? `Model not found. Pull it with: ollama pull ${model}` : body || "Unknown error"));
|
|
58
|
+
}
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
if (!Array.isArray(data.embedding) || data.embedding.length === 0) {
|
|
61
|
+
throw new Error(`Ollama returned empty embedding for model="${model}"`);
|
|
62
|
+
}
|
|
63
|
+
return data.embedding;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Compute cosine similarity between two vectors
|
|
67
|
+
*/
|
|
68
|
+
cosineSimilarity(a, b) {
|
|
69
|
+
if (a.length !== b.length) {
|
|
70
|
+
throw new Error("Vectors must have same length");
|
|
71
|
+
}
|
|
72
|
+
if (a.length === 0) {
|
|
73
|
+
throw new Error("Vectors must not be empty");
|
|
74
|
+
}
|
|
75
|
+
let dotProduct = 0;
|
|
76
|
+
let normA = 0;
|
|
77
|
+
let normB = 0;
|
|
78
|
+
for (let i = 0; i < a.length; i++) {
|
|
79
|
+
dotProduct += a[i] * b[i];
|
|
80
|
+
normA += a[i] * a[i];
|
|
81
|
+
normB += b[i] * b[i];
|
|
82
|
+
}
|
|
83
|
+
if (normA === 0 || normB === 0) {
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Perform similarity search across documents
|
|
90
|
+
* Returns topK most similar documents sorted by score descending
|
|
91
|
+
*/
|
|
92
|
+
async similaritySearch(query, documents, topK = 5, model = "locusai/all-minilm-l6-v2") {
|
|
93
|
+
if (documents.length === 0) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
if (topK <= 0) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const queryEmbedding = await this.generateEmbedding(query, model);
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const doc of documents) {
|
|
102
|
+
const docEmbedding = await this.generateEmbedding(doc.text, model);
|
|
103
|
+
const score = this.cosineSimilarity(queryEmbedding, docEmbedding);
|
|
104
|
+
results.push({
|
|
105
|
+
doc,
|
|
106
|
+
score
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
results.sort((a, b) => b.score - a.score);
|
|
110
|
+
const topResults = results.slice(0, Math.min(topK, results.length));
|
|
111
|
+
return topResults.map((r) => ({
|
|
112
|
+
id: r.doc.id,
|
|
113
|
+
text: r.doc.text,
|
|
114
|
+
score: r.score
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
EmbeddingHelper = _ts_decorate([
|
|
119
|
+
Injectable(),
|
|
120
|
+
_ts_metadata("design:type", Function),
|
|
121
|
+
_ts_metadata("design:paramtypes", [
|
|
122
|
+
String
|
|
123
|
+
])
|
|
124
|
+
], EmbeddingHelper);
|
|
125
|
+
generateEmbedding = /* @__PURE__ */ __name((...args) => new EmbeddingHelper().generateEmbedding(...args), "generateEmbedding");
|
|
126
|
+
cosineSimilarity = /* @__PURE__ */ __name((...args) => new EmbeddingHelper().cosineSimilarity(...args), "cosineSimilarity");
|
|
127
|
+
similaritySearch = /* @__PURE__ */ __name((...args) => new EmbeddingHelper().similaritySearch(...args), "similaritySearch");
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
EmbeddingHelper,
|
|
133
|
+
generateEmbedding,
|
|
134
|
+
cosineSimilarity,
|
|
135
|
+
similaritySearch,
|
|
136
|
+
init_embedding
|
|
137
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
__name,
|
|
32
|
+
__require,
|
|
33
|
+
__esm,
|
|
34
|
+
__export,
|
|
35
|
+
__toCommonJS
|
|
36
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
|
|
2
|
+
import {
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-D7ADMHK2.js";
|
|
5
|
+
|
|
6
|
+
// src/constants/coordinator.ts
|
|
7
|
+
var OFFICIAL_COORDINATOR_URL = "https://api.synapseia.network";
|
|
8
|
+
var OFFICIAL_COORDINATOR_WS_URL = "https://ws.synapseia.network";
|
|
9
|
+
function getCoordinatorUrl() {
|
|
10
|
+
const v = process.env.COORDINATOR_URL?.trim();
|
|
11
|
+
return v && v.length > 0 ? v : OFFICIAL_COORDINATOR_URL;
|
|
12
|
+
}
|
|
13
|
+
__name(getCoordinatorUrl, "getCoordinatorUrl");
|
|
14
|
+
function getCoordinatorWsUrl() {
|
|
15
|
+
const v = process.env.COORDINATOR_WS_URL?.trim();
|
|
16
|
+
return v && v.length > 0 ? v : OFFICIAL_COORDINATOR_WS_URL;
|
|
17
|
+
}
|
|
18
|
+
__name(getCoordinatorWsUrl, "getCoordinatorWsUrl");
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
getCoordinatorUrl,
|
|
22
|
+
getCoordinatorWsUrl
|
|
23
|
+
};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
|
|
2
|
+
import {
|
|
3
|
+
priceUsdFromEnv
|
|
4
|
+
} from "./chunk-NEHR6XY7.js";
|
|
5
|
+
import {
|
|
6
|
+
beginChatInference,
|
|
7
|
+
endChatInference
|
|
8
|
+
} from "./chunk-5ZAJBIAV.js";
|
|
9
|
+
import {
|
|
10
|
+
init_logger,
|
|
11
|
+
logger_default
|
|
12
|
+
} from "./chunk-V2L5SXTL.js";
|
|
13
|
+
import {
|
|
14
|
+
__name
|
|
15
|
+
} from "./chunk-D7ADMHK2.js";
|
|
16
|
+
|
|
17
|
+
// src/modules/inference/inference-server.ts
|
|
18
|
+
init_logger();
|
|
19
|
+
import { Injectable } from "@nestjs/common";
|
|
20
|
+
import * as http from "http";
|
|
21
|
+
import * as crypto from "crypto";
|
|
22
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
23
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
26
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
+
}
|
|
28
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
29
|
+
var serverStartTime;
|
|
30
|
+
function parseBody(req) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let body = "";
|
|
33
|
+
req.on("data", (chunk) => {
|
|
34
|
+
body += chunk.toString();
|
|
35
|
+
});
|
|
36
|
+
req.on("end", () => {
|
|
37
|
+
try {
|
|
38
|
+
resolve(body ? JSON.parse(body) : {});
|
|
39
|
+
} catch (e) {
|
|
40
|
+
reject(e);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
req.on("error", reject);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
__name(parseBody, "parseBody");
|
|
47
|
+
async function forwardToOllama(request) {
|
|
48
|
+
const ollamaRequest = {
|
|
49
|
+
model: request.model,
|
|
50
|
+
messages: request.messages,
|
|
51
|
+
stream: false
|
|
52
|
+
};
|
|
53
|
+
if (request.temperature !== void 0 || request.max_tokens !== void 0) {
|
|
54
|
+
ollamaRequest.options = {};
|
|
55
|
+
if (request.temperature !== void 0) {
|
|
56
|
+
ollamaRequest.options.temperature = request.temperature;
|
|
57
|
+
}
|
|
58
|
+
if (request.max_tokens !== void 0) {
|
|
59
|
+
ollamaRequest.options.num_predict = request.max_tokens;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const ollamaResponse = await fetch("http://localhost:11434/api/chat", {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json"
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify(ollamaRequest)
|
|
68
|
+
});
|
|
69
|
+
if (!ollamaResponse.ok) {
|
|
70
|
+
throw new Error(`Ollama API error: ${ollamaResponse.status} ${ollamaResponse.statusText}`);
|
|
71
|
+
}
|
|
72
|
+
return ollamaResponse.json();
|
|
73
|
+
}
|
|
74
|
+
__name(forwardToOllama, "forwardToOllama");
|
|
75
|
+
function transformToOpenAI(ollamaResponse, model) {
|
|
76
|
+
return {
|
|
77
|
+
id: `chatcmpl-${crypto.randomUUID()}`,
|
|
78
|
+
object: "chat.completion",
|
|
79
|
+
created: Math.floor(Date.now() / 1e3),
|
|
80
|
+
model,
|
|
81
|
+
choices: [
|
|
82
|
+
{
|
|
83
|
+
index: 0,
|
|
84
|
+
message: {
|
|
85
|
+
role: ollamaResponse.message.role,
|
|
86
|
+
content: ollamaResponse.message.content
|
|
87
|
+
},
|
|
88
|
+
finish_reason: "stop"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
__name(transformToOpenAI, "transformToOpenAI");
|
|
94
|
+
async function notifyCoordinatorInferenceRequest(coordinatorUrl, peerId) {
|
|
95
|
+
try {
|
|
96
|
+
await fetch(`${coordinatorUrl}/peers/${encodeURIComponent(peerId)}/inference-request`, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
__name(notifyCoordinatorInferenceRequest, "notifyCoordinatorInferenceRequest");
|
|
106
|
+
async function handleChatCompletions(req, res, peerId, coordinatorUrl) {
|
|
107
|
+
try {
|
|
108
|
+
const body = await parseBody(req);
|
|
109
|
+
if (!body.model || !body.messages || !Array.isArray(body.messages) || body.messages.length === 0) {
|
|
110
|
+
res.writeHead(400, {
|
|
111
|
+
"Content-Type": "application/json"
|
|
112
|
+
});
|
|
113
|
+
res.end(JSON.stringify({
|
|
114
|
+
error: {
|
|
115
|
+
message: "Invalid request: model and messages are required",
|
|
116
|
+
type: "invalid_request_error"
|
|
117
|
+
}
|
|
118
|
+
}));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
beginChatInference();
|
|
122
|
+
let ollamaResponse;
|
|
123
|
+
try {
|
|
124
|
+
ollamaResponse = await forwardToOllama(body);
|
|
125
|
+
} finally {
|
|
126
|
+
endChatInference();
|
|
127
|
+
}
|
|
128
|
+
const openaiResponse = transformToOpenAI(ollamaResponse, body.model);
|
|
129
|
+
res.writeHead(200, {
|
|
130
|
+
"Content-Type": "application/json"
|
|
131
|
+
});
|
|
132
|
+
res.end(JSON.stringify(openaiResponse));
|
|
133
|
+
if (coordinatorUrl) {
|
|
134
|
+
void notifyCoordinatorInferenceRequest(coordinatorUrl, peerId);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
res.writeHead(500, {
|
|
138
|
+
"Content-Type": "application/json"
|
|
139
|
+
});
|
|
140
|
+
res.end(JSON.stringify({
|
|
141
|
+
error: {
|
|
142
|
+
message: error.message || "Internal server error",
|
|
143
|
+
type: "server_error"
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
__name(handleChatCompletions, "handleChatCompletions");
|
|
149
|
+
async function handleState(req, res, config) {
|
|
150
|
+
const uptime = process.uptime();
|
|
151
|
+
res.writeHead(200, {
|
|
152
|
+
"Content-Type": "application/json"
|
|
153
|
+
});
|
|
154
|
+
res.end(JSON.stringify({
|
|
155
|
+
peerId: config.peerId,
|
|
156
|
+
hardwareClass: config.hardwareClass,
|
|
157
|
+
models: config.models,
|
|
158
|
+
uptime: Math.floor(uptime)
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
__name(handleState, "handleState");
|
|
162
|
+
async function handleHealth(req, res) {
|
|
163
|
+
const uptime = process.uptime();
|
|
164
|
+
res.writeHead(200, {
|
|
165
|
+
"Content-Type": "application/json"
|
|
166
|
+
});
|
|
167
|
+
res.end(JSON.stringify({
|
|
168
|
+
status: "ok",
|
|
169
|
+
uptime: Math.floor(uptime)
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
__name(handleHealth, "handleHealth");
|
|
173
|
+
async function handleQuote(req, res) {
|
|
174
|
+
try {
|
|
175
|
+
const body = await parseBody(req);
|
|
176
|
+
const query = typeof body?.query === "string" ? body.query : "";
|
|
177
|
+
const priceUsd = priceUsdFromEnv(query);
|
|
178
|
+
res.writeHead(200, {
|
|
179
|
+
"Content-Type": "application/json"
|
|
180
|
+
});
|
|
181
|
+
res.end(JSON.stringify({
|
|
182
|
+
priceUsd
|
|
183
|
+
}));
|
|
184
|
+
} catch {
|
|
185
|
+
res.writeHead(200, {
|
|
186
|
+
"Content-Type": "application/json"
|
|
187
|
+
});
|
|
188
|
+
res.end(JSON.stringify({
|
|
189
|
+
priceUsd: priceUsdFromEnv("")
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
__name(handleQuote, "handleQuote");
|
|
194
|
+
function handleNotFound(req, res) {
|
|
195
|
+
res.writeHead(404, {
|
|
196
|
+
"Content-Type": "application/json"
|
|
197
|
+
});
|
|
198
|
+
res.end(JSON.stringify({
|
|
199
|
+
error: {
|
|
200
|
+
message: "Not found",
|
|
201
|
+
type: "not_found_error"
|
|
202
|
+
}
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
__name(handleNotFound, "handleNotFound");
|
|
206
|
+
function startInferenceServer(config) {
|
|
207
|
+
serverStartTime = Date.now();
|
|
208
|
+
const port = config.port !== void 0 && config.port !== null ? config.port : 8080;
|
|
209
|
+
const server = http.createServer(async (req, res) => {
|
|
210
|
+
const { applyLocalCors } = await import("./local-cors-J6RWNMMD.js");
|
|
211
|
+
if (applyLocalCors(req, res)) return;
|
|
212
|
+
const url = req.url || "";
|
|
213
|
+
try {
|
|
214
|
+
if (req.method === "POST" && url === "/v1/chat/completions") {
|
|
215
|
+
await handleChatCompletions(req, res, config.peerId, config.coordinatorUrl);
|
|
216
|
+
} else if (req.method === "POST" && url === "/inference/quote") {
|
|
217
|
+
await handleQuote(req, res);
|
|
218
|
+
} else if (req.method === "GET" && url === "/api/v1/state") {
|
|
219
|
+
await handleState(req, res, config);
|
|
220
|
+
} else if (req.method === "GET" && url === "/health") {
|
|
221
|
+
await handleHealth(req, res);
|
|
222
|
+
} else {
|
|
223
|
+
handleNotFound(req, res);
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
logger_default.error(`[InferenceServer] request error: ${error.message}`);
|
|
227
|
+
res.writeHead(500, {
|
|
228
|
+
"Content-Type": "application/json"
|
|
229
|
+
});
|
|
230
|
+
res.end(JSON.stringify({
|
|
231
|
+
error: {
|
|
232
|
+
message: "Internal server error",
|
|
233
|
+
type: "server_error"
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
server.listen(port, () => {
|
|
239
|
+
logger_default.log(`[InferenceServer] listening on port ${port} (POST /v1/chat/completions, POST /inference/quote, GET /api/v1/state, GET /health)`);
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
server,
|
|
243
|
+
close: /* @__PURE__ */ __name(() => {
|
|
244
|
+
server.close();
|
|
245
|
+
logger_default.log("[InferenceServer] closed");
|
|
246
|
+
}, "close")
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
__name(startInferenceServer, "startInferenceServer");
|
|
250
|
+
var InferenceServerHelper = class {
|
|
251
|
+
static {
|
|
252
|
+
__name(this, "InferenceServerHelper");
|
|
253
|
+
}
|
|
254
|
+
parseBody(req) {
|
|
255
|
+
return parseBody(req);
|
|
256
|
+
}
|
|
257
|
+
forwardToOllama(request) {
|
|
258
|
+
return forwardToOllama(request);
|
|
259
|
+
}
|
|
260
|
+
transformToOpenAI(ollamaResponse, model) {
|
|
261
|
+
return transformToOpenAI(ollamaResponse, model);
|
|
262
|
+
}
|
|
263
|
+
handleChatCompletions(req, res, peerId, coordinatorUrl) {
|
|
264
|
+
return handleChatCompletions(req, res, peerId, coordinatorUrl);
|
|
265
|
+
}
|
|
266
|
+
handleState(req, res, config) {
|
|
267
|
+
return handleState(req, res, config);
|
|
268
|
+
}
|
|
269
|
+
handleHealth(req, res) {
|
|
270
|
+
return handleHealth(req, res);
|
|
271
|
+
}
|
|
272
|
+
startInferenceServer(config) {
|
|
273
|
+
return startInferenceServer(config);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
InferenceServerHelper = _ts_decorate([
|
|
277
|
+
Injectable()
|
|
278
|
+
], InferenceServerHelper);
|
|
279
|
+
|
|
280
|
+
export {
|
|
281
|
+
parseBody,
|
|
282
|
+
forwardToOllama,
|
|
283
|
+
transformToOpenAI,
|
|
284
|
+
handleChatCompletions,
|
|
285
|
+
handleState,
|
|
286
|
+
handleHealth,
|
|
287
|
+
startInferenceServer,
|
|
288
|
+
InferenceServerHelper
|
|
289
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { fileURLToPath as __synFup } from "url";import { dirname as __synDn } from "path";const __filename = __synFup(import.meta.url);const __dirname = __synDn(__filename);
|
|
2
|
+
import {
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-D7ADMHK2.js";
|
|
5
|
+
|
|
6
|
+
// src/modules/inference/QueryCostCalculator.ts
|
|
7
|
+
var DEFAULT_MIN = 0.1;
|
|
8
|
+
var DEFAULT_MAX = 1;
|
|
9
|
+
var BIOMEDICAL_TERMS = [
|
|
10
|
+
"als",
|
|
11
|
+
"amyotrophic",
|
|
12
|
+
"alzheimer",
|
|
13
|
+
"parkinson",
|
|
14
|
+
"huntington",
|
|
15
|
+
"ms",
|
|
16
|
+
"multiple sclerosis",
|
|
17
|
+
"oncology",
|
|
18
|
+
"tumor",
|
|
19
|
+
"cancer",
|
|
20
|
+
"carcinoma",
|
|
21
|
+
"leukemia",
|
|
22
|
+
"lymphoma",
|
|
23
|
+
"glioma",
|
|
24
|
+
"cardiovascular",
|
|
25
|
+
"hypertension",
|
|
26
|
+
"diabetes",
|
|
27
|
+
"autoimmune",
|
|
28
|
+
"c9orf72",
|
|
29
|
+
"sod1",
|
|
30
|
+
"tardbp",
|
|
31
|
+
"fus",
|
|
32
|
+
"apoe",
|
|
33
|
+
"brca",
|
|
34
|
+
"tp53",
|
|
35
|
+
"kras",
|
|
36
|
+
"mrna",
|
|
37
|
+
"rna",
|
|
38
|
+
"dna",
|
|
39
|
+
"protein",
|
|
40
|
+
"enzyme",
|
|
41
|
+
"receptor",
|
|
42
|
+
"mutation",
|
|
43
|
+
"gene",
|
|
44
|
+
"genotype",
|
|
45
|
+
"phenotype",
|
|
46
|
+
"biomarker",
|
|
47
|
+
"pathway",
|
|
48
|
+
"mitochondri",
|
|
49
|
+
"microglia",
|
|
50
|
+
"riluzole",
|
|
51
|
+
"edaravone",
|
|
52
|
+
"pembrolizumab",
|
|
53
|
+
"inhibitor",
|
|
54
|
+
"antagonist",
|
|
55
|
+
"agonist",
|
|
56
|
+
"rna-seq",
|
|
57
|
+
"crispr",
|
|
58
|
+
"immunotherapy",
|
|
59
|
+
"chemotherapy",
|
|
60
|
+
"randomized",
|
|
61
|
+
"placebo",
|
|
62
|
+
"double-blind",
|
|
63
|
+
"meta-analysis",
|
|
64
|
+
"systematic review",
|
|
65
|
+
"clinical trial",
|
|
66
|
+
"phase ii",
|
|
67
|
+
"phase iii",
|
|
68
|
+
"in vitro",
|
|
69
|
+
"in vivo",
|
|
70
|
+
"mesh",
|
|
71
|
+
"rxnorm",
|
|
72
|
+
"hgnc",
|
|
73
|
+
"umls",
|
|
74
|
+
"doi",
|
|
75
|
+
"pubmed"
|
|
76
|
+
];
|
|
77
|
+
function computeQueryPriceUsd(query, config) {
|
|
78
|
+
const min = Number.isFinite(config.minPriceUsd) ? config.minPriceUsd : DEFAULT_MIN;
|
|
79
|
+
const max = Number.isFinite(config.maxPriceUsd) ? config.maxPriceUsd : DEFAULT_MAX;
|
|
80
|
+
if (max <= min) return min;
|
|
81
|
+
const text = (query ?? "").trim();
|
|
82
|
+
if (text.length === 0) return min;
|
|
83
|
+
const lower = text.toLowerCase();
|
|
84
|
+
const words = text.split(/\s+/).filter(Boolean).length;
|
|
85
|
+
const lengthScore = Math.min(words / 20, 1);
|
|
86
|
+
let techTerms = 0;
|
|
87
|
+
for (const term of BIOMEDICAL_TERMS) {
|
|
88
|
+
if (lower.includes(term)) techTerms++;
|
|
89
|
+
}
|
|
90
|
+
const techScore = Math.min(techTerms / 5, 1);
|
|
91
|
+
const hasCodeBlock = /```|\bcode\b|\bscript\b|\bsql\b|\bjson\b/.test(lower) ? 0.3 : 0;
|
|
92
|
+
const hasCitation = /\bcit(e|ation)\b|\breference(s)?\b|\bsource(s)?\b|\bdoi\b/.test(lower) ? 0.3 : 0;
|
|
93
|
+
const raw = lengthScore + techScore + hasCodeBlock + hasCitation;
|
|
94
|
+
const complexity = Math.min(raw / 2.6, 1);
|
|
95
|
+
return Number((min + (max - min) * complexity).toFixed(6));
|
|
96
|
+
}
|
|
97
|
+
__name(computeQueryPriceUsd, "computeQueryPriceUsd");
|
|
98
|
+
function priceUsdFromEnv(query) {
|
|
99
|
+
const minPriceUsd = parseFloat(process.env.QUERY_MIN_PRICE ?? String(DEFAULT_MIN));
|
|
100
|
+
const maxPriceUsd = parseFloat(process.env.QUERY_MAX_PRICE ?? String(DEFAULT_MAX));
|
|
101
|
+
return computeQueryPriceUsd(query, {
|
|
102
|
+
minPriceUsd,
|
|
103
|
+
maxPriceUsd
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
__name(priceUsdFromEnv, "priceUsdFromEnv");
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
computeQueryPriceUsd,
|
|
110
|
+
priceUsdFromEnv
|
|
111
|
+
};
|