Stephen Lee commited on
Commit
95941cd
·
1 Parent(s): 7cdae4e

feat: add real query cache coverage

Browse files
coastwise/locations.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """California coastal location parsing helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ from coastwise.schemas import LocationContext
8
+
9
+
10
+ LOCATION_ALIASES: dict[str, tuple[str, str, str]] = {
11
+ "crescent city": ("Crescent City", "northern", "Northern California ocean region"),
12
+ "fort bragg": ("Fort Bragg", "mendocino", "Mendocino California ocean region"),
13
+ "pacifica": ("Pacifica", "san_francisco", "San Francisco coast"),
14
+ "half moon bay": ("Half Moon Bay", "san_francisco", "San Francisco coast"),
15
+ "ocean beach": ("Ocean Beach SF", "san_francisco", "San Francisco coast"),
16
+ "monterey": ("Monterey", "central", "Central California ocean region"),
17
+ "santa barbara": ("Santa Barbara", "southern", "Southern California ocean region"),
18
+ "san diego bay": ("San Diego Bay", "southern", "Southern California ocean region"),
19
+ "san francisco bay": ("San Francisco Bay", "san_francisco_bay", "San Francisco Bay region"),
20
+ }
21
+
22
+ UNSUPPORTED_LOCATION_TERMS = ("lake", "tahoe", "river", "stream", "freshwater", "inland")
23
+
24
+
25
+ def _normalize(value: str) -> str:
26
+ return " ".join(re.sub(r"[^a-z0-9]+", " ", value.casefold()).split())
27
+
28
+
29
+ def parse_location(question: str, location_text: str | None = None) -> LocationContext:
30
+ """Parse a user-facing beach or coastal-area location."""
31
+
32
+ normalized_question = _normalize(question)
33
+ normalized_input = _normalize(location_text or "")
34
+ combined = " ".join(part for part in (normalized_input, normalized_question) if part)
35
+
36
+ if any(term in combined for term in UNSUPPORTED_LOCATION_TERMS):
37
+ return LocationContext(
38
+ raw_question_location=None,
39
+ raw_location_input=location_text or None,
40
+ matched_place=None,
41
+ area_or_scope="Unsupported non-coastal or non-ocean location",
42
+ confidence="unsupported",
43
+ warning="This MVP covers California ocean guidance, not freshwater, inland, or non-coastal locations.",
44
+ region_key=None,
45
+ )
46
+
47
+ for alias, (display, region_key, scope) in LOCATION_ALIASES.items():
48
+ if alias in combined:
49
+ raw_question_location = alias if alias in normalized_question else None
50
+ return LocationContext(
51
+ raw_question_location=raw_question_location,
52
+ raw_location_input=location_text or None,
53
+ matched_place=display,
54
+ area_or_scope=scope,
55
+ confidence="recognized",
56
+ warning=None,
57
+ region_key=region_key,
58
+ )
59
+
60
+ if normalized_input:
61
+ return LocationContext(
62
+ raw_question_location=None,
63
+ raw_location_input=location_text,
64
+ matched_place=None,
65
+ area_or_scope="General California ocean",
66
+ confidence="ambiguous",
67
+ warning="Location was not recognized clearly; local, area, or emergency rules may differ, so verify official local guidance before harvest.",
68
+ region_key=None,
69
+ )
70
+
71
+ return LocationContext(
72
+ raw_question_location=None,
73
+ raw_location_input=None,
74
+ matched_place=None,
75
+ area_or_scope="General California ocean",
76
+ confidence="absent",
77
+ warning="No location was provided; local, area, or emergency rules may differ, so verify official local guidance before harvest.",
78
+ region_key=None,
79
+ )
data/source-cache/seed/chunks.json ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "chunk_lingcod_groundfish",
4
+ "source_id": "cdfw_groundfish_summary",
5
+ "source_url": "https://wildlife.ca.gov/Fishing/Ocean/Regulations/Groundfish-Summary",
6
+ "source_title": "CDFW Groundfish Summary",
7
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
8
+ "heading": "Statewide Daily Bag Limits and Minimum Size Limits",
9
+ "text": "Lingcod, section 28.27, daily bag limit is 2 fish per person. Minimum size limit is 22 inches total length.",
10
+ "tokens": ["lingcod", "daily", "bag", "limit", "minimum", "size", "22", "inches"],
11
+ "species_or_category_terms": ["lingcod"],
12
+ "location_terms": []
13
+ },
14
+ {
15
+ "id": "chunk_dungeness_crab",
16
+ "source_id": "cdfw_crabs",
17
+ "source_url": "https://wildlife.ca.gov/Conservation/Marine/Invertebrates/Crabs",
18
+ "source_title": "CDFW Crabs",
19
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
20
+ "heading": "Recreational Crab Fishery",
21
+ "text": "CDFW recreational crab fishery materials link Dungeness crab season, crab trap, hoop net, public health closure, and current recreational ocean fishing regulation information. Verify current CDFW crab rules and public health closures before harvest.",
22
+ "tokens": ["dungeness", "crab", "season", "trap", "hoop", "net", "public", "health", "closure", "verify"],
23
+ "species_or_category_terms": ["dungeness crab", "crab"],
24
+ "location_terms": []
25
+ },
26
+ {
27
+ "id": "chunk_cdph_quarantine",
28
+ "source_id": "cdph_annual_mussel_quarantine",
29
+ "source_url": "https://www.cdph.ca.gov/Programs/CEH/DRSEM/Pages/EMB/Shellfish/Annual-Mussel-Quarantine.aspx",
30
+ "source_title": "CDPH Annual Mussel Quarantine",
31
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
32
+ "heading": "Annual Mussel Quarantine",
33
+ "text": "The annual quarantine is normally in effect from May 1 through October 31 along the California coast. Cooking does not reliably destroy the toxins.",
34
+ "tokens": ["annual", "mussel", "quarantine", "may", "october", "california", "coast", "cooking", "toxins"],
35
+ "species_or_category_terms": ["mussel", "mussels", "shellfish"],
36
+ "location_terms": []
37
+ },
38
+ {
39
+ "id": "chunk_cdph_shellfish_verify",
40
+ "source_id": "cdph_shellfish_advisories",
41
+ "source_url": "https://www.cdph.ca.gov/Programs/OPA/Pages/Shellfish-Advisories.aspx",
42
+ "source_title": "CDPH Shellfish Advisories",
43
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
44
+ "heading": "Shellfish Advisories",
45
+ "text": "CDPH shellfish advisories and recreational bivalve shellfish advisory map should be checked before harvesting or consuming sport-harvested shellfish.",
46
+ "tokens": ["cdph", "shellfish", "advisory", "map", "harvesting", "consuming", "verify"],
47
+ "species_or_category_terms": ["shellfish", "mussel", "mussels", "clams", "crab"],
48
+ "location_terms": []
49
+ },
50
+ {
51
+ "id": "chunk_monterey_regional",
52
+ "source_id": "cdfw_region_central",
53
+ "source_url": "https://wildlife.ca.gov/Fishing/Ocean/Regulations/Fishing-Map/Central",
54
+ "source_title": "CDFW Central Ocean Region",
55
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
56
+ "heading": "Central Region",
57
+ "text": "Central California ocean region source entry includes Monterey area context. Use cited regional snippets and verify current CDFW guidance when exact structured regional facts are unavailable.",
58
+ "tokens": ["central", "california", "ocean", "monterey", "regional", "verify"],
59
+ "species_or_category_terms": ["regional"],
60
+ "location_terms": ["monterey"]
61
+ },
62
+ {
63
+ "id": "chunk_dungeness_crab_season_context",
64
+ "source_id": "cdfw_crabs",
65
+ "source_url": "https://wildlife.ca.gov/Conservation/Marine/Invertebrates/Crabs",
66
+ "source_title": "CDFW Crabs",
67
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
68
+ "heading": "Dungeness Crab Season Context",
69
+ "text": "Saved CDFW crab information references Dungeness crab season, crab trap rules, hoop net rules, public health closures, and current recreational ocean fishing regulation information for California ocean crab harvest. This saved cache does not include a clear open or closed season date for Fort Bragg or Pacifica.",
70
+ "tokens": ["saved", "cdfw", "dungeness", "crab", "season", "trap", "hoop", "net", "public", "health", "closure", "fort", "bragg", "pacifica"],
71
+ "species_or_category_terms": ["dungeness crab", "crab"],
72
+ "location_terms": ["fort bragg", "pacifica"]
73
+ },
74
+ {
75
+ "id": "chunk_rock_crab_context",
76
+ "source_id": "cdfw_crabs",
77
+ "source_url": "https://wildlife.ca.gov/Conservation/Marine/Invertebrates/Crabs",
78
+ "source_title": "CDFW Crabs",
79
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
80
+ "heading": "Rock Crab Context",
81
+ "text": "Saved CDFW crab information covers recreational crab rules and public health closure context for crab species including rock crab. This saved cache does not confirm whether rock crab can be consumed.",
82
+ "tokens": ["saved", "cdfw", "crab", "rock", "public", "health", "closure", "consume"],
83
+ "species_or_category_terms": ["rock crab", "crab"],
84
+ "location_terms": []
85
+ },
86
+ {
87
+ "id": "chunk_cabezon_context",
88
+ "source_id": "cdfw_groundfish_summary",
89
+ "source_url": "https://wildlife.ca.gov/Fishing/Ocean/Regulations/Groundfish-Summary",
90
+ "source_title": "CDFW Groundfish Summary",
91
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
92
+ "heading": "Cabezon Context",
93
+ "text": "Saved CDFW groundfish summary information includes cabezon as a groundfish species. Restrictions may include season, depth, bag, size, gear, and area rules, but this saved cache does not include a reviewed exact cabezon limit.",
94
+ "tokens": ["saved", "cdfw", "groundfish", "cabezon", "restrictions", "season", "depth", "bag", "size", "gear", "area"],
95
+ "species_or_category_terms": ["cabezon"],
96
+ "location_terms": []
97
+ },
98
+ {
99
+ "id": "chunk_half_moon_bay_shellfish_context",
100
+ "source_id": "cdph_shellfish_advisories",
101
+ "source_url": "https://www.cdph.ca.gov/Programs/OPA/Pages/Shellfish-Advisories.aspx",
102
+ "source_title": "CDPH Shellfish Advisories",
103
+ "retrieved_at": "2026-06-14T12:00:00+00:00",
104
+ "heading": "Half Moon Bay Shellfish Context",
105
+ "text": "Saved CDPH shellfish advisory information should be checked for California coastal shellfish areas including Half Moon Bay. This saved cache does not certify shellfish safety for Half Moon Bay.",
106
+ "tokens": ["saved", "cdph", "shellfish", "advisory", "half", "moon", "bay", "safety"],
107
+ "species_or_category_terms": ["shellfish", "clams", "mussels"],
108
+ "location_terms": ["half moon bay"]
109
+ }
110
+ ]
tests/test_locations.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Location parsing tests."""
2
+
3
+ from coastwise.locations import parse_location
4
+
5
+
6
+ def test_parse_location_from_question_or_optional_beach_field():
7
+ pacifica = parse_location("can I eat mussels from Pacifica today?", "")
8
+ monterey = parse_location("what's the lingcod size?", "Monterey")
9
+ blank = parse_location("what's the min size of lingcod?", "")
10
+ unsupported = parse_location("what are trout rules?", "Lake Tahoe")
11
+
12
+ assert pacifica.matched_place == "Pacifica"
13
+ assert pacifica.region_key == "san_francisco"
14
+ assert monterey.matched_place == "Monterey"
15
+ assert monterey.region_key == "central"
16
+ assert blank.confidence == "absent"
17
+ assert "local" in (blank.warning or "").lower()
18
+ assert unsupported.confidence == "unsupported"
19
+ assert "california ocean" in (unsupported.warning or "").lower()
20
+
21
+
22
+ def test_parse_location_recognizes_half_moon_bay_for_public_queries():
23
+ context = parse_location("is shellfish quarantine in half moon bay?", "")
24
+
25
+ assert context.matched_place == "Half Moon Bay"
26
+ assert context.region_key == "san_francisco"
27
+ assert context.confidence == "recognized"
tests/test_source_cache.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Source cache tests."""
2
+
3
+ from dataclasses import replace
4
+ from datetime import datetime, timedelta, timezone
5
+
6
+ from coastwise.source_cache import (
7
+ DEFAULT_SEED_CACHE_DIR,
8
+ SourceCache,
9
+ cache_status_summary,
10
+ freshness_status,
11
+ load_seed_cache,
12
+ load_latest_cache,
13
+ validate_seed_cache,
14
+ write_runtime_cache,
15
+ )
16
+ from coastwise.schemas import FreshnessStatus, RefreshStatus, SourceSnapshot
17
+ from coastwise.source_registry import load_official_sources, statewide_source_coverage
18
+
19
+
20
+ def test_seed_cache_loads_and_covers_required_statewide_sources():
21
+ cache = load_seed_cache(DEFAULT_SEED_CACHE_DIR)
22
+ issues = validate_seed_cache(cache, load_official_sources(), statewide_source_coverage())
23
+
24
+ assert not [issue for issue in issues if issue.severity == "error"]
25
+ assert "lingcod_min_size" in cache.facts_by_id
26
+ assert "dungeness_crab_bag_limit" in cache.facts_by_id
27
+ assert not hasattr(cache, "question_history")
28
+ assert not hasattr(cache, "location_history")
29
+
30
+
31
+ def test_freshness_status_uses_24_hour_threshold():
32
+ now = datetime(2026, 6, 14, 12, tzinfo=timezone.utc)
33
+
34
+ assert freshness_status(now - timedelta(hours=24), now) is FreshnessStatus.CURRENT
35
+ assert freshness_status(now - timedelta(hours=24, seconds=1), now) is FreshnessStatus.STALE
36
+
37
+
38
+ def test_runtime_cache_overlays_valid_snapshot_and_preserves_seed_on_failure(tmp_path):
39
+ seed = load_seed_cache(DEFAULT_SEED_CACHE_DIR)
40
+ original = seed.snapshots_by_source_id["cdfw_ocean_regulations"]
41
+ replacement = SourceSnapshot(
42
+ source_id=original.source_id,
43
+ url=original.url,
44
+ title=original.title,
45
+ retrieved_at="2026-06-14T13:00:00+00:00",
46
+ content_hash="new-hash",
47
+ raw_text="Updated official source text",
48
+ origin="refresh",
49
+ refresh_status=RefreshStatus.UPDATED,
50
+ )
51
+
52
+ write_runtime_cache(tmp_path, seed, snapshots=(replacement,))
53
+ latest = load_latest_cache(seed, tmp_path)
54
+
55
+ assert latest.snapshots_by_source_id[original.source_id].content_hash == "new-hash"
56
+
57
+ preserved = load_latest_cache(seed, tmp_path)
58
+ assert preserved.snapshots_by_source_id["cdfw_groundfish_summary"].content_hash == seed.snapshots_by_source_id["cdfw_groundfish_summary"].content_hash
59
+
60
+
61
+ def test_cache_status_summary_reports_seed_runtime_stale_and_no_cache():
62
+ now = datetime(2026, 6, 14, 13, tzinfo=timezone.utc)
63
+ seed = load_seed_cache(DEFAULT_SEED_CACHE_DIR)
64
+ stale = SourceCache(
65
+ sources=seed.sources,
66
+ snapshots=tuple(replace(snapshot, retrieved_at=now - timedelta(hours=25)) for snapshot in seed.snapshots),
67
+ chunks=seed.chunks,
68
+ facts=seed.facts,
69
+ advisory_facts=seed.advisory_facts,
70
+ )
71
+
72
+ assert cache_status_summary(seed, now)["usable_cache"] is True
73
+ assert cache_status_summary(stale, now)["freshness_status"] == "stale"
74
+ assert cache_status_summary(SourceCache((), (), (), ()), now)["freshness_status"] == "no_cache"
75
+
76
+
77
+ def test_seed_cache_contains_real_query_saved_evidence_chunks():
78
+ cache = load_seed_cache(DEFAULT_SEED_CACHE_DIR)
79
+ text = " ".join(chunk.text.casefold() for chunk in cache.chunks)
80
+
81
+ for term in ("dungeness crab", "rock crab", "cabezon", "half moon bay", "pacifica"):
82
+ assert term in text