Add Telegram delivery pacing and session runtime controls#12
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThis PR hardens Telecodex against Telegram rate limits and bot restarts. It introduces database instance locking and interrupted turn recovery on startup to handle stale sessions; implements outbound rate limiting per chat with ChangesDatabase Instance Safety & Turn Recovery
Telegram Rate Limit Resilience & Retry Flow
Service Tier & Bot Restart Improvements
Sequence Diagram(s)sequenceDiagram
participant App as App Startup
participant Store as Store
participant DB as SQLite DB
participant Heartbeat as Heartbeat Loop
App->>Store: Store::open()
activate Store
Store->>Store: Generate instance_id
Store->>DB: Claim app_instance_lock
alt Lock held by live instance
DB-->>Store: Lock error
Store-->>App: Fail (concurrent instance)
else Lock acquired
Store->>DB: Query stale running turns
DB-->>Store: Stale turn IDs
Store->>DB: Mark stale turns failed
Store-->>App: Success, instance_id
deactivate Store
App->>Heartbeat: Spawn heartbeat loop
activate Heartbeat
loop Every 30 seconds
Heartbeat->>Store: heartbeat_instance()
Store->>DB: UPDATE lock heartbeat_at
DB-->>Store: OK
alt Lock lost
Heartbeat-->>App: Cancel shutdown
deactivate Heartbeat
else Lock held
Heartbeat-->>Heartbeat: Continue
end
end
end
sequenceDiagram
participant User as User/Telegram
participant App as App
participant Limiter as Outbound<br/>Limiter
participant TG as Telegram API
User->>App: /turn request
alt Private chat + use_message_drafts
App->>Limiter: Wait per-chat interval
Limiter->>TG: sendMessageDraft
alt 429 Too Many Requests
TG-->>Limiter: Error + retry_after
Limiter->>Limiter: Record backoff
Limiter-->>App: Fail
App->>App: Mark turn failed
App->>User: Rate-limit notice + Retry button
else Success
TG-->>App: draft_id
App->>App: Create LiveTurnSink
end
else Group/Topic
App->>App: Create preview sink
end
sequenceDiagram
participant User as User
participant App as App
participant Store as Store
participant Codex as Codex
User->>App: /retry 42
App->>Store: retry_request_for_turn(42)
alt Turn is failed/cancelled
Store-->>App: TurnRequest
App->>App: Set search_mode
App->>Codex: Execute turn
Codex-->>App: Response
App->>Store: Update turn status
App-->>User: Results
else Turn not retryable
App-->>User: Not retryable
end
sequenceDiagram
participant User as User
participant App as App
participant Store as Store
User->>App: /fast on
App->>Store: set_session_service_tier(key, "fast")
Store-->>App: OK
App-->>User: Fast mode enabled
Note over App,Store: Subsequent turns include<br/>service_tier in Codex config
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
I tested this branch on my real Telecodex instance connected to a Telegram forum chat. After building the release binary and restarting the service, I ran multiple concurrent tasks in separate Telegram topics/sessions. During the test, I also started one additional turn while the others were still active to check behavior under live concurrent load. What I checked:
Result: all turns completed successfully, the final state was |
|
и еще раз спасибо за отличный проект! Вам срочно надо подключить монетизацию, я бы стал одним из первых кто готов поддержать монетой=) |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/app/turns.rs (1)
624-633: 💤 Low valueRedundant guard in
flush().The first guard (
!force && last_flushed_text == visible_text && elapsed < min_flush_interval) is strictly subsumed by the second (!force && elapsed < min_flush_interval). Whenever the first would return, the second would too, so the text-equality check has no effect. Either drop the first guard, or — if the intent was to throttle unchanged text more aggressively than changed text — give the two checks distinct intervals.♻️ Proposed simplification (remove the dead branch)
let visible_text = self.visible_text(force); let min_flush_interval = self.min_flush_interval(); - if !force - && self.last_flushed_text == visible_text - && self.last_flush_at.elapsed() < min_flush_interval - { - return Ok(()); - } if !force && self.last_flush_at.elapsed() < min_flush_interval { return Ok(()); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/turns.rs` around lines 624 - 633, The flush() method contains a redundant guard: the first if-check using min_flush_interval, last_flushed_text == visible_text, last_flush_at.elapsed() and !force is subsumed by the subsequent if-check that also tests !force and last_flush_at.elapsed() < min_flush_interval; remove the dead branch to simplify. Locate the flush() function and eliminate the entire first if block that references min_flush_interval(), self.last_flushed_text == visible_text, and self.last_flush_at.elapsed(), or alternatively implement distinct intervals (e.g., a shorter min_flush_interval and a separate unchanged_text_interval) and adjust the two guards so they check different thresholds; update any comments referencing the old behavior accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/store.rs`:
- Around line 531-552: The query currently reads turns.review_json into a String
which will fail on NULL; change the row mapping to get review_json as
Option<String> (e.g., row.get::<_, Option<String>>(1)) and then handle the None
case before deserializing: only call serde_json::from_str when
review_json.is_some(), otherwise set review_mode = None (or appropriate default)
so retry reconstruction for failed/cancelled turns does not error; update
references to review_json and review_mode accordingly in the surrounding logic
(look for variables review_json, review_mode, and the row mapping closure).
---
Nitpick comments:
In `@src/app/turns.rs`:
- Around line 624-633: The flush() method contains a redundant guard: the first
if-check using min_flush_interval, last_flushed_text == visible_text,
last_flush_at.elapsed() and !force is subsumed by the subsequent if-check that
also tests !force and last_flush_at.elapsed() < min_flush_interval; remove the
dead branch to simplify. Locate the flush() function and eliminate the entire
first if block that references min_flush_interval(), self.last_flushed_text ==
visible_text, and self.last_flush_at.elapsed(), or alternatively implement
distinct intervals (e.g., a shorter min_flush_interval and a separate
unchanged_text_interval) and adjust the two guards so they check different
thresholds; update any comments referencing the old behavior accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dc69dd61-a8db-493a-bd68-27cde1c4b395
📒 Files selected for processing (12)
README.mdREADME.ru.mdsrc/app.rssrc/app/presentation.rssrc/app/support.rssrc/app/tests.rssrc/app/turns.rssrc/codex.rssrc/commands.rssrc/models.rssrc/store.rssrc/telegram.rs
|
@Headcrab, Alex, i'am done=) |
|
🥳🥳🥳 |
Summary
This replaces the now-closed #9 and #10 so the related runtime and Telegram delivery changes can be reviewed together.
chat_id, use a safer group/topic cadence, and honor Bot APIretry_aftersendMessageDraftpreviews in private chats while keeping group/topic previews on throttlededitMessageText/fastmode and surface fast mode in turn-start statusNotes
Transparency note: this is an honest vibe-coded contribution. I mostly work in Go and used Codex heavily for the Rust implementation, so I kept the changes focused and added tests around the touched behavior.
Validation
cargo fmtcargo testcargo clippy --all-targets --all-features -- -D warningscargo build --release