adithya9903 commited on
Commit
fa51dd9
Β·
1 Parent(s): 982afcc

Flatten project to root for OpenEnv submission readiness.

Browse files

Move backend/frontend/data/scripts and manifests out of nested openenv-polypharmacy directory, add root-compliant inference/server entrypoints, and align validator artifacts for Hugging Face submission checks.

Made-with: Cursor

This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .dockerignore +1 -1
  2. openenv-polypharmacy/.env.example β†’ .env.example +0 -0
  3. .gitignore +1 -5
  4. Dockerfile +9 -9
  5. openenv-polypharmacy/PROMPT.md β†’ PROMPT.md +0 -0
  6. README.md +247 -1
  7. {openenv-polypharmacy/backend β†’ backend}/Dockerfile +0 -0
  8. {openenv-polypharmacy/backend β†’ backend}/__init__.py +0 -0
  9. {openenv-polypharmacy/backend β†’ backend}/main.py +0 -0
  10. {openenv-polypharmacy/backend β†’ backend}/requirements.txt +0 -0
  11. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/__init__.py +0 -0
  12. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/__init__.py +0 -0
  13. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/app.py +0 -0
  14. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/routes/__init__.py +0 -0
  15. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/routes/agent.py +0 -0
  16. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/server.py +0 -0
  17. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/__init__.py +0 -0
  18. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/heuristic_agent.py +0 -0
  19. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/random_agent.py +0 -0
  20. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/client.py +0 -0
  21. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/config.py +0 -0
  22. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/data_loader.py +0 -0
  23. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/ddi_simulator.py +0 -0
  24. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/env_core.py +0 -0
  25. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/graders.py +0 -0
  26. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/models.py +0 -0
  27. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/rewards.py +0 -0
  28. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/services/__init__.py +0 -0
  29. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/services/groq_agent.py +0 -0
  30. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tasks.py +0 -0
  31. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/__init__.py +0 -0
  32. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/test_api.py +0 -0
  33. {openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/test_env_core.py +0 -0
  34. {openenv-polypharmacy/data β†’ data}/lookups/beers_criteria.csv +0 -0
  35. {openenv-polypharmacy/data β†’ data}/lookups/ddi_rules.csv +0 -0
  36. {openenv-polypharmacy/data β†’ data}/lookups/drug_metadata.csv +0 -0
  37. {openenv-polypharmacy/data β†’ data}/processed/patients_polypharmacy.csv +0 -0
  38. openenv-polypharmacy/docker-compose.yml β†’ docker-compose.yml +0 -0
  39. {openenv-polypharmacy/frontend β†’ frontend}/Dockerfile +0 -0
  40. {openenv-polypharmacy/frontend β†’ frontend}/index.html +0 -0
  41. {openenv-polypharmacy/frontend β†’ frontend}/package-lock.json +0 -0
  42. {openenv-polypharmacy/frontend β†’ frontend}/package.json +0 -0
  43. {openenv-polypharmacy/frontend β†’ frontend}/src/App.jsx +0 -0
  44. {openenv-polypharmacy/frontend β†’ frontend}/src/main.jsx +0 -0
  45. {openenv-polypharmacy/frontend β†’ frontend}/src/styles.css +0 -0
  46. {openenv-polypharmacy/frontend β†’ frontend}/vite.config.js +0 -0
  47. inference.py +188 -0
  48. openenv-polypharmacy/.dockerignore +0 -8
  49. openenv-polypharmacy/Dockerfile +0 -39
  50. openenv-polypharmacy/README.md +0 -245
.dockerignore CHANGED
@@ -9,4 +9,4 @@
9
  **/dist
10
  **/.env
11
  **/.env.*
12
- !openenv-polypharmacy/.env.example
 
9
  **/dist
10
  **/.env
11
  **/.env.*
12
+ !.env.example
openenv-polypharmacy/.env.example β†’ .env.example RENAMED
File without changes
.gitignore CHANGED
@@ -4,7 +4,7 @@ venv/
4
  env/
5
  .env
6
  .env.*
7
- !openenv-polypharmacy/.env.example
8
  *.py[cod]
9
  __pycache__/
10
  .pytest_cache/
@@ -29,7 +29,3 @@ pnpm-debug.log*
29
  *.swp
30
  .DS_Store
31
 
32
- # --- Project-specific nested paths ---
33
- openenv-polypharmacy/frontend/node_modules/
34
- openenv-polypharmacy/frontend/dist/
35
- openenv-polypharmacy/.pytest_cache/
 
4
  env/
5
  .env
6
  .env.*
7
+ !.env.example
8
  *.py[cod]
9
  __pycache__/
10
  .pytest_cache/
 
29
  *.swp
30
  .DS_Store
31
 
 
 
 
 
Dockerfile CHANGED
@@ -1,8 +1,8 @@
1
  FROM node:20-alpine AS frontend-builder
2
  WORKDIR /app/frontend
3
- COPY openenv-polypharmacy/frontend/package*.json ./
4
  RUN npm ci
5
- COPY openenv-polypharmacy/frontend/ ./
6
  RUN npm run build
7
 
8
  FROM python:3.11-slim
@@ -13,15 +13,15 @@ RUN apt-get update && \
13
 
14
  WORKDIR /app
15
 
16
- COPY openenv-polypharmacy/backend/requirements.txt /app/backend/requirements.txt
17
  RUN pip install --no-cache-dir -r /app/backend/requirements.txt
18
 
19
- COPY openenv-polypharmacy/backend /app/backend
20
- COPY openenv-polypharmacy/data /app/data
21
- COPY openenv-polypharmacy/scripts /app/scripts
22
- COPY openenv-polypharmacy/openenv.yaml /app/openenv.yaml
23
- COPY openenv-polypharmacy/.env.example /app/.env.example
24
- COPY openenv-polypharmacy/inference.py /app/inference.py
25
 
26
  COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
27
 
 
1
  FROM node:20-alpine AS frontend-builder
2
  WORKDIR /app/frontend
3
+ COPY frontend/package*.json ./
4
  RUN npm ci
5
+ COPY frontend/ ./
6
  RUN npm run build
7
 
8
  FROM python:3.11-slim
 
13
 
14
  WORKDIR /app
15
 
16
+ COPY backend/requirements.txt /app/backend/requirements.txt
17
  RUN pip install --no-cache-dir -r /app/backend/requirements.txt
18
 
19
+ COPY backend /app/backend
20
+ COPY data /app/data
21
+ COPY scripts /app/scripts
22
+ COPY openenv.yaml /app/openenv.yaml
23
+ COPY .env.example /app/.env.example
24
+ COPY inference.py /app/inference.py
25
 
26
  COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
27
 
openenv-polypharmacy/PROMPT.md β†’ PROMPT.md RENAMED
File without changes
README.md CHANGED
@@ -7,4 +7,250 @@ sdk: docker
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  pinned: false
8
  ---
9
 
10
+ # PolypharmacyEnv
11
+
12
+ Monorepo for an OpenEnv-compatible medication safety environment with:
13
+
14
+ - a FastAPI backend (`backend/`)
15
+ - a React frontend (`frontend/`)
16
+ - data assets (`data/`)
17
+ - utility scripts (`scripts/`)
18
+
19
+ ---
20
+
21
+ ## Repository Structure
22
+
23
+ ```text
24
+ backend/
25
+ main.py # ASGI entrypoint (uvicorn target)
26
+ requirements.txt # Backend dependencies
27
+ Dockerfile # Backend container
28
+ src/polypharmacy_env/ # Python package source
29
+ api/
30
+ app.py # FastAPI/OpenEnv app assembly
31
+ server.py # Compatibility import wrapper
32
+ routes/agent.py # /agent/suggest route
33
+ services/
34
+ groq_agent.py # Groq-based action suggestion logic
35
+ env_core.py # OpenEnv environment core
36
+ models.py # Action/observation/state models
37
+ data_loader.py # CSV loading
38
+ ddi_simulator.py # DDI and Beers lookups
39
+ rewards.py # Reward shaping
40
+ graders.py # Task graders
41
+ tasks.py # Task/episode selection
42
+ tests/ # Backend tests
43
+ frontend/
44
+ src/ # React UI code
45
+ package.json
46
+ Dockerfile # Frontend container
47
+ data/
48
+ lookups/ # drug_metadata.csv, ddi_rules.csv, beers_criteria.csv
49
+ processed/ # patients_polypharmacy.csv
50
+ scripts/
51
+ preprocess_data.py # Synthetic data generation
52
+ dev_backend.sh # Local backend run helper
53
+ dev_frontend.sh # Local frontend run helper
54
+ run_validation.sh # Tests + baseline validation
55
+ docker-compose.yml # Full stack orchestration
56
+ openenv.yaml # OpenEnv manifest
57
+ inference.py # Baseline inference script (required at root)
58
+ .env.example # Environment template
59
+ ```
60
+
61
+ ---
62
+
63
+ ## What It Does
64
+
65
+ The environment simulates elderly polypharmacy review. Agent actions:
66
+
67
+ - `query_ddi`
68
+ - `propose_intervention`
69
+ - `finish_review`
70
+
71
+ Supported tasks:
72
+
73
+ - `easy_screening`
74
+ - `budgeted_screening`
75
+ - `complex_tradeoff`
76
+
77
+ ---
78
+
79
+ ## Prerequisites
80
+
81
+ - Python 3.10+
82
+ - Node.js 18+ (or 20+ recommended)
83
+ - npm
84
+ - Docker + Docker Compose (optional, for containerized run)
85
+
86
+ ---
87
+
88
+ ## Environment Setup
89
+
90
+ Create `.env`:
91
+
92
+ ```bash
93
+ cp .env.example .env
94
+ ```
95
+
96
+ Set values for local backend integrations as needed.
97
+
98
+ ---
99
+
100
+ ## Local Run (Recommended During Development)
101
+
102
+ ### 1) Install dependencies
103
+
104
+ Backend:
105
+
106
+ ```bash
107
+ pip install -r backend/requirements.txt
108
+ ```
109
+
110
+ Frontend:
111
+
112
+ ```bash
113
+ cd frontend
114
+ npm install
115
+ cd ..
116
+ ```
117
+
118
+ ### 2) Generate/update synthetic data (if needed)
119
+
120
+ ```bash
121
+ python scripts/preprocess_data.py
122
+ ```
123
+
124
+ ### 3) Start services in two terminals
125
+
126
+ Terminal A:
127
+
128
+ ```bash
129
+ ./scripts/dev_backend.sh
130
+ ```
131
+
132
+ Terminal B:
133
+
134
+ ```bash
135
+ ./scripts/dev_frontend.sh
136
+ ```
137
+
138
+ ### 4) Open app
139
+
140
+ - Frontend: [http://localhost:5173](http://localhost:5173)
141
+ - Backend health: [http://localhost:7860/health](http://localhost:7860/health)
142
+
143
+ ---
144
+
145
+ ## Docker Run
146
+
147
+ Run both services:
148
+
149
+ ```bash
150
+ docker compose up --build
151
+ ```
152
+
153
+ Stop:
154
+
155
+ ```bash
156
+ docker compose down
157
+ ```
158
+
159
+ Ports:
160
+
161
+ - backend: `7860`
162
+ - frontend: `5173`
163
+
164
+ ---
165
+
166
+ ## Hugging Face Spaces Deployment (Docker)
167
+
168
+ This repo now includes a **root `Dockerfile`** that builds frontend + backend into one container, so Spaces can host both API and UI together.
169
+
170
+ ### 1) Create a new Space
171
+
172
+ - Go to [Hugging Face Spaces](https://huggingface.co/new-space)
173
+ - Choose **Docker** SDK
174
+ - Create the Space
175
+
176
+ ### 2) Add Space secrets/variables
177
+
178
+ In Space Settings -> Variables and Secrets:
179
+
180
+ - Secret: `HF_TOKEN`
181
+ - Variable: `API_BASE_URL=https://router.huggingface.co/v1`
182
+ - Variable: `MODEL_NAME=Qwen/Qwen2.5-72B-Instruct`
183
+
184
+ ### 3) Push this repository to the Space
185
+
186
+ Commit and push all files, including root `Dockerfile`.
187
+
188
+ ### 4) Verify after build
189
+
190
+ - Space root URL loads the React UI
191
+ - `/health` returns healthy status
192
+ - OpenEnv endpoints are available (`/reset`, `/step`, `/state`, `/schema`)
193
+
194
+ Notes:
195
+
196
+ - Container reads `PORT` (defaults to `7860`) which is Space-friendly.
197
+ - Frontend static assets are served by FastAPI from `frontend/dist`.
198
+
199
+ ---
200
+
201
+ ## API Endpoints
202
+
203
+ OpenEnv/health:
204
+
205
+ - `POST /reset`
206
+ - `POST /step`
207
+ - `GET /state`
208
+ - `GET /health`
209
+ - `GET /schema`
210
+ - `WS /ws` (stateful session)
211
+
212
+ AI helper:
213
+
214
+ - `POST /agent/suggest`
215
+
216
+ ---
217
+
218
+ ## Testing
219
+
220
+ Run backend tests:
221
+
222
+ ```bash
223
+ python -m pytest backend/src/polypharmacy_env/tests -v
224
+ ```
225
+
226
+ Or run validation script:
227
+
228
+ ```bash
229
+ ./scripts/run_validation.sh
230
+ ```
231
+
232
+ ### Submission validation
233
+
234
+ ```bash
235
+ openenv validate
236
+ python inference.py
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Notes
242
+
243
+ - OpenEnv HTTP reset/step is stateless; multi-step episode continuity should use websocket (`/ws`).
244
+ - The frontend uses websocket for episode continuity and HTTP for AI suggestion.
245
+ - AI behavior includes rule-based guardrails to avoid repetitive low-value loops.
246
+
247
+ ---
248
+
249
+ ## Troubleshooting
250
+
251
+ - `ModuleNotFoundError: polypharmacy_env`
252
+ - Start backend using `./scripts/dev_backend.sh` from repo root.
253
+ - `/agent/suggest` fails
254
+ - Check `.env` keys and restart backend.
255
+ - UI state looks stale
256
+ - Hard refresh browser and click `Reset Episode`.
{openenv-polypharmacy/backend β†’ backend}/Dockerfile RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/main.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/requirements.txt RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/app.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/routes/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/routes/agent.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/api/server.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/heuristic_agent.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/baselines/random_agent.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/client.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/config.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/data_loader.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/ddi_simulator.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/env_core.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/graders.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/models.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/rewards.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/services/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/services/groq_agent.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tasks.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/__init__.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/test_api.py RENAMED
File without changes
{openenv-polypharmacy/backend β†’ backend}/src/polypharmacy_env/tests/test_env_core.py RENAMED
File without changes
{openenv-polypharmacy/data β†’ data}/lookups/beers_criteria.csv RENAMED
File without changes
{openenv-polypharmacy/data β†’ data}/lookups/ddi_rules.csv RENAMED
File without changes
{openenv-polypharmacy/data β†’ data}/lookups/drug_metadata.csv RENAMED
File without changes
{openenv-polypharmacy/data β†’ data}/processed/patients_polypharmacy.csv RENAMED
File without changes
openenv-polypharmacy/docker-compose.yml β†’ docker-compose.yml RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/Dockerfile RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/index.html RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/package-lock.json RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/package.json RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/src/App.jsx RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/src/main.jsx RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/src/styles.css RENAMED
File without changes
{openenv-polypharmacy/frontend β†’ frontend}/vite.config.js RENAMED
File without changes
inference.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Submission inference script for Polypharmacy OpenEnv environment.
3
+
4
+ Required environment variables:
5
+ API_BASE_URL OpenAI-compatible base URL
6
+ MODEL_NAME Model identifier
7
+ HF_TOKEN API key/token
8
+
9
+ Optional:
10
+ POLYPHARMACY_ENV_URL Environment API base (default: http://localhost:7860)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import os
17
+ import re
18
+ from typing import Any, Dict, List
19
+
20
+ import requests
21
+ from openai import OpenAI
22
+
23
+ API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
24
+ MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct")
25
+ HF_TOKEN = os.getenv("HF_TOKEN", "")
26
+ ENV_URL = os.getenv("POLYPHARMACY_ENV_URL", "http://localhost:7860").rstrip("/")
27
+
28
+ BENCHMARK = "polypharmacy_env"
29
+ TASKS = ["easy_screening", "budgeted_screening", "complex_tradeoff"]
30
+ MAX_STEPS = 16
31
+ TEMPERATURE = 0.0
32
+ MAX_TOKENS = 220
33
+
34
+ SYSTEM_PROMPT = (
35
+ "You are a clinical-pharmacist agent. "
36
+ "Return one JSON action only with keys matching this schema: "
37
+ '{"action_type":"query_ddi|propose_intervention|finish_review",'
38
+ '"drug_id_1":"", "drug_id_2":"", "target_drug_id":"",'
39
+ '"intervention_type":"stop|dose_reduce|substitute|add_monitoring",'
40
+ '"proposed_new_drug_id":"", "rationale":""}. '
41
+ "Prefer safe, high-impact actions and finish when useful actions are exhausted."
42
+ )
43
+
44
+
45
+ def _b(v: bool) -> str:
46
+ return str(bool(v)).lower()
47
+
48
+
49
+ def _fmt_reward(v: float) -> str:
50
+ return f"{float(v):.2f}"
51
+
52
+
53
+ def _clamp01(v: float) -> float:
54
+ return max(0.0, min(1.0, float(v)))
55
+
56
+
57
+ def log_start(task: str) -> None:
58
+ print(f"[START] task={task} env={BENCHMARK} model={MODEL_NAME}", flush=True)
59
+
60
+
61
+ def log_step(step: int, action_str: str, reward: float, done: bool, error: str | None) -> None:
62
+ err = error if error else "null"
63
+ print(
64
+ f"[STEP] step={step} action={action_str} reward={_fmt_reward(reward)} "
65
+ f"done={_b(done)} error={err}",
66
+ flush=True,
67
+ )
68
+
69
+
70
+ def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
71
+ rewards_str = ",".join(_fmt_reward(r) for r in rewards)
72
+ print(
73
+ f"[END] success={_b(success)} steps={steps} score={_clamp01(score):.3f} rewards={rewards_str}",
74
+ flush=True,
75
+ )
76
+
77
+
78
+ def _safe_json(text: str) -> Dict[str, Any]:
79
+ text = text.strip()
80
+ if text.startswith("```"):
81
+ text = re.sub(r"^```[a-zA-Z]*\n?", "", text)
82
+ text = text.replace("```", "").strip()
83
+ try:
84
+ data = json.loads(text)
85
+ if isinstance(data, dict):
86
+ return data
87
+ except Exception:
88
+ pass
89
+ return {"action_type": "finish_review"}
90
+
91
+
92
+ def _llm_action(client: OpenAI, obs: Dict[str, Any]) -> Dict[str, Any]:
93
+ meds = obs.get("current_medications", [])
94
+ summary = {
95
+ "step_index": obs.get("step_index", 0),
96
+ "remaining_query_budget": obs.get("remaining_query_budget", 0),
97
+ "remaining_intervention_budget": obs.get("remaining_intervention_budget", 0),
98
+ "conditions": obs.get("conditions", []),
99
+ "current_medications": [
100
+ {
101
+ "drug_id": m.get("drug_id"),
102
+ "generic_name": m.get("generic_name"),
103
+ "dose_mg": m.get("dose_mg"),
104
+ "beers_flags": m.get("beers_flags", []),
105
+ }
106
+ for m in meds
107
+ ],
108
+ "interaction_queries": obs.get("interaction_queries", []),
109
+ "interventions": obs.get("interventions", []),
110
+ }
111
+ resp = client.chat.completions.create(
112
+ model=MODEL_NAME,
113
+ temperature=TEMPERATURE,
114
+ max_tokens=MAX_TOKENS,
115
+ messages=[
116
+ {"role": "system", "content": SYSTEM_PROMPT},
117
+ {"role": "user", "content": json.dumps(summary, separators=(",", ":"))},
118
+ ],
119
+ )
120
+ content = (resp.choices[0].message.content or "").strip()
121
+ return _safe_json(content)
122
+
123
+
124
+ def _reset(task_id: str) -> Dict[str, Any]:
125
+ r = requests.post(f"{ENV_URL}/reset", json={"task_id": task_id}, timeout=45)
126
+ r.raise_for_status()
127
+ return r.json()
128
+
129
+
130
+ def _step(action: Dict[str, Any]) -> Dict[str, Any]:
131
+ r = requests.post(f"{ENV_URL}/step", json={"action": action}, timeout=45)
132
+ r.raise_for_status()
133
+ return r.json()
134
+
135
+
136
+ def run_task(client: OpenAI, task_id: str) -> None:
137
+ rewards: List[float] = []
138
+ steps = 0
139
+ success = False
140
+ score = 0.0
141
+ log_start(task_id)
142
+ try:
143
+ reset_payload = _reset(task_id)
144
+ obs = reset_payload.get("observation", {})
145
+ done = bool(reset_payload.get("done", False))
146
+
147
+ for i in range(1, MAX_STEPS + 1):
148
+ if done:
149
+ break
150
+ action = _llm_action(client, obs)
151
+ action_str = json.dumps(action, separators=(",", ":"))
152
+ step_payload = _step(action)
153
+ obs = step_payload.get("observation", {})
154
+ reward = float(step_payload.get("reward") or 0.0)
155
+ done = bool(step_payload.get("done", False))
156
+ metadata = (obs or {}).get("metadata", {}) or {}
157
+ last_error = metadata.get("error")
158
+ rewards.append(reward)
159
+ steps = i
160
+ log_step(i, action_str, reward, done, str(last_error) if last_error else None)
161
+
162
+ if done:
163
+ raw_score = metadata.get("grader_score", None)
164
+ if raw_score is not None:
165
+ score = _clamp01(float(raw_score))
166
+ else:
167
+ score = _clamp01(sum(max(0.0, r) for r in rewards) / max(len(rewards), 1))
168
+ success = score > 0.0
169
+ break
170
+ except Exception:
171
+ # Still emit END to keep evaluator parser stable.
172
+ success = False
173
+ finally:
174
+ log_end(success=success, steps=steps, score=score, rewards=rewards)
175
+
176
+
177
+ def main() -> int:
178
+ if not HF_TOKEN:
179
+ print("HF_TOKEN is required", flush=True)
180
+ return 1
181
+ client = OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
182
+ for task in TASKS:
183
+ run_task(client, task)
184
+ return 0
185
+
186
+
187
+ if __name__ == "__main__":
188
+ raise SystemExit(main())
openenv-polypharmacy/.dockerignore DELETED
@@ -1,8 +0,0 @@
1
- .git
2
- .gitignore
3
- **/__pycache__/
4
- **/.pytest_cache/
5
- **/.DS_Store
6
- .env
7
- frontend/node_modules
8
- frontend/dist
 
 
 
 
 
 
 
 
 
openenv-polypharmacy/Dockerfile DELETED
@@ -1,39 +0,0 @@
1
- FROM node:20-alpine AS frontend-builder
2
- WORKDIR /app/frontend
3
- COPY frontend/package*.json ./
4
- RUN npm ci
5
- COPY frontend/ ./
6
- RUN npm run build
7
-
8
- FROM python:3.11-slim
9
-
10
- RUN apt-get update && \
11
- apt-get install -y --no-install-recommends build-essential curl && \
12
- rm -rf /var/lib/apt/lists/*
13
-
14
- WORKDIR /app
15
-
16
- COPY backend/requirements.txt /app/backend/requirements.txt
17
- RUN pip install --no-cache-dir -r /app/backend/requirements.txt
18
-
19
- COPY backend /app/backend
20
- COPY data /app/data
21
- COPY scripts /app/scripts
22
- COPY openenv.yaml /app/openenv.yaml
23
- COPY .env.example /app/.env.example
24
- COPY inference.py /app/inference.py
25
-
26
- COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist
27
-
28
- RUN python3 /app/scripts/preprocess_data.py
29
-
30
- ENV PORT=7860
31
- ENV PYTHONPATH="/app/backend/src:${PYTHONPATH}"
32
- ENV PYTHONUNBUFFERED=1
33
-
34
- EXPOSE 7860
35
-
36
- HEALTHCHECK --interval=30s --timeout=3s --start-period=15s --retries=3 \
37
- CMD curl -f http://localhost:7860/health || exit 1
38
-
39
- CMD ["sh", "-c", "uvicorn backend.main:app --host 0.0.0.0 --port ${PORT:-7860}"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
openenv-polypharmacy/README.md DELETED
@@ -1,245 +0,0 @@
1
- # PolypharmacyEnv
2
-
3
- Monorepo for an OpenEnv-compatible medication safety environment with:
4
-
5
- - a FastAPI backend (`backend/`)
6
- - a React frontend (`frontend/`)
7
- - data assets (`data/`)
8
- - utility scripts (`scripts/`)
9
-
10
- ---
11
-
12
- ## Repository Structure
13
-
14
- ```text
15
- openenv-polypharmacy/
16
- backend/
17
- main.py # ASGI entrypoint (uvicorn target)
18
- requirements.txt # Backend dependencies
19
- Dockerfile # Backend container
20
- src/polypharmacy_env/ # Python package source
21
- api/
22
- app.py # FastAPI/OpenEnv app assembly
23
- server.py # Compatibility import wrapper
24
- routes/agent.py # /agent/suggest route
25
- services/
26
- groq_agent.py # Groq-based action suggestion logic
27
- env_core.py # OpenEnv environment core
28
- models.py # Action/observation/state models
29
- data_loader.py # CSV loading
30
- ddi_simulator.py # DDI and Beers lookups
31
- rewards.py # Reward shaping
32
- graders.py # Task graders
33
- tasks.py # Task/episode selection
34
- tests/ # Backend tests
35
- frontend/
36
- src/ # React UI code
37
- package.json
38
- Dockerfile # Frontend container
39
- data/
40
- lookups/ # drug_metadata.csv, ddi_rules.csv, beers_criteria.csv
41
- processed/ # patients_polypharmacy.csv
42
- scripts/
43
- preprocess_data.py # Synthetic data generation
44
- dev_backend.sh # Local backend run helper
45
- dev_frontend.sh # Local frontend run helper
46
- run_validation.sh # Tests + baseline validation
47
- docker-compose.yml # Full stack orchestration
48
- openenv.yaml # OpenEnv manifest
49
- inference.py # Optional CLI inference baseline
50
- .env.example # Environment template
51
- ```
52
-
53
- ---
54
-
55
- ## What It Does
56
-
57
- The environment simulates elderly polypharmacy review. Agent actions:
58
-
59
- - `query_ddi`
60
- - `propose_intervention`
61
- - `finish_review`
62
-
63
- Supported tasks:
64
-
65
- - `easy_screening`
66
- - `budgeted_screening`
67
- - `complex_tradeoff`
68
-
69
- ---
70
-
71
- ## Prerequisites
72
-
73
- - Python 3.10+
74
- - Node.js 18+ (or 20+ recommended)
75
- - npm
76
- - Docker + Docker Compose (optional, for containerized run)
77
-
78
- ---
79
-
80
- ## Environment Setup
81
-
82
- Create `.env`:
83
-
84
- ```bash
85
- cp .env.example .env
86
- ```
87
-
88
- Set values:
89
-
90
- - `GROQ_API_KEY=...` (required)
91
- - `GROQ_BASE_URL=https://api.groq.com/openai/v1` (recommended)
92
- - `GROQ_MODEL_NAME=llama-3.3-70b-versatile` (recommended)
93
-
94
- ---
95
-
96
- ## Local Run (Recommended During Development)
97
-
98
- ### 1) Install dependencies
99
-
100
- Backend:
101
-
102
- ```bash
103
- pip install -r backend/requirements.txt
104
- ```
105
-
106
- Frontend:
107
-
108
- ```bash
109
- cd frontend
110
- npm install
111
- cd ..
112
- ```
113
-
114
- ### 2) Generate/update synthetic data (if needed)
115
-
116
- ```bash
117
- python scripts/preprocess_data.py
118
- ```
119
-
120
- ### 3) Start services in two terminals
121
-
122
- Terminal A:
123
-
124
- ```bash
125
- ./scripts/dev_backend.sh
126
- ```
127
-
128
- Terminal B:
129
-
130
- ```bash
131
- ./scripts/dev_frontend.sh
132
- ```
133
-
134
- ### 4) Open app
135
-
136
- - Frontend: [http://localhost:5173](http://localhost:5173)
137
- - Backend health: [http://localhost:7860/health](http://localhost:7860/health)
138
-
139
- ---
140
-
141
- ## Docker Run
142
-
143
- Run both services:
144
-
145
- ```bash
146
- docker compose up --build
147
- ```
148
-
149
- Stop:
150
-
151
- ```bash
152
- docker compose down
153
- ```
154
-
155
- Ports:
156
-
157
- - backend: `7860`
158
- - frontend: `5173`
159
-
160
- ---
161
-
162
- ## Hugging Face Spaces Deployment (Docker)
163
-
164
- This repo now includes a **root `Dockerfile`** that builds frontend + backend into one container, so Spaces can host both API and UI together.
165
-
166
- ### 1) Create a new Space
167
-
168
- - Go to [Hugging Face Spaces](https://huggingface.co/new-space)
169
- - Choose **Docker** SDK
170
- - Create the Space
171
-
172
- ### 2) Add Space secrets/variables
173
-
174
- In Space Settings -> Variables and Secrets:
175
-
176
- - Secret: `GROQ_API_KEY`
177
- - Variable: `GROQ_BASE_URL=https://api.groq.com/openai/v1`
178
- - Variable: `GROQ_MODEL_NAME=llama-3.3-70b-versatile`
179
-
180
- ### 3) Push this repository to the Space
181
-
182
- Commit and push all files, including root `Dockerfile`.
183
-
184
- ### 4) Verify after build
185
-
186
- - Space root URL loads the React UI
187
- - `/health` returns healthy status
188
- - OpenEnv endpoints are available (`/reset`, `/step`, `/state`, `/schema`)
189
-
190
- Notes:
191
-
192
- - Container reads `PORT` (defaults to `7860`) which is Space-friendly.
193
- - Frontend static assets are served by FastAPI from `frontend/dist`.
194
-
195
- ---
196
-
197
- ## API Endpoints
198
-
199
- OpenEnv/health:
200
-
201
- - `POST /reset`
202
- - `POST /step`
203
- - `GET /state`
204
- - `GET /health`
205
- - `GET /schema`
206
- - `WS /ws` (stateful session)
207
-
208
- AI helper:
209
-
210
- - `POST /agent/suggest`
211
-
212
- ---
213
-
214
- ## Testing
215
-
216
- Run backend tests:
217
-
218
- ```bash
219
- python -m pytest backend/src/polypharmacy_env/tests -v
220
- ```
221
-
222
- Or run validation script:
223
-
224
- ```bash
225
- ./scripts/run_validation.sh
226
- ```
227
-
228
- ---
229
-
230
- ## Notes
231
-
232
- - OpenEnv HTTP reset/step is stateless; multi-step episode continuity should use websocket (`/ws`).
233
- - The frontend uses websocket for episode continuity and HTTP for AI suggestion.
234
- - AI behavior includes rule-based guardrails to avoid repetitive low-value loops.
235
-
236
- ---
237
-
238
- ## Troubleshooting
239
-
240
- - `ModuleNotFoundError: polypharmacy_env`
241
- - Start backend using `./scripts/dev_backend.sh` from repo root.
242
- - `/agent/suggest` fails
243
- - Check `.env` keys and restart backend.
244
- - UI state looks stale
245
- - Hard refresh browser and click `Reset Episode`.