Skip to content

Add OpenAI streaming support for lyrics analysis#63

Draft
vtvz wants to merge 1 commit intomasterfrom
analysis-streaming
Draft

Add OpenAI streaming support for lyrics analysis#63
vtvz wants to merge 1 commit intomasterfrom
analysis-streaming

Conversation

@vtvz
Copy link
Owner

@vtvz vtvz commented Jan 12, 2026

Replaces blocking OpenAI API calls with streaming for lyrics analysis. Messages now update incrementally every 500ms as the AI generates the response, providing real-time feedback instead of a 10-20 second wait.

🤖 Generated with Claude Code

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

This change replaces the single-shot OpenAI API call in the analyze action with a streaming approach that accumulates tokens at 500ms intervals, sends partial results to users, and handles message length limits by cropping content when necessary.

Changes

Cohort / File(s) Summary
Localization
locales/analyze.yml
Updated analysis.waiting message text in English and Russian; added new analysis.partial-result entry with bilingual blocks containing result placeholder and hourglass emoji
Streaming Analysis Logic
src/telegram/actions/analyze.rs
Replaced single OpenAI request with streaming approach using create_stream, token accumulation with 500ms periodic update intervals, message length cropping logic, dynamic partial-result rendering with HTML formatting, and error handling for empty streams

Sequence Diagram

sequenceDiagram
    participant User
    participant TelegramAction as Telegram Action
    participant OpenAIStream as OpenAI Stream
    participant Timer as Update Timer
    participant TelegramAPI as Telegram API

    User->>TelegramAction: Request analysis
    TelegramAction->>OpenAIStream: create_stream()
    activate OpenAIStream
    TelegramAction->>Timer: Initialize timer (500ms)
    
    loop Token Streaming
        OpenAIStream->>TelegramAction: delta.content
        TelegramAction->>TelegramAction: Append to accumulator
        Timer->>TelegramAction: Interval reached
        alt Content length valid
            TelegramAction->>TelegramAPI: Send partial result
        else Content exceeds limit
            TelegramAction->>TelegramAction: Crop analysis
            TelegramAction->>TelegramAPI: Send cropped partial
            TelegramAction->>OpenAIStream: Break (hit_limit)
        end
    end
    deactivate OpenAIStream
    
    alt Content accumulated
        TelegramAction->>TelegramAPI: Send final analysis
    else No content
        TelegramAction->>User: Error (empty stream)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add OpenAI streaming support for lyrics analysis' directly describes the main change: implementing streaming instead of blocking OpenAI API calls for analysis.
Description check ✅ Passed The description clearly explains the streaming implementation and its benefit (real-time feedback), which aligns with the code changes that replace blocking calls with streaming updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebd54d3 and 35b36ef.

📒 Files selected for processing (2)
  • locales/analyze.yml
  • src/telegram/actions/analyze.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-01T17:47:59.430Z
Learnt from: vtvz
Repo: vtvz/rustify PR: 59
File: src/telegram/actions/recommendasion.rs:494-534
Timestamp: 2026-01-01T17:47:59.430Z
Learning: When constructing CreateChatCompletionRequestArgs, you can pass either a single ChatCompletionTool or a Vec<ChatCompletionTool> thanks to Into<Vec<ChatCompletionTool>>. This means you can pass a single tool directly (it will be wrapped into a Vec by the Into implementation) or pass a full Vec, depending on your usage. Apply this pattern across Rust files that build chat completion requests, not just the specific file.

Applied to files:

  • src/telegram/actions/analyze.rs
📚 Learning: 2026-01-06T11:42:12.473Z
Learnt from: vtvz
Repo: vtvz/rustify PR: 61
File: src/telegram/actions/details.rs:158-199
Timestamp: 2026-01-06T11:42:12.473Z
Learning: In the rustify repository, do not suggest logging for errors that are expected to occur regularly (e.g., anticipated API failures). Treat these as non-actionable errors and avoid generating log spam. When handling known, recoverable errors, do not emit logs at info/error levels unless the failure is truly unexpected or critical; consider using lower-severity or structured tracing (e.g., tracing::debug or tracing::trace) for noise-free visibility, and reserve logs for unexpected failures that require alerting.

Applied to files:

  • src/telegram/actions/analyze.rs
🧬 Code graph analysis (1)
src/telegram/actions/analyze.rs (1)
src/telegram/utils.rs (1)
  • link_preview_small_top (14-22)
🔇 Additional comments (5)
locales/analyze.yml (1)

80-97: LGTM!

The locale changes are well-structured and consistent with the streaming implementation. The message update from "finish" to "start" accurately reflects the new streaming behavior, and the new analysis.partial-result template follows the established pattern of other templates in this file.

src/telegram/actions/analyze.rs (4)

6-13: LGTM!

The imports are correctly added for streaming support: StreamExt for async stream iteration and Duration/Instant for timing the update intervals.


167-188: LGTM!

Good design choices in the streaming setup:

  • The 500ms interval is well-suited for Telegram's rate limits
  • Reinitializing the timer on first token is a smart optimization to handle API startup latency
  • Proper error propagation with ? operator

200-228: Consider the implications of breaking early on message limit.

When hit_limit is true and the loop breaks (Line 224), the stream is abandoned before OpenAI finishes generating. The final accumulator (returned at Line 235) will contain incomplete analysis, which is then used in the final result processing at Lines 284-299.

This seems intentional since there's no point accumulating content that can't be displayed, but be aware that the final message will contain a truncated analysis rather than a complete one that's cropped for display. If users expect the full analysis (perhaps with a "see more" option in the future), this behavior would need to change.


231-236: LGTM!

Good defensive programming. The empty accumulator check catches edge cases where the stream completes without delivering any content. The error is appropriately propagated to the caller where it's logged at warn level (Line 136), which aligns with the repository's pattern of reserving logs for unexpected failures. Based on learnings, this is appropriate since "no analysis content received" is an unexpected failure rather than a regular occurrence.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vtvz vtvz marked this pull request as draft January 12, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant