--- title: GeoGuessr Arena emoji: 🌍 colorFrom: indigo colorTo: yellow sdk: docker app_port: 7860 hf_oauth: true hf_oauth_scopes: - inference-api pinned: true models: - mistralai/Mistral-Large-4.0-1T05-A52B --- # GeoGuessr Arena: you vs the machines Everyone is dropped at the same random Street View location with the same clock. You explore with a draggable Street View. The AI agents explore with tools: | tool | what it does | |---|---| | `look` / `pan` / `zoom` | change heading, pitch or field of view and get a new 640×640 view | | `move` | walk forward or backward along the current heading, snapping to the nearest Google panorama | | `view_map` | a roadmap of any coordinates the agent picks, so it can check a hypothesis. It never shows where the agent actually is. | | `place_pin` / `reverse_geocode` | drop a candidate pin, or look up which place a coordinate falls in | | `submit_guess` | lock in a final answer | While the round is live you only see a **blind feed** of what each rival is doing ("walked 23 m", "zoomed in"), never what they see or think. At the reveal you get the guesses map, every agent's walked path, and a full step-by-step replay of its views, reasoning and tool calls. ## Space secrets | secret | needed for | |---|---| | `GOOGLE_MAPS_API_KEY` | server side: Street View Static + metadata, Static Maps, Geocoding | | `GOOGLE_MAPS_BROWSER_KEY` | Maps JavaScript API in the browser. Restrict it by HTTP referrer. Falls back to the server key. | | `HF_TOKEN` | trace uploads (needs write access to the traces dataset) | | `MISTRAL_API_KEY` | Mistral models | | `HF_TRACES_REPO` | optional. Private dataset for traces (default `Jofthomas/geoguessr-arena-traces`) | Open-weight models (every `key="hf_login"` entry in `MODELS`) run through the HF router on the **player's own Hugging Face account**: they click "Sign in with Hugging Face" and the Space's OAuth token (scope `inference-api`) is used for their games, so usage is billed to them. Proprietary models (`key="byok"`) run on the player's own key. Those keys are held in memory for the duration of the game only. They are never logged and never written to traces. Optional tuning variables: `ROUNDS_PER_GAME`, `ROUND_SECONDS`, `AGENT_MAX_STEPS`, `AGENT_MAX_GEOCODES`, `AGENT_WRAPUP_SECONDS`, `GAMES_PER_IP_PER_HOUR`. ## Changing the model list Every opponent is one entry in the `MODELS` dict in [`arena/config.py`](arena/config.py). The lobby, the live feed, the reveal and the traces all read from it, so adding, removing or editing an entry is the only change needed. | field | meaning | |---|---| | `label`, `color` | name and colour shown in the UI | | `provider` | `"openai_compat"` (any OpenAI-style `/chat/completions` with tools), `"openai_responses"` (OpenAI's Responses API, required for their reasoning models with tools) or `"anthropic"` | | `base_url` | endpoint for `openai_compat`, e.g. `HF_ROUTER_BASE_URL` | | `model_id` | model name sent to the provider (HF router: the Hub repo id) | | `key` | `"hf_login"` (player's HF account), `"byok"` (player pastes a key, set `key_hint`), or the name of an env var holding a server key | | `avatar` | profile picture, e.g. `/static/avatars/qwen.webp` | | `default_on` | pre-selected in the lobby | | `catalog` | BYOK only: `"openai"`, `"anthropic"` or `"gemini"`. Once a key is pasted, the lobby lists that provider's models in a dropdown; `model_id` is the default | | `reasoning_effort` | `openai_responses` (e.g. `"medium"`) or `openai_compat` providers that accept it, e.g. Mistral Large 4 (`"high"` / `"none"`) | | `max_images` | optional cap on images per request for providers that limit it (also learned automatically from a 413) | | `token_param`, `extra_body` | optional provider quirks for `openai_compat` | Model keys (the dict keys) end up in DOM ids: use letters, digits, `-` and `_` only. Models must accept images **and** support tool calling. For the HF router, check `https://router.huggingface.co/v1/models`: `input_modalities` must include `image` and at least one live provider must have `supports_tools: true`. ## Traces Each revealed round is written to `data/{date}/{game_id}/round_NN.jsonl` in the private dataset, with one row per participant: the guess, distance, score, rank, token usage, walked path, pins and the full provider-neutral message trace. Street View pixels are **not** uploaded (Google Maps ToS). Each image is stored as its request parameters instead (`pano`, `heading`, `pitch`, `fov` / map center + zoom), so you can re-fetch it with your own key. ## Local dev ```bash uv venv -p 3.12 .venv && uv pip install -r requirements.txt cp .env.example .env # fill in keys .venv/bin/uvicorn arena.main:app --reload --port 7860 ```