feat(advice): LLM brewing advice on every observation #14
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user