Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jhonyes04/new-wayfy/llms.txt

Use this file to discover all available pages before exploring further.

WayFy’s AI Assistant translates natural-language queries into structured map searches without requiring users to know filter slugs or category names. Type (or say) something like “accessible restaurants near the Sagrada Família” and the assistant extracts the location, identifies the relevant OSM category (gastronomia), sets the wheelchair filters to ['yes', 'limited'], geocodes the POI, and flies the map to Barcelona — all in a single interaction. The assistant is powered by the Groq API running llama-3.3-70b-versatile and is accessible via the floating input bar at the bottom of the Accessibility Map.

Architecture Overview

User input


AIAssistant component (AIAssistant.jsx)
    │  calls

useAIAssistant hook
    │  calls

fetchMapData() — mapgpt.api.js

    ├── POST /api/ai/mapgpt  ──→  Groq llama-3.3-70b-versatile
    │                               Returns: poi, address, place,
    │                                        categories, filters, message

    ├── GET /api/ai/geocode-poi?q=  ──→  Photon (Komoot) → Nominatim fallback
    └── GET /api/ai/geocode?q=      ──→  Nominatim (OpenStreetMap)


useAIAssistant dispatches:
  SET_SELECTED_LOCATION  → map flies to geocoded coordinates
  SET_ACTIVE_FILTERS     → wheelchair filter values applied
  SET_ACTIVE_CATEGORIES  → category slugs applied

API Endpoints

Parses a natural-language query and returns a structured JSON object.Rate limit: 20 requests / minuteRequest
{
  "prompt": "hoteles accesibles cerca de atocha"
}
Response
{
  "poi": "atocha",
  "address": "",
  "place": "",
  "categories": ["alojamiento"],
  "filters": ["yes", "limited"],
  "message": "Búsqueda de hoteles accesibles cerca de atocha"
}
Error responses
StatusBodyCause
400{"msg": "Prompt vacío"}Empty or whitespace-only prompt
429Rate limit headerMore than 20 requests/minute
500{"msg": "Falta GROQ_API_KEY en el servidor"}GROQ_API_KEY env var not set
502{"msg": "La IA devolvio una respuesta invalida"}Groq returned non-JSON content

Groq Model Configuration

The controller (ai_controller.py) initialises the Groq client and calls chat.completions.create with the following parameters:
ParameterValue
modelllama-3.3-70b-versatile
response_format{ "type": "json_object" }
temperature0.2 (near-deterministic for consistent structured output)
max_tokens300
You must set the GROQ_API_KEY environment variable on the Flask backend. Without it, all /api/ai/mapgpt requests return 500. Obtain a key from console.groq.com.

System Prompt Logic

The MAPGPT_SYSTEM_PROMPT in backend/src/api/prompts/mapgpt_prompt.py instructs the model to always return a JSON object with exactly six keys. Here is the contract:

Output Schema

poi
string
Named place with a proper noun — a landmark, station, neighbourhood, or specific venue. Empty string if absent.
address
string
A street address, partial or complete. Empty string if absent.
place
string
A city, district, region, or country. Empty string if absent.
categories
string[]
One or more of the valid category slugs. If the user doesn’t specify a category, all 11 default categories are returned.Valid values: alojamiento, gastronomia, transporte, salud, cultura_turismo, recreacion, deporte, gobierno, baños, dinero, tiendas, otros
filters
string[]
Wheelchair accessibility filter values. If the user doesn’t mention accessibility, defaults to ["yes", "limited"].Valid values: yes, limited, no
message
string
A short (max 12 words) human-readable description of the search. Displayed in the map’s AI query info banner.

Location Priority

When the user mentions multiple locations, the model picks the most specific one: POI > ADDRESS > PLACE. A street address like “Calle Alcalá 43 Madrid” populates address, not place. A named landmark like “Sol” populates poi.

Filter Rules

User saysfilters output
Nothing about accessibility["yes", "limited"]
”accesible” (general)["yes", "limited"]
”totalmente accesible”["yes"]
”accesibilidad parcial”["limited"]
”no accesibles”["no"]

Example Queries & Responses

Input: restaurantes en madridResponse:
{
  "poi": "",
  "address": "",
  "place": "madrid",
  "categories": ["gastronomia"],
  "filters": ["yes", "limited"],
  "message": "Búsqueda de restaurantes en madrid"
}
The geocoder resolves madrid via GET /api/ai/geocode?q=madrid and the map flies to the centre of Madrid with only the gastronomia category active.
Input: hoteles accesibles cerca de atochaResponse:
{
  "poi": "atocha",
  "address": "",
  "place": "",
  "categories": ["alojamiento"],
  "filters": ["yes", "limited"],
  "message": "Búsqueda de hoteles accesibles cerca de atocha"
}
fetchMapData appends the place context to the POI: "atocha"GET /api/ai/geocode-poi?q=atocha → geocoded to Madrid Atocha station.
Input: farmacias en calle alcala 43 madridResponse:
{
  "poi": "",
  "address": "calle alcala 43 madrid",
  "place": "",
  "categories": ["salud"],
  "filters": ["yes", "limited"],
  "message": "Búsqueda de farmacias en calle alcala 43 madrid"
}
The frontend geocodes the address via GET /api/ai/geocode?q=calle+alcala+43+madrid.
Input: bares no accesibles en gran viaResponse:
{
  "poi": "",
  "address": "",
  "place": "gran via",
  "categories": ["gastronomia"],
  "filters": ["no"],
  "message": "Búsqueda de bares no accesibles en gran via"
}
Only the no filter is active; the map shows venues explicitly marked as non-accessible.
Input: restaurantes y hoteles en malasañaResponse:
{
  "poi": "",
  "address": "",
  "place": "malasaña",
  "categories": ["gastronomia", "alojamiento"],
  "filters": ["yes", "limited"],
  "message": "Búsqueda de restaurantes y hoteles en malasaña"
}
Multiple categories can be returned when the user explicitly names more than one.

Frontend Integration

AIAssistant Component

The AIAssistant component renders a floating form fixed to the bottom centre of the Accessibility Map. It includes:
  • A text input (placeholder="Pregunta a Wayfy...") bound to local query state
  • Submit on Enter (without Shift) or the form submit button
  • A microphone icon (fa-microphone-lines) that toggles the Web Speech API
  • A full-screen loading overlay while isProcessing is true
The component listens for a custom focus-ai-input DOM event so other parts of the UI can programmatically focus the input.

useAIAssistant Hook

const {
  processQuery,    // async (text: string) => result | null
  isProcessing,    // boolean — true while waiting for Groq + geocoding
  isListening,     // boolean — true while Web Speech API is recording
  toggleListening, // () => void — start/stop voice input
  inputRef,        // React ref for the text input element
  focusInput,      // () => void — programmatically focus the input
} = useAIAssistant();
processQuery uses an AbortController so rapidly-submitted queries cancel the previous in-flight request. Voice recognition is configured for es-ES (Spanish) and automatically calls processQuery with the transcript on recognition.onresult.

Geocoding Resolution Order

fetchMapData in mapgpt.api.js tries to resolve a geographic coordinate in this priority order:
  1. geocode-poi(poi + " " + place) — POI name with place context
  2. geocode-poi(poi) — POI name alone
  3. geocode(address + " " + place) — address with place context
  4. geocode(address) — address alone
  5. geocode(place) — place/city name
The first non-null result is used. If none resolves, feature is null and the map position is not changed (only filters and categories update).

Rate Limits

EndpointLimit
POST /api/ai/mapgpt20 requests / minute per IP
GET /api/ai/geocode30 requests / minute per IP
GET /api/ai/geocode-poi30 requests / minute per IP
Rate limiting is enforced by Flask-Limiter (api.utils.limiter). Exceeding a limit returns HTTP 429.
The Nominatim geocoder (used in both /geocode and /geocode-poi fallback) requires a descriptive User-Agent header per the OpenStreetMap usage policy. WayFy sends WayfyAI/1.0 (contact@example.com) — set the CONTACT_EMAIL environment variable on the backend to customise the address included in this header.

Build docs developers (and LLMs) love