Spaces:
Paused
Paused
Commit History
-
JSON import: convert to the form layout, load for editing; accept slash/URL DOIs
Import: uploaded rubrics are rearranged into the exact layout the form shows (every dimension block for the type, in order, padded to the default criterion rows), so a partially-specified derivation_required question gets all three blocks instead of one. The button is always enabled and now 'Load into the form'; it collapses the import panel, clears the uploader and shows the result banner at the top of Questions (load/copy/import results now appear there; Save draft/Submit results stay by their buttons). A draft is still saved automatically when DOI + username are filled. DOI: normalize_doi() maps 10.1056/NEJMoa2511478 and doi.org links to the stored 10.1056_nejmoa2511478; applied to the DOI field on edit, every read site, the trials.csv join, and both table searches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>f39cb78 -
Add a global search box (matches all columns) to the trial and reference tables
Reactable-style search: one input at the top of each table; every whitespace-separated term must appear somewhere in the row (any column, case-insensitive), so "oncology phase 3 nejm" works. The old per-column boxes are kept as optional filters inside a "Filter by column" expander and combine with the global query (AND). Shared logic in lib.ui.row_matches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>878d20c
-
Import externally written questions from a JSON file as the first draft
People sometimes write their questions outside the intake system. The form now has a toggle-gated 'Import questions from a JSON file' panel: - lib/schema.py: validate_and_normalize_prompts() checks an uploaded payload (bare list, {prompts: [...]}, or a full saved record) and reports precise per-field errors; it leniently normalizes enum casing, assigns missing ids, maps an unknown design_element to Others, generates empty dimension blocks when rubrics are omitted, fills importance/scoring defaults, and converts the legacy single-criterion rubric shape. example_import_json() provides a downloadable template that round-trips through the validator. - app.py: template download + field reference + uploader; errors block the import, warnings are shown, valid input is previewed read-only, and 'Import as first draft' populates the form and saves a draft under the DOI + username (app-scope rerun so the questions fragment refreshes). Verified: template validates cleanly and n…2a9e0f5
-
Actually make the Question box grow: native content height + CSS that wins
Root cause of the no-op: Streamlit sets the textarea height as an INLINE style, so the stylesheet's field-sizing rule never applied. - lib/ui.py feature-detects height='content' (Streamlit >=1.44) from the docstring and uses it when available, falling back to the pixel estimate on older versions. - The CSS now overrides the inline height (height: auto !important), gated behind @supports (field-sizing: content) so unsupported browsers keep Streamlit's height instead of collapsing. - Bump the Space's sdk_version 1.39.0 -> 1.58.0 to match what we run locally, so the native content height is available there too.1fd676d -
Grow the Question/criterion boxes with their content
height='content' only exists in Streamlit >=1.44 and the Space runs 1.39, so lib/ui.py adds autoheight(): a pixel height estimated from the text (clamped 80-420px), used by the Question box in the form and the Admin read-only view and by the criterion boxes. A one-line CSS rule (field-sizing: content) additionally grows the box live while typing on browsers that support it; where it isn't supported the computed height still fits the saved text.f2ce67c
-
Revert "Password-gate the intake form (separate from the reviewer password)"
This reverts commit 60008e1356b4c36258cbfd291e6a3bdcd44fbaba.7765139 -
Password-gate the intake form (separate from the reviewer password)
- storage: FORM_PASSWORD env var + check_form_password(), mirroring the admin gate; unset means open (dev mode). - app.py: password screen before the form renders, same pattern as the Admin page. The two passwords are independent — neither opens the other's page. - README: document FORM_PASSWORD in the Space secrets table.60008e1
-
Intake form: browse other people's submissions as a reference
- storage.list_reference_submissions(): latest version of every submission, one file read each and no review loading (much lighter than list_submissions); drafts and agent runs excluded, real DOI preserved. - app.py: toggle-gated 'See how others filled the form' fragment with a dropdown (user · DOI · #Q · date), a read-only markdown view of the questions and rubrics (incl. importance/scoring, with legacy fallback), a Refresh button that clears the 5-min cache, and a 'Copy these questions into my form' button that populates the editor and triggers an app-scope rerun so the (separate) questions fragment picks it up.1b1eb38 -
Turn Run agent into a real agentic loop with a workspace and tools
The page was a single LLM call with the whole SAP in the prompt. Now: - lib/tools.py: a Workspace sandboxed to one root, exposing list_files, read_file (line-paged), grep, write_file and run_r (Rscript with a timeout). Paths are confined chroot-style; traversal is rejected. - lib/agent_loop.py: the tool-use loop for both providers. The agent gets the document as a file (sap.txt/protocol.txt with page markers) instead of ~120K tokens of prompt, greps/reads it on demand, writes output.json + output.R into the workspace, and can run the R script and fix it before finishing. Tracks iterations, token usage and a full transcript, with an iteration cap. - Page: builds a temp workspace per run, streams each tool call to a live status log, collects the files the agent produced, and saves the transcript/usage alongside the outputs. - apt.txt installs r-base-core so run_r works on the Space; without R the tool says so and the agent continues. Verified with stubbed SDKs (no API …74239a8 -
Run agent: cascading provider -> model -> effort -> key; current models
Verified against the providers' docs (Aug 2026): - Add a MODELS registry in lib/agent.py — Anthropic: claude-opus-5 (default), claude-fable-5, claude-sonnet-5, claude-haiku-4-5-20251001, plus 4.8/4.6 legacy; OpenAI: gpt-5.6-sol/terra/luna. Replaces the stale gpt-4o/opus-4-8 suggestions. - Effort is passed correctly per provider: Anthropic needs output_config={'effort': ...} (NOT a top-level arg), OpenAI uses reasoning_effort. Per-model level lists (Haiku 4.5 supports none; Sonnet 4.6 has no xhigh) drive the picker, and high-effort Anthropic runs get a larger max_tokens since thinking counts against it. - Page now selects provider, then model (or a custom id), then reasoning effort, then the matching API key — one model per run. - save_agent_run records provider + effort; past runs show the effort used. - run_llm.py gains --effort and updated DEFAULT_MODELS.7fccc75 -
Add 'Run agent' page: run LLMs on a submitted version and view results
- New pages/2_Run_Agent.py: pick a submission version, choose models (claude-* -> Anthropic, gpt-*/o*-* -> OpenAI, or type a custom id), paste your OWN API key (never stored), and run the trial-statistician prompt against the trial's SAP/protocol text. Answers render per question with output.R and download buttons; past runs are browsable. - New lib/agent.py: single source of truth for the prompt, SAP loading, prompt block, model calls (explicit api_key) and response parsing. - llm/run_llm.py now imports from lib/agent.py instead of duplicating the prompt (verified: --help and a full offline run still work). - storage: save_agent_run/list_agent_runs write to submissions/<DOI>__<user>/agent_runs/<stamp>__<model>.json; agent runs are excluded from list_versions and the admin list_submissions. - requirements.txt: add anthropic + openai for the Space.9ceb217
-
Drafts are timestamped; Load draft restores the latest
- save_draft writes drafts/<stamp>.json (keeps history) instead of overwriting a single draft.json; get_draft returns the newest draft. - Messages updated; still excluded from version/admin listings.b92c42a -
Persist Save draft to a drafts/ subfolder + add Load draft
- save_draft() writes submissions/<DOI>__<username>/drafts/draft.json (overwrite); get_draft() reads it back. - Form: Save draft now persists (needs DOI + username); new 'Load draft' button next to 'Find versions' restores it. - Drafts are excluded from list_versions and the admin list_submissions (paths containing /drafts/ are filtered out). - Refactored the load path into a shared _populate_form() used by both version load and draft load.2e38fe8
-
Add per-criterion scoring (Add/Deduct points)
Each criterion now has a Scoring selectbox (Add / Deduct) next to Importance, defaulting to Add. Threaded through build/load/seed, content hash, and the admin read-only display.e53de88
-
Remove the tolerance field from criteria
Each criterion is now just criterion text + importance. Dropped tolerance from the Criterion type, content hash, form UI, build/load, and the admin display (old saved tolerances are simply ignored on load).842481b
-
Consistent casing: importance High/Medium/Low; capitalize field labels
- IMPORTANCE_OPTIONS now title-case (High/Medium/Low); load seeding maps old HIGH/medium/low case-insensitively. - Capitalize visible labels: Username, Design element, Question type, Question, Criterion N, Importance, Tolerance (form + admin read-only).086c738 -
Method dimension: 3 default criteria (extras optional, empty ones not saved)
- dimensions_for_type carries a default_criteria count; Method=3, others=1. - Extra criterion rows are labeled '(optional)'. - _build_prompts drops any criterion with empty text, so unfilled optional rows are not saved.042edfb -
Rubrics: drop Reproducibility, multiple criteria per dimension, importance
- derivation_required now has 3 dimensions on output.json (Inputs used, Calculated value, Method) — Reproducibility/output.R removed. - Each dimension holds multiple criteria; each criterion has its own criterion text, importance (HIGH/medium/low), and tolerance ('points' replaced by an importance selectbox). - Stable criterion ids (cid) keep widget state correct across add/remove. - Admin renders criteria read-only; content hash + load handle the new shape (with backward-compat for old single-criterion records).23b513a
-
Invalidate a question's review status when the question is edited
- add schema.question_content_hash(): stable hash of a question's content (excludes id). - add_review() snapshots the reviewed question's content hash. - Form compares the current question's content hash to the latest review's stored hash; if they differ, shows '✏️ Modified since last review — resubmit for re-review' instead of the stale Pass/needs_fix badge.3475963 -
Form: show full review history across all versions (not just loaded one)
- add storage.pair_reviews(trial_id, username) returning all reviews across every version, tagged with version. - Form fetches it on 'Find versions'; overall feedback box and per-question feedback now show the whole history with version tags, and the per-question Pass/Needs fix badge reflects the latest review across versions.916f12b -
Per-question reviews; render questionnaire in app layout
- add_review() takes question_id; reviews can target a specific question or be overall (empty question_id). Review file name carries the question tag. - Admin renders each question in the form's layout (read-only) with its own review thread + 'Add review for this question' form. - Overall status now ignores per-question reviews (driven by overall reviews on the latest version). Overall review history shown separately. - Removed unused _legacy_base_id; README updated.b67309b -
Reviewer sees review history across all versions
- Admin still shows one row per trial (latest version), but now displays the full review history spanning every version, each review tagged with the version it was made on. - add list_pair_reviews(); list_submissions() includes all_reviews per trial. - Current status still reflects the latest version's most recent review; new reviews attach to the latest version.f3a87c7 -
Admin shows only the latest version per trial
- list_submissions() now dedupes by (trial_id, username), keeping the newest version, so reviewers see one row per trial instead of every version. - Submitters still see/load all their versions via 'Find versions' on the form. - Admin caption + README updated.fa43f54 -
Show reviews when retrieving a version
- list_versions() now includes each version's status, review_count, and full reviews timeline (single repo listing, no extra round-trips). - Form version selector shows status + review count per version. - After 'Load selected version', the submitter sees that version's reviewer feedback (status + reviewer + comment) so they can edit accordingly.5b2ebd8 -
Save every submit as a new version; let users pick a version to load
- save_submission() now writes submissions/<trial>__<user>/<stamp>.json per submit (no overwrite); full version history is kept. - add list_versions(trial_id, username); reviews are keyed per-version under reviews/<trial>__<user>/<stamp>/. - Form: 'Find versions' lists all versions for a trial_id+username; a selectbox + 'Load selected version' pulls the chosen one in for editing. - Admin/list_submissions treats each version as its own row with its own reviews; README + Python loader updated for the nested layout.a9aec68 -
Make submissions editable; load by trial_id + username
- One submission file per (trial_id, username): submitting again updates the same file (upsert) instead of creating a new timestamped one. - save_submission() preserves createdAt, sets updatedAt on each save. - Add get_submission_by_key() / submission_id_for(); form gets a 'Load existing submission' button that pulls a prior submission back in for editing. - Submit no longer clears the form, so editing can continue. - Question widgets keyed by a form_nonce bumped on load, so loaded values actually populate the fields (Streamlit widget-state gotcha). - Admin list sorts by updatedAt and exposes it.089c7d1
-
Store each review as a separate file under reviews/<submission>/
- Submissions are now immutable; reviews live in their own files, so concurrent reviews by different people never conflict (no read-modify-write). - add_review() writes reviews/<base>/<stamp>__<reviewer>.json. - list_submissions() derives current status from the latest review and bundles the full review timeline + submission record per item (single repo listing). - Admin page reads bundled data; shows timeline + 'Add a review' form. - README documents the new submissions/ + reviews/ layout and Python loader.eb0abff -
Support multiple reviews per submission with full history
- Submissions now carry a review_history array; each review appends an entry (at / reviewer / status / note) rather than overwriting. - Top-level status/reviewer/reviewerNote/reviewedAt mirror the latest review for filtering and sorting. - add_review() replaces update_submission(); Admin page renders the full review timeline (newest first) and an 'Add a review' form (reviewer name required). - list_submissions summaries include review_count, shown in the list label.62f8380 -
Add Streamlit/HF-Space version; move Next.js code into legacy/
- New entry point: app.py (Streamlit intake form). - pages/1_Admin.py: password-gated review console (status filter + edit). - lib/storage.py: Hugging Face Dataset I/O via huggingface_hub.HfApi, with local ./data/ fallback for dev. lib/schema.py: form constants. - README.md gains HF Space frontmatter (sdk: streamlit, app_file: app.py). - All Next.js/TS code moved to legacy/ (no deletions, history preserved).2aabc58 -
Switch storage to Hugging Face Dataset; add /admin review console
64e3b80 -
Add free-text input when design_element is 'Others'
da4c21e -
Dropdowns for design_element/question_type; auto-generate rubrics from question type
4327036
-
Initial commit: clinical trial intake form
8a03f63