Skip to content

feat: Weather Assistant - conversational AI chat with function calling, screen reader support, and global weather coverage#285

Merged
Orinks merged 48 commits intodevfrom
feature/weather-chat
Feb 12, 2026
Merged

feat: Weather Assistant - conversational AI chat with function calling, screen reader support, and global weather coverage#285
Orinks merged 48 commits intodevfrom
feature/weather-chat

Conversation

@Orinks
Copy link
Owner

@Orinks Orinks commented Feb 10, 2026

Weather Assistant (Phase 1 + 2)

Adds a conversational AI weather assistant accessible via View > Weather Assistant (Ctrl+T).

Features

  • Multi-turn chat with context-aware responses optimized for screen reader users
  • 11 function-calling tools for live weather data:
    • Core: current conditions, forecast, alerts
    • Extended: hourly forecast, location search, Open-Meteo queries, save/list locations
    • Discussions: area forecast discussions, WPC discussions, SPC outlooks
  • Tiered tool loading — only sends relevant tools based on message keywords to save tokens
  • Global coverage — NWS for US, Open-Meteo for international, Visual Crossing for global alerts (when configured)
  • Combined weather client — tries NWS first, falls back to Open-Meteo automatically
  • Screen reader integration via Prismatoid — announces messages with speaker prefixes
  • Model fallback chain — Qwen3 Coder → Mistral Small 3.1 → Llama 3.3 for free tier tool calling; respects user's model selection for paid models
  • Retries on provider errors — handles rate limits, empty responses, and providers that don't support tools
  • Retry without tools on empty response from free models (graceful degradation)
  • Focus management — message input focused on open; stays focused during generation

Files

  • src/accessiweather/ai_tools.py — tool schemas, tiered selection, WeatherToolExecutor
  • src/accessiweather/screen_reader.py — Prismatoid wrapper with lazy import
  • src/accessiweather/ui/dialogs/weather_assistant_dialog.py — dialog with tool call loop, combined client, fallback chain
  • tests/test_ai_tools.py, test_ai_tools_extended.py, test_ai_tools_formatters.py — tool coverage
  • tests/test_weather_assistant_*.py, test_screen_reader.py — dialog and screen reader tests

Notes

  • Default free model: OpenRouter Free Router (overridden to Qwen3 Coder for tool requests)
  • Paid autorouter and specific models used as-is without override
  • Prismatoid is a default dependency (not optional) since it's core to accessibility
  • Debug logging still present (will clean up in follow-up)

Multi-turn conversational AI weather assistant accessible via
View > WeatherChat (Ctrl+T). Uses current weather data as context,
supports conversation history, and routes through OpenRouter.

- New WeatherChatDialog with accessible UI (labeled controls, keyboard nav)
- Builds weather context from current conditions, forecast, alerts, trends
- Background threaded API calls with proper error handling
- Clear chat, copy to clipboard, Escape to close
- 16 unit tests for context building and configuration
Rename dialog, module, tests, and menu items from WeatherChat
to Weather Assistant per user preference.
Disabling the input control caused screen readers to lose focus
on it. Now we keep it enabled and use the _is_generating flag
to block sends instead.
TrendInsight has metric/direction/change/unit/summary fields,
not description. Also builds a fallback string from metric +
direction when summary is None.
@Orinks Orinks force-pushed the feature/weather-chat branch from 73586f1 to 2bca303 Compare February 11, 2026 03:12
@Orinks Orinks changed the title feat: Weather Assistant — conversational AI chat feat: Integrate Prism screen reader into Weather Assistant Feb 11, 2026
Copy link
Owner Author

@Orinks Orinks left a comment

Choose a reason for hiding this comment

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

Review: Changes Requested 🔍

Good work overall — the Prism integration is clean and the graceful fallback pattern is solid. However, CI is failing with 2 test bugs that need fixing:

Must Fix (CI failures)

1. tests/test_weather_assistant_dialog.py::TestSystemPrompt::test_prompt_exists
The test asserts 'WeatherChat' in SYSTEM_PROMPT but the prompt text says "Weather Assistant", not "WeatherChat". Fix the assertion to match:

assert "Weather Assistant" in SYSTEM_PROMPT

2. tests/test_weather_assistant_dialog.py::TestBuildWeatherContext::test_with_forecast
The test asserts '55°F' but MockForecastPeriod.temperature is 55.0 (a float), so output is 55.0°F. Either change the mock to temperature: int = 55, update the assertion to '55.0°F', or format with :.0f in the dialog code.

Everything Else Looks Good

  • ScreenReaderAnnouncer wrapper is well-structured with proper exception handling
  • Graceful fallback when prismatoid isn't installed works correctly
  • Dialog integration is clean — announcements on welcome, responses, and errors
  • Proper cleanup in _on_close via shutdown()
  • Good test coverage for the announcer module

- Updated SYSTEM_PROMPT to mention available weather tools
  (get_current_weather, get_forecast, get_alerts) and guide the AI
  to use them for location-specific queries
- Added tests/test_weather_assistant_integration.py with 8 tests:
  - Full tool call flow (single, multiple, chained tool calls)
  - Direct response without tools
  - Error recovery flow
  - System prompt content verification (3 tests)
- Used AST parsing to test SYSTEM_PROMPT without importing wx/prism
- Make prism import lazy to avoid Python 3.13 + pytest ValueError
- Fix mock backend to set features.is_supported_at_runtime
- Update pyproject dep test for prismatoid as main dependency
- Remove broken announcer integration tests (wx import in CI)
Use context manager pattern so sys.modules mock stays active when
ScreenReaderAnnouncer.__init__ calls _try_import_prism().
- get_hourly_forecast: 12-period hourly breakdown for time-specific questions
- search_location: geocoding search for ambiguous place names/ZIP codes
- Updated system prompt to document new tools
- add_location: save a location with name/lat/lon to user's list
- list_locations: show all saved locations with current marker
- Pass config_manager to WeatherToolExecutor for location persistence
- Updated system prompt with new tools and usage guidance
Generic Open-Meteo API tool that lets the AI construct custom queries
with any combination of current/hourly/daily variables. Covers soil
temperature, UV index, cloud cover, dew point, snow depth, visibility,
precipitation probability, and dozens more variables with global coverage.

The AI picks the right parameters based on the user's question.
Split tools into CORE_TOOLS (current weather, forecast, alerts) sent
on every request, and EXTENDED_TOOLS (hourly, search, add/list
locations, query_open_meteo) only sent when keyword triggers detected
in the user's message. Saves tokens on simple weather questions.
- get_area_forecast_discussion: local NWS forecaster discussion via API
- get_wpc_discussion: WPC short range discussion via BeautifulSoup scraper
- get_spc_outlook: SPC Day 1 convective outlook via scraper
- Uses existing national_discussion_scraper.py (already in codebase)
- Discussion tools loaded only when keywords match (severe, tornado, etc.)
- Truncates long discussions to 3000 chars for AI context
@Orinks Orinks changed the title feat: Integrate Prism screen reader into Weather Assistant feat: Weather Assistant - conversational AI chat with function calling and screen reader support Feb 11, 2026
Antfarm-generated tests assumed original 3 tools. Updated counts and
removed assumption that all tools have a location parameter.
When tools are included in the request and the user is on a free
router or default free model, automatically switch to Qwen3 Coder
which has reliable function calling support. Users who pick a
specific model keep their choice.
The app object has weather_client, not weather_service. This was
causing _get_tool_executor to always return None, so tools were
never sent to the model.
Instead of hardcoding one model, tries Qwen3 Coder -> Mistral Small
3.1 -> Llama 3.3 when rate limited. Only applies when using free
router models with tool calls.
Some free models return empty choices when tools are sent. Now
falls back to a no-tools conversational response instead of
showing an error.
Use whatever model the user has in settings. Only enable fallback
chain when on a free router. No more overriding to Qwen3 Coder.
The app's WeatherClient is async with Location objects, but the
tool executor expects sync get_current_conditions(lat, lon) etc.
NoaaApiClient has the right interface.
NWS only covers US locations. CombinedWeatherClient tries NWS first,
falls back to Open-Meteo for international locations. Alerts and
discussions remain NWS-only (US).
CombinedWeatherClient now tries NWS -> Open-Meteo for weather data,
and NWS -> Visual Crossing for alerts (when VC API key is configured).
This gives global alert coverage for non-US locations.
Free router can route to providers that don't support tools (e.g.
StepFun returning 400 'tool_calls.function.arguments is required').
Now catches 400s, provider errors, and tool_calls errors too.
Free router routes to models that narrate tools instead of calling
them. Always use Qwen3 Coder (with fallback chain) for tool requests
when on a free router.
openrouter/auto is the paid router which handles tools fine.
Only override openrouter/free and the default free Llama model.
@Orinks Orinks changed the title feat: Weather Assistant - conversational AI chat with function calling and screen reader support feat: Weather Assistant - conversational AI chat with function calling, screen reader support, and global weather coverage Feb 12, 2026
@Orinks Orinks merged commit 7355cbe into dev Feb 12, 2026
6 checks passed
@Orinks Orinks deleted the feature/weather-chat branch February 12, 2026 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant