dhti-cli 0.1.0 → 0.1.1
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/dist/commands/compose.d.ts +14 -0
- package/dist/commands/compose.js +112 -0
- package/dist/commands/conch.d.ts +19 -0
- package/dist/commands/conch.js +119 -0
- package/dist/commands/docker.d.ts +16 -0
- package/dist/commands/docker.js +76 -0
- package/dist/commands/elixir.d.ts +20 -0
- package/dist/commands/elixir.js +129 -0
- package/dist/commands/mimic.d.ts +9 -0
- package/dist/commands/mimic.js +156 -0
- package/dist/commands/synthetic.d.ts +17 -0
- package/dist/commands/synthetic.js +88 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/utils/bootstrap.d.ts +3 -0
- package/dist/utils/bootstrap.js +57 -0
- package/dist/utils/card.d.ts +58 -0
- package/dist/utils/card.js +69 -0
- package/dist/utils/chain.d.ts +5 -0
- package/dist/utils/chain.js +17 -0
- package/dist/utils/getCard.d.ts +3 -0
- package/dist/utils/getCard.js +11 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/request.d.ts +30 -0
- package/dist/utils/request.js +34 -0
- package/dist/utils/useDhti.d.ts +5 -0
- package/dist/utils/useDhti.js +21 -0
- package/oclif.manifest.json +442 -2
- package/package.json +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
export default class Mimic extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
server: Args.string({ default: 'http://localhost/fhir/$import', description: 'Server URL to submit' }), // object with input, instruction (rationale in distillation), output
|
|
5
|
+
};
|
|
6
|
+
static description = 'Submit a FHIR request to a server';
|
|
7
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
8
|
+
async run() {
|
|
9
|
+
const { args, flags } = await this.parse(Mimic);
|
|
10
|
+
const mimic_request = `{
|
|
11
|
+
|
|
12
|
+
"resourceType": "Parameters",
|
|
13
|
+
|
|
14
|
+
"parameter": [ {
|
|
15
|
+
|
|
16
|
+
"name": "inputFormat",
|
|
17
|
+
|
|
18
|
+
"valueCode": "application/fhir+ndjson"
|
|
19
|
+
|
|
20
|
+
}, {
|
|
21
|
+
|
|
22
|
+
"name": "inputSource",
|
|
23
|
+
|
|
24
|
+
"valueUri": "http://example.com/fhir/"
|
|
25
|
+
|
|
26
|
+
}, {
|
|
27
|
+
|
|
28
|
+
"name": "storageDetail",
|
|
29
|
+
|
|
30
|
+
"part": [ {
|
|
31
|
+
|
|
32
|
+
"name": "type",
|
|
33
|
+
|
|
34
|
+
"valueCode": "https"
|
|
35
|
+
|
|
36
|
+
}, {
|
|
37
|
+
|
|
38
|
+
"name": "credentialHttpBasic",
|
|
39
|
+
|
|
40
|
+
"valueString": "admin:password"
|
|
41
|
+
|
|
42
|
+
}, {
|
|
43
|
+
|
|
44
|
+
"name": "maxBatchResourceCount",
|
|
45
|
+
|
|
46
|
+
"valueString": "500"
|
|
47
|
+
|
|
48
|
+
} ]
|
|
49
|
+
|
|
50
|
+
}, {
|
|
51
|
+
|
|
52
|
+
"name": "input",
|
|
53
|
+
|
|
54
|
+
"part": [ {
|
|
55
|
+
|
|
56
|
+
"name": "type",
|
|
57
|
+
|
|
58
|
+
"valueCode": "Observation"
|
|
59
|
+
|
|
60
|
+
}, {
|
|
61
|
+
|
|
62
|
+
"name": "url",
|
|
63
|
+
|
|
64
|
+
"valueUri": "https://physionet.org/files/mimic-iv-fhir-demo/2.0/mimic-fhir/ObservationLabevents.ndjson"
|
|
65
|
+
|
|
66
|
+
} ]
|
|
67
|
+
|
|
68
|
+
}, {
|
|
69
|
+
|
|
70
|
+
"name": "input",
|
|
71
|
+
|
|
72
|
+
"part": [ {
|
|
73
|
+
|
|
74
|
+
"name": "type",
|
|
75
|
+
|
|
76
|
+
"valueCode": "Medication"
|
|
77
|
+
|
|
78
|
+
}, {
|
|
79
|
+
|
|
80
|
+
"name": "url",
|
|
81
|
+
|
|
82
|
+
"valueUri": "https://physionet.org/files/mimic-iv-fhir-demo/2.0/mimic-fhir/Medication.ndjson"
|
|
83
|
+
|
|
84
|
+
} ]
|
|
85
|
+
|
|
86
|
+
}, {
|
|
87
|
+
|
|
88
|
+
"name": "input",
|
|
89
|
+
|
|
90
|
+
"part": [ {
|
|
91
|
+
|
|
92
|
+
"name": "type",
|
|
93
|
+
|
|
94
|
+
"valueCode": "Procedure"
|
|
95
|
+
|
|
96
|
+
}, {
|
|
97
|
+
|
|
98
|
+
"name": "url",
|
|
99
|
+
|
|
100
|
+
"valueUri": "https://physionet.org/files/mimic-iv-fhir-demo/2.0/mimic-fhir/Procedure.ndjson"
|
|
101
|
+
|
|
102
|
+
} ]
|
|
103
|
+
|
|
104
|
+
}, {
|
|
105
|
+
|
|
106
|
+
"name": "input",
|
|
107
|
+
|
|
108
|
+
"part": [ {
|
|
109
|
+
|
|
110
|
+
"name": "type",
|
|
111
|
+
|
|
112
|
+
"valueCode": "Condition"
|
|
113
|
+
|
|
114
|
+
}, {
|
|
115
|
+
|
|
116
|
+
"name": "url",
|
|
117
|
+
|
|
118
|
+
"valueUri": "https://physionet.org/files/mimic-iv-fhir-demo/2.0/mimic-fhir/Condition.ndjson"
|
|
119
|
+
|
|
120
|
+
} ]
|
|
121
|
+
|
|
122
|
+
}, {
|
|
123
|
+
|
|
124
|
+
"name": "input",
|
|
125
|
+
|
|
126
|
+
"part": [ {
|
|
127
|
+
|
|
128
|
+
"name": "type",
|
|
129
|
+
|
|
130
|
+
"valueCode": "Patient"
|
|
131
|
+
|
|
132
|
+
}, {
|
|
133
|
+
|
|
134
|
+
"name": "url",
|
|
135
|
+
|
|
136
|
+
"valueUri": "https://physionet.org/files/mimic-iv-fhir-demo/2.0/mimic-fhir/Patient.ndjson"
|
|
137
|
+
|
|
138
|
+
} ]
|
|
139
|
+
|
|
140
|
+
} ]
|
|
141
|
+
|
|
142
|
+
}`;
|
|
143
|
+
// send a POST request to the server with the mimic_request body
|
|
144
|
+
const response = await fetch(args.server, {
|
|
145
|
+
body: mimic_request,
|
|
146
|
+
headers: {
|
|
147
|
+
'Content-Type': 'application/fhir+json',
|
|
148
|
+
'Prefer': 'respond-async'
|
|
149
|
+
},
|
|
150
|
+
method: 'POST',
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
console.error(`Error: ${response.status} ${response.statusText}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Synthetic extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
input: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
output: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
6
|
+
prompt: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
static flags: {
|
|
11
|
+
inputField: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
maxCycles: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
maxRecords: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
outputField: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import bootstrap from '../utils/bootstrap.js';
|
|
4
|
+
import { ChainService } from '../utils/chain.js';
|
|
5
|
+
export default class Synthetic extends Command {
|
|
6
|
+
static args = {
|
|
7
|
+
input: Args.string({ default: "", description: 'Input file to process' }), // object with input, instruction (rationale in distillation), output
|
|
8
|
+
output: Args.string({ description: 'Output file to write' }),
|
|
9
|
+
prompt: Args.string({ default: "", description: 'Prompt file to read' }),
|
|
10
|
+
};
|
|
11
|
+
static description = 'Generate synthetic data using LLM';
|
|
12
|
+
static examples = [
|
|
13
|
+
'<%= config.bin %> <%= command.id %>',
|
|
14
|
+
];
|
|
15
|
+
static flags = {
|
|
16
|
+
inputField: Flags.string({ char: 'i', default: 'input', description: 'Input field to use', options: ['input', 'instruction', 'output'] }),
|
|
17
|
+
maxCycles: Flags.integer({ char: 'm', default: 0, description: 'Maximum number of cycles to run' }),
|
|
18
|
+
maxRecords: Flags.integer({ char: 'r', default: 10, description: 'Maximum number of records to generate' }),
|
|
19
|
+
outputField: Flags.string({ char: 'o', default: 'output', description: 'Output field to use', options: ['input', 'instruction', 'output'] }),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { args, flags } = await this.parse(Synthetic);
|
|
23
|
+
let prompt = "";
|
|
24
|
+
// read prompt file if provided
|
|
25
|
+
if (args.prompt)
|
|
26
|
+
prompt = fs.readFileSync(args.prompt ?? '', 'utf8');
|
|
27
|
+
const container = await bootstrap();
|
|
28
|
+
const chain = new ChainService(container);
|
|
29
|
+
// if no output file, exit with error
|
|
30
|
+
if (!args.output) {
|
|
31
|
+
console.log("Please provide an output file");
|
|
32
|
+
this.exit(1);
|
|
33
|
+
}
|
|
34
|
+
if (flags.maxCycles) { // No input file, can process in batches
|
|
35
|
+
const input = {
|
|
36
|
+
input: prompt,
|
|
37
|
+
};
|
|
38
|
+
let responses = [];
|
|
39
|
+
for (let i = 0; i < flags.maxCycles; i++) {
|
|
40
|
+
const response = await chain.Chain(input);
|
|
41
|
+
let cycle = [];
|
|
42
|
+
const jsonArrayMatch = response.match(/\[[^\]]*]/);
|
|
43
|
+
if (jsonArrayMatch) {
|
|
44
|
+
try {
|
|
45
|
+
cycle = JSON.parse(jsonArrayMatch[0]);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error('Failed to parse JSON array from response:', error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
responses = responses.concat(cycle);
|
|
52
|
+
console.log(`Iteration ${i + 1}: Collected ${responses.length} records so far, ${flags.maxRecords - responses.length} to go`);
|
|
53
|
+
if (responses.length >= flags.maxRecords)
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
// convert to json
|
|
57
|
+
const jsonOutput = [];
|
|
58
|
+
for (const response of responses) {
|
|
59
|
+
jsonOutput.push({
|
|
60
|
+
[flags.outputField]: response,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
fs.writeFileSync(args.output ?? '', JSON.stringify(jsonOutput, null, 4));
|
|
64
|
+
console.log(`${args.output} has been created with ${flags.maxRecords} records`);
|
|
65
|
+
}
|
|
66
|
+
else { // Input file, process one by one
|
|
67
|
+
// read input file
|
|
68
|
+
const input = JSON.parse(fs.readFileSync(args.input ?? '', 'utf8'));
|
|
69
|
+
const responses = [];
|
|
70
|
+
// for each record in input file
|
|
71
|
+
for (const [i, record] of input.entries()) {
|
|
72
|
+
const chainInput = {
|
|
73
|
+
input: record[flags.inputField],
|
|
74
|
+
prompt,
|
|
75
|
+
};
|
|
76
|
+
const response = await chain.Chain(chainInput);
|
|
77
|
+
record[flags.outputField] = response;
|
|
78
|
+
responses.push(record);
|
|
79
|
+
if (responses.length >= flags.maxRecords)
|
|
80
|
+
break;
|
|
81
|
+
console.log(`Processed ${i + 1} records so far, ${flags.maxRecords - responses.length} to go`);
|
|
82
|
+
}
|
|
83
|
+
// write to output file
|
|
84
|
+
fs.writeFileSync(args.output ?? '', JSON.stringify(responses, null, 4));
|
|
85
|
+
console.log(`${args.output} has been created with ${responses.length} records`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ChatPromptTemplate } from "@langchain/core/prompts";
|
|
2
|
+
import { Ollama } from "@langchain/ollama";
|
|
3
|
+
import "reflect-metadata";
|
|
4
|
+
import { container } from "tsyringe";
|
|
5
|
+
// import { VertexAI } from "@langchain/google-vertexai";
|
|
6
|
+
// import { GoogleVertexAIEmbeddings } from "@langchain/community/embeddings/googlevertexai";
|
|
7
|
+
import { FakeListChatModel } from "@langchain/core/utils/testing";
|
|
8
|
+
const bootstrap = async () => {
|
|
9
|
+
let main_llm = null;
|
|
10
|
+
// try{
|
|
11
|
+
// const vertex = new VertexAI({
|
|
12
|
+
// temperature: 0.6,
|
|
13
|
+
// maxOutputTokens: 256,
|
|
14
|
+
// model: "gemini-pro",
|
|
15
|
+
// })
|
|
16
|
+
// main_llm = vertex;
|
|
17
|
+
// } catch (error) {
|
|
18
|
+
const ollama = new Ollama({
|
|
19
|
+
baseUrl: process.env.NEXT_PUBLIC_OLLAMA_URL || "http://localhost:11434",
|
|
20
|
+
model: process.env.NEXT_PUBLIC_OLLAMA_MODEL || "phi3:mini",
|
|
21
|
+
numPredict: 128,
|
|
22
|
+
temperature: 0.6,
|
|
23
|
+
});
|
|
24
|
+
const fake_llm = new FakeListChatModel({
|
|
25
|
+
responses: ["I'll callback later.", "You 'console' them!"],
|
|
26
|
+
});
|
|
27
|
+
main_llm = fake_llm;
|
|
28
|
+
// }
|
|
29
|
+
// const main_llm = new OllamaFunctions({
|
|
30
|
+
// temperature: 0.6,
|
|
31
|
+
// model: "phi3:mini",
|
|
32
|
+
// numPredict: 32,
|
|
33
|
+
// });
|
|
34
|
+
const gen_prompt = ChatPromptTemplate.fromMessages([
|
|
35
|
+
[
|
|
36
|
+
"system",
|
|
37
|
+
"You are a medical doctor.",
|
|
38
|
+
],
|
|
39
|
+
["human", "{input}"],
|
|
40
|
+
]);
|
|
41
|
+
const instruct_prompt = ChatPromptTemplate.fromMessages([
|
|
42
|
+
[
|
|
43
|
+
"system",
|
|
44
|
+
"You are a HL7 FHIR expert.",
|
|
45
|
+
],
|
|
46
|
+
["human", "{prompt} {input}"],
|
|
47
|
+
]);
|
|
48
|
+
const prompt = instruct_prompt;
|
|
49
|
+
container.register("main-llm", {
|
|
50
|
+
useValue: main_llm,
|
|
51
|
+
});
|
|
52
|
+
container.register("prompt", {
|
|
53
|
+
useValue: prompt,
|
|
54
|
+
});
|
|
55
|
+
return container;
|
|
56
|
+
};
|
|
57
|
+
export default bootstrap;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript models for CDS Hook Card
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* {
|
|
6
|
+
* "summary": "Patient is at high risk for opioid overdose.",
|
|
7
|
+
* "detail": "According to CDC guidelines, the patient's opioid dosage should be tapered to less than 50 MME. [Link to CDC Guideline](https://www.cdc.gov/drugoverdose/prescribing/guidelines.html)",
|
|
8
|
+
* "indicator": "warning",
|
|
9
|
+
* "source": {
|
|
10
|
+
* "label": "CDC Opioid Prescribing Guidelines",
|
|
11
|
+
* "url": "https://www.cdc.gov/drugoverdose/prescribing/guidelines.html",
|
|
12
|
+
* "icon": "https://example.org/img/cdc-icon.png"
|
|
13
|
+
* },
|
|
14
|
+
* "links": [
|
|
15
|
+
* {
|
|
16
|
+
* "label": "View MME Conversion Table",
|
|
17
|
+
* "url": "https://www.cdc.gov/drugoverdose/prescribing/mme.html"
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The allowed indicators for a CDS Hook Card.
|
|
24
|
+
* Mirrors: Literal["info", "warning", "hard-stop"]
|
|
25
|
+
*/
|
|
26
|
+
export type CDSHookCardIndicator = 'hard-stop' | 'info' | 'warning';
|
|
27
|
+
/**
|
|
28
|
+
* Source of the CDS Hook Card
|
|
29
|
+
*/
|
|
30
|
+
export declare class CDSHookCardSource {
|
|
31
|
+
icon?: string;
|
|
32
|
+
label: string;
|
|
33
|
+
url?: string;
|
|
34
|
+
constructor(init?: Partial<CDSHookCardSource>);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Link associated with the CDS Hook Card
|
|
38
|
+
*/
|
|
39
|
+
export declare class CDSHookCardLink {
|
|
40
|
+
label: string;
|
|
41
|
+
url: string;
|
|
42
|
+
constructor(init?: Partial<CDSHookCardLink>);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* CDS Hook Card Model
|
|
46
|
+
*/
|
|
47
|
+
export declare class CDSHookCard {
|
|
48
|
+
detail?: string;
|
|
49
|
+
indicator?: CDSHookCardIndicator;
|
|
50
|
+
links?: CDSHookCardLink[];
|
|
51
|
+
source?: CDSHookCardSource;
|
|
52
|
+
summary: string;
|
|
53
|
+
constructor(init?: Partial<CDSHookCard>);
|
|
54
|
+
/**
|
|
55
|
+
* Factory to build a CDSHookCard from a plain object, ensuring nested types are instantiated.
|
|
56
|
+
*/
|
|
57
|
+
static from(obj: Partial<CDSHookCard>): CDSHookCard;
|
|
58
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript models for CDS Hook Card
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* {
|
|
6
|
+
* "summary": "Patient is at high risk for opioid overdose.",
|
|
7
|
+
* "detail": "According to CDC guidelines, the patient's opioid dosage should be tapered to less than 50 MME. [Link to CDC Guideline](https://www.cdc.gov/drugoverdose/prescribing/guidelines.html)",
|
|
8
|
+
* "indicator": "warning",
|
|
9
|
+
* "source": {
|
|
10
|
+
* "label": "CDC Opioid Prescribing Guidelines",
|
|
11
|
+
* "url": "https://www.cdc.gov/drugoverdose/prescribing/guidelines.html",
|
|
12
|
+
* "icon": "https://example.org/img/cdc-icon.png"
|
|
13
|
+
* },
|
|
14
|
+
* "links": [
|
|
15
|
+
* {
|
|
16
|
+
* "label": "View MME Conversion Table",
|
|
17
|
+
* "url": "https://www.cdc.gov/drugoverdose/prescribing/mme.html"
|
|
18
|
+
* }
|
|
19
|
+
* ]
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Source of the CDS Hook Card
|
|
24
|
+
*/
|
|
25
|
+
export class CDSHookCardSource {
|
|
26
|
+
icon;
|
|
27
|
+
label;
|
|
28
|
+
url;
|
|
29
|
+
constructor(init) {
|
|
30
|
+
Object.assign(this, init);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Link associated with the CDS Hook Card
|
|
35
|
+
*/
|
|
36
|
+
export class CDSHookCardLink {
|
|
37
|
+
label;
|
|
38
|
+
url;
|
|
39
|
+
constructor(init) {
|
|
40
|
+
Object.assign(this, init);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* CDS Hook Card Model
|
|
45
|
+
*/
|
|
46
|
+
export class CDSHookCard {
|
|
47
|
+
detail;
|
|
48
|
+
indicator;
|
|
49
|
+
links;
|
|
50
|
+
source;
|
|
51
|
+
summary;
|
|
52
|
+
constructor(init) {
|
|
53
|
+
if (init) {
|
|
54
|
+
// Shallow assign for primitives; nested objects handled below if present
|
|
55
|
+
const { links, source, ...rest } = init;
|
|
56
|
+
Object.assign(this, rest);
|
|
57
|
+
if (source)
|
|
58
|
+
this.source = new CDSHookCardSource(source);
|
|
59
|
+
if (links)
|
|
60
|
+
this.links = links.map((l) => new CDSHookCardLink(l));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Factory to build a CDSHookCard from a plain object, ensuring nested types are instantiated.
|
|
65
|
+
*/
|
|
66
|
+
static from(obj) {
|
|
67
|
+
return new CDSHookCard(obj);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { StringOutputParser } from "@langchain/core/output_parsers";
|
|
2
|
+
import { RunnablePassthrough, RunnableSequence } from "@langchain/core/runnables";
|
|
3
|
+
export class ChainService {
|
|
4
|
+
container;
|
|
5
|
+
constructor(container) {
|
|
6
|
+
this.container = container;
|
|
7
|
+
}
|
|
8
|
+
async Chain(input) {
|
|
9
|
+
const output = RunnableSequence.from([
|
|
10
|
+
new RunnablePassthrough(),
|
|
11
|
+
this.container.resolve("prompt"),
|
|
12
|
+
this.container.resolve("main-llm"),
|
|
13
|
+
new StringOutputParser(),
|
|
14
|
+
]);
|
|
15
|
+
return output.invoke(input);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CDSHookCard } from './card.js';
|
|
2
|
+
const cards = (response) => {
|
|
3
|
+
const _cards = response?.data?.cards;
|
|
4
|
+
if (Array.isArray(_cards) && _cards.length > 0) {
|
|
5
|
+
const lastCard = _cards.at(-1);
|
|
6
|
+
const card = new CDSHookCard(lastCard);
|
|
7
|
+
return [card];
|
|
8
|
+
}
|
|
9
|
+
return [new CDSHookCard()];
|
|
10
|
+
};
|
|
11
|
+
export default cards;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDS Hook Request Model (TypeScript)
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* {
|
|
6
|
+
* "hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
|
|
7
|
+
* "fhirServer": "https://example.com/fhir",
|
|
8
|
+
* "fhirAuthorization": { ... },
|
|
9
|
+
* "hook": "patient-view",
|
|
10
|
+
* "context": { ... },
|
|
11
|
+
* "prefetch": { ... }
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
export declare class CDSHookRequest {
|
|
15
|
+
/** Context object passed by the EHR */
|
|
16
|
+
context?: Record<string, any> | null;
|
|
17
|
+
/** Authorization details (opaque to this model) */
|
|
18
|
+
fhirAuthorization?: Record<string, any> | null;
|
|
19
|
+
/** Base URL of the FHIR server associated with the hook */
|
|
20
|
+
fhirServer?: string;
|
|
21
|
+
/** Name of the hook (e.g., "patient-view", "order-select", etc.) */
|
|
22
|
+
hook?: string;
|
|
23
|
+
/** A unique identifier for this hook invocation */
|
|
24
|
+
hookInstance?: string;
|
|
25
|
+
/** Prefetched FHIR resources keyed by name */
|
|
26
|
+
prefetch?: Record<string, any> | null;
|
|
27
|
+
constructor(init?: Partial<CDSHookRequest>);
|
|
28
|
+
/** Factory to build a CDSHookRequest from a plain object. */
|
|
29
|
+
static from(obj: Partial<CDSHookRequest>): CDSHookRequest;
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDS Hook Request Model (TypeScript)
|
|
3
|
+
*
|
|
4
|
+
* Example:
|
|
5
|
+
* {
|
|
6
|
+
* "hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea",
|
|
7
|
+
* "fhirServer": "https://example.com/fhir",
|
|
8
|
+
* "fhirAuthorization": { ... },
|
|
9
|
+
* "hook": "patient-view",
|
|
10
|
+
* "context": { ... },
|
|
11
|
+
* "prefetch": { ... }
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
export class CDSHookRequest {
|
|
15
|
+
/** Context object passed by the EHR */
|
|
16
|
+
context;
|
|
17
|
+
/** Authorization details (opaque to this model) */
|
|
18
|
+
fhirAuthorization;
|
|
19
|
+
/** Base URL of the FHIR server associated with the hook */
|
|
20
|
+
fhirServer;
|
|
21
|
+
/** Name of the hook (e.g., "patient-view", "order-select", etc.) */
|
|
22
|
+
hook;
|
|
23
|
+
/** A unique identifier for this hook invocation */
|
|
24
|
+
hookInstance;
|
|
25
|
+
/** Prefetched FHIR resources keyed by name */
|
|
26
|
+
prefetch;
|
|
27
|
+
constructor(init) {
|
|
28
|
+
Object.assign(this, init);
|
|
29
|
+
}
|
|
30
|
+
/** Factory to build a CDSHookRequest from a plain object. */
|
|
31
|
+
static from(obj) {
|
|
32
|
+
return new CDSHookRequest(obj);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { CDSHookRequest } from './request.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sends a CDS Hook Request where the request.context contains the user's input message.
|
|
5
|
+
*/
|
|
6
|
+
const handleBundle = (newMessage) => {
|
|
7
|
+
const request = new CDSHookRequest({
|
|
8
|
+
context: { input: newMessage },
|
|
9
|
+
});
|
|
10
|
+
// TODO: Investigate why nested input is required
|
|
11
|
+
const _request = {
|
|
12
|
+
input: request,
|
|
13
|
+
};
|
|
14
|
+
const endpoint = process.env.LANGSERVE_POST_ENDPOINT || '/langserve/dhti_elixir_template/invoke';
|
|
15
|
+
return axios.post(endpoint, {
|
|
16
|
+
config: {},
|
|
17
|
+
input: _request,
|
|
18
|
+
kwargs: {},
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
export default handleBundle;
|