market-aware AI cadence + incremental log updates
Two changes that together cut OpenRouter spend ~50% and give the daily log temporal awareness. 1. CadencePolicy (app/services/cadence.py): expensive AI jobs only fire hourly during the EU/US active window (Mon-Fri 07-21 UTC). Off-hours weekdays throttle to every 4h; weekends to every 12h. ai_log_job and indicator_summary_job both consult the policy before doing real work; market/news/portfolio ingest jobs stay hourly (cheap, no API cost). Skipped runs land in job_runs with status 'skipped' and the throttle reason in error. 2. Update mode for ai_log_job: when an earlier log exists for the current UTC day, it's passed to the model as 'Earlier log from today (generated HH:MM UTC)'. The system prompt grows an Update mode section instructing the model to revise — not restart — and anchor on what has CHANGED since the earlier draft. The TL;DR leads with intra-day change when meaningful, the watch list evolves rather than restarts. PROMPT_VERSION bumped to 5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2f223b75a3
commit
40cfb50e37
4 changed files with 157 additions and 6 deletions
|
|
@ -13,7 +13,8 @@ from sqlalchemy import desc, func, select
|
|||
from app.config import get_settings, load_groups
|
||||
from app.db import utcnow
|
||||
from app.jobs._helpers import job_lifecycle, log
|
||||
from app.models import AICall, IndicatorSummary, Quote
|
||||
from app.models import AICall, IndicatorSummary, JobRun, Quote
|
||||
from app.services.cadence import DEFAULT_POLICY
|
||||
from app.services.openrouter import (
|
||||
PROMPT_VERSION,
|
||||
build_aggregate_summary_system_prompt,
|
||||
|
|
@ -234,6 +235,21 @@ async def run() -> None:
|
|||
jr.status = "skipped"
|
||||
return
|
||||
|
||||
# Cadence — same policy as ai_log_job: hourly during EU/US active,
|
||||
# throttled off-hours and weekends.
|
||||
last_success = (await session.execute(
|
||||
select(func.max(JobRun.finished_at)).where(
|
||||
JobRun.name == "indicator_summary_job",
|
||||
JobRun.status == "success",
|
||||
)
|
||||
)).scalar()
|
||||
should_run, reason = DEFAULT_POLICY.should_run(last_success)
|
||||
if not should_run:
|
||||
log.info("ind_summary.cadence_skip", reason=reason)
|
||||
jr.status = "skipped"
|
||||
jr.error = reason
|
||||
return
|
||||
|
||||
spent = await _month_spend(session)
|
||||
if spent >= s.OPENROUTER_MONTHLY_CAP_USD:
|
||||
jr.status = "skipped"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue