Spaces:
Sleeping
Sleeping
Commit Β·
2a534fa
1
Parent(s): e5f1ed2
AGENTS.md: point agents at the v2 surface (app.py + ui/ + wavereader/), legacy/ frozen
Browse files
AGENTS.md
CHANGED
|
@@ -1,102 +1,50 @@
|
|
| 1 |
# AGENTS.md
|
| 2 |
|
| 3 |
Guidance for AI coding agents working in this repo. The active surface is
|
| 4 |
-
`
|
| 5 |
-
|
|
|
|
| 6 |
|
| 7 |
## What is live now
|
| 8 |
|
| 9 |
-
- `
|
| 10 |
-
|
| 11 |
-
- `
|
| 12 |
-
|
| 13 |
-
`
|
| 14 |
-
`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
`surf-break-example-bells.json` (prompt worked example).
|
|
|
|
| 16 |
|
| 17 |
-
##
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
-
`AUSTRALIA_CENTER`/`ZOOM`, shared strings (`SCORE_EXPLAINER_MD`,
|
| 24 |
-
`BROWSE_INTRO`, `AGENT_INTRO`, ...). `app/theme.py` β `APP_CSS` only.
|
| 25 |
-
- `app/breaks_data.py` β `load_breaks()` reads
|
| 26 |
-
`data/australia-surf-breaks-enriched.json` into `DF` (handles legacy
|
| 27 |
-
`{"name | state | region": {...}}` dict format too, drops `error` rows).
|
| 28 |
-
Derives `STATES`, `REGIONS_BY_STATE`, `ALL_REGIONS`, `SKILLS`.
|
| 29 |
-
`filter_breaks(df, state, region, skill)` β `"All"`/None = no filter,
|
| 30 |
-
case-insensitive on canonical `state` / `region` / `skillLevel`.
|
| 31 |
-
- `app/maps.py` β `build_map(records, default_view)` (Plotly `Scattermap`,
|
| 32 |
-
`open-street-map` style, marker colour by `SKILL_COLORS`, point order ==
|
| 33 |
-
records order). Auto-centers/zooms via `_zoom_for_span()` unless
|
| 34 |
-
`default_view` (unfiltered β whole Australia). `build_map_with_custom()`
|
| 35 |
-
adds the gold star marker for the session break. `_lat()` / `_lng()`
|
| 36 |
-
return None on missing coords.
|
| 37 |
-
- `app/break_details.py` β `break_to_table(break_)` flattens one record to a
|
| 38 |
-
Field/Value dataframe; `_join()` stringifies list fields; `_cam_*` wrappers
|
| 39 |
-
+ `CAMS` overlay via direct `from app import surf_cams` (optional, never
|
| 40 |
-
breaks the UI).
|
| 41 |
-
- `app/browse_sync.py` β `update_map`, `on_break_pick`, region-choice
|
| 42 |
-
helpers, `mirror_*` one-way filterβprefs sync, `resync_on_mode_toggle`,
|
| 43 |
-
`_format_pref_chip`. `_resolve_pick()` maps a break-list label back to its
|
| 44 |
-
record (custom break carries a `β (your break)` suffix).
|
| 45 |
-
- `app/custom_break.py` β `generate_custom_break()` streams
|
| 46 |
-
`(telemetry, details, map, dropdown, state)` tuples via `yield` (lazy
|
| 47 |
-
`from app import generate_surf_break`, no `importlib` shims). Empty custom
|
| 48 |
-
state/region fields fall back to the main map filters; explicit values win.
|
| 49 |
-
Exactly one custom break per session (`gr.State`, never written to disk).
|
| 50 |
-
`clear_custom_break()` deletes it.
|
| 51 |
-
- `app/forecast_handlers.py` β `fetch_forecast()` (deterministic charts via
|
| 52 |
-
lazy `app.surf_forecast`) + `generate_reports()` (streams
|
| 53 |
-
`app.generate_surf_report` markdown).
|
| 54 |
-
- `app/agent_trace.py` β trace coercion (`_coerce_scored_hours`,
|
| 55 |
-
`_coerce_rank_rows`, `_coerce_forecast_hours`), telemetry builders,
|
| 56 |
-
chart trios. `app/agent_chat.py` β `chat_fn` (lazy
|
| 57 |
-
`from app import agent`, session-persistent `SurfAgent`), spot dropdown,
|
| 58 |
-
leaderboard select.
|
| 59 |
-
- `app/ui.py` β `build_demo()` layout + event wiring only (no logic).
|
| 60 |
-
|
| 61 |
-
Conventions: lo-fi theme via `APP_CSS` (light blue bg `#d6e9f8`, dark blue
|
| 62 |
-
`#0b2c5c`, Courier). Don't restyle without asking. Keep callbacks wired in
|
| 63 |
-
`build_demo()` (`state_dd`/`region_dd`/`skill_dd` β `update_map`;
|
| 64 |
-
`break_dd` β `on_break_pick`; `generate_btn` β `generate_custom_break`;
|
| 65 |
-
`clear_btn` β `clear_custom_break`).
|
| 66 |
-
|
| 67 |
-
## `app/` map of the code
|
| 68 |
-
|
| 69 |
-
`app/generate_surf_break.py`:
|
| 70 |
-
|
| 71 |
-
- `MODEL_ID = "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16"`,
|
| 72 |
-
`PROVIDER = "fireworks-ai"`. Swap only to a model actually served by an
|
| 73 |
-
Inference Provider you have enabled.
|
| 74 |
-
- `load_prompt_parts()` reads schema + Bells example from `data/`.
|
| 75 |
-
- `build_surf_break_prompt(break_name, state, region)` β rules: JSON only,
|
| 76 |
-
populate required fields, strict enums, real-world geography (not generic
|
| 77 |
-
defaults), best-estimate coords, primary takeoff zone, 2β4 sentence
|
| 78 |
-
description, copy state/region verbatim into top-level AND
|
| 79 |
-
`location.state`/`location.region`.
|
| 80 |
-
- `extract_json()` tolerates ```fences, else grabs outermost`{...}`.
|
| 81 |
-
- `generate_surf_break(...)` β one `InferenceClient.chat.completions.create`
|
| 82 |
-
call (temp 0.4, max_tokens 2048). Deliberately NOT a smolagents CodeAgent
|
| 83 |
-
(ReAct `<code>` format conflicts with raw-JSON instruction β see module
|
| 84 |
-
docstring). CLI: `python app/generate_surf_break.py [name] [state] [region]`.
|
| 85 |
-
- Needs `HF_TOKEN` env (Inference Providers access).
|
| 86 |
-
|
| 87 |
-
`app/generate_base_data.py`:
|
| 88 |
-
|
| 89 |
-
- `load_break_list()` flattens `Australia_Surf_Breaks: state β region β
|
| 90 |
-
[names]` into `{break_name, state, region}` in stable order.
|
| 91 |
-
- `enrich_result()` merges LLM output with canonical `state`/`region` from
|
| 92 |
-
the input list + stable `id`; normalizes `location.state/region`.
|
| 93 |
-
- `load_existing_results()` / `save_results()` β resume-safe; accepts legacy
|
| 94 |
-
dict format, writes sorted list by (state, region, name).
|
| 95 |
-
- `generate_with_retries()` β 2 attempts, linear backoff; failures are NOT
|
| 96 |
-
persisted (missing entries retry next run).
|
| 97 |
-
- `main()` saves after every break + `REQUEST_DELAY = 1.0`s between calls.
|
| 98 |
-
|
| 99 |
-
## `data/` contracts
|
| 100 |
|
| 101 |
- Required break fields: `name`, `state`, `region`, `location`,
|
| 102 |
`skillLevel`, `breakType`, `peakType`, `idealSwell`, `idealWind`,
|
|
@@ -107,25 +55,16 @@ Conventions: lo-fi theme via `APP_CSS` (light blue bg `#d6e9f8`, dark blue
|
|
| 107 |
model. `location.country` is `"Australia"`.
|
| 108 |
- New prompts must load schema + example from `data/` (don't paste copies).
|
| 109 |
|
| 110 |
-
## `wavereader/` (deleted after port β see `app/agent.py` + `app/agent_tools.py`)
|
| 111 |
-
|
| 112 |
-
- Ported: `app/agent_tools.py` (deterministic forecast/score/knowledge
|
| 113 |
-
functions over `data/australia-surf-breaks-enriched.json` via
|
| 114 |
-
`app/forecasts.py` + `app/scoring.py` + `app/adapters.py`),
|
| 115 |
-
`app/agent.py` (smolagents CodeAgent, HF Router, Nemotron defaults, trace
|
| 116 |
-
capture, single Tool definitions).
|
| 117 |
-
- `main.py` "surf agent" tab chats via `SurfAgent.run_stream` and rebuilds
|
| 118 |
-
score/swell/wind charts from the trace's `score_week` payload.
|
| 119 |
-
- The LLM never owns numbers β scores/forecasts stay deterministic.
|
| 120 |
-
|
| 121 |
## Rules for edits
|
| 122 |
|
| 123 |
-
1.
|
| 124 |
-
2. Never commit `.env`, `.venv/`, `__pycache__/`, `.DS_Store`
|
|
|
|
| 125 |
3. Never read `.env` or print secrets/tokens. Use `.env.example` for var
|
| 126 |
names; check presence with `printenv HF_TOKEN | wc -c` or
|
| 127 |
`[ -n "$HF_TOKEN" ]` style checks that never echo the value.
|
| 128 |
-
4. Keep prompt files in `data/`; keep `
|
| 129 |
-
5. Test cheaply: `uv run
|
| 130 |
-
|
| 131 |
-
|
|
|
|
|
|
| 1 |
# AGENTS.md
|
| 2 |
|
| 3 |
Guidance for AI coding agents working in this repo. The active surface is
|
| 4 |
+
`app.py` + `ui/` + `wavereader/` + `data/` (the v2 rebuild). `legacy/`
|
| 5 |
+
holds the frozen v1 front end (`main.py` + `app/`) β don't extend it; it
|
| 6 |
+
survives only as the fallback layer in `ui/_compat.py`.
|
| 7 |
|
| 8 |
## What is live now
|
| 9 |
|
| 10 |
+
- `app.py` β v2 entrypoint: `build()` + `gr.api` tools +
|
| 11 |
+
`demo.launch(css=APP_CSS, mcp_server=True)`. Run `uv run python app.py`.
|
| 12 |
+
- `ui/` β the front end: `app.py` (layout + wiring, β€3 `gr.State`),
|
| 13 |
+
`panels/{book,swell,agent}.py`, `charts/{score,swell,wind,seafloor,climate,map}.py`,
|
| 14 |
+
`_compat.py` (backend access: v2 core β `legacy.app.*` β stubs),
|
| 15 |
+
`theme.py` (`APP_CSS`, lo-fi Courier identity β don't restyle without asking).
|
| 16 |
+
- `wavereader/` β pure core, typed + tested, no Gradio imports:
|
| 17 |
+
`breaks.py` (catalogue, memoized `load_breaks`), `openmeteo.py`
|
| 18 |
+
(versioned/atomic disk cache, seaward grid-snap), `scoring.py` (deterministic
|
| 19 |
+
0β10 engine β the LLM never owns numbers), `seafloor.py` (GEBCO world model),
|
| 20 |
+
`climate.py` (ERA5 5-yr profiles + audit), `tools.py` (the typed tool
|
| 21 |
+
functions shared by agent + MCP), `agent.py` (smolagents `ToolCallingAgent`,
|
| 22 |
+
native tool calls, budget: 6 steps / 2 score_week / 700 tokens),
|
| 23 |
+
`narrate.py` + `llm.py` (one InferenceClient factory:
|
| 24 |
+
Nemotron 3.5 Lightning via fireworks-ai, `WR_*` env overrides).
|
| 25 |
+
- `scripts/warm_caches.py` β pre-fill forecast + seafloor caches (demo hot
|
| 26 |
+
start). `scripts/build_climate.py` β climatology builder.
|
| 27 |
+
- `data/` β `australia-surf-breaks-enriched.json` (238 breaks),
|
| 28 |
+
`surf-break-schema.json` (contract), `climate/*.json` (ERA5 profiles),
|
| 29 |
`surf-break-example-bells.json` (prompt worked example).
|
| 30 |
+
- `documents/VIDEO_SCRIPT.md` β the GTC entry video shot list.
|
| 31 |
|
| 32 |
+
## Conventions that matter
|
| 33 |
|
| 34 |
+
- Staged spin-ups: `ui/panels/swell.py::fetch_forecast` is a **generator**
|
| 35 |
+
that yields stage 1 (charts, ~ms) then stage 2 (seafloor world model from
|
| 36 |
+
a parallel thread). Status lines name the engine + latency. Keep that
|
| 37 |
+
pattern for anything slow.
|
| 38 |
+
- Charts: theme through `ui/charts/_style.py::style_fig`; weekend shading
|
| 39 |
+
via `add_weekend_shading`. Seafloor display is bicubically smoothed
|
| 40 |
+
(scipy) β smoothing is display-only; stats stay on the raw grid.
|
| 41 |
+
- MCP surface = exactly `score_week` / `rank_region_week` / `explain_score`
|
| 42 |
+
(`gr.api` in `app.py`). All event listeners are registered with
|
| 43 |
+
`api_name=False` β keep it that way.
|
| 44 |
+
- The LLM never owns numbers. Scores/forecasts/analysis stay deterministic;
|
| 45 |
+
the model narrates and cites tools.
|
| 46 |
|
| 47 |
+
## Data contracts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
- Required break fields: `name`, `state`, `region`, `location`,
|
| 50 |
`skillLevel`, `breakType`, `peakType`, `idealSwell`, `idealWind`,
|
|
|
|
| 55 |
model. `location.country` is `"Australia"`.
|
| 56 |
- New prompts must load schema + example from `data/` (don't paste copies).
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
## Rules for edits
|
| 59 |
|
| 60 |
+
1. Everything is tracked in this repo (no nested git β don't re-init one).
|
| 61 |
+
2. Never commit `.env`, `.venv/`, `__pycache__/`, `.DS_Store`, `.cache/`
|
| 62 |
+
(all ignored).
|
| 63 |
3. Never read `.env` or print secrets/tokens. Use `.env.example` for var
|
| 64 |
names; check presence with `printenv HF_TOKEN | wc -c` or
|
| 65 |
`[ -n "$HF_TOKEN" ]` style checks that never echo the value.
|
| 66 |
+
4. Keep prompt files in `data/`; keep `app.py` free of embedded schema text.
|
| 67 |
+
5. Test cheaply: `uv run pytest -q` (offline fixtures) +
|
| 68 |
+
`uv run python -c "import app"` for import health. Live LLM calls cost
|
| 69 |
+
inference β dry-run the agent via the injected fake model
|
| 70 |
+
(`tests/test_agent_dryrun.py`) before any live turn.
|