@sjcrh/proteinpaint-server 2.184.0 → 2.184.1-0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-server",
3
- "version": "2.184.0",
3
+ "version": "2.184.1-0",
4
4
  "type": "module",
5
5
  "description": "a genomics visualization tool for exploring a cohort's genotype and phenotype data",
6
6
  "main": "src/app.js",
@@ -66,7 +66,7 @@
66
66
  "@sjcrh/proteinpaint-r": "2.181.0",
67
67
  "@sjcrh/proteinpaint-rust": "2.183.0",
68
68
  "@sjcrh/proteinpaint-shared": "2.183.0",
69
- "@sjcrh/proteinpaint-types": "2.184.0",
69
+ "@sjcrh/proteinpaint-types": "2.184.1-0",
70
70
  "@types/express": "^5.0.0",
71
71
  "@types/express-session": "^1.18.1",
72
72
  "better-sqlite3": "^12.4.1",
@@ -0,0 +1,191 @@
1
+ import { ChatPayload } from "#types/checkers";
2
+ import { mayLog } from "#src/helpers.ts";
3
+ import { formatElapsedTime } from "#shared";
4
+ import { readJSONFile, parse_geneset_db } from "./chat/utils.ts";
5
+ import { classifyQuery } from "./chat/classify1.ts";
6
+ import { classifyPlotType } from "./chat/plot.ts";
7
+ import { classifyNotPlot } from "./chat/classify2.ts";
8
+ import { inferScaffold } from "./chat/scaffold.ts";
9
+ import serverconfig from "../src/serverconfig.js";
10
+ import { getDsAllowedTermTypes } from "./termdb.config.ts";
11
+ import { phrase2entity } from "./chat/phrase2entity.ts";
12
+ import { inferTermObjFromEntity } from "./chat/entity2termObj.ts";
13
+ import { resolveToTwTvs } from "./chat/entity2twTvs.ts";
14
+ import path from "path";
15
+ import fs from "fs";
16
+ import { resolveToPlotState } from "./chat/scaffold2state.ts";
17
+ const api = {
18
+ endpoint: "termdb/chat3",
19
+ methods: {
20
+ get: {
21
+ ...ChatPayload,
22
+ init
23
+ },
24
+ post: {
25
+ ...ChatPayload,
26
+ init
27
+ }
28
+ }
29
+ };
30
+ function init({ genomes }) {
31
+ return async (req, res) => {
32
+ const q = req.query;
33
+ try {
34
+ const g = genomes[q.genome];
35
+ if (!g) throw "invalid genome";
36
+ const ds = g.datasets?.[q.dslabel];
37
+ if (!ds) throw "invalid dslabel";
38
+ const aiFilesDir = serverconfig.binpath + "/../../dataset/ai/" + q.dslabel;
39
+ let agentFiles = [];
40
+ try {
41
+ agentFiles = await fs.readdirSync(aiFilesDir).filter((file) => file.endsWith(".json"));
42
+ } catch (err) {
43
+ if (err.code === "ENOENT") throw new Error(`Directory not found: ${aiFilesDir}`);
44
+ if (err.code === "ENOTDIR") throw new Error(`Path is not a directory: ${aiFilesDir}`);
45
+ throw err;
46
+ }
47
+ const llm = serverconfig.llm;
48
+ if (!llm) throw "serverconfig.llm is not configured";
49
+ if (llm.provider !== "SJ" && llm.provider !== "ollama" && llm.provider !== "huggingface" && llm.provider !== "azure") {
50
+ throw "llm.provider must be 'SJ', 'ollama', 'huggingface', or 'azure'";
51
+ }
52
+ const rawFilter = typeof q.filter === "string" ? JSON.parse(q.filter) : q.filter;
53
+ const filter = rawFilter && typeof rawFilter === "object" ? rawFilter : {};
54
+ const lst = Array.isArray(filter.lst) ? filter.lst : [];
55
+ const cohortFilter = lst.find((item) => item.tag === "cohortFilter");
56
+ const cohortKey = cohortFilter ? cohortFilter.tvs.values[0].key : "";
57
+ const supportedChartTypes = ds.cohort.termdb.q?.getSupportedChartTypes(req)?.[cohortKey];
58
+ const genedb = serverconfig.tpmasterdir + "/" + g.genedb.dbfile;
59
+ const _allowedTermTypes = getDsAllowedTermTypes(ds);
60
+ const ai_output_json = await run_chat_pipeline(
61
+ q.prompt,
62
+ llm,
63
+ ds,
64
+ genedb,
65
+ agentFiles,
66
+ aiFilesDir,
67
+ supportedChartTypes,
68
+ _allowedTermTypes
69
+ // testing
70
+ );
71
+ mayLog("From init: Final AI output JSON:", JSON.stringify(ai_output_json));
72
+ res.send(ai_output_json);
73
+ } catch (e) {
74
+ if (e.stack) mayLog(e.stack);
75
+ res.send({ error: e?.message || e });
76
+ }
77
+ };
78
+ }
79
+ async function run_chat_pipeline(user_prompt, llm, ds, genedb, agentFiles, aiFilesDir, supportedChartTypes, _allowedTermTypes) {
80
+ if (!fs.existsSync(path.join(aiFilesDir, "main.json")))
81
+ throw "Main data file is not specified for dataset:" + ds.label;
82
+ const dataset_json = await readJSONFile(path.join(aiFilesDir, "main.json"));
83
+ const time1 = (/* @__PURE__ */ new Date()).valueOf();
84
+ const class_response = await classifyQuery(user_prompt, llm);
85
+ mayLog("Time taken for classification:", formatElapsedTime(Date.now() - time1));
86
+ let ai_output_json;
87
+ if (class_response.type == "notplot") {
88
+ const time2 = (/* @__PURE__ */ new Date()).valueOf();
89
+ const notPlotResult = await classifyNotPlot(user_prompt, llm, agentFiles, aiFilesDir);
90
+ mayLog("Time taken for classify2:", formatElapsedTime(Date.now() - time2));
91
+ if (notPlotResult.type == "html") {
92
+ ai_output_json = notPlotResult;
93
+ } else {
94
+ ai_output_json = {
95
+ type: "text",
96
+ text: "Your query does not appear to be related to the available data visualizations. Please try rephrasing your question."
97
+ };
98
+ }
99
+ } else if (class_response.type == "plot") {
100
+ let time = (/* @__PURE__ */ new Date()).valueOf();
101
+ const plotType = await classifyPlotType(user_prompt, llm);
102
+ mayLog("Time taken to classify plot type:", formatElapsedTime(Date.now() - time));
103
+ if (!supportedChartTypes) {
104
+ const errorMsg = "Supported chart types list is undefined. Please check the dataset configuration and ensure that getSupportedChartTypes is implemented correctly. Skipping chart type validation, but this may lead to unsupported chart type errors downstream.";
105
+ console.warn(errorMsg);
106
+ const errorResponse = {
107
+ type: "text",
108
+ text: errorMsg
109
+ };
110
+ return errorResponse;
111
+ }
112
+ if (plotType === "summary") {
113
+ if (!supportedChartTypes.includes("dictionary")) {
114
+ const log = 'Plot type: "' + plotType + '" is not supported.';
115
+ ai_output_json = {
116
+ type: "text",
117
+ text: log
118
+ };
119
+ mayLog(log);
120
+ return ai_output_json;
121
+ }
122
+ } else if (plotType === "dge") {
123
+ if (!supportedChartTypes.includes("DA")) {
124
+ const log = 'Plot type: "' + plotType + '" is not supported.';
125
+ ai_output_json = {
126
+ type: "text",
127
+ text: log
128
+ };
129
+ mayLog(log);
130
+ return ai_output_json;
131
+ }
132
+ } else {
133
+ mayLog(`Supported chart types for this cohort: ${supportedChartTypes}`);
134
+ if (!supportedChartTypes.includes(plotType)) {
135
+ const log = 'Plot type: "' + plotType + '" is not supported.';
136
+ ai_output_json = {
137
+ type: "text",
138
+ text: log
139
+ };
140
+ mayLog(log);
141
+ return ai_output_json;
142
+ }
143
+ }
144
+ mayLog("####### First phase: Infer Plot Scaffolds #######");
145
+ time = (/* @__PURE__ */ new Date()).valueOf();
146
+ const scaffoldResult = await inferScaffold(user_prompt, plotType, llm);
147
+ mayLog("ScaffoldResult: ", scaffoldResult);
148
+ mayLog("Time taken to infer scaffold:", formatElapsedTime(Date.now() - time));
149
+ if (!scaffoldResult)
150
+ throw "Scaffold result is empty or undefined, which is unexpected. Please check the inferScaffold agent for potential issues.";
151
+ const subplotType = scaffoldResult.plotType === "summary" ? scaffoldResult.chartType : void 0;
152
+ mayLog("####### Second phase: From Scaffolds's phrases infer Entities #######");
153
+ const genes_list = await parse_geneset_db(genedb);
154
+ time = (/* @__PURE__ */ new Date()).valueOf();
155
+ const phrase2entityResult = await phrase2entity(scaffoldResult, plotType, llm, genes_list, dataset_json, ds);
156
+ mayLog("Time taken to phrase 2 entity:", formatElapsedTime(Date.now() - time));
157
+ if ("type" in phrase2entityResult && phrase2entityResult.type === "text") {
158
+ return phrase2entityResult;
159
+ }
160
+ mayLog(phrase2entityResult);
161
+ mayLog("####### Third phase: From Entities infer Term Objects #######");
162
+ const dataset_db = serverconfig.tpmasterdir + "/" + ds.cohort.db.file;
163
+ time = (/* @__PURE__ */ new Date()).valueOf();
164
+ const termObj = await inferTermObjFromEntity(
165
+ phrase2entityResult,
166
+ plotType,
167
+ llm,
168
+ dataset_db,
169
+ genes_list
170
+ );
171
+ mayLog("Time taken to infer term objects:", formatElapsedTime(Date.now() - time));
172
+ mayLog("Inferred termObj from entity:", JSON.stringify(termObj));
173
+ mayLog("####### Fourth phase: From Term Objects to TwTvs Objects #######");
174
+ time = (/* @__PURE__ */ new Date()).valueOf();
175
+ const twTvsObj = await resolveToTwTvs(termObj, plotType, llm, dataset_db);
176
+ mayLog("Time taken to resolve to TwTvs object from termObj:", formatElapsedTime(Date.now() - time));
177
+ if ("type" in twTvsObj && twTvsObj.type === "text") {
178
+ return twTvsObj;
179
+ }
180
+ mayLog("twTvsObj:", twTvsObj);
181
+ mayLog("####### Fifth/Final phase: From TwTvs Objects to Plot States #######");
182
+ time = (/* @__PURE__ */ new Date()).valueOf();
183
+ ai_output_json = resolveToPlotState(twTvsObj, plotType, subplotType);
184
+ mayLog("Time taken to resolve to plot state:", formatElapsedTime(Date.now() - time));
185
+ }
186
+ return ai_output_json;
187
+ }
188
+ export {
189
+ api,
190
+ run_chat_pipeline
191
+ };
@@ -47,7 +47,7 @@ function make(q, req, res, ds, genome) {
47
47
  selectCohort: getSelectCohort(ds, req),
48
48
  supportedChartTypes: tdb.q?.getSupportedChartTypes(req),
49
49
  renamedChartTypes: ds.cohort.renamedChartTypes,
50
- allowedTermTypes: getAllowedTermTypes(ds),
50
+ allowedTermTypes: getDsAllowedTermTypes(ds),
51
51
  massSessionDuration: serverconfig.features.massSessionDuration || 30,
52
52
  dataDownloadCatch: tdb.dataDownloadCatch,
53
53
  matrix: tdb.matrix,
@@ -293,7 +293,7 @@ function addNonDictionaryQueries(c, ds, genome) {
293
293
  q2.images = {};
294
294
  }
295
295
  }
296
- function getAllowedTermTypes(ds) {
296
+ function getDsAllowedTermTypes(ds) {
297
297
  const typeSet = /* @__PURE__ */ new Set();
298
298
  for (const r of ds.cohort.termdb.termtypeByCohort) {
299
299
  if (r.termType) typeSet.add(r.termType);
@@ -329,5 +329,6 @@ function getSelectCohort(ds, req) {
329
329
  return copy;
330
330
  }
331
331
  export {
332
- api
332
+ api,
333
+ getDsAllowedTermTypes
333
334
  };