feat(advice): LLM brewing advice on every observation #14

Merged
hermes merged 17 commits from feat/llm-brew-tips into master 2026-11-09 20:23:32 +10:00
3 changed files with 34 additions and 2 deletions
Showing only changes of commit 8da3601941 - Show all commits
+6
View File
@@ -716,6 +716,12 @@ table.data .comment { color: var(--text-muted); font-size: .84rem; }
color: var(--amber-text);
}
/* The advice on the active-beers card and the run detail page - the
full text, not the clamped table cell. */
.advice--reading {
margin-top: .5rem;
}
/* Advice that could not be generated - it says why. */
.advice--failed {
border-left-color: var(--danger);
+14 -2
View File
@@ -60,7 +60,8 @@ def _start_advice(run_id: str, reading_id: str, corrected: bool = False) -> None
comment is never touched.
The background thread cannot use the request-scoped repository - that
client is closed when the request ends - so it builds its own.
client is closed when the request ends - so it uses the shared
process-wide one.
"""
if not reading_id:
return
@@ -130,7 +131,18 @@ def _start_advice(run_id: str, reading_id: str, corrected: bool = False) -> None
try:
shared = get_shared_repository()
shared.set_reading_advice(read_id, text, ok, expected_sg=advice_sg)
written = shared.set_reading_advice(read_id, text, ok,
expected_sg=advice_sg)
if not written:
# The reading was corrected while the advice was being
# generated, so the advice no longer matches it. That is
# the guard working - the fresh advice for the corrected
# reading will land in its own turn.
log.info(
"the advice for reading %s was not stored: the reading "
"no longer holds the gravity it was written for",
read_id,
)
except Exception: # noqa: BLE001 - never let the thread die noisily
log.exception("could not store the generated advice")
+14
View File
@@ -498,6 +498,20 @@ def test_the_advice_is_about_the_reading_that_was_saved():
"the advice was generated about the wrong reading"
)
# And the PROMPT must ask about that reading - not just pass it
# through. Asserting only which dict was passed would not have
# caught the prompt still saying "give advice for the most recent
# reading", which is the bug that made the model advise about the
# wrong number.
prompt = brew_advice.build_prompt(
{"canonical_name": "Ale"},
captured["reading"],
captured["history"],
)
assert "THIS IS THE READING TO ADVISE ON" in prompt
assert "2026-01-01" in prompt
assert "most recent reading" not in prompt
def test_the_shared_repository_is_built_only_once():
"""Concurrent advice threads must not each build their own client.