Dynamic Opus/Sonnet model switching based on rolling quota burn rate (_select_model_based_on_quota() in loop.py). Reads rate_limits.json, compares actual vs expected usage, switches to Sonnet when burning too fast
System message handler now uses quota-selected model instead of hardcoded self.model
Loguru format strings fixed from printf-style (%s/%d) to {} — previous format printed literal %s instead of values
Consolidated response logging into a single line showing stop_reason, tool_calls, thinking chars, and token usage
Changes
nanobot/agent/loop.py: Added _select_model_based_on_quota(), applied to both main loop and system handler, added debug logging for model selection
nanobot/providers/anthropic_oauth.py: Fixed loguru format strings, consolidated response logging, added tool count to request logging
Test plan
Verified quota-based switching works (logs show Quota: 95% used → Sonnet)
Verified thinking mode works (API returns thinking blocks, model uses tools)
Verified logging shows correct values (no more literal %s)
Deployed to production and tested via Telegram
## Summary
- **Dynamic Opus/Sonnet model switching** based on rolling quota burn rate (`_select_model_based_on_quota()` in loop.py). Reads `rate_limits.json`, compares actual vs expected usage, switches to Sonnet when burning too fast
- **System message handler** now uses quota-selected model instead of hardcoded `self.model`
- **Loguru format strings** fixed from printf-style (`%s/%d`) to `{}` — previous format printed literal `%s` instead of values
- **Consolidated response logging** into a single line showing stop_reason, tool_calls, thinking chars, and token usage
## Changes
- `nanobot/agent/loop.py`: Added `_select_model_based_on_quota()`, applied to both main loop and system handler, added debug logging for model selection
- `nanobot/providers/anthropic_oauth.py`: Fixed loguru format strings, consolidated response logging, added tool count to request logging
## Test plan
- [x] Verified quota-based switching works (logs show `Quota: 95% used → Sonnet`)
- [x] Verified thinking mode works (API returns thinking blocks, model uses tools)
- [x] Verified logging shows correct values (no more literal `%s`)
- [x] Deployed to production and tested via Telegram
Implement intelligent model selection to manage 7-day Opus quota burn rate:
- Add _select_model_based_on_quota() method to AgentLoop
- Reads rate limit data from memory/rate_limits.json
- Calculates expected vs actual quota usage (100%/168h = 0.595% per hour)
- If actual > expected × 1.17 (17% overage), downgrades to Sonnet
- If actual ≤ expected, uses Opus
- Caches decision for 5 minutes to minimize file I/O
- Add /quota slash command to display real-time quota status
- Shows current usage vs expected usage
- Shows hours until weekly reset
- Shows selected model and burn rate multiplier
- Main agent now calls _select_model_based_on_quota() before each conversation
- Heartbeat subagent unaffected (explicitly uses claude-sonnet-4-20250514)
This replaces the wrong approach from PR #5 which throttled heartbeat
frequency instead of switching the main agent's model.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The system message handler was using self.model instead of the
quota-selected model, bypassing the Opus/Sonnet switching logic.
Also added debug logging for model selection and thinking_budget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Changed printf-style (%s/%d) to loguru format ({}) in 3 log statements
- Consolidated response logging into a single line showing stop_reason,
tool_calls count, thinking chars, and token usage
- Added tool count to request logging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comprehensive implementation that addresses multiple issues in a single coherent PR:
✅ Quota-based Opus/Sonnet model switching
✅ System message handler model selection fix
✅ Critical loguru format string bug fixes
✅ Improved consolidated logging
✅ Production tested and deployed
Key Strengths
1. Complete Solution: This PR supersedes PR #6 by including additional critical fixes beyond just quota management.
2. Critical Bug Fix: The loguru format string correction (%s/%d → {}) fixes a production logging bug where literal %s was being printed instead of actual values.
3. Model Selection Fix: Both the main agent loop AND system message handler now correctly use the quota-selected model (not hardcoded self.model).
4. Production Validated: Already deployed and tested via Telegram, reducing merge risk.
Changes Verified
_select_model_based_on_quota() implementation is sound
5-minute cache prevents excessive file I/O
Safe fallback to Sonnet if rate_limits.json missing
System handler uses selected_model correctly
Loguru format strings fixed throughout
Response logging consolidated for better debuggability
Minor Observations
Branch naming: feat/rate-limit-tracking is confusing since closed PR #5 used the same name. Consider renaming to feat/quota-switching-and-fixes for clarity.
No breaking changes: All changes are additive or bug fixes. Safe to merge.
Recommendation
MERGE IMMEDIATELY✅
This is the most complete implementation of the quota management system. PR #6 has been closed in favor of this PR.
Post-merge monitoring:
Watch for quota-based model switching in logs
Verify thinking mode continues to work correctly
Monitor 7-day burn rate to tune tolerance threshold if needed
## Code Review: PR #9 ✅ APPROVED
### Summary
Comprehensive implementation that addresses multiple issues in a single coherent PR:
- ✅ Quota-based Opus/Sonnet model switching
- ✅ System message handler model selection fix
- ✅ Critical loguru format string bug fixes
- ✅ Improved consolidated logging
- ✅ Production tested and deployed
### Key Strengths
**1. Complete Solution**: This PR supersedes PR #6 by including additional critical fixes beyond just quota management.
**2. Critical Bug Fix**: The loguru format string correction (`%s`/`%d` → `{}`) fixes a production logging bug where literal `%s` was being printed instead of actual values.
**3. Model Selection Fix**: Both the main agent loop AND system message handler now correctly use the quota-selected model (not hardcoded `self.model`).
**4. Production Validated**: Already deployed and tested via Telegram, reducing merge risk.
### Changes Verified
- [x] `_select_model_based_on_quota()` implementation is sound
- [x] 5-minute cache prevents excessive file I/O
- [x] Safe fallback to Sonnet if `rate_limits.json` missing
- [x] System handler uses `selected_model` correctly
- [x] Loguru format strings fixed throughout
- [x] Response logging consolidated for better debuggability
### Minor Observations
1. **Branch naming**: `feat/rate-limit-tracking` is confusing since closed PR #5 used the same name. Consider renaming to `feat/quota-switching-and-fixes` for clarity.
2. **No breaking changes**: All changes are additive or bug fixes. Safe to merge.
### Recommendation
**MERGE IMMEDIATELY** ✅
This is the most complete implementation of the quota management system. PR #6 has been closed in favor of this PR.
---
**Post-merge monitoring**:
- Watch for quota-based model switching in logs
- Verify thinking mode continues to work correctly
- Monitor 7-day burn rate to tune tolerance threshold if needed
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
_select_model_based_on_quota()in loop.py). Readsrate_limits.json, compares actual vs expected usage, switches to Sonnet when burning too fastself.model%s/%d) to{}— previous format printed literal%sinstead of valuesChanges
nanobot/agent/loop.py: Added_select_model_based_on_quota(), applied to both main loop and system handler, added debug logging for model selectionnanobot/providers/anthropic_oauth.py: Fixed loguru format strings, consolidated response logging, added tool count to request loggingTest plan
Quota: 95% used → Sonnet)%s)- Changed printf-style (%s/%d) to loguru format ({}) in 3 log statements - Consolidated response logging into a single line showing stop_reason, tool_calls count, thinking chars, and token usage - Added tool count to request logging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>Code Review: PR #9 ✅ APPROVED
Summary
Comprehensive implementation that addresses multiple issues in a single coherent PR:
Key Strengths
1. Complete Solution: This PR supersedes PR #6 by including additional critical fixes beyond just quota management.
2. Critical Bug Fix: The loguru format string correction (
%s/%d→{}) fixes a production logging bug where literal%swas being printed instead of actual values.3. Model Selection Fix: Both the main agent loop AND system message handler now correctly use the quota-selected model (not hardcoded
self.model).4. Production Validated: Already deployed and tested via Telegram, reducing merge risk.
Changes Verified
_select_model_based_on_quota()implementation is soundrate_limits.jsonmissingselected_modelcorrectlyMinor Observations
Branch naming:
feat/rate-limit-trackingis confusing since closed PR #5 used the same name. Consider renaming tofeat/quota-switching-and-fixesfor clarity.No breaking changes: All changes are additive or bug fixes. Safe to merge.
Recommendation
MERGE IMMEDIATELY ✅
This is the most complete implementation of the quota management system. PR #6 has been closed in favor of this PR.
Post-merge monitoring: