lucashudsn commited on
Commit
2a534fa
Β·
1 Parent(s): e5f1ed2

AGENTS.md: point agents at the v2 surface (app.py + ui/ + wavereader/), legacy/ frozen

Browse files
Files changed (1) hide show
  1. AGENTS.md +45 -106
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
- `main.py` + `app/` + `data/`. `wavereader/` was deleted after its port
5
- to `app/agent.py` + `app/agent_tools.py`.
 
6
 
7
  ## What is live now
8
 
9
- - `main.py` β€” Gradio map front end. Run with `uv run python main.py`.
10
- - `app/generate_surf_break.py` β€” single-shot structured-JSON generator.
11
- - `app/generate_base_data.py` β€” batch/resumable enrichment runner.
12
- - `data/` β€” `australia-surf-breaks.json` (input list),
13
- `australia-surf-breaks-enriched.json` (generated output),
14
- `surf-break-schema.json` (conformance contract),
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  `surf-break-example-bells.json` (prompt worked example).
 
16
 
17
- ## Front-end modules (`main.py` + `app/` UI layer)
18
 
19
- Entry: `main.py` (thin: re-exports + `main()` β†’ `demo.launch(css=APP_CSS)`)
20
- calls `app/ui.py::build_demo()`. Run with `uv run python main.py`.
 
 
 
 
 
 
 
 
 
 
21
 
22
- - `app/config.py` β€” paths (`DATA_PATH`), `ALL`, `SKILL_ORDER`/`SKILL_COLORS`,
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. `app/` is tracked in this repo (no nested git β€” don't re-init one).
124
- 2. Never commit `.env`, `.venv/`, `__pycache__/`, `.DS_Store` (all ignored).
 
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 `main.py` free of embedded schema text.
129
- 5. Test cheaply: `uv run python -c "from pathlib import Path; import main"` for
130
- import health; full generation calls cost inference β€” use the single-break
131
- CLI before running the batch.
 
 
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.