dhti-cli 0.3.3 → 0.4.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/README.md +12 -9
- package/dist/commands/compose.d.ts +1 -0
- package/dist/commands/compose.js +54 -12
- package/dist/commands/conch.d.ts +1 -0
- package/dist/commands/conch.js +75 -9
- package/dist/commands/docker.d.ts +2 -1
- package/dist/commands/docker.js +50 -10
- package/dist/commands/elixir.d.ts +1 -0
- package/dist/commands/elixir.js +85 -26
- package/dist/commands/mimic.d.ts +3 -0
- package/dist/commands/mimic.js +17 -2
- package/dist/commands/synthetic.d.ts +1 -0
- package/dist/commands/synthetic.js +29 -2
- package/oclif.manifest.json +48 -11
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The essence of DHTI is *modularity* with an emphasis on *configuration!* It is n
|
|
|
31
31
|
<img src="https://github.com/dermatologist/openmrs-esm-dhti-template/blob/develop/notes/conch.jpg" />
|
|
32
32
|
</p>
|
|
33
33
|
|
|
34
|
-
*[OpenMRS ESM DHTI template](https://github.com/dermatologist/openmrs-esm-dhti-template) + [DHTI elixir template](https://github.com/dermatologist/dhti-elixir-template) together forms a simple but functional EMR chatbot too!* 👉 [Try it out today!](#try-it-out)
|
|
34
|
+
*[OpenMRS ESM DHTI template](https://github.com/dermatologist/openmrs-esm-dhti-template) (frontend) + [DHTI elixir template](https://github.com/dermatologist/dhti-elixir-template) (backend) together forms a simple but functional EMR chatbot too!* 👉 [Try it out today!](#try-it-out)
|
|
35
35
|
|
|
36
36
|
<p align="center">
|
|
37
37
|
<img src="https://github.com/dermatologist/dhti/blob/develop/notes/cds-hook-sandbox.jpg" />
|
|
@@ -66,6 +66,7 @@ The essence of DHTI is *modularity* with an emphasis on *configuration!* It is n
|
|
|
66
66
|
* **Quick prototyping**: CLI helps in quick prototyping and testing of Gen AI routines and UI elements.
|
|
67
67
|
* **Easy to use**: Can be installed in a few minutes.
|
|
68
68
|
* **Developer friendly**: Copy working files to running containers for testing.
|
|
69
|
+
* **Dry-run mode**: Preview changes before execution with the `--dry-run` flag.
|
|
69
70
|
* **Dependency Injection**: Dependency injection for models and hyperparameters for configuring elixirs.
|
|
70
71
|
* **Generate synthetic data**: DHTI supports generating synthetic data for testing.
|
|
71
72
|
* **CQL support**: [CQL for clinical decision support](https://nuchange.ca/2025/06/v-llm-in-the-loop-cql-execution-with-unstructured-data-and-fhir-terminology-support.html).
|
|
@@ -113,8 +114,8 @@ Tools to fine-tune language models for the stack are on our roadmap. We encourag
|
|
|
113
114
|
|
|
114
115
|
## :sparkles: Resources (in Beta)
|
|
115
116
|
* [dhti-elixir-base](https://github.com/dermatologist/dhti-elixir-base): Base classes for dhti-elixir
|
|
116
|
-
* [dhti-elixir-template](https://github.com/dermatologist/dhti-elixir-template): A template for creating new dhti-elixirs
|
|
117
|
-
* [openmrs-esm-dhti-template](https://github.com/dermatologist/openmrs-esm-dhti-template): A conch template for OpenMRS
|
|
117
|
+
* [dhti-elixir-template](https://github.com/dermatologist/dhti-elixir-template): A template for creating new dhti-elixirs & a **simple EMR chatbot backend**.
|
|
118
|
+
* [openmrs-esm-dhti-template](https://github.com/dermatologist/openmrs-esm-dhti-template): A conch template for OpenMRS & a **simple EMR chatbot frontend**.
|
|
118
119
|
* [fhir-mcp-server](https://github.com/dermatologist/fhir-mcp-server): A MCP server for hosting FHIR-compliant tools.
|
|
119
120
|
|
|
120
121
|
## :sparkles: Resources (in Alpha)
|
|
@@ -123,10 +124,8 @@ Tools to fine-tune language models for the stack are on our roadmap. We encourag
|
|
|
123
124
|
|
|
124
125
|
## :sunglasses: Coming soon
|
|
125
126
|
|
|
126
|
-
* [dhti-elixir-fhire](https://github.com/dermatologist/dhti-elixir-fhire): An elixir for
|
|
127
|
-
* [dhti-elixir-fhirs](https://github.com/dermatologist/dhti-elixir-fhirs): An elixir for text to FHIR search query conversion.
|
|
127
|
+
* [dhti-elixir-fhire](https://github.com/dermatologist/dhti-elixir-fhire): An elixir for FHIR embeddings.
|
|
128
128
|
* [dhti-elixir-upload](https://github.com/dermatologist/dhti-elixir-upload-file): Upload documents to the vector store for clinical knowledgebase and clinical trial matching.
|
|
129
|
-
* [openmrs-esm-qa](https://github.com/dermatologist/openmrs-esm-genai): A sample conch for Q&A on patient records using the dhti-elixir-fhire elixir.
|
|
130
129
|
|
|
131
130
|
## Try it out
|
|
132
131
|
|
|
@@ -136,7 +135,7 @@ Tools to fine-tune language models for the stack are on our roadmap. We encourag
|
|
|
136
135
|
|
|
137
136
|
* `npx dhti-cli compose add -m openmrs -m langserve` to add OpenMRS and Langserve elixirs to your docker-compose.yml at ~/dhti. Other available modules: `ollama, langfuse, cqlFhir, redis, neo4j and mcpFhir`. You can read the newly created docker-compose by: `npx dhti-cli compose read`
|
|
138
137
|
|
|
139
|
-
* `npx dhti-cli elixir install -g https://github.com/dermatologist/dhti-elixir-template.git -n dhti-elixir-template` to install a sample elixir from github. (
|
|
138
|
+
* `npx dhti-cli elixir install -g https://github.com/dermatologist/dhti-elixir-template.git -n dhti-elixir-template` to install a sample elixir from github. *(Optional)* You may configure the LLM and hyperparameters in `~/dhti/elixir/app/bootstrap.py`. You can install multiple elixirs.
|
|
140
139
|
|
|
141
140
|
* `npx dhti-cli docker -n yourdockerhandle/genai-test:1.0 -t elixir` to build a docker image for the elixir.
|
|
142
141
|
|
|
@@ -146,6 +145,10 @@ Tools to fine-tune language models for the stack are on our roadmap. We encourag
|
|
|
146
145
|
|
|
147
146
|
* `npx dhti-cli docker -u` to start all the docker images in your docker-compose.yml.
|
|
148
147
|
|
|
148
|
+
* *(Optional)* **🔍 Dry-run mode**: Add the `--dry-run` flag to any command to preview what changes will be made without actually executing them. For example:
|
|
149
|
+
- `npx dhti-cli compose add -m langserve --dry-run` to preview modules that would be added
|
|
150
|
+
- `npx dhti-cli elixir install -n test-elixir --dry-run` to see what files would be created/modified
|
|
151
|
+
|
|
149
152
|
### :clap: Access the Conch in OpenMRS and test the integration
|
|
150
153
|
|
|
151
154
|
* Go to `http://localhost/openmrs/spa/home`
|
|
@@ -161,9 +164,9 @@ You will see the text above the textbox.
|
|
|
161
164
|
|
|
162
165
|
* `npx dhti-cli docker -d` to stop and delete all the docker containers.
|
|
163
166
|
|
|
164
|
-
Read
|
|
167
|
+
Read [](https://github.com/dermatologist/dhti/wiki) for more details.
|
|
165
168
|
|
|
166
|
-
|
|
169
|
+
## 👋 The demo uses mock LLM. 👉 [Check out how to add real LLMs and configure them.](https://github.com/dermatologist/dhti/wiki/Configuration)
|
|
167
170
|
|
|
168
171
|
:hugs: **Thank you for trying out DHTI!**
|
|
169
172
|
|
|
@@ -6,6 +6,7 @@ export default class Compose extends Command {
|
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
module: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
package/dist/commands/compose.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import yaml from 'js-yaml';
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import os from 'node:os';
|
|
@@ -11,6 +12,10 @@ export default class Compose extends Command {
|
|
|
11
12
|
static description = 'Generates a docker-compose.yml file from a list of modules';
|
|
12
13
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
13
14
|
static flags = {
|
|
15
|
+
'dry-run': Flags.boolean({
|
|
16
|
+
default: false,
|
|
17
|
+
description: 'Show what changes would be made without actually making them',
|
|
18
|
+
}),
|
|
14
19
|
file: Flags.string({
|
|
15
20
|
char: 'f',
|
|
16
21
|
default: `${os.homedir()}/dhti/docker-compose.yml`,
|
|
@@ -71,6 +76,10 @@ export default class Compose extends Command {
|
|
|
71
76
|
if (fs.existsSync(flags.file)) {
|
|
72
77
|
existingData = yaml.load(fs.readFileSync(flags.file, 'utf8'));
|
|
73
78
|
}
|
|
79
|
+
else if (flags['dry-run']) {
|
|
80
|
+
console.log(chalk.yellow(`[DRY RUN] Would create directory: ${os.homedir()}/dhti`));
|
|
81
|
+
console.log(chalk.yellow(`[DRY RUN] Would create file: ${flags.file}`));
|
|
82
|
+
}
|
|
74
83
|
else {
|
|
75
84
|
Compose.init(); // Create the file if it does not exist
|
|
76
85
|
}
|
|
@@ -81,8 +90,14 @@ export default class Compose extends Command {
|
|
|
81
90
|
}
|
|
82
91
|
// Delete flags.file if args.op is reset
|
|
83
92
|
if (args.op === 'reset') {
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
if (flags['dry-run']) {
|
|
94
|
+
console.log(chalk.yellow(`[DRY RUN] Would delete file: ${flags.file}`));
|
|
95
|
+
console.log(chalk.yellow(`[DRY RUN] Would recreate file: ${flags.file}`));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
fs.unlinkSync(flags.file);
|
|
99
|
+
Compose.init(); // Recreate the file
|
|
100
|
+
}
|
|
86
101
|
}
|
|
87
102
|
// if existing data is not null and arg is delete, remove the modules from the existing data
|
|
88
103
|
if (Object.keys(existingData.services).length > 0 && args.op === 'delete') {
|
|
@@ -91,9 +106,19 @@ export default class Compose extends Command {
|
|
|
91
106
|
for (const module of flags.module ?? []) {
|
|
92
107
|
modulesToDelete = modulesToDelete.concat(_modules[module]);
|
|
93
108
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
if (flags['dry-run']) {
|
|
110
|
+
console.log(chalk.yellow('[DRY RUN] Would delete the following modules:'));
|
|
111
|
+
for (const module of modulesToDelete ?? []) {
|
|
112
|
+
if (existingData.services[module]) {
|
|
113
|
+
console.log(chalk.cyan(` - ${module}`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
for (const module of modulesToDelete ?? []) {
|
|
119
|
+
if (existingData.services[module]) {
|
|
120
|
+
delete existingData.services[module];
|
|
121
|
+
}
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
}
|
|
@@ -104,18 +129,35 @@ export default class Compose extends Command {
|
|
|
104
129
|
for (const module of flags.module ?? []) {
|
|
105
130
|
modulesToAdd = modulesToAdd.concat(_modules[module]);
|
|
106
131
|
}
|
|
107
|
-
|
|
108
|
-
|
|
132
|
+
if (flags['dry-run']) {
|
|
133
|
+
console.log(chalk.yellow('[DRY RUN] Would add the following modules:'));
|
|
134
|
+
for (const module of modulesToAdd ?? []) {
|
|
135
|
+
console.log(chalk.cyan(` - ${module}`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
for (const module of modulesToAdd ?? []) {
|
|
140
|
+
existingData.services[module] = masterData.services[module];
|
|
141
|
+
}
|
|
109
142
|
}
|
|
110
143
|
}
|
|
111
144
|
// Add all volumes from master data to existing data by default
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
if (!flags['dry-run']) {
|
|
146
|
+
existingData.volumes = {};
|
|
147
|
+
for (const key of Object.keys(masterData.volumes)) {
|
|
148
|
+
existingData.volumes[key] = masterData.volumes[key];
|
|
149
|
+
}
|
|
115
150
|
}
|
|
116
151
|
const toWrite = yaml.dump(existingData).replaceAll('null', '');
|
|
117
|
-
|
|
118
|
-
|
|
152
|
+
if (flags['dry-run']) {
|
|
153
|
+
console.log(chalk.yellow(`[DRY RUN] Would write to file: ${flags.file}`));
|
|
154
|
+
console.log(chalk.green('[DRY RUN] File content would be:'));
|
|
155
|
+
console.log(toWrite);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log('Writing file:', toWrite);
|
|
159
|
+
fs.writeFileSync(flags.file, toWrite, 'utf8');
|
|
160
|
+
}
|
|
119
161
|
}
|
|
120
162
|
catch (error) {
|
|
121
163
|
console.error(error);
|
package/dist/commands/conch.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export default class Conch extends Command {
|
|
|
9
9
|
branch: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
container: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
dev: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
git: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
image: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
package/dist/commands/conch.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { exec } from 'node:child_process';
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import os from 'node:os';
|
|
@@ -18,6 +19,10 @@ export default class Conch extends Command {
|
|
|
18
19
|
description: 'Name of the container to copy the conch to while in dev mode',
|
|
19
20
|
}),
|
|
20
21
|
dev: Flags.string({ char: 'd', default: 'none', description: 'Dev folder to install' }),
|
|
22
|
+
'dry-run': Flags.boolean({
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Show what changes would be made without actually making them',
|
|
25
|
+
}),
|
|
21
26
|
git: Flags.string({ char: 'g', default: 'none', description: 'Github repository to install' }),
|
|
22
27
|
image: Flags.string({
|
|
23
28
|
char: 'i',
|
|
@@ -46,9 +51,17 @@ export default class Conch extends Command {
|
|
|
46
51
|
// docker cp ../../openmrs-esm-genai/dist/. dhti-frontend-1:/usr/share/nginx/html/openmrs-esm-genai-1.0.0
|
|
47
52
|
// docker restart dhti-frontend-1
|
|
48
53
|
if (args.op === 'dev') {
|
|
49
|
-
|
|
54
|
+
const buildCommand = `cd ${flags.dev} && yarn build && docker cp dist/. ${flags.container}:/usr/share/nginx/html/${flags.name}-${flags.repoVersion}`;
|
|
55
|
+
const restartCommand = `docker restart ${flags.container}`;
|
|
56
|
+
if (flags['dry-run']) {
|
|
57
|
+
console.log(chalk.yellow('[DRY RUN] Would execute commands:'));
|
|
58
|
+
console.log(chalk.cyan(` ${buildCommand}`));
|
|
59
|
+
console.log(chalk.cyan(` ${restartCommand}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log(buildCommand);
|
|
50
63
|
try {
|
|
51
|
-
exec(
|
|
64
|
+
exec(buildCommand, (error, stdout, stderr) => {
|
|
52
65
|
if (error) {
|
|
53
66
|
console.error(`exec error: ${error}`);
|
|
54
67
|
return;
|
|
@@ -56,7 +69,7 @@ export default class Conch extends Command {
|
|
|
56
69
|
console.log(`stdout: ${stdout}`);
|
|
57
70
|
console.error(`stderr: ${stderr}`);
|
|
58
71
|
});
|
|
59
|
-
exec(
|
|
72
|
+
exec(restartCommand, (error, stdout, stderr) => {
|
|
60
73
|
if (error) {
|
|
61
74
|
console.error(`exec error: ${error}`);
|
|
62
75
|
return;
|
|
@@ -68,15 +81,53 @@ export default class Conch extends Command {
|
|
|
68
81
|
catch (error) {
|
|
69
82
|
console.log('Error copying conch to container', error);
|
|
70
83
|
}
|
|
84
|
+
return;
|
|
71
85
|
}
|
|
72
86
|
// Create a directory to install the elixir
|
|
73
87
|
if (!fs.existsSync(`${flags.workdir}/conch`)) {
|
|
74
|
-
|
|
88
|
+
if (flags['dry-run']) {
|
|
89
|
+
console.log(chalk.yellow(`[DRY RUN] Would create directory: ${flags.workdir}/conch`));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
fs.mkdirSync(`${flags.workdir}/conch`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (flags['dry-run']) {
|
|
96
|
+
console.log(chalk.yellow(`[DRY RUN] Would copy resources from ${RESOURCES_DIR}/spa to ${flags.workdir}/conch`));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
fs.cpSync(path.join(RESOURCES_DIR, 'spa'), `${flags.workdir}/conch`, { recursive: true });
|
|
75
100
|
}
|
|
76
|
-
fs.cpSync(path.join(RESOURCES_DIR, 'spa'), `${flags.workdir}/conch`, { recursive: true });
|
|
77
101
|
// Rewrite files
|
|
78
102
|
const rewrite = () => {
|
|
79
103
|
flags.name = flags.name ?? 'openmrs-esm-genai';
|
|
104
|
+
if (flags['dry-run']) {
|
|
105
|
+
console.log(chalk.yellow('[DRY RUN] Would update configuration files:'));
|
|
106
|
+
console.log(chalk.cyan(` - ${flags.workdir}/conch/def/importmap.json`));
|
|
107
|
+
if (args.op === 'install') {
|
|
108
|
+
console.log(chalk.green(` Add import: ${flags.name.replace('openmrs-', '@openmrs/')} -> ./${flags.name}-${flags.repoVersion}/${flags.name}.js`));
|
|
109
|
+
}
|
|
110
|
+
if (args.op === 'uninstall') {
|
|
111
|
+
console.log(chalk.green(` Remove import: ${flags.name.replace('openmrs-', '@openmrs/')}`));
|
|
112
|
+
}
|
|
113
|
+
console.log(chalk.cyan(` - ${flags.workdir}/conch/def/spa-assemble-config.json`));
|
|
114
|
+
if (args.op === 'install') {
|
|
115
|
+
console.log(chalk.green(` Add module: ${flags.name.replace('openmrs-', '@openmrs/')} = ${flags.repoVersion}`));
|
|
116
|
+
}
|
|
117
|
+
if (args.op === 'uninstall') {
|
|
118
|
+
console.log(chalk.green(` Remove module: ${flags.name.replace('openmrs-', '@openmrs/')}`));
|
|
119
|
+
}
|
|
120
|
+
console.log(chalk.cyan(` - ${flags.workdir}/conch/Dockerfile`));
|
|
121
|
+
console.log(chalk.green(` Update with conch=${flags.name}, version=${flags.repoVersion}, image=${flags.image}`));
|
|
122
|
+
console.log(chalk.cyan(` - ${flags.workdir}/conch/def/routes.registry.json`));
|
|
123
|
+
if (args.op === 'install') {
|
|
124
|
+
console.log(chalk.green(` Add routes for ${flags.name.replace('openmrs-', '@openmrs/')}`));
|
|
125
|
+
}
|
|
126
|
+
if (args.op === 'uninstall') {
|
|
127
|
+
console.log(chalk.green(` Remove routes for ${flags.name.replace('openmrs-', '@openmrs/')}`));
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
80
131
|
// Read and process importmap.json
|
|
81
132
|
const importmap = JSON.parse(fs.readFileSync(`${flags.workdir}/conch/def/importmap.json`, 'utf8'));
|
|
82
133
|
if (args.op === 'install')
|
|
@@ -109,14 +160,23 @@ export default class Conch extends Command {
|
|
|
109
160
|
fs.writeFileSync(`${flags.workdir}/conch/def/routes.registry.json`, JSON.stringify(registry, null, 2));
|
|
110
161
|
};
|
|
111
162
|
if (flags.git !== 'none') {
|
|
163
|
+
const cloneCommand = `git clone ${flags.git} ${flags.workdir}/conch/${flags.name}`;
|
|
164
|
+
const checkoutCommand = `cd ${flags.workdir}/conch/${flags.name} && git checkout ${flags.branch}`;
|
|
165
|
+
if (flags['dry-run']) {
|
|
166
|
+
console.log(chalk.yellow('[DRY RUN] Would execute git commands:'));
|
|
167
|
+
console.log(chalk.cyan(` ${cloneCommand}`));
|
|
168
|
+
console.log(chalk.cyan(` ${checkoutCommand}`));
|
|
169
|
+
rewrite();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
112
172
|
// git clone the repository
|
|
113
|
-
exec(
|
|
173
|
+
exec(cloneCommand, (error, stdout, stderr) => {
|
|
114
174
|
if (error) {
|
|
115
175
|
console.error(`exec error: ${error}`);
|
|
116
176
|
return;
|
|
117
177
|
}
|
|
118
178
|
// Checkout the branch
|
|
119
|
-
exec(
|
|
179
|
+
exec(checkoutCommand, (error, stdout, stderr) => {
|
|
120
180
|
if (error) {
|
|
121
181
|
console.error(`exec error: ${error}`);
|
|
122
182
|
return;
|
|
@@ -131,8 +191,14 @@ export default class Conch extends Command {
|
|
|
131
191
|
}
|
|
132
192
|
// If flags.dev is not none, copy the dev folder to the conch directory
|
|
133
193
|
if (flags.dev !== 'none' && args.op !== 'dev') {
|
|
134
|
-
|
|
135
|
-
|
|
194
|
+
if (flags['dry-run']) {
|
|
195
|
+
console.log(chalk.yellow(`[DRY RUN] Would copy ${flags.dev} to ${flags.workdir}/conch/${flags.name}`));
|
|
196
|
+
rewrite();
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
fs.cpSync(flags.dev, `${flags.workdir}/conch/${flags.name}`, { recursive: true });
|
|
200
|
+
rewrite();
|
|
201
|
+
}
|
|
136
202
|
}
|
|
137
203
|
}
|
|
138
204
|
}
|
|
@@ -6,9 +6,10 @@ export default class Docker extends Command {
|
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
+
container: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
down: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
12
|
file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
container: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
up: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
package/dist/commands/docker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import yaml from 'js-yaml';
|
|
3
4
|
import { exec } from 'node:child_process';
|
|
4
5
|
import fs from 'node:fs';
|
|
@@ -11,17 +12,21 @@ export default class Docker extends Command {
|
|
|
11
12
|
static description = 'Build a docker project and update docker-compose file';
|
|
12
13
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
13
14
|
static flags = {
|
|
15
|
+
container: Flags.string({
|
|
16
|
+
char: 'c',
|
|
17
|
+
default: 'dhti-langserve-1',
|
|
18
|
+
description: 'Name of the container to copy the bootstrap file to while in dev mode',
|
|
19
|
+
}),
|
|
14
20
|
down: Flags.boolean({ char: 'd', default: false, description: 'Run docker-compose down after building' }),
|
|
21
|
+
'dry-run': Flags.boolean({
|
|
22
|
+
default: false,
|
|
23
|
+
description: 'Show what changes would be made without actually making them',
|
|
24
|
+
}),
|
|
15
25
|
file: Flags.string({
|
|
16
26
|
char: 'f',
|
|
17
27
|
default: `${os.homedir()}/dhti/docker-compose.yml`,
|
|
18
28
|
description: 'Full path to the docker compose file to edit or run.',
|
|
19
29
|
}),
|
|
20
|
-
container: Flags.string({
|
|
21
|
-
char: 'c',
|
|
22
|
-
default: 'dhti-langserve-1',
|
|
23
|
-
description: 'Name of the container to copy the bootstrap file to while in dev mode',
|
|
24
|
-
}),
|
|
25
30
|
name: Flags.string({ char: 'n', description: 'Name of the container to build' }),
|
|
26
31
|
type: Flags.string({ char: 't', default: 'elixir', description: 'Type of the service (elixir/conch)' }),
|
|
27
32
|
up: Flags.boolean({ char: 'u', default: false, description: 'Run docker-compose up after building' }),
|
|
@@ -29,7 +34,13 @@ export default class Docker extends Command {
|
|
|
29
34
|
async run() {
|
|
30
35
|
const { args, flags } = await this.parse(Docker);
|
|
31
36
|
if (flags.up) {
|
|
32
|
-
|
|
37
|
+
const upCommand = `docker compose -f ${flags.file} up -d`;
|
|
38
|
+
if (flags['dry-run']) {
|
|
39
|
+
console.log(chalk.yellow('[DRY RUN] Would execute:'));
|
|
40
|
+
console.log(chalk.cyan(` ${upCommand}`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
exec(upCommand, (error, stdout, stderr) => {
|
|
33
44
|
if (error) {
|
|
34
45
|
console.error(`exec error: ${error}`);
|
|
35
46
|
return;
|
|
@@ -40,7 +51,13 @@ export default class Docker extends Command {
|
|
|
40
51
|
return;
|
|
41
52
|
}
|
|
42
53
|
if (flags.down) {
|
|
43
|
-
|
|
54
|
+
const downCommand = `docker compose -f ${flags.file} down`;
|
|
55
|
+
if (flags['dry-run']) {
|
|
56
|
+
console.log(chalk.yellow('[DRY RUN] Would execute:'));
|
|
57
|
+
console.log(chalk.cyan(` ${downCommand}`));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
exec(downCommand, (error, stdout, stderr) => {
|
|
44
61
|
if (error) {
|
|
45
62
|
console.error(`exec error: ${error}`);
|
|
46
63
|
return;
|
|
@@ -60,8 +77,16 @@ export default class Docker extends Command {
|
|
|
60
77
|
console.log('Please provide a valid path to bootstrap.py file');
|
|
61
78
|
this.exit(1);
|
|
62
79
|
}
|
|
80
|
+
const copyCommand = `docker cp ${flags.file} ${flags.container}:/app/app/bootstrap.py`;
|
|
81
|
+
const restartCommand = `docker restart ${flags.container}`;
|
|
82
|
+
if (flags['dry-run']) {
|
|
83
|
+
console.log(chalk.yellow('[DRY RUN] Would execute:'));
|
|
84
|
+
console.log(chalk.cyan(` ${copyCommand}`));
|
|
85
|
+
console.log(chalk.cyan(` ${restartCommand}`));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
63
88
|
// copy -f to container:/app/app/ and only restart after copy completes
|
|
64
|
-
exec(
|
|
89
|
+
exec(copyCommand, (error, stdout, stderr) => {
|
|
65
90
|
if (error) {
|
|
66
91
|
console.error(`exec error: ${error}`);
|
|
67
92
|
return;
|
|
@@ -69,7 +94,7 @@ export default class Docker extends Command {
|
|
|
69
94
|
console.log(`stdout: ${stdout}`);
|
|
70
95
|
console.error(`stderr: ${stderr}`);
|
|
71
96
|
// restart the container only after copy completes
|
|
72
|
-
exec(
|
|
97
|
+
exec(restartCommand, (restartError, restartStdout, restartStderr) => {
|
|
73
98
|
if (restartError) {
|
|
74
99
|
console.error(`exec error: ${restartError}`);
|
|
75
100
|
return;
|
|
@@ -81,8 +106,23 @@ export default class Docker extends Command {
|
|
|
81
106
|
return;
|
|
82
107
|
}
|
|
83
108
|
// cd to path, docker build tag with name
|
|
109
|
+
const buildCommand = `cd ${args.path}/${flags.type} && docker build -t ${flags.name} . > /dev/null 2>&1`;
|
|
110
|
+
if (flags['dry-run']) {
|
|
111
|
+
console.log(chalk.yellow('[DRY RUN] Would execute:'));
|
|
112
|
+
console.log(chalk.cyan(` ${buildCommand}`));
|
|
113
|
+
console.log(chalk.yellow(`[DRY RUN] Would update docker-compose file: ${flags.file}`));
|
|
114
|
+
if (flags.type === 'elixir') {
|
|
115
|
+
console.log(chalk.green(` Set langserve.image = ${flags.name}`));
|
|
116
|
+
console.log(chalk.green(` Set langserve.pull_policy = if_not_present`));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
console.log(chalk.green(` Set frontend.image = ${flags.name}`));
|
|
120
|
+
console.log(chalk.green(` Set frontend.pull_policy = if_not_present`));
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
84
124
|
const spinner = ora('Running docker build ..').start();
|
|
85
|
-
exec(
|
|
125
|
+
exec(buildCommand, (error, stdout, stderr) => {
|
|
86
126
|
if (error) {
|
|
87
127
|
spinner.fail('Docker build failed');
|
|
88
128
|
console.error(`exec error: ${error}`);
|
|
@@ -9,6 +9,7 @@ export default class Elixir extends Command {
|
|
|
9
9
|
branch: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
container: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
dev: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
git: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
pypi: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
package/dist/commands/elixir.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import { exec } from 'node:child_process';
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import os from 'node:os';
|
|
@@ -18,6 +19,10 @@ export default class Elixir extends Command {
|
|
|
18
19
|
description: 'Name of the container to copy the elixir to while in dev mode',
|
|
19
20
|
}),
|
|
20
21
|
dev: Flags.string({ char: 'd', default: 'none', description: 'Dev folder to install' }),
|
|
22
|
+
'dry-run': Flags.boolean({
|
|
23
|
+
default: false,
|
|
24
|
+
description: 'Show what changes would be made without actually making them',
|
|
25
|
+
}),
|
|
21
26
|
git: Flags.string({ char: 'g', default: 'none', description: 'Github repository to install' }),
|
|
22
27
|
name: Flags.string({ char: 'n', description: 'Name of the elixir' }),
|
|
23
28
|
pypi: Flags.string({
|
|
@@ -47,9 +52,17 @@ export default class Elixir extends Command {
|
|
|
47
52
|
// if arg is dev then copy to docker as below
|
|
48
53
|
// docker restart dhti-langserve-1
|
|
49
54
|
if (args.op === 'dev') {
|
|
50
|
-
|
|
55
|
+
const devCommand = `cd ${flags.dev} && docker cp src/${expoName}/. ${flags.container}:/app/.venv/lib/python3.12/site-packages/${expoName}`;
|
|
56
|
+
const restartCommand = `docker restart ${flags.container}`;
|
|
57
|
+
if (flags['dry-run']) {
|
|
58
|
+
console.log(chalk.yellow('[DRY RUN] Would execute commands:'));
|
|
59
|
+
console.log(chalk.cyan(` ${devCommand}`));
|
|
60
|
+
console.log(chalk.cyan(` ${restartCommand}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
console.log(devCommand);
|
|
51
64
|
try {
|
|
52
|
-
exec(
|
|
65
|
+
exec(devCommand, (error, stdout, stderr) => {
|
|
53
66
|
if (error) {
|
|
54
67
|
console.error(`exec error: ${error}`);
|
|
55
68
|
return;
|
|
@@ -57,7 +70,7 @@ export default class Elixir extends Command {
|
|
|
57
70
|
console.log(`stdout: ${stdout}`);
|
|
58
71
|
console.error(`stderr: ${stderr}`);
|
|
59
72
|
});
|
|
60
|
-
exec(
|
|
73
|
+
exec(restartCommand, (error, stdout, stderr) => {
|
|
61
74
|
if (error) {
|
|
62
75
|
console.error(`exec error: ${error}`);
|
|
63
76
|
return;
|
|
@@ -69,23 +82,45 @@ export default class Elixir extends Command {
|
|
|
69
82
|
catch (error) {
|
|
70
83
|
console.log('Error copying conch to container', error);
|
|
71
84
|
}
|
|
85
|
+
return;
|
|
72
86
|
}
|
|
73
87
|
// Create a directory to install the elixir
|
|
74
88
|
if (!fs.existsSync(`${flags.workdir}/elixir`)) {
|
|
75
|
-
|
|
89
|
+
if (flags['dry-run']) {
|
|
90
|
+
console.log(chalk.yellow(`[DRY RUN] Would create directory: ${flags.workdir}/elixir`));
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
fs.mkdirSync(`${flags.workdir}/elixir`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (flags['dry-run']) {
|
|
97
|
+
console.log(chalk.yellow(`[DRY RUN] Would copy resources from ${RESOURCES_DIR}/genai to ${flags.workdir}/elixir`));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
fs.cpSync(path.join(RESOURCES_DIR, 'genai'), `${flags.workdir}/elixir`, { recursive: true });
|
|
76
101
|
}
|
|
77
|
-
fs.cpSync(path.join(RESOURCES_DIR, 'genai'), `${flags.workdir}/elixir`, { recursive: true });
|
|
78
102
|
// if whl is not none, copy the whl file to thee whl directory
|
|
79
103
|
if (flags.whl !== 'none') {
|
|
80
104
|
if (!fs.existsSync(`${flags.workdir}/elixir/whl/`)) {
|
|
81
|
-
|
|
105
|
+
if (flags['dry-run']) {
|
|
106
|
+
console.log(chalk.yellow(`[DRY RUN] Would create directory: ${flags.workdir}/whl/`));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
fs.mkdirSync(`${flags.workdir}/whl/`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (flags['dry-run']) {
|
|
113
|
+
console.log(chalk.yellow(`[DRY RUN] Would copy ${flags.whl} to ${flags.workdir}/elixir/whl/${path.basename(flags.whl)}`));
|
|
114
|
+
console.log(chalk.cyan('[DRY RUN] Installing elixir from whl file. Please modify boostrap.py file if needed'));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
fs.cpSync(flags.whl, `${flags.workdir}/elixir/whl/${path.basename(flags.whl)}`);
|
|
118
|
+
console.log('Installing elixir from whl file. Please modify boostrap.py file if needed');
|
|
82
119
|
}
|
|
83
|
-
fs.cpSync(flags.whl, `${flags.workdir}/elixir/whl/${path.basename(flags.whl)}`);
|
|
84
|
-
console.log('Installing elixir from whl file. Please modify boostrap.py file if needed');
|
|
85
120
|
}
|
|
86
121
|
// Install the elixir from git adding to the pyproject.toml file
|
|
87
|
-
let pyproject = fs.readFileSync(`${flags.workdir}/elixir/pyproject.toml`, 'utf8');
|
|
88
|
-
const originalServer = fs.readFileSync(`${flags.workdir}/elixir/app/server.py`, 'utf8');
|
|
122
|
+
let pyproject = flags['dry-run'] ? '' : fs.readFileSync(`${flags.workdir}/elixir/pyproject.toml`, 'utf8');
|
|
123
|
+
const originalServer = flags['dry-run'] ? '' : fs.readFileSync(`${flags.workdir}/elixir/app/server.py`, 'utf8');
|
|
89
124
|
let lineToAdd = '';
|
|
90
125
|
if (flags.whl !== 'none') {
|
|
91
126
|
lineToAdd = `${flags.name} = { file = "whl/${path.basename(flags.whl)}" }`;
|
|
@@ -96,8 +131,10 @@ export default class Elixir extends Command {
|
|
|
96
131
|
if (flags.pypi !== 'none') {
|
|
97
132
|
lineToAdd = flags.pypi;
|
|
98
133
|
}
|
|
99
|
-
|
|
100
|
-
|
|
134
|
+
if (!flags['dry-run']) {
|
|
135
|
+
pyproject = pyproject.replace('dependencies = [', `dependencies = [\n"${flags.name}",`);
|
|
136
|
+
pyproject = pyproject.replace('[tool.uv.sources]', `[tool.uv.sources]\n${lineToAdd}\n`);
|
|
137
|
+
}
|
|
101
138
|
const newPyproject = pyproject;
|
|
102
139
|
// Add the elixir import and bootstrap to the server.py file
|
|
103
140
|
let CliImport = `from ${expoName}.bootstrap import bootstrap as ${expoName}_bootstrap\n`;
|
|
@@ -108,27 +145,49 @@ ${expoName}_chain = ${expoName}_chain_class().get_chain_as_langchain_tool()
|
|
|
108
145
|
${expoName}_mcp_tool = ${expoName}_chain_class().get_chain_as_mcp_tool
|
|
109
146
|
mcp_server.add_tool(${expoName}_mcp_tool) # type: ignore
|
|
110
147
|
`;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
148
|
+
let newCliImport = '';
|
|
149
|
+
if (!flags['dry-run']) {
|
|
150
|
+
newCliImport = fs
|
|
151
|
+
.readFileSync(`${flags.workdir}/elixir/app/server.py`, 'utf8')
|
|
152
|
+
.replace('# DHTI_CLI_IMPORT', `#DHTI_CLI_IMPORT\n${CliImport}`);
|
|
153
|
+
}
|
|
114
154
|
const langfuseRoute = `add_routes(app, ${expoName}_chain.with_config(config), path="/langserve/${expoName}")`;
|
|
115
|
-
const newLangfuseRoute = newCliImport.replace('# DHTI_LANGFUSE_ROUTE', `#DHTI_LANGFUSE_ROUTE\n ${langfuseRoute}`);
|
|
155
|
+
const newLangfuseRoute = flags['dry-run'] ? '' : newCliImport.replace('# DHTI_LANGFUSE_ROUTE', `#DHTI_LANGFUSE_ROUTE\n ${langfuseRoute}`);
|
|
116
156
|
const normalRoute = `add_routes(app, ${expoName}_chain, path="/langserve/${expoName}")`;
|
|
117
|
-
const newNormalRoute = newLangfuseRoute.replace('# DHTI_NORMAL_ROUTE', `#DHTI_NORMAL_ROUTE\n ${normalRoute}`);
|
|
157
|
+
const newNormalRoute = flags['dry-run'] ? '' : newLangfuseRoute.replace('# DHTI_NORMAL_ROUTE', `#DHTI_NORMAL_ROUTE\n ${normalRoute}`);
|
|
118
158
|
const commonRoutes = `\nadd_invokes(app, path="/langserve/${expoName}")\nadd_services(app, path="/langserve/${expoName}")`;
|
|
119
|
-
const finalRoute = newNormalRoute.replace('# DHTI_COMMON_ROUTE', `#DHTI_COMMON_ROUTES${commonRoutes}`);
|
|
159
|
+
const finalRoute = flags['dry-run'] ? '' : newNormalRoute.replace('# DHTI_COMMON_ROUTE', `#DHTI_COMMON_ROUTES${commonRoutes}`);
|
|
120
160
|
// if args.op === install, add the line to the pyproject.toml file
|
|
121
161
|
if (args.op === 'install') {
|
|
122
|
-
|
|
123
|
-
|
|
162
|
+
if (flags['dry-run']) {
|
|
163
|
+
console.log(chalk.yellow('[DRY RUN] Would update files:'));
|
|
164
|
+
console.log(chalk.cyan(` - ${flags.workdir}/elixir/pyproject.toml`));
|
|
165
|
+
console.log(chalk.green(` Add dependency: "${flags.name}"`));
|
|
166
|
+
console.log(chalk.green(` Add source: ${lineToAdd}`));
|
|
167
|
+
console.log(chalk.cyan(` - ${flags.workdir}/elixir/app/server.py`));
|
|
168
|
+
console.log(chalk.green(` Add import and routes for ${expoName}`));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
fs.writeFileSync(`${flags.workdir}/elixir/pyproject.toml`, newPyproject);
|
|
172
|
+
fs.writeFileSync(`${flags.workdir}/elixir/app/server.py`, finalRoute);
|
|
173
|
+
}
|
|
124
174
|
}
|
|
125
175
|
if (args.op === 'uninstall') {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
176
|
+
if (flags['dry-run']) {
|
|
177
|
+
console.log(chalk.yellow('[DRY RUN] Would update files:'));
|
|
178
|
+
console.log(chalk.cyan(` - ${flags.workdir}/elixir/pyproject.toml`));
|
|
179
|
+
console.log(chalk.green(` Remove source: ${lineToAdd}`));
|
|
180
|
+
console.log(chalk.cyan(` - ${flags.workdir}/elixir/app/server.py`));
|
|
181
|
+
console.log(chalk.green(` Remove import and routes for ${expoName}`));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// if args.op === uninstall, remove the line from the pyproject.toml file
|
|
185
|
+
fs.writeFileSync(`${flags.workdir}/elixir/pyproject.toml`, pyproject.replace(lineToAdd, ''));
|
|
186
|
+
let newServer = originalServer.replace(CliImport, '');
|
|
187
|
+
newServer = newServer.replace(langfuseRoute, '');
|
|
188
|
+
newServer = newServer.replace(normalRoute, '');
|
|
189
|
+
fs.writeFileSync(`${flags.workdir}/elixir/app/server.py`, newServer);
|
|
190
|
+
}
|
|
132
191
|
}
|
|
133
192
|
}
|
|
134
193
|
}
|
package/dist/commands/mimic.d.ts
CHANGED
package/dist/commands/mimic.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { Args, Command } from '@oclif/core';
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
export default class Mimic extends Command {
|
|
3
4
|
static args = {
|
|
4
5
|
server: Args.string({ default: 'http://localhost/fhir/$import', description: 'Server URL to submit' }), // object with input, instruction (rationale in distillation), output
|
|
5
6
|
};
|
|
6
7
|
static description = 'Submit a FHIR request to a server';
|
|
7
8
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
9
|
+
static flags = {
|
|
10
|
+
'dry-run': Flags.boolean({
|
|
11
|
+
default: false,
|
|
12
|
+
description: 'Show what changes would be made without actually making them',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
8
15
|
async run() {
|
|
9
16
|
const { args, flags } = await this.parse(Mimic);
|
|
10
17
|
const mimic_request = `{
|
|
@@ -140,6 +147,15 @@ export default class Mimic extends Command {
|
|
|
140
147
|
} ]
|
|
141
148
|
|
|
142
149
|
}`;
|
|
150
|
+
if (flags['dry-run']) {
|
|
151
|
+
console.log(chalk.yellow(`[DRY RUN] Would send POST request to: ${args.server}`));
|
|
152
|
+
console.log(chalk.cyan('[DRY RUN] Request headers:'));
|
|
153
|
+
console.log(chalk.green(' Content-Type: application/fhir+json'));
|
|
154
|
+
console.log(chalk.green(' Prefer: respond-async'));
|
|
155
|
+
console.log(chalk.cyan('[DRY RUN] Request body:'));
|
|
156
|
+
console.log(mimic_request);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
143
159
|
// send a POST request to the server with the mimic_request body
|
|
144
160
|
const response = await fetch(args.server, {
|
|
145
161
|
body: mimic_request,
|
|
@@ -151,7 +167,6 @@ export default class Mimic extends Command {
|
|
|
151
167
|
});
|
|
152
168
|
if (!response.ok) {
|
|
153
169
|
console.error(`Error: ${response.status} ${response.statusText}`);
|
|
154
|
-
return;
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
172
|
}
|
|
@@ -8,6 +8,7 @@ export default class Synthetic extends Command {
|
|
|
8
8
|
static description: string;
|
|
9
9
|
static examples: string[];
|
|
10
10
|
static flags: {
|
|
11
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
inputField: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
maxCycles: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
14
|
maxRecords: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import bootstrap from '../utils/bootstrap.js';
|
|
4
5
|
import { ChainService } from '../utils/chain.js';
|
|
@@ -13,6 +14,10 @@ export default class Synthetic extends Command {
|
|
|
13
14
|
'<%= config.bin %> <%= command.id %>',
|
|
14
15
|
];
|
|
15
16
|
static flags = {
|
|
17
|
+
'dry-run': Flags.boolean({
|
|
18
|
+
default: false,
|
|
19
|
+
description: 'Show what changes would be made without actually making them',
|
|
20
|
+
}),
|
|
16
21
|
inputField: Flags.string({ char: 'i', default: 'input', description: 'Input field to use', options: ['input', 'instruction', 'output'] }),
|
|
17
22
|
maxCycles: Flags.integer({ char: 'm', default: 0, description: 'Maximum number of cycles to run' }),
|
|
18
23
|
maxRecords: Flags.integer({ char: 'r', default: 10, description: 'Maximum number of records to generate' }),
|
|
@@ -24,13 +29,35 @@ export default class Synthetic extends Command {
|
|
|
24
29
|
// read prompt file if provided
|
|
25
30
|
if (args.prompt)
|
|
26
31
|
prompt = fs.readFileSync(args.prompt ?? '', 'utf8');
|
|
27
|
-
const container = await bootstrap();
|
|
28
|
-
const chain = new ChainService(container);
|
|
29
32
|
// if no output file, exit with error
|
|
30
33
|
if (!args.output) {
|
|
31
34
|
console.log("Please provide an output file");
|
|
32
35
|
this.exit(1);
|
|
33
36
|
}
|
|
37
|
+
if (flags['dry-run']) {
|
|
38
|
+
console.log(chalk.yellow('[DRY RUN] Synthetic data generation simulation'));
|
|
39
|
+
console.log(chalk.cyan(` Output file: ${args.output}`));
|
|
40
|
+
console.log(chalk.cyan(` Max records: ${flags.maxRecords}`));
|
|
41
|
+
if (flags.maxCycles) {
|
|
42
|
+
console.log(chalk.cyan(` Max cycles: ${flags.maxCycles}`));
|
|
43
|
+
console.log(chalk.cyan(` Output field: ${flags.outputField}`));
|
|
44
|
+
console.log(chalk.green('[DRY RUN] Would generate synthetic data in batches using LLM'));
|
|
45
|
+
console.log(chalk.green(`[DRY RUN] Would write ${flags.maxRecords} records to ${args.output}`));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log(chalk.cyan(` Input file: ${args.input}`));
|
|
49
|
+
console.log(chalk.cyan(` Input field: ${flags.inputField}`));
|
|
50
|
+
console.log(chalk.cyan(` Output field: ${flags.outputField}`));
|
|
51
|
+
if (args.prompt) {
|
|
52
|
+
console.log(chalk.cyan(` Prompt file: ${args.prompt}`));
|
|
53
|
+
}
|
|
54
|
+
console.log(chalk.green('[DRY RUN] Would process input file records using LLM'));
|
|
55
|
+
console.log(chalk.green(`[DRY RUN] Would write processed records to ${args.output}`));
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const container = await bootstrap();
|
|
60
|
+
const chain = new ChainService(container);
|
|
34
61
|
if (flags.maxCycles) { // No input file, can process in batches
|
|
35
62
|
const input = {
|
|
36
63
|
input: prompt,
|
package/oclif.manifest.json
CHANGED
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
"<%= config.bin %> <%= command.id %>"
|
|
14
14
|
],
|
|
15
15
|
"flags": {
|
|
16
|
+
"dry-run": {
|
|
17
|
+
"description": "Show what changes would be made without actually making them",
|
|
18
|
+
"name": "dry-run",
|
|
19
|
+
"allowNo": false,
|
|
20
|
+
"type": "boolean"
|
|
21
|
+
},
|
|
16
22
|
"file": {
|
|
17
23
|
"char": "f",
|
|
18
24
|
"description": "Full path to the docker compose file to read from. Creates if it does not exist",
|
|
@@ -86,6 +92,12 @@
|
|
|
86
92
|
"multiple": false,
|
|
87
93
|
"type": "option"
|
|
88
94
|
},
|
|
95
|
+
"dry-run": {
|
|
96
|
+
"description": "Show what changes would be made without actually making them",
|
|
97
|
+
"name": "dry-run",
|
|
98
|
+
"allowNo": false,
|
|
99
|
+
"type": "boolean"
|
|
100
|
+
},
|
|
89
101
|
"git": {
|
|
90
102
|
"char": "g",
|
|
91
103
|
"description": "Github repository to install",
|
|
@@ -160,6 +172,15 @@
|
|
|
160
172
|
"<%= config.bin %> <%= command.id %>"
|
|
161
173
|
],
|
|
162
174
|
"flags": {
|
|
175
|
+
"container": {
|
|
176
|
+
"char": "c",
|
|
177
|
+
"description": "Name of the container to copy the bootstrap file to while in dev mode",
|
|
178
|
+
"name": "container",
|
|
179
|
+
"default": "dhti-langserve-1",
|
|
180
|
+
"hasDynamicHelp": false,
|
|
181
|
+
"multiple": false,
|
|
182
|
+
"type": "option"
|
|
183
|
+
},
|
|
163
184
|
"down": {
|
|
164
185
|
"char": "d",
|
|
165
186
|
"description": "Run docker-compose down after building",
|
|
@@ -167,6 +188,12 @@
|
|
|
167
188
|
"allowNo": false,
|
|
168
189
|
"type": "boolean"
|
|
169
190
|
},
|
|
191
|
+
"dry-run": {
|
|
192
|
+
"description": "Show what changes would be made without actually making them",
|
|
193
|
+
"name": "dry-run",
|
|
194
|
+
"allowNo": false,
|
|
195
|
+
"type": "boolean"
|
|
196
|
+
},
|
|
170
197
|
"file": {
|
|
171
198
|
"char": "f",
|
|
172
199
|
"description": "Full path to the docker compose file to edit or run.",
|
|
@@ -176,15 +203,6 @@
|
|
|
176
203
|
"multiple": false,
|
|
177
204
|
"type": "option"
|
|
178
205
|
},
|
|
179
|
-
"container": {
|
|
180
|
-
"char": "c",
|
|
181
|
-
"description": "Name of the container to copy the bootstrap file to while in dev mode",
|
|
182
|
-
"name": "container",
|
|
183
|
-
"default": "dhti-langserve-1",
|
|
184
|
-
"hasDynamicHelp": false,
|
|
185
|
-
"multiple": false,
|
|
186
|
-
"type": "option"
|
|
187
|
-
},
|
|
188
206
|
"name": {
|
|
189
207
|
"char": "n",
|
|
190
208
|
"description": "Name of the container to build",
|
|
@@ -265,6 +283,12 @@
|
|
|
265
283
|
"multiple": false,
|
|
266
284
|
"type": "option"
|
|
267
285
|
},
|
|
286
|
+
"dry-run": {
|
|
287
|
+
"description": "Show what changes would be made without actually making them",
|
|
288
|
+
"name": "dry-run",
|
|
289
|
+
"allowNo": false,
|
|
290
|
+
"type": "boolean"
|
|
291
|
+
},
|
|
268
292
|
"git": {
|
|
269
293
|
"char": "g",
|
|
270
294
|
"description": "Github repository to install",
|
|
@@ -347,7 +371,14 @@
|
|
|
347
371
|
"examples": [
|
|
348
372
|
"<%= config.bin %> <%= command.id %>"
|
|
349
373
|
],
|
|
350
|
-
"flags": {
|
|
374
|
+
"flags": {
|
|
375
|
+
"dry-run": {
|
|
376
|
+
"description": "Show what changes would be made without actually making them",
|
|
377
|
+
"name": "dry-run",
|
|
378
|
+
"allowNo": false,
|
|
379
|
+
"type": "boolean"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
351
382
|
"hasDynamicHelp": false,
|
|
352
383
|
"hiddenAliases": [],
|
|
353
384
|
"id": "mimic",
|
|
@@ -386,6 +417,12 @@
|
|
|
386
417
|
"<%= config.bin %> <%= command.id %>"
|
|
387
418
|
],
|
|
388
419
|
"flags": {
|
|
420
|
+
"dry-run": {
|
|
421
|
+
"description": "Show what changes would be made without actually making them",
|
|
422
|
+
"name": "dry-run",
|
|
423
|
+
"allowNo": false,
|
|
424
|
+
"type": "boolean"
|
|
425
|
+
},
|
|
389
426
|
"inputField": {
|
|
390
427
|
"char": "i",
|
|
391
428
|
"description": "Input field to use",
|
|
@@ -449,5 +486,5 @@
|
|
|
449
486
|
]
|
|
450
487
|
}
|
|
451
488
|
},
|
|
452
|
-
"version": "0.
|
|
489
|
+
"version": "0.4.0"
|
|
453
490
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dhti-cli",
|
|
3
3
|
"description": "DHTI CLI",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"author": "Bell Eapen",
|
|
6
6
|
"bin": {
|
|
7
7
|
"dhti-cli": "bin/run.js"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"@oclif/core": "^4",
|
|
14
14
|
"@oclif/plugin-help": "^6",
|
|
15
15
|
"@oclif/plugin-plugins": "^5",
|
|
16
|
+
"chalk": "^4.1.2",
|
|
16
17
|
"child_process": "^1.0.2",
|
|
17
18
|
"js-yaml": "^4.1.0",
|
|
18
19
|
"medpromptjs": ">=0.4.3",
|