Main chat weather (Open-Meteo) and semantic search result IDs β
This document summarizes work on field-scoped weather in the main streaming chat, resolving the correct database IDs for Fields vs ParseResults, and fixing semantic search so downstream tools receive ParseResult row IDs instead of embedding chunk IDs.
1. Goals β
- Give the main chat agent (ReAct orchestrator in
streaming-chat) aget_weathertool that calls Open-Meteo for forecast data (current, hourly, daily, soil-oriented variables). - Tie weather to agricultural fields: resolve coordinates from Field geometry or centroid, scoped to the userβs company.
- Support agronomic-style questions (spray windows, frost, weekly outlook, soil-related series) via a
focusparameter that maps to different Open-Meteo query shapes. - Fix failures where the model passed vector embedding document IDs to tools that expect ParseResult or Field document IDs.
2. Open-Meteo integration (apps/agri-backend/src/weather/) β
2.1 Module layout β
| File | Role |
|---|---|
weather.module.ts | Nest module; provides and exports OpenMeteoClient. |
open-meteo.client.ts | HTTP fetch to Open-Meteo Forecast API; Zod validation of JSON; builds a compact summary (highlights) for the LLM instead of dumping full hourly arrays. |
field-coordinates.util.ts | Derives latitude / longitude from a Field document: prefers GeoJSON centroid ([lon, lat]); otherwise averages the exterior ring of Polygon / first polygon of MultiPolygon. |
weather.enabled.ts | isOpenMeteoEnabled() β reads OPEN_METEO_ENABLED; tool registration is skipped when set to false. |
2.2 Environment variables β
| Variable | Purpose |
|---|---|
OPEN_METEO_ENABLED | If set to false, get_weather is not registered in main chat (getTools / getToolHandlers). Default behavior: enabled when unset. |
OPEN_METEO_BASE_URL | Optional override for the forecast API base URL (defaults to https://api.open-meteo.com/v1/forecast). Useful for tests or proxies. |
No API key is required for the public Open-Meteo endpoint; refer to Open-Meteo terms and pricing for commercial / high-volume use.
2.3 focus modes (client) β
The client maps focus to different current / hourly / daily parameter sets, for example:
generalβ short overview (current + hourly + some daily).spray_windowβ wind, precipitation probability, humidity, etc., over a few days.frostβ hourly temperature emphasis; summary includes hints such as min temp in next 24h and hours below thresholds.weeklyβ daily max/min temperature, precipitation, wind max, weather code for ~7 days.soilβ hourly soil temperature / moisture variables where the API exposes them (e.g.soil_temperature_6cm,soil_moisture_3_9cm).
3. Main chat wiring β
3.1 StreamingChatModule β
- Imports
FieldModulesoMainChatAgentServicecan injectFieldsService. - Imports
WeatherModuleforOpenMeteoClient.
File: apps/agri-backend/src/streaming-chat/streaming-chat.module.ts.
3.2 MainChatAgentService β
File: apps/agri-backend/src/streaming-chat/services/main-chat-agent.service.ts.
Tool: get_weather (when Open-Meteo is enabled)
Parameters
field_id(required): Identifier the agent obtained fromread_datafor the field row or field record.focus(optional): One ofgeneral,spray_window,frost,weekly,soil.
Handler flow
- Resolve the
Fielddocument viaFieldsService.findFieldDocumentForWeather(see Β§4). - Compute lat/lon with
resolveFieldLatLon. - Call
OpenMeteoClient.fetchForecastSummary. - Return a structured payload (
success,field_name,location,forecast, etc.) for the ReAct loop.
- Resolve the
System prompt updates describe:
- The third tool alongside
read_dataandwrite_data. - That weather is forecast / model data, not a substitute for a licensed PCA for spray decisions (aligned with existing guardrails).
- Workflow: resolve field context with
read_datawhen needed, thenget_weather.
4. Field resolution: findFieldDocumentForWeather β
4.1 Problem β
The UI and read_data / ReportGenerationAgent often surface IDs that are not the MongoDB _id of the fields collection:
- Fields workspace rows live in
parseresults(ParseResult documents) with afieldproperty pointing at the realFielddocument. - The agent might pass the ParseResult
_id(table row) or, before the search fix, wrong IDs entirely (see Β§5).
Calling findByIdWithAccess only on fields therefore returned 404 for common IDs.
4.2 Solution β
FieldsService.findFieldDocumentForWeather(id, user) in apps/agri-backend/src/fields/fields.service.ts resolves in order:
Fieldby_id(with company / sharing filter viaapplyCompanyFilter).ParseResultby_id; if present, readfield(24-hex ObjectId string); loadFieldby that id.FieldbyinternalReferenceId(when the id is the stable reference id).ParseResultbyinternalReferenceId; again followfieldtoField.
If nothing matches, it throws the same not-found pattern as other company-scoped resources (I18nExceptions.notFound).
This mirrors the idea already used in updateFieldWithParseResult, where the API accepts either a Field id or a ParseResult id and follows parseResult.field.
4.3 Coordinates β
If the Field has no usable centroid or geometry, resolveFieldLatLon throws a clear error (user must fix boundaries in the app).
5. Semantic search: id vs embedding chunk (Report Generation Agent) β
5.1 Problem β
search_parse_results runs a vector search on parseResultEmbeddingsModel. Each hit is an embedding chunk document whose _id is not the ParseResult workspace row id.
The tool previously set:
id: doc._id?.toString() || doc.parseResultId?.toString();So results[].id was almost always the chunk id. The main chat model then passed that value to get_weather as field_id, which findFieldDocumentForWeather could not resolve β βfield not found / accessβ style failures.
5.2 Fix (backend shaping) β
File: apps/agri-backend/src/agents/report-generation-agent/tools/search-parse-results.tool.ts.
idis nowparseResultIdfirst, falling back to_idonly ifparseResultIdis missing.embeddingChunkIdis set when the chunk_iddiffers fromid, so the model and logs can distinguish row id vs chunk id.
Tool description text states explicitly:
- Use
idas the ParseResult row id (same logical record asquery_parse_results_id). - Do not use
embeddingChunkIdfor tools that need ParseResult / Field ids (e.g.get_weather).
5.3 Report agent system prompt β
File: apps/agri-backend/src/agents/report-generation-agent/utils/system-prompt.utils.ts.
Under the semantic search strategy, a line was added: each resultβs id is the ParseResult row id, not the embedding chunk id.
5.4 Remaining caveat β
Semantic search can still return a chunk whose parseResultId belongs to another row (irrelevant hit). id is then a valid ParseResult id but possibly the wrong field. For exact field rows, query_parse_results on the Fields workspace remains the most reliable path; the search fix ensures the type of id is correct when the hit is relevant.
6. End-to-end flow (successful path) β
7. Files touched (reference) β
| Area | Path |
|---|---|
| Weather | apps/agri-backend/src/weather/* |
| Streaming chat module | apps/agri-backend/src/streaming-chat/streaming-chat.module.ts |
| Main chat agent | apps/agri-backend/src/streaming-chat/services/main-chat-agent.service.ts |
| Field resolution | apps/agri-backend/src/fields/fields.service.ts |
| Semantic search tool | apps/agri-backend/src/agents/report-generation-agent/tools/search-parse-results.tool.ts |
| Report agent prompt | apps/agri-backend/src/agents/report-generation-agent/utils/system-prompt.utils.ts |
| Weather integration tests | apps/agri-backend/src/weather/weather.integration.spec.ts |
8. Verification β
- Typecheck:
pnpm --filter agri-backend exec tsc --noEmit - Integration tests (require test DB/containers, e.g.
pnpm --filter agri-backend test:upthenpnpm exec jest weather.integration.spec.tsfromapps/agri-backend):src/weather/weather.integration.spec.tscoversOpenMeteoClient(mockedfetch),findFieldDocumentForWeather, andget_weatherviaMainChatAgentService.getToolHandlers(). - Manual: Ask main chat for weather for a named field after
read_datareturns a Fields row; confirmget_weathersucceeds and coordinates match the field map. - After the search fix: if the agent uses
search_parse_resultsids for follow-up, thoseidvalues should beparseResultId-based and resolvable when the row links to afield.
9. Related documentation β
- Backend overview
- Records vs references β workspace ParseResults vs reference entities
- Open-Meteo: Forecast API documentation