Add Kimi Coding Plan provider - #1
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a Kimi Code provider for subscription usage, including OAuth credentials, token refresh, usage parsing, service registration, frontend labels, tests, workspace wiring, and setup documentation. ChangesKimi Code provider
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant Service
participant KimiProvider
participant OAuth
participant UsageAPI
CLI->>Service: check kimi
Service->>KimiProvider: refresh usage
KimiProvider->>OAuth: refresh token when needed
OAuth-->>KimiProvider: refreshed credentials
KimiProvider->>UsageAPI: fetch subscription usage
UsageAPI-->>KimiProvider: usage windows
KimiProvider-->>Service: UsageSnapshot
Service-->>CLI: rendered provider usage
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
c29313f to
7519585
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@Cargo.toml`:
- Line 30: Replace the outdated fs2 dependency with maintained fs4 in
Cargo.toml, then update all dependent lock-call usage to match fs4’s API rather
than assuming drop-in compatibility. Verify the project builds and preserves
existing filesystem locking behavior.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 2a1850db-2d6e-425f-863d-9054dbc61705
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
Cargo.tomlREADME.mdapps/linux/src/app.rsapps/macos/Sources/BrainDrainApp/ProviderListModel.swiftapps/plasma/package/contents/ui/main.qmlcrates/core/src/lib.rscrates/providers-kimi/Cargo.tomlcrates/providers-kimi/src/lib.rscrates/service/Cargo.tomlcrates/service/src/lib.rs
Read and safely refresh first-party Kimi Code OAuth credentials, fetch and normalize subscription usage, and expose Kimi across the service and desktop frontends.
7519585 to
0460dd5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@crates/providers-kimi/src/lib.rs`:
- Around line 684-702: Update the duration method to use the fallible Duration
constructor when converting value * multiplier, returning None if the input is
non-finite or overflows instead of panicking. Preserve the existing
non-positive-value handling and time-unit multiplier selection.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: a9125afe-0bde-4ee6-bc0f-65b70c50a066
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
Cargo.tomlREADME.mdapps/linux/src/app.rsapps/macos/Sources/BrainDrainApp/ProviderListModel.swiftapps/plasma/package/contents/ui/main.qmlcrates/core/src/lib.rscrates/providers-kimi/Cargo.tomlcrates/providers-kimi/src/lib.rscrates/service/Cargo.tomlcrates/service/src/lib.rs
| fn duration(&self) -> Option<Duration> { | ||
| let value = self.duration.as_ref().and_then(value_to_f64)?; | ||
| if value <= 0.0 { | ||
| return None; | ||
| } | ||
| let multiplier = match self | ||
| .time_unit | ||
| .as_deref() | ||
| .unwrap_or("") | ||
| .to_ascii_uppercase() | ||
| .as_str() | ||
| { | ||
| unit if unit.contains("MINUTE") => 60.0, | ||
| unit if unit.contains("HOUR") => 3_600.0, | ||
| unit if unit.contains("DAY") => 86_400.0, | ||
| _ => 1.0, | ||
| }; | ||
| Some(Duration::from_secs_f64(value * multiplier)) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Rust Duration try_from_secs_f64 stabilized version
💡 Result:
The Duration::try_from_secs_f64 method was stabilized in Rust 1.66.0 [1]. It is the checked version of from_secs_f64, providing a way to convert a floating-point value of seconds into a Duration without panicking [2][3]. Instead of panicking on negative, non-finite (e.g., NaN, infinity), or overflowing input, it returns a Result<Duration, TryFromFloatSecsError> [2][1]. The stabilization was finalized via pull request #102271 [4].
Citations:
- 1: https://doc.rust-lang.org/std/time/struct.TryFromFloatSecsError.html
- 2: https://doc.rust-lang.org/std/time/struct.Duration.html
- 3: https://doc.rust-lang.org/beta/std/time/struct.Duration.html
- 4: Stabilize
duration_checked_floatrust-lang/rust#102271
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== workspace manifests ==\n'
git ls-files | rg '(^|/)(Cargo\.toml|rust-toolchain(\.toml)?|README\.md)$'
printf '\n== rust-version / toolchain hints ==\n'
for f in Cargo.toml rust-toolchain.toml rust-toolchain; do
if [ -f "$f" ]; then
echo "--- $f ---"
cat -n "$f"
fi
done
printf '\n== target file around duration() ==\n'
cat -n crates/providers-kimi/src/lib.rs | sed -n '670,710p'Repository: sargunv/braindrain
Length of output: 4089
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== value_to_f64 and nearby helpers ==\n'
rg -n "fn value_to_f64|value_to_f64\\(" crates/providers-kimi/src/lib.rs
cat -n crates/providers-kimi/src/lib.rs | sed -n '1,220p'
printf '\n== all uses of KimiWindow::duration / label ==\n'
rg -n "KimiWindow::duration|\\.duration\\(" crates/providers-kimi/src/lib.rsRepository: sargunv/braindrain
Length of output: 9999
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n crates/providers-kimi/src/lib.rs | sed -n '835,875p'Repository: sargunv/braindrain
Length of output: 1570
Avoid panicking on malformed duration values. value_to_f64() can produce non-finite or very large inputs from the API, and Duration::from_secs_f64(value * multiplier) will panic on NaN/∞/overflow. Use the fallible constructor here instead.
Proposed change
- Some(Duration::from_secs_f64(value * multiplier))
+ Duration::try_from_secs_f64(value * multiplier).ok()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn duration(&self) -> Option<Duration> { | |
| let value = self.duration.as_ref().and_then(value_to_f64)?; | |
| if value <= 0.0 { | |
| return None; | |
| } | |
| let multiplier = match self | |
| .time_unit | |
| .as_deref() | |
| .unwrap_or("") | |
| .to_ascii_uppercase() | |
| .as_str() | |
| { | |
| unit if unit.contains("MINUTE") => 60.0, | |
| unit if unit.contains("HOUR") => 3_600.0, | |
| unit if unit.contains("DAY") => 86_400.0, | |
| _ => 1.0, | |
| }; | |
| Some(Duration::from_secs_f64(value * multiplier)) | |
| } | |
| fn duration(&self) -> Option<Duration> { | |
| let value = self.duration.as_ref().and_then(value_to_f64)?; | |
| if value <= 0.0 { | |
| return None; | |
| } | |
| let multiplier = match self | |
| .time_unit | |
| .as_deref() | |
| .unwrap_or("") | |
| .to_ascii_uppercase() | |
| .as_str() | |
| { | |
| unit if unit.contains("MINUTE") => 60.0, | |
| unit if unit.contains("HOUR") => 3_600.0, | |
| unit if unit.contains("DAY") => 86_400.0, | |
| _ => 1.0, | |
| }; | |
| Duration::try_from_secs_f64(value * multiplier).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 `@crates/providers-kimi/src/lib.rs` around lines 684 - 702, Update the duration
method to use the fallible Duration constructor when converting value *
multiplier, returning None if the input is non-finite or overflows instead of
panicking. Preserve the existing non-positive-value handling and time-unit
multiplier selection.
Summary
https://api.kimi.com/coding/v1/usagesendpoint~/.kimi/credentials/kimi-code.json/KIMI_SHARE_DIR, refresh expiring OAuth credentials with Kimi CLI-compatible locking and atomic unknown-field-preserving writes, and retry usage once after a 4014a550effdfcb29a25a5d325bf935296cc50cd417(1.49.0)Test plan
mise exec -- cargo test --workspace --all-features --exclude braindrain-guimise exec -- cargo clippy --workspace --all-targets --all-features --exclude braindrain-gui -- -D warningsmise exec -- cargo fmt --all -- --checkmise exec -- dprint checkbraindrain providersandbraindrain info kimiwith an isolatedKIMI_SHARE_DIRThe full local
mise run checkreaches the Linux GUI package but this host does not have the required GLib 2.88 development package; all non-GUI workspace targets pass, and CI covers the configured project matrix.