feat(ccip): refresh task + beat + retrain hook for character prototypes (#1317, m138 step 3)
- refresh_character_prototypes celery task wraps the incremental builder (sync ml worker); returns skipped / rebuilt=N removed=N. - Beat: every ~15 min (cheap global-gate no-op when idle) + a nightly full=True reconcile as belt-and-suspenders. - train_heads enqueues it on success, so the Retrain button AND the nightly head retrain refresh CCIP on the SAME trigger — unified lifecycle, as asked. The initial (cold) full build loads the whole reference set once in the background, never on a /suggestions request. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -107,6 +107,15 @@ def make_celery() -> Celery:
|
|||||||
"task": "backend.app.tasks.ml.scheduled_train_heads",
|
"task": "backend.app.tasks.ml.scheduled_train_heads",
|
||||||
"schedule": 86400.0, # passive cadence; manual retrain stays available
|
"schedule": 86400.0, # passive cadence; manual retrain stays available
|
||||||
},
|
},
|
||||||
|
"refresh-character-prototypes": {
|
||||||
|
"task": "backend.app.tasks.ml.refresh_character_prototypes",
|
||||||
|
"schedule": 900.0, # ~15 min; cheap global-gate no-op when idle (#1317)
|
||||||
|
},
|
||||||
|
"reconcile-character-prototypes-nightly": {
|
||||||
|
"task": "backend.app.tasks.ml.refresh_character_prototypes",
|
||||||
|
"schedule": 86400.0, # nightly FULL reconcile (belt-and-suspenders)
|
||||||
|
"args": (True,), # full=True
|
||||||
|
},
|
||||||
"apply-head-tags-daily": {
|
"apply-head-tags-daily": {
|
||||||
"task": "backend.app.tasks.ml.scheduled_apply_head_tags",
|
"task": "backend.app.tasks.ml.scheduled_apply_head_tags",
|
||||||
"schedule": 86400.0, # no-op unless head_auto_apply_enabled
|
"schedule": 86400.0, # no-op unless head_auto_apply_enabled
|
||||||
|
|||||||
@@ -328,6 +328,11 @@ def train_heads(self, run_id: int) -> str:
|
|||||||
run.status = "ready"
|
run.status = "ready"
|
||||||
run.finished_at = datetime.now(UTC)
|
run.finished_at = datetime.now(UTC)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
# A retrain folds the day's accepts/rejects into the heads; refresh the
|
||||||
|
# CCIP character prototypes on the SAME trigger (#1317) so the Retrain
|
||||||
|
# button + nightly run keep character matching current too. Cheap no-op
|
||||||
|
# when references didn't change.
|
||||||
|
refresh_character_prototypes.delay()
|
||||||
return "ready"
|
return "ready"
|
||||||
|
|
||||||
|
|
||||||
@@ -361,6 +366,31 @@ def scheduled_train_heads() -> str:
|
|||||||
return "dispatched"
|
return "dispatched"
|
||||||
|
|
||||||
|
|
||||||
|
@celery.task(
|
||||||
|
name="backend.app.tasks.ml.refresh_character_prototypes",
|
||||||
|
# Global-gate no-op when idle; a rebuild touches only changed characters. The
|
||||||
|
# initial (cold) build loads the whole reference set once, in the BACKGROUND
|
||||||
|
# — never on a /suggestions request.
|
||||||
|
soft_time_limit=1800, time_limit=2100,
|
||||||
|
)
|
||||||
|
def refresh_character_prototypes(full: bool = False) -> str:
|
||||||
|
"""Incrementally refresh the CCIP character-prototype store (#1317, m138):
|
||||||
|
cheap global-gate skip when nothing changed, else rebuild only the characters
|
||||||
|
whose references moved. Triggered by a ~15-min beat, after every head retrain
|
||||||
|
(train_heads), and nightly with full=True. Returns a short status."""
|
||||||
|
from ..services.ml.character_prototypes import (
|
||||||
|
refresh_character_prototypes as _refresh,
|
||||||
|
)
|
||||||
|
|
||||||
|
SessionLocal = _sync_session_factory()
|
||||||
|
with SessionLocal() as session:
|
||||||
|
result = _refresh(session, full=full)
|
||||||
|
return (
|
||||||
|
"skipped" if result["skipped"]
|
||||||
|
else f"rebuilt={result['rebuilt']} removed={result['removed']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@celery.task(
|
@celery.task(
|
||||||
name="backend.app.tasks.ml.apply_head_tags",
|
name="backend.app.tasks.ml.apply_head_tags",
|
||||||
bind=True,
|
bind=True,
|
||||||
|
|||||||
Reference in New Issue
Block a user