dhti-cli 0.1.0 → 0.3.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.
Files changed (43) hide show
  1. package/README.md +2 -0
  2. package/dist/commands/compose.d.ts +14 -0
  3. package/dist/commands/compose.js +112 -0
  4. package/dist/commands/conch.d.ts +19 -0
  5. package/dist/commands/conch.js +119 -0
  6. package/dist/commands/docker.d.ts +16 -0
  7. package/dist/commands/docker.js +76 -0
  8. package/dist/commands/elixir.d.ts +20 -0
  9. package/dist/commands/elixir.js +129 -0
  10. package/dist/commands/mimic.d.ts +9 -0
  11. package/dist/commands/mimic.js +157 -0
  12. package/dist/commands/synthetic.d.ts +17 -0
  13. package/dist/commands/synthetic.js +88 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +2 -0
  16. package/dist/resources/docker-compose-master.yml +238 -0
  17. package/dist/resources/genai/Dockerfile +27 -0
  18. package/dist/resources/genai/README.md +1 -0
  19. package/dist/resources/genai/app/__init__.py +0 -0
  20. package/dist/resources/genai/app/bootstrap.py +43 -0
  21. package/dist/resources/genai/app/server.py +57 -0
  22. package/dist/resources/genai/pyproject.toml +84 -0
  23. package/dist/resources/genai/whl/placeholder.md +0 -0
  24. package/dist/resources/spa/Dockerfile +16 -0
  25. package/dist/resources/spa/def/importmap.json +39 -0
  26. package/dist/resources/spa/def/routes.registry.json +1599 -0
  27. package/dist/resources/spa/def/spa-assemble-config.json +40 -0
  28. package/dist/utils/bootstrap.d.ts +3 -0
  29. package/dist/utils/bootstrap.js +57 -0
  30. package/dist/utils/card.d.ts +58 -0
  31. package/dist/utils/card.js +76 -0
  32. package/dist/utils/chain.d.ts +5 -0
  33. package/dist/utils/chain.js +17 -0
  34. package/dist/utils/getCard.d.ts +3 -0
  35. package/dist/utils/getCard.js +11 -0
  36. package/dist/utils/index.d.ts +2 -0
  37. package/dist/utils/index.js +3 -0
  38. package/dist/utils/request.d.ts +30 -0
  39. package/dist/utils/request.js +34 -0
  40. package/dist/utils/useDhti.d.ts +5 -0
  41. package/dist/utils/useDhti.js +21 -0
  42. package/oclif.manifest.json +442 -2
  43. package/package.json +7 -5
@@ -0,0 +1,157 @@
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
+ return;
155
+ }
156
+ }
157
+ }
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export { Cards, handleBundle } from './utils/index.js';
2
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { Cards, handleBundle } from './utils/index.js';
2
+ export { run } from '@oclif/core';
@@ -0,0 +1,238 @@
1
+ version: "3.7"
2
+
3
+ services:
4
+ gateway:
5
+ image: beapen/dhti-gateway:latest
6
+ restart: "unless-stopped"
7
+ pull_policy: always
8
+ depends_on:
9
+ - frontend
10
+ - backend
11
+ ports:
12
+ - "80:80"
13
+ - "9000:80"
14
+
15
+
16
+ frontend:
17
+ image: openmrs/openmrs-reference-application-3-frontend:3.0.0-beta.17
18
+ # image: openmrs/openmrs-reference-application-3-frontend:${TAG:-3.0.0-beta.17} # dev3, qa, demo, 3.0.0-beta.18
19
+ ports:
20
+ - "8003:80"
21
+ restart: "unless-stopped"
22
+ pull_policy: always
23
+ environment:
24
+ SPA_PATH: /openmrs/spa
25
+ API_URL: /openmrs
26
+ SPA_CONFIG_URLS: /openmrs/spa/config-core_demo.json
27
+ SPA_DEFAULT_LOCALE:
28
+ healthcheck:
29
+ test: ["CMD", "curl", "-f", "http://localhost/"]
30
+ timeout: 5s
31
+ depends_on:
32
+ - backend
33
+ # volumes:
34
+ # - ./spa:/usr/share/nginx/html
35
+
36
+ backend:
37
+ image: openmrs/openmrs-reference-application-3-backend:${TAG:-3.0.0-beta.17} # dev3, qa, demo, 3.0.0-beta.18
38
+ ports:
39
+ - "8002:8080"
40
+ restart: "unless-stopped"
41
+ depends_on:
42
+ - openmrs-db
43
+ environment:
44
+ OMRS_CONFIG_MODULE_WEB_ADMIN: "true"
45
+ OMRS_CONFIG_AUTO_UPDATE_DATABASE: "true"
46
+ OMRS_CONFIG_CREATE_TABLES: "true"
47
+ OMRS_CONFIG_CONNECTION_SERVER: openmrs-db
48
+ OMRS_CONFIG_CONNECTION_DATABASE: openmrs
49
+ OMRS_CONFIG_CONNECTION_USERNAME: ${OPENMRS_DB_USER:-openmrs}
50
+ OMRS_CONFIG_CONNECTION_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs}
51
+ healthcheck:
52
+ test: ["CMD", "curl", "-f", "http://localhost:8080/openmrs"]
53
+ timeout: 5s
54
+ volumes:
55
+ - openmrs-data:/openmrs/data
56
+
57
+ openmrs-db:
58
+ image: mariadb:10.11.7
59
+ restart: "unless-stopped"
60
+ command: "mysqld --character-set-server=utf8 --collation-server=utf8_general_ci"
61
+ healthcheck:
62
+ test: "mysql --user=${OMRS_DB_USER:-openmrs} --password=${OMRS_DB_PASSWORD:-openmrs} --execute \"SHOW DATABASES;\""
63
+ interval: 3s
64
+ timeout: 1s
65
+ retries: 5
66
+ environment:
67
+ MYSQL_DATABASE: openmrs
68
+ MYSQL_USER: ${OMRS_DB_USER:-openmrs}
69
+ MYSQL_PASSWORD: ${OMRS_DB_PASSWORD:-openmrs}
70
+ MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-openmrs}
71
+ volumes:
72
+ - openmrs-db:/var/lib/mysql
73
+
74
+ langserve:
75
+ image: beapen/genai:latest
76
+ pull_policy: always
77
+ ports:
78
+ - "8001:8001"
79
+ restart: "unless-stopped"
80
+ environment:
81
+ - OLLAMA_SERVER_URL==http://ollama:11434
82
+ - OLLAMA_WEBUI=http://ollama-webui:8080
83
+ - LANGFUSE_HOST=http://langfuse:3000
84
+ - LANGFUSE_PUBLIC_KEY=pk-lf-abcd
85
+ - LANGFUSE_SECRET_KEY=sk-lf-abcd
86
+
87
+ ollama:
88
+ image: ollama/ollama:latest
89
+ ports:
90
+ - 11434:11434
91
+ volumes:
92
+ - ollama-code:/code
93
+ - ollama-root:/root/.ollama
94
+ tty: true
95
+ restart: "unless-stopped"
96
+ environment:
97
+ - OLLAMA_ORIGINS=*
98
+
99
+ ollama-webui:
100
+ image: ghcr.io/open-webui/open-webui:main
101
+ volumes:
102
+ - ollama-webui:/app/backend/data
103
+ depends_on:
104
+ - ollama
105
+ ports:
106
+ - 8080:8080
107
+ environment:
108
+ - '/ollama/api=http://ollama:11434/api'
109
+ extra_hosts:
110
+ - host.docker.internal:host-gateway
111
+ restart: unless-stopped
112
+
113
+ fhir:
114
+ image: alphora/cqf-ruler:0.14.0 # includes cql
115
+ ports:
116
+ - 8005:8080
117
+ restart: "unless-stopped"
118
+ depends_on:
119
+ - postgres-db
120
+ environment:
121
+ - "spring.datasource.url=jdbc:postgresql://postgres-db:5432/postgres"
122
+ - "spring.datasource.username=postgres"
123
+ - "spring.datasource.password=postgres"
124
+ - "spring.datasource.driverClassName=org.postgresql.Driver"
125
+ - "spring.jpa.properties.hibernate.dialect=ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect"
126
+ - "hapi.fhir.fhir_version=R4"
127
+ - "hapi.fhir.cors.allowed-origins=*"
128
+ - "hapi.fhir.cors.allowCredentials=true"
129
+ - "hapi.fhir.bulkdata.enabled=true"
130
+ - "hapi.fhir.bulk_export_enabled=true"
131
+ - "hapi.fhir.bulk_import_enabled=true"
132
+ - "hapi.fhir.enforce_referential_integrity_on_write=false"
133
+ - "hapi.fhir.enforce_referential_integrity_on_delete=false"
134
+
135
+ mcp-fhir:
136
+ image: beapen/fhir-mcp-server:1.0
137
+ ports:
138
+ - 8006:8000
139
+ restart: "unless-stopped"
140
+ depends_on:
141
+ - fhir
142
+ environment:
143
+ - FHIR_SERVER_BASE_URL="http://fhir:8005/baseR4"
144
+ - FHIR_SERVER_SCOPES="*"
145
+ - FHIR_SERVER_ACCESS_TOKEN="none"
146
+
147
+ cql-elm:
148
+ image: cqframework/cql-translation-service:latest
149
+ ports:
150
+ - 8091:8080
151
+ restart: "unless-stopped"
152
+
153
+ cql-web:
154
+ image: beapen/cql_runner:latest
155
+ ports:
156
+ - 8092:80
157
+ restart: "unless-stopped"
158
+ depends_on:
159
+ - fhir
160
+
161
+ langfuse:
162
+ image: ghcr.io/langfuse/langfuse:latest
163
+ depends_on:
164
+ - postgres-db
165
+ ports:
166
+ - "3000:3000"
167
+ environment:
168
+ - DATABASE_URL=postgresql://postgres:postgres@postgres-db:5432/postgres
169
+ - NEXTAUTH_SECRET=mysecret
170
+ - SALT=mysalt
171
+ - NEXTAUTH_URL=http://langfuse:3000
172
+ - TELEMETRY_ENABLED=${TELEMETRY_ENABLED:-false}
173
+ - LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
174
+
175
+ postgres-db:
176
+ image: postgres
177
+ restart: "unless-stopped"
178
+ environment:
179
+ - POSTGRES_USER=postgres
180
+ - POSTGRES_PASSWORD=postgres
181
+ - POSTGRES_DB=postgres
182
+ ports:
183
+ - 5432:5432
184
+ volumes:
185
+ - postgres-db:/var/lib/postgresql/data
186
+
187
+ redis:
188
+ image: redislabs/redisearch:2.8.8
189
+ ports:
190
+ - 6379:6379
191
+ restart: "unless-stopped"
192
+ volumes:
193
+ - redis-db:/data
194
+
195
+ redis-commander:
196
+ image: rediscommander/redis-commander:latest
197
+ restart: "unless-stopped"
198
+ environment:
199
+ - REDIS_HOSTS=local:redis:6379
200
+ ports:
201
+ - "8081:8081"
202
+
203
+ neo4j:
204
+ image: neo4j:5.1-enterprise
205
+ ports:
206
+ - 7474:7474
207
+ - 7687:7687
208
+ environment:
209
+ - NEO4J_AUTH=neo4j/password
210
+ - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
211
+ - NEO4J_PLUGINS=["apoc", "graph-data-science", "n10s"]
212
+ - NEO4J_dbms_security_procedures_unrestricted=apoc.*,gds.*,n10s.*
213
+ - NEO4J_dbms_security_procedures_whitelist=apoc.*,gds.*,n10s.*
214
+ restart: "unless-stopped"
215
+ volumes:
216
+ - neo4j-db:/data
217
+
218
+ fhirg:
219
+ image: beapen/fhirg:latest
220
+ ports:
221
+ - 8004:8080
222
+ restart: "unless-stopped"
223
+ environment:
224
+ - spring.neo4j.uri=bolt://neo4j:7687
225
+ - spring.neo4j.authentication.username=neo4j
226
+ - spring.neo4j.authentication.password=password
227
+
228
+
229
+ volumes:
230
+ openmrs-data: ~
231
+ openmrs-db: ~
232
+ fhir-db: ~
233
+ postgres-db: ~
234
+ redis-db: ~
235
+ neo4j-db: ~
236
+ ollama-code: ~
237
+ ollama-root: ~
238
+ ollama-webui: ~
@@ -0,0 +1,27 @@
1
+ # Use Python 3.12 slim as base image
2
+ FROM python:3.12-slim AS base
3
+
4
+ # Update package lists and install Git
5
+ RUN apt-get update && \
6
+ apt-get install -y --no-install-recommends git && \
7
+ rm -rf /var/lib/apt/lists/*
8
+
9
+ # Install uv
10
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
11
+
12
+ # Set working directory
13
+ WORKDIR /app
14
+
15
+ # Copy pyproject.toml into the image
16
+ COPY pyproject.toml /app/pyproject.toml
17
+ COPY README.md /app/README.md
18
+
19
+ # Install dependencies and generate uv.lock
20
+ RUN uv sync --no-dev
21
+
22
+ # Copy the project into the image
23
+ COPY . /app
24
+
25
+
26
+ # Run the server
27
+ CMD ["uv", "run", "python", "app/server.py"]
@@ -0,0 +1 @@
1
+ # README
File without changes
@@ -0,0 +1,43 @@
1
+ from kink import di
2
+ from os import getenv
3
+ from dotenv import load_dotenv
4
+ from langchain_core.prompts import PromptTemplate
5
+ from langchain_core.language_models.fake import FakeListLLM
6
+
7
+ ## Override the default configuration of elixirs here if needed
8
+
9
+
10
+ def bootstrap():
11
+ load_dotenv()
12
+ fake_llm = FakeListLLM(responses=["Paris", "I don't know"])
13
+ di["main_prompt"] = PromptTemplate.from_template(
14
+ "Summarize the following in 100 words: {input}"
15
+ )
16
+ di["main_llm"] = fake_llm
17
+ di["cds_hook_discovery"] = {
18
+ "services": [
19
+ {
20
+ "id": "dhti-service",
21
+ "hook": "order-select",
22
+ "title": "MyOrg Order Assistant",
23
+ "description": "Provides suggestions and actions for selected draft orders, including handling CommunicationRequest resources.",
24
+ "prefetch": {
25
+ "patient": "Patient/{{context.patientId}}",
26
+ "draftOrders": "Bundle?patient={{context.patientId}}&status=draft",
27
+ },
28
+ "scopes": [
29
+ "launch",
30
+ "patient/Patient.read",
31
+ "user/Practitioner.read",
32
+ "patient/CommunicationRequest.read",
33
+ ],
34
+ "metadata": {
35
+ "author": "MyOrg CDS Team",
36
+ "version": "1.0.0",
37
+ "supportedResources": [
38
+ "CommunicationRequest",
39
+ ],
40
+ },
41
+ }
42
+ ]
43
+ }
@@ -0,0 +1,57 @@
1
+ from fastapi import FastAPI
2
+ from langserve import add_routes
3
+ from langchain_core.runnables.config import RunnableConfig
4
+ from dhti_elixir_base.cds_hook.routes import add_services, add_invokes
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from mcp.server.fastmcp import FastMCP
7
+ mcp_server = FastMCP(name="dhti-mcp-server")
8
+
9
+ # ! DO NOT REMOVE THE COMMENT BELOW
10
+ # DHTI_CLI_IMPORT
11
+ import uvicorn
12
+
13
+ # Comes after elixir bootstraps, so can override elixir configurations
14
+ from bootstrap import bootstrap
15
+ bootstrap()
16
+
17
+ app = FastAPI(title="dhti-elixir-server")
18
+ # Mount the MCP server's ASGI application at a specific path (Exposes /messages and /sse endpoints)
19
+ app.mount("/langserve/mcp", mcp_server.sse_app())
20
+
21
+ origins = [
22
+ "*",
23
+ ]
24
+
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=origins,
28
+ allow_credentials=True,
29
+ allow_methods=["*"],
30
+ allow_headers=["*"],
31
+ )
32
+
33
+ # Define a root endpoint
34
+ @app.get("/langserve")
35
+ async def read_root():
36
+ return {"message": "Hello from DHTI!"}
37
+
38
+ try:
39
+ from langfuse import Langfuse
40
+ from langfuse.callback import CallbackHandler
41
+ langfuse_handler = CallbackHandler()
42
+ langfuse_handler.auth_check()
43
+ config = RunnableConfig(callbacks=[langfuse_handler])
44
+ # ! DO NOT REMOVE THE COMMENT BELOW
45
+ # DHTI_LANGFUSE_ROUTE
46
+
47
+ except:
48
+ # ! DO NOT REMOVE THE COMMENT BELOW
49
+ # DHTI_NORMAL_ROUTE
50
+ x = True
51
+
52
+ # ! DO NOT REMOVE THE COMMENT BELOW
53
+ # DHTI_COMMON_ROUTE
54
+
55
+
56
+ if __name__ == "__main__":
57
+ uvicorn.run(app, host="0.0.0.0", port=8001)