Default Dream QA to Modal endpoints
#23
by ADJCJH - opened
README.md
CHANGED
|
@@ -39,7 +39,7 @@ This is not a therapy, diagnosis, or prophecy product.
|
|
| 39 |
- `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
|
| 40 |
- `openbmb/MiniCPM5-1B` for dream summary, follow-up questions, interpretation, and Today Tip generation.
|
| 41 |
- A small ASR adapter may be used only for voice transcription.
|
| 42 |
-
- The app defaults to
|
| 43 |
- Optional Ollama adapters are included for local MiniCPM testing.
|
| 44 |
- MiniCPM prompts are language-aware: English by default, Chinese when the user chooses `中文`.
|
| 45 |
|
|
@@ -107,7 +107,7 @@ The public Space stays lightweight and can call private Modal endpoints through
|
|
| 107 |
|
| 108 |
Set these only as Hugging Face Space repository secrets or local shell variables. Do not store values in `.env`, docs, logs, screenshots, or git. Missing endpoints or route failures fall back to deterministic demo behavior.
|
| 109 |
|
| 110 |
-
The Gradio UI defaults to `
|
| 111 |
|
| 112 |
The Hugging Face Space may run on ZeroGPU for hackathon hardware eligibility. `dream_customs.zerogpu` registers a lightweight `@spaces.GPU` startup probe so ZeroGPU accepts the app, but real MiniCPM inference still happens on the private Modal backend.
|
| 113 |
|
|
|
|
| 39 |
- `openbmb/MiniCPM-V-4.6` for image/sketch/note understanding.
|
| 40 |
- `openbmb/MiniCPM5-1B` for dream summary, follow-up questions, interpretation, and Today Tip generation.
|
| 41 |
- A small ASR adapter may be used only for voice transcription.
|
| 42 |
+
- The app defaults to the Modal-hosted MiniCPM route when endpoint secrets are configured, with stable demo fallback when hosted routes are unavailable.
|
| 43 |
- Optional Ollama adapters are included for local MiniCPM testing.
|
| 44 |
- MiniCPM prompts are language-aware: English by default, Chinese when the user chooses `中文`.
|
| 45 |
|
|
|
|
| 107 |
|
| 108 |
Set these only as Hugging Face Space repository secrets or local shell variables. Do not store values in `.env`, docs, logs, screenshots, or git. Missing endpoints or route failures fall back to deterministic demo behavior.
|
| 109 |
|
| 110 |
+
The Gradio UI defaults to `modal` for both text and vision backends, so a configured Space enters the private Modal MiniCPM endpoints first. The `demo` backend remains available in developer settings and as the deterministic fallback path when hosted routes are missing or fail.
|
| 111 |
|
| 112 |
The Hugging Face Space may run on ZeroGPU for hackathon hardware eligibility. `dream_customs.zerogpu` registers a lightweight `@spaces.GPU` startup probe so ZeroGPU accepts the app, but real MiniCPM inference still happens on the private Modal backend.
|
| 113 |
|
docs/handoff.md
CHANGED
|
@@ -181,7 +181,7 @@ Hugging Face Space Gradio UI
|
|
| 181 |
-> MiniCPM5-1B text generation and MiniCPM-V-4.6 visual clue extraction
|
| 182 |
```
|
| 183 |
|
| 184 |
-
The Space defaults to `
|
| 185 |
|
| 186 |
The public Space can run on ZeroGPU after `dream_customs.zerogpu` registers the lightweight `@spaces.GPU` startup probe. Modal credits still pay for the hidden GPU backend; the ZeroGPU probe is only there to make the HF hardware setting valid for this Gradio frontend.
|
| 187 |
|
|
|
|
| 181 |
-> MiniCPM5-1B text generation and MiniCPM-V-4.6 visual clue extraction
|
| 182 |
```
|
| 183 |
|
| 184 |
+
The Space defaults to `modal` for both text and vision backends, so a configured Space enters the private Modal MiniCPM endpoints first. The `model` auto route remains available for compatibility, and the `demo` backend remains available in developer settings as the deterministic fallback path.
|
| 185 |
|
| 186 |
The public Space can run on ZeroGPU after `dream_customs.zerogpu` registers the lightweight `@spaces.GPU` startup probe. Modal credits still pay for the hidden GPU backend; the ZeroGPU probe is only there to make the HF hardware setting valid for this Gradio frontend.
|
| 187 |
|
docs/smoke/2026-06-10-modal-default-entrypoint-smoke.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modal Default Entrypoint Smoke - 2026-06-10
|
| 2 |
+
|
| 3 |
+
## Scope
|
| 4 |
+
|
| 5 |
+
Verify that Dream QA now enters the Modal-hosted MiniCPM route by default for text and vision while preserving deterministic fallback behavior when hosted endpoint secrets are not available in the local shell.
|
| 6 |
+
|
| 7 |
+
No endpoint URLs, bearer tokens, Hugging Face tokens, Modal tokens, or secret values were printed or recorded.
|
| 8 |
+
|
| 9 |
+
## Local Default Action Smoke
|
| 10 |
+
|
| 11 |
+
Command:
|
| 12 |
+
|
| 13 |
+
```bash
|
| 14 |
+
.venv/bin/python - <<'PY'
|
| 15 |
+
import json
|
| 16 |
+
from dream_customs.ui.actions import submit_dream_action, skip_to_card_action
|
| 17 |
+
|
| 18 |
+
state, view_json = submit_dream_action(
|
| 19 |
+
dream_text='I dreamed of a late elevator with melted buttons.',
|
| 20 |
+
mood='Uneasy',
|
| 21 |
+
)
|
| 22 |
+
view = json.loads(view_json)
|
| 23 |
+
print({
|
| 24 |
+
'submit_status': view['status'],
|
| 25 |
+
'submit_phase': view['phase'],
|
| 26 |
+
'text_backend': view['debug']['text_backend'],
|
| 27 |
+
'vision_backend': view['debug']['vision_backend'],
|
| 28 |
+
'question_present': bool(view['question']),
|
| 29 |
+
})
|
| 30 |
+
|
| 31 |
+
state, view_json = skip_to_card_action(state)
|
| 32 |
+
view = json.loads(view_json)
|
| 33 |
+
print({
|
| 34 |
+
'card_status': view['status'],
|
| 35 |
+
'card_phase': view['phase'],
|
| 36 |
+
'card_title': view['card_title'],
|
| 37 |
+
'tip_mentions_elevator': 'elevator' in view['card_text'].lower(),
|
| 38 |
+
})
|
| 39 |
+
PY
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
Observed:
|
| 43 |
+
|
| 44 |
+
```text
|
| 45 |
+
{'submit_status': 'ask', 'submit_phase': 'ask', 'text_backend': 'modal', 'vision_backend': 'modal', 'question_present': True}
|
| 46 |
+
{'card_status': 'tip', 'card_phase': 'tip', 'card_title': 'Today Tip', 'tip_mentions_elevator': True}
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
Result: pass. The default backend values are `modal` and the local no-secret path still falls back to a usable dream Q&A flow.
|
| 50 |
+
|
| 51 |
+
## Test Evidence
|
| 52 |
+
|
| 53 |
+
- Focused regression: `.venv/bin/python -m pytest tests/test_ui_actions.py::test_mobile_defaults_to_modal_backends tests/test_app_logic.py::test_defaults_use_modal_model_entrypoint -q`
|
| 54 |
+
- Full suite: `.venv/bin/python -m pytest -q` -> 98 passed, 2 warnings.
|
| 55 |
+
- Today Tip eval: `.venv/bin/python scripts/evaluate_today_tip_quality.py` -> 11 cases passed.
|
| 56 |
+
- Whitespace check: `git diff --check` -> passed.
|
| 57 |
+
- Local Gradio config: `Text generation` default value is `modal`; `Image understanding` default value is `modal`.
|
| 58 |
+
- Public Space check: pending.
|
docs/superpowers/plans/2026-06-10-modal-default-entrypoint.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modal Default Entrypoint Plan - 2026-06-10
|
| 2 |
+
|
| 3 |
+
## Goal
|
| 4 |
+
|
| 5 |
+
Make the public Dream QA default backend route explicit: text and image understanding should enter through the Modal-hosted MiniCPM path by default, while deterministic demo behavior remains available as a user-selectable and failure fallback route.
|
| 6 |
+
|
| 7 |
+
## Guardrails
|
| 8 |
+
|
| 9 |
+
- Do not store or print endpoint URLs, tokens, HF tokens, Modal tokens, or secret values.
|
| 10 |
+
- Keep text-only and demo fallback behavior working when hosted endpoints are absent, cold, slow, or failing.
|
| 11 |
+
- Do not replace the Hugging Face Space Gradio frontend with Modal.
|
| 12 |
+
- Do not add non-MiniCPM default models.
|
| 13 |
+
|
| 14 |
+
## Tasks
|
| 15 |
+
|
| 16 |
+
- [x] Add a regression test that expects default text and vision backends to be `modal`.
|
| 17 |
+
- [x] Change the default text and vision backend constants to `modal`.
|
| 18 |
+
- [x] Make the Advanced backend labels show Modal as the intentional default entrypoint.
|
| 19 |
+
- [x] Update docs to describe Modal as the default configured route and demo as fallback.
|
| 20 |
+
- [x] Run focused tests, full pytest, eval, local smoke, and whitespace checks.
|
| 21 |
+
- [ ] Commit, push, sync to Hugging Face Space, merge, and re-check the public app.
|
| 22 |
+
|
| 23 |
+
## Progress
|
| 24 |
+
|
| 25 |
+
- Focused regression tests pass after changing defaults to `modal`.
|
| 26 |
+
- Local default action smoke passes without configured endpoint secrets; the app records `modal` in debug and falls back to a usable Q&A flow.
|
| 27 |
+
- Full pytest passes with 98 tests; Today Tip eval passes 11 cases; `git diff --check` passes.
|
dream_customs/defaults.py
CHANGED
|
@@ -1,3 +1,2 @@
|
|
| 1 |
-
DEFAULT_TEXT_BACKEND = "
|
| 2 |
-
DEFAULT_VISION_BACKEND = "
|
| 3 |
-
|
|
|
|
| 1 |
+
DEFAULT_TEXT_BACKEND = "modal"
|
| 2 |
+
DEFAULT_VISION_BACKEND = "modal"
|
|
|
dream_customs/ui/app.py
CHANGED
|
@@ -474,9 +474,9 @@ def build_demo() -> gr.Blocks:
|
|
| 474 |
text_backend = gr.Dropdown(
|
| 475 |
label="Text generation",
|
| 476 |
choices=[
|
|
|
|
| 477 |
("Auto: configured Space model", "model"),
|
| 478 |
("Demo: stable sample data", "demo"),
|
| 479 |
-
("Modal/API: private endpoint", "modal"),
|
| 480 |
("Local Ollama", "ollama"),
|
| 481 |
],
|
| 482 |
value=DEFAULT_TEXT_BACKEND,
|
|
@@ -484,9 +484,9 @@ def build_demo() -> gr.Blocks:
|
|
| 484 |
vision_backend = gr.Dropdown(
|
| 485 |
label="Image understanding",
|
| 486 |
choices=[
|
|
|
|
| 487 |
("Auto: configured vision model", "model"),
|
| 488 |
("Demo: skip image model", "demo"),
|
| 489 |
-
("Modal/API: private endpoint", "modal"),
|
| 490 |
("Local Ollama", "ollama"),
|
| 491 |
],
|
| 492 |
value=DEFAULT_VISION_BACKEND,
|
|
|
|
| 474 |
text_backend = gr.Dropdown(
|
| 475 |
label="Text generation",
|
| 476 |
choices=[
|
| 477 |
+
("Modal: MiniCPM endpoint", "modal"),
|
| 478 |
("Auto: configured Space model", "model"),
|
| 479 |
("Demo: stable sample data", "demo"),
|
|
|
|
| 480 |
("Local Ollama", "ollama"),
|
| 481 |
],
|
| 482 |
value=DEFAULT_TEXT_BACKEND,
|
|
|
|
| 484 |
vision_backend = gr.Dropdown(
|
| 485 |
label="Image understanding",
|
| 486 |
choices=[
|
| 487 |
+
("Modal: MiniCPM-V endpoint", "modal"),
|
| 488 |
("Auto: configured vision model", "model"),
|
| 489 |
("Demo: skip image model", "demo"),
|
|
|
|
| 490 |
("Local Ollama", "ollama"),
|
| 491 |
],
|
| 492 |
value=DEFAULT_VISION_BACKEND,
|
tests/test_app_logic.py
CHANGED
|
@@ -12,9 +12,15 @@ from dream_customs.app_logic import (
|
|
| 12 |
seal_pact_action,
|
| 13 |
start_declaration_action,
|
| 14 |
)
|
|
|
|
| 15 |
from dream_customs.models import HostedASRClient, HostedMiniCPMTextClient, HostedMiniCPMVisionClient
|
| 16 |
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
def test_run_customs_once_generates_demo_outputs():
|
| 19 |
negotiation, pact_text, html, debug_json = run_customs_once(
|
| 20 |
dream_text="I dreamed of a late elevator.",
|
|
|
|
| 12 |
seal_pact_action,
|
| 13 |
start_declaration_action,
|
| 14 |
)
|
| 15 |
+
from dream_customs.defaults import DEFAULT_TEXT_BACKEND, DEFAULT_VISION_BACKEND
|
| 16 |
from dream_customs.models import HostedASRClient, HostedMiniCPMTextClient, HostedMiniCPMVisionClient
|
| 17 |
|
| 18 |
|
| 19 |
+
def test_defaults_use_modal_model_entrypoint():
|
| 20 |
+
assert DEFAULT_TEXT_BACKEND == "modal"
|
| 21 |
+
assert DEFAULT_VISION_BACKEND == "modal"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
def test_run_customs_once_generates_demo_outputs():
|
| 25 |
negotiation, pact_text, html, debug_json = run_customs_once(
|
| 26 |
dream_text="I dreamed of a late elevator.",
|
tests/test_ui_actions.py
CHANGED
|
@@ -8,12 +8,12 @@ from dream_customs.ui.app import _reset
|
|
| 8 |
from dream_customs.ui.copy import DEFAULT_MOOD, PROCESSING_NOTE
|
| 9 |
|
| 10 |
|
| 11 |
-
def
|
| 12 |
_state, view_json = initial_mobile_state()
|
| 13 |
view = json.loads(view_json)
|
| 14 |
|
| 15 |
-
assert view["debug"]["text_backend"] == "
|
| 16 |
-
assert view["debug"]["vision_backend"] == "
|
| 17 |
|
| 18 |
|
| 19 |
def test_runtime_settings_are_collapsed_for_public_flow():
|
|
|
|
| 8 |
from dream_customs.ui.copy import DEFAULT_MOOD, PROCESSING_NOTE
|
| 9 |
|
| 10 |
|
| 11 |
+
def test_mobile_defaults_to_modal_backends():
|
| 12 |
_state, view_json = initial_mobile_state()
|
| 13 |
view = json.loads(view_json)
|
| 14 |
|
| 15 |
+
assert view["debug"]["text_backend"] == "modal"
|
| 16 |
+
assert view["debug"]["vision_backend"] == "modal"
|
| 17 |
|
| 18 |
|
| 19 |
def test_runtime_settings_are_collapsed_for_public_flow():
|