Frontend v0.3 + GCP integration (Vertex AI / Cloud Run / BigQuery)#1
Merged
Conversation
- Persona picker (Hardball / Slippery / Stone Wall) before each session - Adaptive difficulty badge + indicator in session header (Level 1-3) - Voice mode (Web Speech API, en-GB, interim → input field, pulse ring) - Streak counter + sparklines on Dashboard - Replay Turning Point modal with exchange replay - Evidence-anchored debrief with root cause panel - negotiation_ma_indemnity_cap_uklaw_v1.json — M&A SPA English law - difficult_client_ip_uk_v1.json — CDPA 1988 s.11(1) copyright - hot_seat_v1.json teammate update (GDPR Art 33, polished pressure lines) - db.py: adaptive difficulty + persona columns; state.py/main.py updated - adversary.py: per-difficulty modifier + per-arena system prompt builders - Concession gate referee now calibrated to difficulty level
- Dockerfile + .dockerignore for Cloud Run deployment (europe-west2) - llm.py: auto-route to Vertex AI Gemini when model starts with 'gemini' and GCP_PROJECT is configured; OpenRouter remains fallback - config.py: GCP_PROJECT, VERTEX_LOCATION; JUDGE_MODEL auto-defaults to gemini-2.0-flash-001 when GCP_PROJECT is set - data/bigquery.py: BigQuery corpus layer (whetstone.corpus table); graceful fallback to SQLite when GCP not configured - main.py: ensure_table() on startup; /api/health reports GCP status; corpus search prefers BigQuery over SQLite - requirements.txt: google-cloud-aiplatform>=1.60.0, google-cloud-bigquery>=3.11.0 - .gitignore: carve out backend/requirements.txt from *.txt rule
There was a problem hiding this comment.
Pull request overview
This PR upgrades the Whetstone single-file React frontend to “v0.3” (personas, voice mode, adaptive difficulty UI, streak/progress widgets, turning-point replay) and adds GCP prize-track integrations for Cloud Run deployment, Vertex AI (Gemini routing), and BigQuery-backed corpus search with SQLite fallback.
Changes:
- Frontend: persona picker + voice input + richer debrief/dashboard UX (turning-point replay, streak, sparklines, difficulty badge).
- Backend: adaptive difficulty + persona persisted on sessions; optional Vertex AI + BigQuery integration; health endpoint reports GCP status.
- Deployment: Cloud Run-ready Dockerfile/.dockerignore and Python requirements.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/index.html | Frontend v0.3 UX updates (personas, voice mode, debrief/dashboard improvements). |
| Dockerfile | Adds container build/run for Cloud Run (uvicorn entrypoint). |
| backend/requirements.txt | Introduces backend dependency set (FastAPI/uvicorn/OpenAI + GCP libs). |
| backend/app/state.py | Adds persona + adaptive difficulty to session creation/load. |
| backend/app/scenarios/negotiation_ma_indemnity_cap_uklaw_v1.json | Adds new UK-law negotiation scenario (SPA liability cap/basket/disclosure). |
| backend/app/scenarios/hot_seat_v1.json | Updates Hot Seat prompt lines/proposition wording. |
| backend/app/scenarios/difficult_client_ip_uk_v1.json | Adds new UK copyright “difficult client” scenario. |
| backend/app/main.py | Adds BigQuery startup init + GCP status in health + BQ-first corpus search. |
| backend/app/llm.py | Adds routing: Vertex AI Gemini when model starts with gemini and GCP is configured. |
| backend/app/engines/adversary.py | Adds adaptive difficulty + runtime persona override into adversary prompts/referee. |
| backend/app/db.py | Extends sessions schema and adds adaptive difficulty computation. |
| backend/app/data/bigquery.py | Adds BigQuery corpus layer with graceful SQLite fallback. |
| backend/app/config.py | Adds GCP project/location config and defaults judge model to Gemini when GCP is set. |
| .gitignore | Un-ignores backend/requirements.txt despite *.txt rule. |
| .dockerignore | Defines Docker build context exclusions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+10
to
+12
| *.md | ||
| *.txt | ||
Comment on lines
+63
to
68
| def session_create(sid: str, scenario_id: str, lawyer_id: str, | ||
| persona: str = "Hardball", difficulty: int = 2) -> None: | ||
| _conn().execute( | ||
| "INSERT OR REPLACE INTO sessions (id, scenario_id, lawyer_id) VALUES (?,?,?)", | ||
| (sid, scenario_id, lawyer_id)) | ||
| "INSERT OR REPLACE INTO sessions (id, scenario_id, lawyer_id, persona, difficulty) VALUES (?,?,?,?,?)", | ||
| (sid, scenario_id, lawyer_id, persona, difficulty)) | ||
| _conn().commit() |
Comment on lines
224
to
+228
| @app.get("/api/corpus/search") | ||
| def corpus_search(clause_type: str = "liability_cap", limit: int = 5): | ||
| return {"results": db.corpus_search(clause_type, limit)} | ||
| # Prefer BigQuery when GCP is configured, fall back to SQLite | ||
| results = bq.search(clause_type, limit) or db.corpus_search(clause_type, limit) | ||
| return {"results": results, "source": "bigquery" if bq.active() else "sqlite"} |
Comment on lines
+55
to
57
| const {useState,useEffect,useRef,useCallback,useMemo}=React; | ||
| const API="http://localhost:8000"; | ||
|
|
Comment on lines
+331
to
+343
| {msgs.map((m,i)=>{ | ||
| const turnNum=Math.ceil((i)/2)+1; | ||
| const isHighlight=highlightTurn&&turnNum===highlightTurn&&m.role==="user"; | ||
| return( | ||
| <div key={i} id={`turn-${turnNum}`} | ||
| className={cl("flex arise",m.role==="user"?"justify-end":"justify-start")}> | ||
| <div className={cl("max-w-[80%] rounded-2xl px-4 py-3 text-sm leading-relaxed", | ||
| m.role==="user"?"bg-blade text-ink font-medium":"bg-panel2 border border-edge", | ||
| isHighlight&&"ring-2 ring-gold ring-offset-1 ring-offset-panel")}> | ||
| <div className={cl("text-[10px] uppercase tracking-widest font-semibold mb-1.5", | ||
| m.role==="user"?"text-ink/60":"text-muted")}> | ||
| {m.role==="user"?"You":"Opposing Counsel"} | ||
| {m.role==="user"&&<span className="ml-1 opacity-50">Turn {Math.ceil(i/2)+1}</span>} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Dockerfile+.dockerignore→ Cloud Run deployment ineurope-west2llm.py→ auto-routes to Vertex AI Gemini 2.0 Flash whenGCP_PROJECTis set (judge/scoring calls); OpenRouter Claude stays for main adversarydata/bigquery.py→ BigQuerywhetstone.corpustable with graceful SQLite fallbackconfig.py→GCP_PROJECT,VERTEX_LOCATION;JUDGE_MODELdefaults togemini-2.0-flash-001when GCP is configured/api/healthreportsgcp.vertex_aiandgcp.bigquerystatusDeploy to Cloud Run
Test plan
/api/health→gcp.vertex_ai: trueafter adding GCP_PROJECT to env🤖 Generated with Claude Code