Update ARCHITECTURE.md and roadmap to reflect v0.4.0
Architecture doc: add notifications table, v0.4.0 feature history, updated beat schedule (fetch-actions-active-bills, dispatch-notifications), expanded admin API table (17 endpoints), new /api/notifications section, BriefPanel and ActionTimeline component docs, chamberBadgeColor utility, migrations 0010-0011, live LLM model picker endpoint, queue routing corrections. Roadmap: mark Phase 1 notifications complete, check off LLM model picker, admin health panel, chamber badges, action history fallback, backfill all actions, brief regeneration UI; remove completed Phase 1 section. Authored-By: Jack Levy
This commit is contained in:
235
ARCHITECTURE.md
235
ARCHITECTURE.md
@@ -140,7 +140,7 @@ OPENAI_MODEL=gpt-4o
|
||||
ANTHROPIC_API_KEY=
|
||||
ANTHROPIC_MODEL=claude-opus-4-6
|
||||
GEMINI_API_KEY=
|
||||
GEMINI_MODEL=gemini-1.5-pro
|
||||
GEMINI_MODEL=gemini-2.0-flash
|
||||
OLLAMA_BASE_URL=http://host.docker.internal:11434
|
||||
OLLAMA_MODEL=llama3.1
|
||||
|
||||
@@ -247,17 +247,58 @@ Indexes: `bill_id`, `topic_tags` (GIN for JSONB containment queries)
|
||||
### `members`
|
||||
Primary key: `bioguide_id` (Congress.gov canonical identifier).
|
||||
|
||||
| Column | Type |
|
||||
|---|---|
|
||||
| bioguide_id | varchar (PK) |
|
||||
| name | varchar |
|
||||
| first_name / last_name | varchar |
|
||||
| party | varchar |
|
||||
| state | varchar |
|
||||
| chamber | varchar |
|
||||
| district | varchar (nullable, House only) |
|
||||
| photo_url | varchar (nullable) |
|
||||
| created_at / updated_at | timestamptz |
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| bioguide_id | varchar (PK) | |
|
||||
| name | varchar | Stored as "Last, First" |
|
||||
| first_name / last_name | varchar | |
|
||||
| party | varchar | |
|
||||
| state | varchar | |
|
||||
| chamber | varchar | |
|
||||
| district | varchar (nullable) | House only |
|
||||
| photo_url | varchar (nullable) | |
|
||||
| official_url | varchar (nullable) | Member's official website |
|
||||
| congress_url | varchar (nullable) | congress.gov profile link |
|
||||
| birth_year | varchar(10) (nullable) | |
|
||||
| address | varchar (nullable) | DC office address |
|
||||
| phone | varchar(50) (nullable) | DC office phone |
|
||||
| terms_json | json (nullable) | Array of `{congress, startYear, endYear, chamber, partyName, stateName, district}` |
|
||||
| leadership_json | json (nullable) | Array of `{type, congress, current}` |
|
||||
| sponsored_count | int (nullable) | Total bills sponsored (lifetime) |
|
||||
| cosponsored_count | int (nullable) | Total bills cosponsored (lifetime) |
|
||||
| detail_fetched | timestamptz (nullable) | Set when bio detail was enriched from Congress.gov |
|
||||
| created_at / updated_at | timestamptz | |
|
||||
|
||||
Member detail fields (`congress_url` through `detail_fetched`) are populated lazily on first profile view via a Congress.gov detail API call. The `detail_fetched` timestamp is the gate for scheduling member interest scoring.
|
||||
|
||||
### `member_trend_scores`
|
||||
One record per member per day (mirrors `trend_scores` for bills).
|
||||
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| id | int (PK) | |
|
||||
| member_id | varchar (FK → members, CASCADE) | bioguide_id |
|
||||
| score_date | date | |
|
||||
| newsapi_count | int | Articles from NewsAPI (30-day window) |
|
||||
| gnews_count | int | Articles from Google News RSS |
|
||||
| gtrends_score | float | Google Trends interest 0–100 |
|
||||
| composite_score | float | Weighted combination 0–100 (same formula as bill trend scores) |
|
||||
|
||||
Unique constraint: `(member_id, score_date)`. Indexes: `member_id`, `score_date`, `composite_score`.
|
||||
|
||||
### `member_news_articles`
|
||||
News articles correlated to a specific member of Congress.
|
||||
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| id | int (PK) | |
|
||||
| member_id | varchar (FK → members, CASCADE) | bioguide_id |
|
||||
| source | varchar | News outlet |
|
||||
| headline | text | |
|
||||
| url | varchar | Unique per `(member_id, url)` |
|
||||
| published_at | timestamptz | |
|
||||
| relevance_score | float | Default 1.0 |
|
||||
| created_at | timestamptz | |
|
||||
|
||||
---
|
||||
|
||||
@@ -269,7 +310,8 @@ Primary key: `bioguide_id` (Congress.gov canonical identifier).
|
||||
| email | varchar (unique) | |
|
||||
| hashed_password | varchar | bcrypt |
|
||||
| is_admin | bool | First registered user = true |
|
||||
| notification_prefs | jsonb | Future: ntfy, Telegram, RSS config |
|
||||
| notification_prefs | jsonb | ntfy topic URL, ntfy auth token, ntfy enabled, RSS token |
|
||||
| rss_token | varchar (nullable) | Unique token for personal RSS feed URL |
|
||||
| created_at | timestamptz | |
|
||||
|
||||
---
|
||||
@@ -296,7 +338,7 @@ Unique constraint: `(user_id, follow_type, follow_value)`
|
||||
| bill_id | varchar (FK → bills, CASCADE) | |
|
||||
| source | varchar | News outlet |
|
||||
| headline | varchar | |
|
||||
| url | varchar (unique) | Deduplication key |
|
||||
| url | varchar | Unique per `(bill_id, url)` — same article can appear across multiple bills |
|
||||
| published_at | timestamptz | |
|
||||
| relevance_score | float | Default 1.0 |
|
||||
| created_at | timestamptz | |
|
||||
@@ -347,6 +389,22 @@ Key-value store for runtime-configurable settings.
|
||||
|
||||
---
|
||||
|
||||
### `notifications`
|
||||
Stores notification events for dispatching to user channels (ntfy, RSS).
|
||||
|
||||
| Column | Type | Notes |
|
||||
|---|---|---|
|
||||
| id | int (PK) | |
|
||||
| user_id | int (FK → users, CASCADE) | |
|
||||
| bill_id | varchar (FK → bills, SET NULL) | nullable |
|
||||
| event_type | varchar | e.g. `new_brief`, `bill_updated`, `new_action` |
|
||||
| headline | text | Short description for ntfy title |
|
||||
| body | text | Longer description for ntfy message / RSS content |
|
||||
| dispatched_at | timestamptz (nullable) | NULL = not yet sent |
|
||||
| created_at | timestamptz | |
|
||||
|
||||
---
|
||||
|
||||
## Alembic Migrations
|
||||
|
||||
| File | Description |
|
||||
@@ -357,6 +415,11 @@ Key-value store for runtime-configurable settings.
|
||||
| `0004_add_brief_type.py` | BillBrief.brief_type column (`full`/`amendment`) |
|
||||
| `0005_add_users_and_user_follows.py` | users table + user_id FK on follows; drops global follows |
|
||||
| `0006_add_brief_govinfo_url.py` | BillBrief.govinfo_url for frontend source links |
|
||||
| `0007_add_member_bio_fields.py` | Member extended bio: `congress_url`, `birth_year`, `address`, `phone`, `terms_json`, `leadership_json`, `sponsored_count`, `cosponsored_count`, `detail_fetched` |
|
||||
| `0008_add_member_interest_tables.py` | New tables: `member_trend_scores`, `member_news_articles` |
|
||||
| `0009_fix_news_articles_url_uniqueness.py` | Changed `news_articles.url` from globally unique to per-bill unique `(bill_id, url)` |
|
||||
| `0010_backfill_bill_congress_urls.py` | Backfill congress_url on existing bill records |
|
||||
| `0011_add_notifications.py` | `notifications` table + `rss_token` column on users |
|
||||
|
||||
Migrations run automatically on API startup: `alembic upgrade head`.
|
||||
|
||||
@@ -390,8 +453,10 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
| Method | Path | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| GET | `/` | — | Paginated members. Query: `chamber`, `party`, `state`, `q`, `page`, `per_page`. |
|
||||
| GET | `/{bioguide_id}` | — | Member detail. |
|
||||
| GET | `/{bioguide_id}` | — | Member detail. On first view, lazily enriches bio from Congress.gov and queues member interest scoring. Returns `latest_trend` if scored. |
|
||||
| GET | `/{bioguide_id}/bills` | — | Member's sponsored bills, paginated. |
|
||||
| GET | `/{bioguide_id}/trend` | — | Member trend score history. Query: `days` (7–365, default 30). |
|
||||
| GET | `/{bioguide_id}/news` | — | Member's recent news articles, limit 20. |
|
||||
|
||||
### `/api/follows`
|
||||
|
||||
@@ -419,7 +484,17 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
|---|---|---|---|
|
||||
| GET | `/` | Required | Current settings (DB overrides env). |
|
||||
| PUT | `/` | Admin | Update `{key, value}`. Allowed keys: `llm_provider`, `llm_model`, `congress_poll_interval_minutes`. |
|
||||
| POST | `/test-llm` | Admin | Test LLM connection. Returns `{status, provider, model, summary_preview}`. |
|
||||
| POST | `/test-llm` | Admin | Test LLM connection with a lightweight ping (max_tokens=20). Returns `{status, provider, model, reply}`. |
|
||||
| GET | `/llm-models?provider=X` | Admin | Fetch available models from the live provider API. Supports openai, anthropic, gemini, ollama. |
|
||||
|
||||
### `/api/notifications`
|
||||
|
||||
| Method | Path | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| GET | `/settings` | Required | User's notification preferences (ntfy URL/token, ntfy enabled, RSS token). |
|
||||
| PUT | `/settings` | Required | Update notification preferences. |
|
||||
| POST | `/settings/rss-reset` | Required | Regenerate RSS token (invalidates old URL). |
|
||||
| GET | `/feed/{rss_token}.xml` | — | Personal RSS feed of notification events for this user. |
|
||||
|
||||
### `/api/admin`
|
||||
|
||||
@@ -428,11 +503,18 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
| GET | `/users` | Admin | All users with follow counts. |
|
||||
| DELETE | `/users/{id}` | Admin | Delete user (cannot delete self). Cascades follows. |
|
||||
| PATCH | `/users/{id}/toggle-admin` | Admin | Promote/demote admin status (cannot change self). |
|
||||
| GET | `/stats` | Admin | Pipeline progress: total bills, docs fetched, briefs generated, remaining. |
|
||||
| GET | `/stats` | Admin | Pipeline counters: total bills, docs fetched, briefs generated, pending LLM, missing metadata/sponsors/actions, uncited briefs. |
|
||||
| GET | `/api-health` | Admin | Test each external API in parallel; returns status + latency for Congress.gov, GovInfo, NewsAPI, Google News. |
|
||||
| POST | `/trigger-poll` | Admin | Queue immediate Congress.gov poll. |
|
||||
| POST | `/trigger-member-sync` | Admin | Queue member sync. |
|
||||
| POST | `/trigger-trend-scores` | Admin | Queue trend score calculation. |
|
||||
| POST | `/backfill-sponsors` | Admin | Queue one-off task to populate `sponsor_id` on all bills where it is NULL. |
|
||||
| POST | `/trigger-fetch-actions` | Admin | Queue action fetch for recently active bills (last 30 days). |
|
||||
| POST | `/backfill-all-actions` | Admin | Queue action fetch for ALL bills with no action history (one-time catch-up). |
|
||||
| POST | `/backfill-sponsors` | Admin | Queue one-off task to populate `sponsor_id` on bills where it is NULL. |
|
||||
| POST | `/backfill-metadata` | Admin | Fill null `introduced_date`, `chamber`, `congress_url` by re-fetching bill detail. |
|
||||
| POST | `/backfill-citations` | Admin | Delete pre-citation briefs and re-queue LLM using stored document text. |
|
||||
| POST | `/resume-analysis` | Admin | Re-queue LLM for docs with no brief; re-queue doc fetch for bills with no doc. |
|
||||
| POST | `/bills/{bill_id}/reprocess` | Admin | Queue document + action fetches for a specific bill (debugging). |
|
||||
| GET | `/task-status/{task_id}` | Admin | Celery task status and result. |
|
||||
|
||||
### `/api/health`
|
||||
@@ -453,10 +535,10 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
|
||||
| Queue | Workers | Tasks |
|
||||
|---|---|---|
|
||||
| `polling` | worker | `poll_congress_bills`, `sync_members` |
|
||||
| `polling` | worker | `app.workers.congress_poller.*`, `app.workers.notification_dispatcher.*` |
|
||||
| `documents` | worker | `fetch_bill_documents` |
|
||||
| `llm` | worker | `process_document_with_llm` |
|
||||
| `news` | worker | `fetch_news_for_bill`, `fetch_news_for_active_bills`, `calculate_all_trend_scores` |
|
||||
| `news` | worker | `app.workers.news_fetcher.*`, `app.workers.trend_scorer.*`, `app.workers.member_interest.*` |
|
||||
|
||||
**Worker settings:**
|
||||
- `task_acks_late = True` — task removed from queue only after completion, not on pickup
|
||||
@@ -470,6 +552,10 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
| Configurable (default 30 min) | `poll_congress_bills` | Continuous |
|
||||
| Every 6 hours | `fetch_news_for_active_bills` | Ongoing |
|
||||
| Daily 2 AM UTC | `calculate_all_trend_scores` | Nightly |
|
||||
| Every 12 hours (at :30) | `fetch_news_for_active_members` | Ongoing |
|
||||
| Daily 3 AM UTC | `calculate_all_member_trend_scores` | Nightly |
|
||||
| Daily 4 AM UTC | `fetch_actions_for_active_bills` | Nightly |
|
||||
| Every 5 minutes | `dispatch_notifications` | Continuous |
|
||||
|
||||
---
|
||||
|
||||
@@ -503,8 +589,8 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
↳ → fetch_news_for_bill.delay(bill_id)
|
||||
|
||||
4. news_fetcher.fetch_news_for_bill(bill_id)
|
||||
↳ Queries NewsAPI using bill title + topic_tags
|
||||
↳ Deduplicates by URL
|
||||
↳ Queries NewsAPI + Google News RSS using bill title/number
|
||||
↳ Deduplicates by (bill_id, url) — same article can appear for multiple bills
|
||||
↳ Stores NewsArticle records
|
||||
|
||||
5. trend_scorer.calculate_all_trend_scores() [nightly]
|
||||
@@ -513,6 +599,21 @@ Auth header: `Authorization: Bearer <jwt>`
|
||||
↳ Fetches: NewsAPI count + Google News RSS count + Google Trends score
|
||||
↳ Calculates composite_score (0–100)
|
||||
↳ Stores TrendScore record
|
||||
|
||||
Member interest pipeline (independent of bill pipeline):
|
||||
|
||||
6. member_interest.fetch_member_news(bioguide_id) [on first profile view + every 12h]
|
||||
↳ Triggered on first member profile view (non-blocking via .delay())
|
||||
↳ Queries NewsAPI + Google News RSS using member name + title
|
||||
↳ Deduplicates by (member_id, url)
|
||||
↳ Stores MemberNewsArticle records
|
||||
|
||||
7. member_interest.calculate_member_trend_score(bioguide_id) [on first profile view + nightly]
|
||||
↳ Triggered on first member profile view (non-blocking via .delay())
|
||||
↳ Only runs if member detail has been fetched (gate: detail_fetched IS NOT NULL)
|
||||
↳ Fetches: NewsAPI count + Google News RSS count + Google Trends score
|
||||
↳ Uses the same composite formula as bills
|
||||
↳ Stores MemberTrendScore record
|
||||
```
|
||||
|
||||
---
|
||||
@@ -597,7 +698,7 @@ class ReverseBrief:
|
||||
| `/bills` | Browse all bills with search, chamber/topic filters, pagination |
|
||||
| `/bills/[id]` | Bill detail — brief with § citations, action timeline, news, trend chart |
|
||||
| `/members` | Browse members of Congress, filter by chamber/party/state |
|
||||
| `/members/[id]` | Member profile + sponsored bills |
|
||||
| `/members/[id]` | Member profile — bio, contact info, leadership roles, service history, sponsored bills, public interest trend chart, recent news |
|
||||
| `/following` | User's followed bills, members, and topics |
|
||||
| `/topics` | Browse and follow policy topics |
|
||||
| `/settings` | Admin panel (admin only) |
|
||||
@@ -606,6 +707,9 @@ class ReverseBrief:
|
||||
|
||||
### Key Components
|
||||
|
||||
**`BriefPanel.tsx`**
|
||||
Orchestrates AI brief display. If the latest brief is type `amendment`, shows an amber "What Changed" badge. Renders the latest brief via `AIBriefCard`. Below it, a collapsible "Version History" lists all older briefs; clicking one expands an inline `AIBriefCard`.
|
||||
|
||||
**`AIBriefCard.tsx`**
|
||||
Renders the LLM brief. For cited items (new format), shows a `§ Section X(y)` chip next to each bullet. Clicking the chip expands an inline panel with:
|
||||
- Blockquoted verbatim excerpt from the bill
|
||||
@@ -613,11 +717,17 @@ Renders the LLM brief. For cited items (new format), shows a `§ Section X(y)` c
|
||||
- One chip open at a time per card
|
||||
- Old plain-string briefs render without chips (graceful backward compat)
|
||||
|
||||
**`ActionTimeline.tsx`**
|
||||
Renders the legislative action history as a vertical timeline. Accepts optional `latestActionDate`/`latestActionText` fallback props — when `actions` is empty but a latest action exists (actions not yet fetched from Congress.gov), shows a single "latest known action" entry with a note that full history loads in the background.
|
||||
|
||||
**`MobileHeader.tsx`**
|
||||
Top bar shown only on mobile (`md:hidden`). Displays the PocketVeto logo and a hamburger button that opens the slide-in drawer.
|
||||
|
||||
**`AuthGuard.tsx`**
|
||||
Client component wrapping the entire app. Waits for Zustand hydration, then redirects unauthenticated users to `/login`. Public paths (`/login`, `/register`) bypass the guard.
|
||||
Client component wrapping the entire app. Waits for Zustand hydration, then redirects unauthenticated users to `/login`. Public paths (`/login`, `/register`) bypass the guard. Implements the responsive shell: desktop sidebar always-visible (`hidden md:flex`), mobile drawer with backdrop overlay controlled by `drawerOpen` state.
|
||||
|
||||
**`Sidebar.tsx`**
|
||||
Navigation with: Home, Bills, Members, Following, Topics, Settings (admin only). Shows current user email + logout button at the bottom.
|
||||
Navigation with: Home, Bills, Members, Following, Topics, Settings (admin only). Shows current user email + logout button at the bottom. Accepts optional `onClose` prop — when provided (mobile drawer context), renders an X close button in the header and calls `onClose` on every nav link click.
|
||||
|
||||
**`BillCard.tsx`**
|
||||
Compact bill preview showing bill ID, title, sponsor with party badge, latest action date, and status.
|
||||
@@ -633,6 +743,10 @@ partyBadgeColor(party) → Tailwind classes
|
||||
"Democrat" → "bg-blue-600 text-white"
|
||||
other → "bg-slate-500 text-white"
|
||||
|
||||
chamberBadgeColor(chamber) → Tailwind badge classes
|
||||
"Senate" → amber/gold (bg-amber-100 text-amber-700 …)
|
||||
"House" → slate/silver (bg-slate-100 text-slate-600 …)
|
||||
|
||||
partyColor(party) → text color class (used inline)
|
||||
trendColor(score) → color class based on score thresholds
|
||||
billLabel(type, number) → "H.R. 1234", "S. 567", etc.
|
||||
@@ -730,6 +844,77 @@ Nginx uses `resolver 127.0.0.11 valid=10s` (Docker's internal DNS) so upstream c
|
||||
- AuthGuard with login/register pages
|
||||
- Analysis status dashboard (auto-refresh every 30s)
|
||||
|
||||
### v0.3.0 — Member Profiles & Mobile UI
|
||||
|
||||
**Member Interest Tracking:**
|
||||
- `member_trend_scores` and `member_news_articles` tables (migration 0008)
|
||||
- `member_interest` Celery worker: `fetch_member_news`, `calculate_member_trend_score`, `fetch_news_for_active_members`, `calculate_all_member_trend_scores`
|
||||
- Member interest scoring uses the identical composite formula as bills (NewsAPI + GNews + pytrends)
|
||||
- New beat schedules: member news every 12h, member trend scores nightly at 3 AM UTC
|
||||
- Lazy enrichment: on first profile view, bio is fetched from Congress.gov detail API and interest scoring is queued non-blocking
|
||||
- Member detail fields added: `congress_url`, `birth_year`, `address`, `phone`, `terms_json`, `leadership_json`, `sponsored_count`, `cosponsored_count`, `detail_fetched` (migration 0007)
|
||||
- New API endpoints: `GET /api/members/{id}/trend` and `GET /api/members/{id}/news`
|
||||
- Member detail page redesigned: photo, bio header with party/state/district/birth year, contact info (address, phone, website, congress.gov), current leadership badges, trend chart ("Public Interest"), news panel, legislation stats (sponsored/cosponsored counts), full service history timeline, all leadership roles history
|
||||
|
||||
**News Deduplication Fix:**
|
||||
- `news_articles.url` changed from globally unique to per-bill unique `(bill_id, url)` (migration 0009)
|
||||
- The same article can now appear in multiple bills' news panels
|
||||
- `fetch_news_for_bill` now fetches from both NewsAPI and Google News RSS (previously GNews was volume-signal only)
|
||||
|
||||
**Mobile UI:**
|
||||
- `MobileHeader.tsx` — hamburger + logo top bar, hidden on desktop (`md:hidden`)
|
||||
- `AuthGuard.tsx` — responsive shell: desktop sidebar always-on, mobile slide-in drawer with backdrop
|
||||
- `Sidebar.tsx` — `onClose` prop for drawer mode (X button + close on nav click)
|
||||
- Dashboard grid: `grid-cols-1 md:grid-cols-3` (single column on mobile)
|
||||
- Members page: `grid-cols-1 sm:grid-cols-2` (single column on mobile, two on tablet+)
|
||||
- Topics page: `grid-cols-1 sm:grid-cols-2`
|
||||
|
||||
### v0.4.0 — Notifications, Admin Health Panel, Bill Action Pipeline
|
||||
|
||||
**Notifications (Phase 1 complete):**
|
||||
- `notifications` table — stores events per user (new_brief, bill_updated, new_action)
|
||||
- ntfy dispatch — Celery task POSTs to user's ntfy topic URL (self-hosted or ntfy.sh); optional auth token
|
||||
- RSS feed — tokenized per-user XML feed at `/api/notifications/feed/{token}.xml`
|
||||
- `dispatch_notifications` beat task — runs every 5 minutes, fans out unsent events to enabled channels
|
||||
- Notification settings UI — ntfy topic URL, auth token, enable/disable, RSS URL with copy button
|
||||
|
||||
**Bill Action Pipeline:**
|
||||
- `fetch_bill_actions` Celery task — fetches full legislative history from Congress.gov, idempotent on `(bill_id, action_date, action_text)`, updates `Bill.actions_fetched_at`
|
||||
- `fetch_actions_for_active_bills` nightly batch — queues action fetches for bills active in last 30 days
|
||||
- `backfill_all_bill_actions` — one-time task to fetch actions for all bills with `actions_fetched_at IS NULL`
|
||||
- Beat schedule entry at 4 AM UTC
|
||||
- `ActionTimeline` updated: shows full history when fetched; falls back to `latest_action_date`/`latest_action_text` with "latest known action" label when history not yet loaded
|
||||
|
||||
**"What Changed" — BriefPanel:**
|
||||
- New `BriefPanel.tsx` component wrapping `AIBriefCard`
|
||||
- When latest brief is type `amendment`: shows amber "What Changed" badge row + date
|
||||
- Collapsible "Version History" section listing older briefs (date, type badge, truncated summary)
|
||||
- Clicking a history row expands an inline `AIBriefCard` for that version
|
||||
|
||||
**LLM Provider Improvements:**
|
||||
- Live model picker — `GET /api/settings/llm-models?provider=X` fetches available models from each provider's API (OpenAI SDK, Anthropic REST, Gemini SDK, Ollama tags endpoint)
|
||||
- DB overrides now fully propagated: `get_llm_provider(provider, model)` accepts explicit params; all call sites read from `app_settings`
|
||||
- Default Gemini model updated: `gemini-1.5-pro` (deprecated) → `gemini-2.0-flash`
|
||||
- Test connection replaced with lightweight ping (max_tokens=20, 3-word prompt) instead of full brief generation
|
||||
|
||||
**Admin Panel Overhaul:**
|
||||
- Bill Pipeline section: progress bar + breakdown table (total, text published, no text yet, AI briefs, pending LLM, uncited)
|
||||
- External API Health: Run Tests button, parallel health checks for Congress.gov / GovInfo / NewsAPI / Google News RSS with latency display
|
||||
- Manual Controls redesigned as health panel: each action has a status dot (green/red/gray), description, contextual count badge (e.g. "⚠ 12 bills missing metadata"), and Run button
|
||||
- Task status polling: after triggering a task, button shows spinning icon; polls `/api/admin/task-status/{id}` every 5s; shows task ID prefix + completion/failure state
|
||||
- New stat fields: `bills_missing_sponsor`, `bills_missing_metadata`, `bills_missing_actions`, `pending_llm`, `no_text_bills`
|
||||
- New admin tasks: Backfill Dates & Links, Backfill All Action Histories, Resume Analysis
|
||||
|
||||
**Chamber Color Badges:**
|
||||
- `chamberBadgeColor(chamber)` utility: amber/gold for Senate, slate/silver for House
|
||||
- Applied everywhere chamber is displayed: BillCard, bill detail header
|
||||
|
||||
**Bill Detail Page:**
|
||||
- "No bill text published" state — shown when `has_document=false` and no briefs; includes bill label, date, and congress.gov link
|
||||
- `has_document` field added to `BillDetailSchema` and `BillDetail` TypeScript type
|
||||
- `introduced_date` shown conditionally (not rendered when null, preventing "Introduced: —")
|
||||
- Admin reprocess endpoint: `POST /api/admin/bills/{bill_id}/reprocess`
|
||||
|
||||
### v0.2.2 — Sponsor Linking & Search Fixes
|
||||
- **Root cause fixed:** Congress.gov list API does not return sponsor data — only the detail endpoint does. Poller now calls the detail endpoint for each new bill to get the sponsor and populate `bill.sponsor_id`
|
||||
- **Backfill task:** `backfill_sponsor_ids` Celery task + `/api/admin/backfill-sponsors` endpoint + "Backfill Sponsors" button in Admin UI — fixes existing bills with `NULL` sponsor_id (~10 req/sec, ~3 min for 1,600 bills)
|
||||
|
||||
Reference in New Issue
Block a user