Skip to content

feat: add Astrai intelligent router to summarization fallback chain#115

Open
beee003 wants to merge 994 commits intokoala73:mainfrom
beee003:feat/astrai-router-provider
Open

feat: add Astrai intelligent router to summarization fallback chain#115
beee003 wants to merge 994 commits intokoala73:mainfrom
beee003:feat/astrai-router-provider

Conversation

@beee003
Copy link

@beee003 beee003 commented Feb 19, 2026

Summary

  • Adds Astrai as a new LLM provider in the summarization fallback chain
  • New chain: Groq → Astrai → OpenRouter → Browser T5 (was Groq → OpenRouter → Browser T5)
  • Astrai is an AI inference router that auto-selects the optimal model/provider per request based on cost, latency, and task complexity
  • Uses strategy: "cheapest" since summarization is lightweight — routes to the most cost-effective model that meets quality requirements
  • Uses model: "auto" so Astrai picks the best model for each request (e.g., Groq Llama for simple summaries, Claude for complex analysis)

Why this fits WorldMonitor

WorldMonitor already has a smart fallback chain (Groq → OpenRouter → Browser T5). Astrai slots in naturally as a second cloud fallback that routes across all providers (OpenAI, Anthropic, Groq, DeepInfra, etc.) from a single API key. When Groq is rate-limited (14.4K/day), Astrai can find the cheapest alternative across all providers instead of falling through to OpenRouter's 50/day free tier.

Changes

File Change
api/astrai-summarize.js New Vercel Edge Function — OpenAI-compatible endpoint with Redis caching (shared cache keys with Groq/OpenRouter)
src/services/summarization.ts Add tryAstrai() + wire into all fallback paths (normal, beta warm, beta cold, translation)
src/services/runtime-config.ts Add ASTRAI_API_KEY secret, aiAstrai feature toggle, feature definition

Configuration

Set ASTRAI_API_KEY as a Vercel environment variable (or desktop keychain secret). The feature toggle aiAstrai is enabled by default and appears in the Runtime Config panel.

# Vercel
vercel env add ASTRAI_API_KEY

Design decisions

  • Shared cache keys — Astrai uses the same summary:v3:... cache format as Groq/OpenRouter, so a cached response from any provider serves all three
  • strategy: "cheapest" — Summarization tasks are lightweight (150 max tokens), so cost optimization makes sense over quality optimization
  • Graceful skip — If ASTRAI_API_KEY is not set, the endpoint returns { fallback: true, skipped: true } and the chain continues to OpenRouter

Test plan

  • Verify existing Groq → OpenRouter → Browser T5 chain still works when ASTRAI_API_KEY is not set
  • Test with ASTRAI_API_KEY set — Astrai should handle requests when Groq fails
  • Verify shared Redis cache works (a Groq-cached result should serve Astrai requests and vice versa)
  • Test translation mode includes Astrai fallback
  • Verify aiAstrai toggle appears in Runtime Config panel and can be disabled
  • Beta mode paths include Astrai in fallback

🤖 Generated with Claude Code

koala73 and others added 30 commits February 15, 2026 08:20
releaseDraft used != (OR) making tag pushes always draft.
Changed to == (AND) so only workflow_dispatch with draft=true creates drafts.
New post-build job generates release notes from git commits between tags
and updates the release body via gh release edit.
Halve radius, reduce intensity/opacity so markers and labels remain visible.
Standard macOS behavior — app stays in dock, reopens on dock click.
Edge function that redirects /api/download?platform=macos-arm64 to
the matching asset from the latest GitHub release. Falls back to
the releases page when no match is found.
Shield.io badges for Windows, macOS ARM, and macOS Intel desktop downloads
pointing to the new /api/download redirect. Web app links restyled as badges.
Replace the small CountryIntelModal with a comprehensive full-page
intelligence product per country. Implements all 5 phases of the plan:

Phase 1 - Full-page shell + bug fixes:
- CountryBriefPage.ts: score ring, component bars, signal chips,
  non-tier fallback, loading states
- Fix hardcoded earthquakes: 0 with bbox + place name filtering
- Export getCountryData, TIER1_COUNTRIES from country-instability
- Expand CountryBriefSignals with displacement, climate, conflict

Phase 2 - D3 multi-lane timeline:
- CountryTimeline.ts: 4 lanes, 7-day filter, severity-sized circles
- XSS-safe tooltip rendering via escapeHtml

Phase 3 - Infrastructure + Evidence:
- Export getNearbyInfrastructure from related-assets
- Local BriefAssetType union, port proximity lookup
- Evidence cards with threat levels and numbered references

Phase 4 - AI brief citations + Export:
- LLM prompt cites headlines as [N], bounded regex parsing
- Export dropdown: Image PNG, JSON, CSV, Print/PDF

Phase 5 - Deep-linking + Polish:
- country field in URL state, pre-capture before sync
- Force URL rewrite on open/close, mobile + print styles

Bug fixes: URL sync race, async response guards,
briefRequestToken cancellation, listener accumulation,
tier-1 detection via membership not data existence
- RuntimeConfigPanel only created when isDesktopApp is true (was leaking
  full desktop config UI including secret key inputs to web visitors)
- Fix polymarket sub-market selection: when event title doesn't match
  country but a sub-market does, restrict candidates to matching
  sub-markets instead of picking highest-volume globally (was showing
  Xi Jinping market for France because Macron sub-market triggered match)
- Add negative country filter: headlines where another country is mentioned
  first in the title are excluded (fixes Venezuela brief showing for US)
- Filtered headlines used for both evidence section AND LLM context
- Add Top News section (5 items) between Intelligence Brief and Markets
- Limit prediction markets to max 3, tighter padding
…ine filter

- Add Brazil and UAE to TIER1_COUNTRIES, BASELINE_RISK, EVENT_MULTIPLIER,
  COUNTRY_KEYWORDS, COUNTRY_BOUNDS, COUNTRY_ALIASES, and Polymarket maps
- Use Intl.DisplayNames to resolve non-tier ISO codes in deep-link URLs
- Guard raw 2-letter codes from becoming overly broad search terms
- Fix military timeline to use same either/or logic as getCountrySignals
…ection

- Remove Evidence/Sources section (duplicate of Top News)
- Top News now shows 8 items with citation anchor IDs
- AI brief [N] citations link to Top News cards with scroll+highlight
- Move Prediction Markets to right column for balanced layout
- Left: Risk + Brief + Top News | Right: Signals + Timeline + Markets + Infra
GeoJSON uses "United States of America" but COUNTRY_TAG_MAP and
variant matching expect "United States". Normalize at entry point
so polymarket, search terms, and all downstream lookups work.
…ws dedup

- Full-page Country Brief replacing modal overlay
- Tightened headline relevance with negative country filter
- Top News section replacing redundant Evidence/Sources
- Shared country-geometry service for local-first detection
- BR/AE added to tier-1, deep-link name resolution
- Prediction Markets moved to right column for balanced layout
The runtime fetch patch previously returned local 4xx/5xx responses
as-is. Now falls back to worldmonitor.app cloud API on any non-OK
local response, matching the existing network-error fallback behavior.
- Add 60s startup cooldown to suppress pulse during initial data hydration
- Consolidate pulse lifecycle into syncPulseAnimation() across all setters
- Replace static predicates (h.level=high, c.maxSeverity=high) with dynamic-only
  checks: recent news, recent ACLED riots (<2h), breaking hotspots
- Exclude GDELT riots from pulse gating (noisy, not actionable)
- Reduce pulse interval from 250ms to 500ms (halves layer rebuild rate)
- Pause pulse when render is paused (country brief overlay)
- Add latestRiotEventTimeMs to MapProtestCluster with raw protest fallback
- Add Playwright tests for startup cooldown and lifecycle start/stop/GDELT exclusion
Side panel slides in 12s after initial load with macOS/Windows download
links. Dismissed per session. Skipped on Tauri desktop runtime.
…tion

Plot live botnet C2 servers, malware distribution nodes, and malicious IPs
on the globe using free abuse.ch APIs (Feodo Tracker + URLhaus).

- Vercel edge API with triple-layer caching (Redis → memory → stale fallback)
- IP geolocation via ipwho.is + ipapi.co (HTTPS-compatible with Edge runtime)
- Severity-based color coding (critical=red, high=orange, medium=amber, low=yellow)
- Feature-gated behind VITE_ENABLE_CYBER_LAYER=true env var
- Frontend circuit breaker, data sanitization, 10min auto-refresh
- Tauri desktop support: 3 new secret keys (URLHAUS, OTX, AbuseIPDB)
- Full test suite (6 unit tests), e2e harness updates, popup + tooltip rendering
Expands from 2 to 5 threat intel sources:
- C2IntelFeeds (free, no auth): ~500 active C2 server IPs (CobaltStrike, Metasploit)
- AlienVault OTX (optional API key): community-sourced IOCs with tags
- AbuseIPDB (optional API key): high-confidence abuse reports with geo
- Feodo: now includes recently-offline entries (still threat-relevant)

All sources fetched in parallel. Graceful degradation when API keys missing.
…nrichment

ipwho.is returns 403 from Node.js/Edge ("CORS not supported on Free plan")
and ipapi.co rate-limits aggressively (429). Both providers only work from
browser-side requests.

Switch to ipinfo.io (HTTPS, 50K/mo free, works from Edge runtime) as primary
with freeipapi.com as fallback.

Validated end-to-end: 20 C2 IPs geo-enriched in <1s with real coordinates.
…wnload banners

Addresses PR koala73#68 review comments:
- P1: hydrateThreatCoordinates now races workers against a 12s overall
  timeout so slow GeoIP providers can't block the entire request
- P2: DownloadBanner adds module-level guard preventing duplicate panels
- Tooltip: show "Cyber Threat" + severity + country instead of raw source names
- Popup header: "Cyber Threat" title instead of raw IP address
- Popup: show all 5 source labels and add malware family field
- Increase dot size for better visibility at global zoom level
Increase geo cap 100→250, concurrency 8→16, per-IP timeout 8→3s.
Add country centroid fallback with jitter for IPs with country codes.
Both WORLD_FEEDS and TECH_FEEDS were missing 'Cyber Threats', causing
updateFeed() calls to be silently rejected by the allowlist check.
koala73 and others added 24 commits February 18, 2026 21:50
…73#103)

## Summary
This release introduces comprehensive localization support, transforming
WorldMonitor into a truly global intelligence platform.

### Key Features
- **Multilingual UI**: Full support for 12 languages (EN, FR, ES, DE,
IT, PT, NL, SV, RU, AR, CN, JP).
- **RTL Support**: Native right-to-left layout for Arabic and Hebrew,
including mirrored UI components and correct text alignment.
- **Regional Intelligence**: 
  - Dynamic feed selection based on language preference.
- Dedicated regional monitoring panels for Africa, LatAm, Middle East,
and Asia.
  - Granular error handling for regionally blocked feeds.
- **AI Integration**: On-demand translation and summarization of news
items using localized prompts.

### Technical Changes
- **Refactoring**: Overhauled \src/services/i18n.ts\ and
\src/config/feeds.ts\ to support dynamic loading.
- **Styling**: Added \src/styles/rtl-overrides.css\ using logical CSS
properties for maintenance-free RTL support.
- **Cleanup**: Resolved CSS lint warnings and improved code quality.

### Verification
- **Build**: \
pm run build\ passed successfully.
- **Manual Testing**: Verified RTL layout, language switching, and
regional feed loading.
…el dragstart handler

e.target can be a text node when dragging text content inside a panel.
Text nodes lack .closest(), causing TypeError. Add instanceof Element check.

Resolves Sentry P2: TypeError: o.closest is not a function
- Fix summarization race: check isModelLoaded before attempting browser
  T5, fall through to cloud providers while model loads in background
- Increase modelLoadTimeoutMs 30s→600s and inferenceTimeoutMs 45s→120s
  to accommodate first-time model downloads
- Use flan-t5-small (60MB) instead of flan-t5-base (250MB) in beta mode
  for country brief fallback
- Skip ML summarization in country brief when model not loaded to avoid
  blocking UX
- Broadcast model-loaded notifications from worker on implicit loads so
  manager's loadedModels stays in sync
- Suppress ONNX CleanUnusedInitializersAndNodeArgs warnings via
  ort.env.logLevel
- Fix non-monotonic progress reporting with separate warm/cold step counts
Spaces inside HTML tags (e.g. `< span class= "trend-up" >`) caused
browsers to render raw markup text instead of elements. Fixed ~45
instances across CIIPanel, ServiceStatusPanel, and StrategicPosturePanel.
france24, euronews, lemonde, dw, africanews, lasillavacia,
channelnewsasia, thehindu were returning 403 in production.
CI already builds AppImage for Linux — this adds the missing UI:
- linux-appimage pattern in api/download.js
- Linux UA detection and button in DownloadBanner
- i18n key added to all 13 locale files
- ALL grid now picks one feed per region (was showing 4x mideast)
- Jerusalem & Tehran adjacent (conflict hotspots)
- Replace broken Berlin with Paris Eiffel Tower webcam
- Switch toolbar SVG icons from stroke to fill for dark mode clarity
- Idle handler now clears isIdle and re-renders on user activity
  (was permanently paused after 5min timeout)
- Add grid back button in single-view switcher row for mobile
  (view toggle hidden at <=768px left users stuck)
- Tokyo: DjdUEyjx8GM (maintenance) → 4pu9sF5Qssw (Tokyo Live Camera 4K)
- Reorder: Taipei → Shanghai → Tokyo → Seoul (strait hotspot first)
- Los Angeles Venice Beach (EO_1LWqsCNE) — confirmed live
- Miami Biscayne Bay (5YCajRjvWCg) — confirmed live
- Swap Dubai → Tel Aviv (live conflict coverage webcam)
- Swap Istanbul → Mecca (Kaaba 24/7 livestream)
- Fix margin-right → margin-inline-end on back button for RTL
…oks)

- Remove (pointer: coarse) from mobile detection so touch-capable
  desktops (e.g. ROG Flow X13) get desktop layout instead of mobile
- Define MOBILE_BREAKPOINT_PX (768) in utils and use in
  isMobileDevice(); CSS @media (max-width: 768px) kept in sync
- MobileWarningModal uses isMobileDevice() for consistent behavior

Ref: koala73#94
Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary

<!-- A set of multiple live webcams to monitor the world live 😃  -->

## Type of change

- [ ] Bug fix
- [x] New feature
- [x] New data source / feed
- [ ] New map layer
- [ ] Refactor / code cleanup
- [ ] Documentation
- [ ] CI / Build / Infrastructure

## Affected areas

- [ ] Map / Globe
- [ ] News panels / RSS feeds
- [ ] AI Insights / World Brief
- [ ] Market Radar / Crypto
- [ ] Desktop app (Tauri)
- [ ] API endpoints (`/api/*`)
- [ ] Config / Settings
- [ ] Other: <!-- specify -->

## Checklist

- [x] Tested on [worldmonitor.app](https://worldmonitor.app) variant
- [ ] Tested on [tech.worldmonitor.app](https://tech.worldmonitor.app)
variant (if applicable)
- [ ] New RSS feed domains added to `api/rss-proxy.js` allowlist (if
adding feeds)
- [x] No API keys or secrets committed
- [x] TypeScript compiles without errors (`npm run typecheck`)

## Screenshots

<!-- If applicable, add screenshots or screen recordings -->
## Summary

<!-- Brief description of what this PR does -->

## Type of change

- [ ] Bug fix
- [x] New feature
- [ ] New data source / feed
- [ ] New map layer
- [ ] Refactor / code cleanup
- [ ] Documentation
- [ ] CI / Build / Infrastructure

## Affected areas

- [ ] Map / Globe
- [ ] News panels / RSS feeds
- [ ] AI Insights / World Brief
- [ ] Market Radar / Crypto
- [ ] Desktop app (Tauri)
- [ ] API endpoints (`/api/*`)
- [ ] Config / Settings
- [x] Other: Notifications

## Checklist

- [x] Tested on [worldmonitor.app](https://worldmonitor.app) variant
- [x] Tested on [tech.worldmonitor.app](https://tech.worldmonitor.app)
variant (if applicable)
- [ ] New RSS feed domains added to `api/rss-proxy.js` allowlist (if
adding feeds)
- [ ] No API keys or secrets committed
- [ ] TypeScript compiles without errors (`npm run typecheck`)

## Screenshots

<!-- If applicable, add screenshots or screen recordings -->
…t) (koala73#109)

## Summary

Improve mobile map popup usability for touch screen

## Type of change

- [ ] Bug fix
- [ ] New feature
- [ ] New data source / feed
- [ ] New map layer
- [x] Refactor / code cleanup
- [ ] Documentation
- [ ] CI / Build / Infrastructure

## Affected areas

- [x] Map / Globe
- [ ] News panels / RSS feeds
- [ ] AI Insights / World Brief
- [ ] Market Radar / Crypto
- [ ] Desktop app (Tauri)
- [ ] API endpoints (`/api/*`)
- [ ] Config / Settings
- [ ] Other: <!-- specify -->

## Checklist

- [ ] Tested on [worldmonitor.app](https://worldmonitor.app) variant
- [ ] Tested on [tech.worldmonitor.app](https://tech.worldmonitor.app)
variant (if applicable)
- [ ] New RSS feed domains added to `api/rss-proxy.js` allowlist (if
adding feeds)
- [ ] No API keys or secrets committed
- [ ] TypeScript compiles without errors (`npm run typecheck`)

## Screenshots

<!-- If applicable, add screenshots or screen recordings -->
- ml-worker: catch unload-model timeout instead of leaking unhandled rejection
- LiveNewsPanel: optional chaining on playVideo/pauseVideo for edge cases
  where YT IFrame API player object isn't fully initialized
- main.ts: add noise filter for Firefox i18next "too much recursion"
- main.ts: extend maplibre beforeSend filter for null 'id'/'type' crashes
# Conflicts:
#	src/styles/main.css
- Live Webcams Panel with region filters and grid/single view (koala73#111)
- Mobile detection: width-only, touch notebooks get desktop layout (koala73#113)
- Le Monde RSS URL fix, workbox precache HTML, panel ordering migration
- Sentry: ML timeout catch, YT player guards, maplibre noise filters
- Changelog for v2.4.0
…chain

Adds Astrai (AI inference router) as a provider in the summarization
fallback chain: Groq → Astrai → OpenRouter → Browser T5. Astrai
auto-selects the optimal model/provider per request based on cost,
latency, and task complexity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 19, 2026

@beemaya003 is attempting to deploy a commit to the eliehabib projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22b5563c61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +66 to +70
const data = await response.json();
const provider = data.cached ? 'cache' : 'astrai';
console.log(`[Summarization] ${provider === 'cache' ? 'Redis cache hit' : 'Astrai success'}:`, data.model);
return {
summary: data.summary,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat Astrai fallback payloads as failed attempts

api/astrai-summarize.js returns HTTP 200 with {fallback: true, skipped: true, summary: null} when ASTRAI_API_KEY is not configured (see the early return around lines 90-95), but this branch accepts any 2xx as success and returns data.summary without checking fallback/summary. That makes generateSummary stop at Astrai with a null summary when Groq fails (e.g., rate limiting) and Astrai is unconfigured, instead of continuing to OpenRouter/Browser fallback.

Useful? React with 👍 / 👎.

Comment on lines +302 to +304
if (response.ok) {
const data = await response.json();
return data.summary;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Continue translation fallback after Astrai skip response

This translation path returns data.summary for any HTTP-200 Astrai response, but the Astrai endpoint uses HTTP 200 for "skipped" fallback responses when the key is missing. In that case data.summary is null, and returning it exits translateText before the OpenRouter step, so translations fail whenever Groq fails and Astrai is enabled but not configured.

Useful? React with 👍 / 👎.

@koala73 koala73 added enhancement New feature or request area: AI/intel AI analysis, intelligence findings, summarization labels Feb 19, 2026
@SebastienMelki SebastienMelki added the claude Generated with Claude Code label Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: AI/intel AI analysis, intelligence findings, summarization claude Generated with Claude Code enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.