Add fair-use periodic info logs for production visibility#5852
Add fair-use periodic info logs for production visibility#5852
Conversation
Adds info-level logs at four points in the fair-use pipeline so operators can verify the system is actively tracking speech: - Session start: stage and session ID - Periodic speech recording (60s): speech_ms delta - Periodic cap check (5min): rolling daily/3day/weekly totals - Session end flush: final speech_ms delta Ref: #5746 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryThis PR adds four The change is low-risk and purely additive (no new dependencies, no logic changes), but there is one efficiency issue:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant StreamHandler as _stream_handler
participant FairUse as fair_use.py
participant Redis
Client->>StreamHandler: WebSocket connect
StreamHandler->>FairUse: get_enforcement_stage(uid)
Note over StreamHandler: LOG: fair_use: session start uid=... stage=...
loop Every ~60s (_record_usage_periodically)
StreamHandler->>FairUse: record_speech_ms(uid, speech_ms)
FairUse->>Redis: ZADD + HINCRBY (bucket)
Note over StreamHandler: LOG: fair_use: recorded Xms speech uid=...
end
loop Every 5min (FAIR_USE_CHECK_INTERVAL_SECONDS)
StreamHandler->>FairUse: get_rolling_speech_ms(uid)
FairUse->>Redis: ZRANGEBYSCORE + HMGET
Note right of FairUse: ⚠️ redundant — check_soft_caps calls this too
StreamHandler->>FairUse: check_soft_caps(uid)
FairUse->>Redis: ZRANGEBYSCORE + HMGET (duplicate read)
alt caps triggered
Note over StreamHandler: LOG: fair_use: soft cap triggered ...
else no caps
Note over StreamHandler: LOG: fair_use: cap check ok daily=... 3day=... weekly=...
end
end
Client->>StreamHandler: WebSocket disconnect
StreamHandler->>FairUse: record_speech_ms(uid, speech_ms)
FairUse->>Redis: ZADD + HINCRBY (final flush)
Note over StreamHandler: LOG: fair_use: session end flush Xms speech uid=...
Last reviewed commit: "Add periodic info lo..." |
| speech_totals = get_rolling_speech_ms(uid) | ||
| triggered_caps = check_soft_caps(uid) |
There was a problem hiding this comment.
Redundant
get_rolling_speech_ms Redis call
get_rolling_speech_ms(uid) is called here unconditionally, but check_soft_caps(uid) (called on the very next line) already calls get_rolling_speech_ms internally. This results in two full Redis round-trips (zrangebyscore + hmget) on every 5-minute cap check interval, even when triggered_caps is truthy (where speech_totals is never used).
Since speech_totals is only consumed in the else branch, the extra read is completely wasted when caps are triggered. The fix is to move the call into the else branch:
triggered_caps = check_soft_caps(uid)
if triggered_caps:
logger.info(
f'fair_use: soft cap triggered for {uid} session={session_id} caps={triggered_caps}'
)
asyncio.create_task(trigger_classifier_if_needed(uid, triggered_caps, session_id))
else:
speech_totals = get_rolling_speech_ms(uid)
logger.info(
f'fair_use: cap check ok uid={uid} session={session_id}'
f' daily={speech_totals["daily_ms"]}ms'
f' 3day={speech_totals["three_day_ms"]}ms'
f' weekly={speech_totals["weekly_ms"]}ms'
)Alternatively, refactor check_soft_caps to accept and return the computed totals so callers can reuse them.
- Move get_rolling_speech_ms() to else branch only, avoiding duplicate Redis read (check_soft_caps already calls it internally) - Downgrade per-minute speech recording log to debug level - Downgrade session-end flush log to debug level - Keep session-start and 5-min cap check at info level for operator visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…is read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ate Redis read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests verify precomputed totals skip Redis read, and default path still calls get_rolling_speech_ms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
All checkpoints passed:
PR is ready for merge. All changes: 3 files, 21 additions (transcribe.py, fair_use.py, test_fair_use_engine.py). by AI for @beastoin |
|
lgtm |
* Add periodic info logs to fair-use system for production visibility Adds info-level logs at four points in the fair-use pipeline so operators can verify the system is actively tracking speech: - Session start: stage and session ID - Periodic speech recording (60s): speech_ms delta - Periodic cap check (5min): rolling daily/3day/weekly totals - Session end flush: final speech_ms delta Ref: #5746 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address CODEx review: reduce log volume and fix duplicate Redis read - Move get_rolling_speech_ms() to else branch only, avoiding duplicate Redis read (check_soft_caps already calls it internally) - Downgrade per-minute speech recording log to debug level - Downgrade session-end flush log to debug level - Keep session-start and 5-min cap check at info level for operator visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add speech_totals parameter to check_soft_caps to avoid duplicate Redis read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Pass precomputed speech totals to check_soft_caps, eliminating duplicate Redis read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add tests for check_soft_caps speech_totals parameter Tests verify precomputed totals skip Redis read, and default path still calls get_rolling_speech_ms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…are#5852) * Add periodic info logs to fair-use system for production visibility Adds info-level logs at four points in the fair-use pipeline so operators can verify the system is actively tracking speech: - Session start: stage and session ID - Periodic speech recording (60s): speech_ms delta - Periodic cap check (5min): rolling daily/3day/weekly totals - Session end flush: final speech_ms delta Ref: BasedHardware#5746 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address CODEx review: reduce log volume and fix duplicate Redis read - Move get_rolling_speech_ms() to else branch only, avoiding duplicate Redis read (check_soft_caps already calls it internally) - Downgrade per-minute speech recording log to debug level - Downgrade session-end flush log to debug level - Keep session-start and 5-min cap check at info level for operator visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add speech_totals parameter to check_soft_caps to avoid duplicate Redis read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Pass precomputed speech totals to check_soft_caps, eliminating duplicate Redis read Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add tests for check_soft_caps speech_totals parameter Tests verify precomputed totals skip Redis read, and default path still calls get_rolling_speech_ms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Adds info-level logs at key points in the fair-use pipeline so operators can verify the system is actively tracking speech in Cloud Logging.
Info-level logs (steady-state visibility):
Debug-level logs (available when needed):
Filter in Cloud Logging:
jsonPayload.message=~"fair_use:"Review cycle changes
speech_totalsparameter tocheck_soft_caps()to eliminate duplicate Redis readspeech_totalsparameter (precomputed vs default path)Test plan
fair_use:logs appear in Cloud Logging within 5 minutesDeploy steps
gcp_backend.ymlwithenvironment=prod, branch=mainjsonPayload.message=~"fair_use: session start"in Cloud LoggingRef: #5746
🤖 Generated with Claude Code