Skip to content

Commit d16cdb0

Browse files
committed
Merge branch 'release/0.7.1' into main
2 parents 77989e1 + a278fad commit d16cdb0

File tree

9 files changed

+288
-10
lines changed

9 files changed

+288
-10
lines changed

.github/workflows/bump.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
bump-version:
1111
name: "Bump Version on release branch"
1212
runs-on: ubuntu-latest
13-
timeout-minutes: 3
13+
timeout-minutes: 5
1414
steps:
1515
- name: "Checkout source code"
1616
uses: "actions/checkout@v6"

.github/workflows/publish.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ on:
77
jobs:
88
build:
99
runs-on: ubuntu-latest
10+
timeout-minutes: 10
11+
# IMPORTANT: This permission is mandatory for trusted publishing
12+
permissions:
13+
id-token: write
1014
steps:
1115
- uses: actions/checkout@v6
1216
- uses: actions/setup-node@v6.1.0
1317
with:
14-
node-version: 20
18+
node-version: 24
1519
registry-url: https://registry.npmjs.org/
1620
- run: npm install
1721
- run: npm run build
18-
- run: npm publish
19-
env:
20-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
22+
- run: npm publish --provenance # Enable trusted publishing with provenance

CHANGELOG.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
## [Unreleased](https://github.com/dermatologist/dhti/tree/HEAD)
44

5-
[Full Changelog](https://github.com/dermatologist/dhti/compare/0.6.0...HEAD)
5+
[Full Changelog](https://github.com/dermatologist/dhti/compare/0.7.0...HEAD)
66

7-
**Closed issues:**
7+
**Implemented enhancements:**
88

9-
- Feature request: Create synthea command with subcommands to install synthea, generate synthetic data, upload generated data to a fhir server. [\#99](https://github.com/dermatologist/dhti/issues/99)
9+
- Feature request: Add a new docker flag to restart the gateway container. [\#101](https://github.com/dermatologist/dhti/issues/101)
1010

1111
**Merged pull requests:**
1212

13+
- Add docker restart flags for gateway and arbitrary containers [\#102](https://github.com/dermatologist/dhti/pull/102) ([Copilot](https://github.com/apps/copilot-swe-agent))
1314
- Add Synthea command for synthetic FHIR data generation and management [\#100](https://github.com/dermatologist/dhti/pull/100) ([Copilot](https://github.com/apps/copilot-swe-agent))
1415
- Feature/medplum 1 [\#98](https://github.com/dermatologist/dhti/pull/98) ([dermatologist](https://github.com/dermatologist))
1516
- Antigravity vibecode 1 [\#96](https://github.com/dermatologist/dhti/pull/96) ([dermatologist](https://github.com/dermatologist))
@@ -22,6 +23,14 @@
2223
- Feature/bump fix 1 [\#87](https://github.com/dermatologist/dhti/pull/87) ([dermatologist](https://github.com/dermatologist))
2324
- Feature/copy bootstrap [\#86](https://github.com/dermatologist/dhti/pull/86) ([dermatologist](https://github.com/dermatologist))
2425

26+
## [0.7.0](https://github.com/dermatologist/dhti/tree/0.7.0) (2025-12-29)
27+
28+
[Full Changelog](https://github.com/dermatologist/dhti/compare/0.6.0...0.7.0)
29+
30+
**Closed issues:**
31+
32+
- Feature request: Create synthea command with subcommands to install synthea, generate synthetic data, upload generated data to a fhir server. [\#99](https://github.com/dermatologist/dhti/issues/99)
33+
2534
## [0.6.0](https://github.com/dermatologist/dhti/tree/0.6.0) (2025-12-27)
2635

2736
[Full Changelog](https://github.com/dermatologist/dhti/compare/0.5.0...0.6.0)

notes/docker.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Restart gateway
2+
dhti-cli docker -g
3+
4+
# Restart any container
5+
dhti-cli docker -r dhti-langserve-1
6+
7+
# Preview without executing
8+
dhti-cli docker -g --dry-run

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "dhti-cli",
33
"description": "DHTI CLI",
4-
"version": "0.7.0",
4+
"version": "0.7.1",
55
"author": "Bell Eapen",
66
"bin": {
77
"dhti-cli": "bin/run.js"

prompts/elixir-sample-request.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The chain should:
2+
- Search for any Blood sugar and HbA1c observations for a patient given their patient ID for the last 6 months using FHIR Search.
3+
- Use the following knowledgebase to interpret the results: https://r.jina.ai/https://ngsp.org/A1ceAG.asp
4+
- Provide patient recommendations based on the interpreted results according to the knowledgebase.
5+

prompts/elixir.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# DHTI elixir coding agent
2+
3+
Elixirs provide backend GenAI capabilities as HTTP endpoints hosted by LangServe. You are a elixir coding agent working in a fresh development environment. Follow these instructions **strictly and sequentially**.
4+
5+
## Environment setup and project scaffolding
6+
7+
* Install \`uv\` in the dev environment\*\* (if not already installed).
8+
* In the project root, run:
9+
10+
```bash
11+
12+
uvx cookiecutter https://github.com/dermatologist/cookiecutter-uv.git
13+
14+
```
15+
16+
- When cookiecutter prompts you:
17+
- **author**: Set to the provided author name (or use a reasonable placeholder if none is given).
18+
- **email**: Set to the provided email (or use a reasonable placeholder if none is given).
19+
- **author_github_handle**: Set to the provided GitHub handle (or a reasonable placeholder if none is given).
20+
- **project_name**: Generate a concise, meaningful project name that reflects the requested features for this Elixir agent. THE NAME MUST START WITH "dhti-elixir-".
21+
- **project_slug**: Generate a clean, snake_case slug derived from the project name. THE SLUG MUST START WITH "dhti_elixir_".
22+
- **project_description**: Generate a detailed project description clearly summarizing:
23+
- the purpose of the project,
24+
- the kind of Elixir-related functionality requested,
25+
- the intended clinical / FHIR context (if implied by the request below).
26+
- **Cookiecutter options:**
27+
- Keep **all cookiecutter options at their default values**, **except**:
28+
- For select dhti:
29+
- Set this to 2-y (yes).
30+
- For open_source_license:
31+
- If an open source license is **explicitly** mentioned in the request, choose the corresponding option.
32+
- Otherwise, select **option 7: "Not open source"**.
33+
34+
## Study the reference implementation
35+
36+
Before editing any generated files, **carefully read and understand** the following reference files, paying attention to patterns, structure, and responsibilities:
37+
38+
- **Chain implementation:**
39+
- chain.py reference:
40+
- <https://github.com/dermatologist/dhti-elixir-template/blob/feature/agent-2/src/dhti_elixir_template/chain.py>
41+
- **Bootstrap / configuration of the chain:**
42+
- bootstrap.py reference:
43+
- <https://github.com/dermatologist/dhti-elixir-template/blob/feature/agent-2/src/dhti_elixir_template/bootstrap.py>
44+
45+
Extract and internalize the following from these references:
46+
47+
- How the LangChain chain is constructed (inputs, outputs, prompts, tools, callbacks, etc.).
48+
- How configuration, environment variables, and settings are wired in bootstrap.py.
49+
- How FHIR or other external services are integrated, if present.
50+
- Any conventions for logging, error handling, and dependency injection.
51+
52+
You must follow the **same architectural and stylistic patterns** in the new project's chain.py and bootstrap.py.
53+
54+
## Implement the new Elixir request
55+
56+
Your primary task is to **update the newly created chain.py and bootstrap.py** in the generated project to implement the following specification:
57+
58+
&lt;new elixir request here&gt;
59+
60+
Interpret this as the high-level functional requirement for the chain. Your implementation should:
61+
62+
- **Align with the reference pattern:**
63+
- Mirror the structure, abstractions, and flow used in the reference chain.py and bootstrap.py.
64+
- Reuse naming conventions, configuration style, and initialization patterns where appropriate.
65+
- **FHIR-based data retrieval:**
66+
- Retrieve any required data using **FHIR search**.
67+
- Read and carefully study FHIR search here: <https://r.jina.ai/https://www.hl7.org/fhir/search.html>
68+
- Read how fhir search is implemented in dhti-elixir-base here: <https://github.com/dermatologist/dhti-elixir-base/tree/develop/src/dhti_elixir_base/fhir>. This is available as a dependency in the generated project. Hence you can use fhir search functionality from dhti-elixir-base in your implementation and avoid code duplication.
69+
- Use appropriate FHIR resource types and query parameters based on the needs implied by the &lt;new elixir request here&gt; specification.
70+
- Implement FHIR interactions in a way that is:
71+
- Configurable (e.g., via environment variables or settings),
72+
- Robust (handles typical error conditions),
73+
- Consistent with the reference template's practices (if defined there).
74+
- **Dependencies and package usage:**
75+
- Prefer existing dependencies and standard library where possible.
76+
- Only introduce **additional Python packages** when clearly necessary.
77+
- If you add any new package:
78+
- Add it to pyproject.toml as a dependency in the appropriate section.
79+
- Ensure compatibility with the existing project structure (e.g., version constraints if needed).
80+
- **Chain behavior:**
81+
- Define the chain's inputs, outputs, and key steps clearly.
82+
- Ensure the chain logic fulfills all aspects of &lt;new elixir request here&gt;, including:
83+
- Any domain logic surrounding Elixir code analysis, generation, or orchestration.
84+
- Any interactions with external services (e.g., FHIR, LLMs, tools) as appropriate.
85+
86+
## 4\. Planning: create a TODO list
87+
88+
Before writing or heavily modifying code, create an **elaborate, structured TODO list** in a notes/todo.md file. This TODO list should:
89+
90+
- Break the work into small, concrete tasks.
91+
- Cover:
92+
- **Environment & setup** (if anything beyond cookiecutter defaults is needed),
93+
- **Chain design** (inputs/outputs, internal steps, FHIR interactions),
94+
- **Implementation tasks** for chain.py and bootstrap.py,
95+
- **Dependency updates** (if new packages are needed),
96+
- **Unit testing** tasks,
97+
- **Documentation updates** (README),
98+
- **Validation and final checks**.
99+
100+
Use clear, actionable items that you can check off logically as you progress.
101+
102+
## Implementation details
103+
104+
- **Update chain.py:**
105+
- Implement the chain logic following the patterns from the reference chain.py.
106+
- Integrate FHIR search where needed.
107+
- Ensure the chain is testable, modular, and readable.
108+
- Include inline comments where non-trivial logic is implemented.
109+
- **Update bootstrap.py:**
110+
- Configure the chain, environment variables, and external services following the patterns of the reference bootstrap.py.
111+
- Ensure:
112+
- FHIR endpoint configuration is clearly defined (and overridable),
113+
- Logging and error handling are consistent with the reference style,
114+
- Any secrets or sensitive settings are expected via environment variables or configuration files, not hard-coded.
115+
- **Dependency management:**
116+
- If you added new packages:
117+
- Confirm they are correctly listed in pyproject.toml.
118+
- Ensure any import paths are correct and code runs without import errors.
119+
120+
## Testing
121+
122+
- **Write unit tests** for the new behavior:
123+
- Place tests in the appropriate test directory or module, consistent with the generated project's testing structure.
124+
- Cover:
125+
- Core chain behavior (including expected inputs/outputs),
126+
- FHIR search integration (using mocks where appropriate),
127+
- Configuration behavior in bootstrap.py (e.g., how environment variables/config affect the chain).
128+
- Ensure tests:
129+
- Are deterministic,
130+
- Avoid real external network calls when possible (use mocking or fixtures),
131+
- Use clear, descriptive test names.
132+
- Run the test suite (e.g., via uv or the project's recommended test command) and ensure all tests pass.
133+
134+
## Documentation
135+
136+
- **Update README.md** to reflect the new functionality:
137+
- Add or update sections describing:
138+
- The purpose and scope of the project.
139+
- The behavior of the chain and what &lt;new elixir request here&gt; means in practice.
140+
- How to configure FHIR endpoints and any relevant environment variables.
141+
- How to run the chain, including command examples.
142+
- How to run tests.
143+
- Ensure the README is clear and suitable for:
144+
- Developers integrating or extending the chain,
145+
- Clinicians or researchers trying to understand the high-level purpose (if relevant).
146+
147+
## Final quality pass
148+
149+
Perform a **final pass** over the project to ensure:
150+
151+
- **Code quality:**
152+
- Code is idiomatic, consistent with the reference style, and well-structured.
153+
- No obvious dead code, unused variables, or leftover debug prints.
154+
- Type hints are used where appropriate (if consistent with the template).
155+
- **Functionality:**
156+
- The chain runs end-to-end for at least one realistic scenario implied by &lt;new elixir request here&gt;.
157+
- FHIR calls behave as expected (or are mockable in tests).
158+
- Configuration is documented and works as described.
159+
- **Project integrity:**
160+
- All imports resolve.
161+
- Tests pass.
162+
- README is up to date.
163+
- The TODO list accurately reflects what has been completed (you may optionally mark completed tasks).
164+
165+
Your final output should include:
166+
167+
- Updated chain.py and bootstrap.py implementing the requested features,
168+
- Any new/updated tests,
169+
- Updated pyproject.toml (if dependencies were added),
170+
- Updated README.md,
171+
- A clear, up-to-date TODO list (with remaining future improvements, if any).
172+
173+
Now proceed to implement the above steps carefully and methodically.

src/commands/docker.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ export default class Docker extends Command {
3030
default: `${os.homedir()}/dhti/docker-compose.yml`,
3131
description: 'Full path to the docker compose file to edit or run.',
3232
}),
33+
gateway: Flags.boolean({char: 'g', default: false, description: 'Restart the gateway container'}),
3334
name: Flags.string({char: 'n', description: 'Name of the container to build'}),
35+
restart: Flags.string({char: 'r', description: 'Restart a specific container by name'}),
3436
type: Flags.string({char: 't', default: 'elixir', description: 'Type of the service (elixir/conch)'}),
3537
up: Flags.boolean({char: 'u', default: false, description: 'Run docker-compose up after building'}),
3638
}
@@ -78,7 +80,43 @@ export default class Docker extends Command {
7880
return
7981
}
8082

81-
if (args.path !== 'bootstrap' && !flags.name && !flags.up && !flags.down) {
83+
if (flags.gateway || flags.restart) {
84+
// Validate that only one restart option is provided
85+
if (flags.gateway && flags.restart) {
86+
console.error('Error: Cannot use both --gateway and --restart flags at the same time')
87+
this.exit(1)
88+
}
89+
90+
const containerName = flags.gateway ? 'dhti-gateway-1' : flags.restart
91+
92+
// Validate container name to prevent command injection
93+
if (containerName && !/^[\w-]+$/.test(containerName)) {
94+
console.error('Error: Invalid container name. Container names can only contain letters, numbers, hyphens, and underscores.')
95+
this.exit(1)
96+
}
97+
98+
const restartCommand = `docker restart ${containerName}`
99+
100+
if (flags['dry-run']) {
101+
console.log(chalk.yellow('[DRY RUN] Would execute:'))
102+
console.log(chalk.cyan(` ${restartCommand}`))
103+
return
104+
}
105+
106+
exec(restartCommand, (error, stdout, stderr) => {
107+
if (error) {
108+
console.error(`exec error: ${error}`)
109+
return
110+
}
111+
112+
console.log(`Successfully restarted container: ${containerName}`)
113+
if (stdout) console.log(`stdout: ${stdout}`)
114+
if (stderr) console.error(`stderr: ${stderr}`)
115+
})
116+
return
117+
}
118+
119+
if (args.path !== 'bootstrap' && !flags.name && !flags.up && !flags.down && !flags.gateway && !flags.restart) {
82120
console.log('Please provide a name for the container to build')
83121
this.exit(1)
84122
}

test/commands/docker.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,47 @@ describe('docker', () => {
1414
expect(stdout).to.contain('docker compose')
1515
})
1616

17+
it('runs docker cmd with --restart flag and --dry-run', async () => {
18+
const {stdout} = await runCommand(['docker', '--restart', 'test-container', '--dry-run'])
19+
expect(stdout).to.contain('[DRY RUN]')
20+
expect(stdout).to.contain('Would execute')
21+
expect(stdout).to.contain('docker restart test-container')
22+
})
23+
24+
it('runs docker cmd with --gateway flag and --dry-run', async () => {
25+
const {stdout} = await runCommand(['docker', '--gateway', '--dry-run'])
26+
expect(stdout).to.contain('[DRY RUN]')
27+
expect(stdout).to.contain('Would execute')
28+
expect(stdout).to.contain('docker restart dhti-gateway-1')
29+
})
30+
31+
it('runs docker cmd with -g flag and --dry-run', async () => {
32+
const {stdout} = await runCommand(['docker', '-g', '--dry-run'])
33+
expect(stdout).to.contain('[DRY RUN]')
34+
expect(stdout).to.contain('Would execute')
35+
expect(stdout).to.contain('docker restart dhti-gateway-1')
36+
})
37+
38+
it('runs docker cmd with -r flag and --dry-run', async () => {
39+
const {stdout} = await runCommand(['docker', '-r', 'my-container', '--dry-run'])
40+
expect(stdout).to.contain('[DRY RUN]')
41+
expect(stdout).to.contain('Would execute')
42+
expect(stdout).to.contain('docker restart my-container')
43+
})
44+
45+
it('rejects when both --gateway and --restart are provided', async () => {
46+
const {error} = await runCommand(['docker', '--gateway', '--restart', 'test-container'])
47+
expect(error?.oclif?.exit).to.equal(1)
48+
})
49+
50+
it('rejects invalid container names with special characters', async () => {
51+
const {error} = await runCommand(['docker', '--restart', 'test@container', '--dry-run'])
52+
expect(error?.oclif?.exit).to.equal(1)
53+
})
54+
55+
it('rejects invalid container names with dollar signs', async () => {
56+
const {error} = await runCommand(['docker', '--restart', 'test$container', '--dry-run'])
57+
expect(error?.oclif?.exit).to.equal(1)
58+
})
59+
1760
})

0 commit comments

Comments
 (0)