Skip to content

Add upstream release watcher (workload-identity auth)#7

Open
aryehlev wants to merge 8 commits into
publishfrom
atuoupdate
Open

Add upstream release watcher (workload-identity auth)#7
aryehlev wants to merge 8 commits into
publishfrom
atuoupdate

Conversation

@aryehlev

@aryehlev aryehlev commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Adds a weekly (and manual) watcher that opens a PR when the upstream library
publishes a newer release than the one pinned in build.rs. Claude reviews the
upstream C API header diff and edits the crate; peter-evans/create-pull-request
opens the PR. The only remote write is the PR itself.

Auth: Anthropic workload identity federation (OIDC) — no stored API key.

Before this can run, add repo secrets: ANTHROPIC_FEDERATION_RULE_ID,
ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID
(+ ANTHROPIC_WORKSPACE_ID if the federation rule spans multiple workspaces).

Note: the federation rule subject should match this repo\x27s default branch once merged.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added optional Polars integration for making CatBoost predictions directly from DataFrames.
    • Supports selecting columns and separating numeric and categorical features.
    • Handles supported numeric, Boolean, and string categorical values with validation for missing or unsupported data.
    • Added the polars crate feature for opt-in use.
  • Maintenance
    • Improved platform-specific library detection and linking.
    • Added automated monitoring for upstream CatBoost releases and update proposals.

aryehlev and others added 8 commits November 13, 2025 23:11
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swap static ANTHROPIC_API_KEY for OIDC/WIF: add id-token: write and the
anthropic_federation_* inputs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds feature-gated Polars DataFrame prediction support, refactors CatBoost platform build handling, and introduces a scheduled workflow that detects upstream releases and creates automated update pull requests.

Changes

Polars integration

Layer / File(s) Summary
Polars prediction API and conversion
Cargo.toml, src/lib.rs, src/polars_ext.rs
Adds an optional Polars feature, exports ModelPolarsExt, and converts numeric, boolean, and categorical DataFrame values into CatBoost prediction inputs with validation.

Platform-specific build handling

Layer / File(s) Summary
Platform helpers and library downloads
build.rs
Replaces runtime platform detection with static helpers for architecture, operating system, library filenames, and download URL selection.
Platform linker configuration
build.rs
Uses target-specific configuration for macOS and Linux rpath and linker arguments, with no Windows rpath output.

Upstream release automation

Layer / File(s) Summary
Release detection and change preparation
.github/workflows/upstream-release.yml
Checks the pinned and latest CatBoost versions, avoids duplicate update branches, and generates upstream header diffs and pull request metadata.
Automated edits and pull request creation
.github/workflows/upstream-release.yml
Runs the constrained Claude action, ensures required labels, and creates the update pull request.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant GitHubAPI
  participant UpstreamCatBoost
  participant ClaudeAction
  participant PullRequest
  GitHubActions->>GitHubAPI: Fetch latest CatBoost release
  GitHubActions->>GitHubActions: Compare pinned and latest versions
  GitHubActions->>GitHubAPI: Check for an existing update branch
  GitHubActions->>UpstreamCatBoost: Download C API headers
  GitHubActions->>GitHubActions: Generate diff and PR body
  GitHubActions->>ClaudeAction: Edit wrapper and version references
  ClaudeAction->>GitHubActions: Write updated PR body
  GitHubActions->>PullRequest: Create labeled update pull request
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the new upstream release watcher and its OIDC-based authentication, which is the main workflow change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch atuoupdate
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch atuoupdate

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
build.rs (1)

178-184: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Windows aarch64 path skips .lib download, will fail at link step.

The ("windows", "x86_64") arm (lines 137–177) downloads both catboostmodel.dll and catboostmodel.lib, then returns early. The ("windows", "aarch64") arm only returns a .dll URL — the code at lines 254–265 downloads just that file. At line 448, cargo:rustc-link-lib=dylib=catboostmodel makes the linker look for catboostmodel.lib, which won't exist, causing a link failure on Windows aarch64.

Mirror the x86_64 logic: download both .dll and .lib inside the aarch64 arm and return Ok(()) early.

🔧 Proposed fix: download both .dll and .lib for Windows aarch64
             ("windows", "aarch64") => {
-                "catboostmodel.dll".to_string(),
-                format!(
-                    "https://github.com/catboost/catboost/releases/download/v{}/catboostmodel-windows-aarch64-{}.dll",
-                    version, version
-                ),
-            ),
+                let dll_url = format!(
+                    "https://github.com/catboost/catboost/releases/download/v{}/catboostmodel-windows-aarch64-{}.dll",
+                    version, version
+                );
+                println!("cargo:warning=Downloading Windows DLL from: {}", dll_url);
+                let dll_response = ureq::get(&dll_url).call()?;
+                if !(200..300).contains(&dll_response.status()) {
+                    return Err(
+                        format!("Failed to download DLL: HTTP {}", dll_response.status()).into(),
+                    );
+                }
+                let dll_path = lib_dir.join("catboostmodel.dll");
+                let mut dll_file = fs::File::create(&dll_path)?;
+                io::copy(&mut dll_response.into_reader(), &mut dll_file)?;
+
+                let lib_url = format!(
+                    "https://github.com/catboost/catboost/releases/download/v{}/catboostmodel-windows-aarch64-{}.lib",
+                    version, version
+                );
+                println!("cargo:warning=Downloading Windows LIB from: {}", lib_url);
+                let lib_response = ureq::get(&lib_url).call()?;
+                if !(200..300).contains(&lib_response.status()) {
+                    return Err(
+                        format!("Failed to download LIB: HTTP {}", lib_response.status()).into(),
+                    );
+                }
+                let lib_path = lib_dir.join("catboostmodel.lib");
+                let mut lib_file = fs::File::create(&lib_path)?;
+                io::copy(&mut lib_response.into_reader(), &mut lib_file)?;
+
+                println!(
+                    "cargo:warning=Downloaded CatBoost library to: {}",
+                    dll_path.display()
+                );
+                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 `@build.rs` around lines 178 - 184, Update the Windows aarch64 branch in the
build target-selection logic to mirror the Windows x86_64 handling: download
both catboostmodel.dll and catboostmodel.lib, then return Ok(()) before the
shared download path. Preserve the existing aarch64 DLL URL and use the
corresponding library URL and filenames expected by the linker.
🧹 Nitpick comments (2)
src/polars_ext.rs (1)

234-309: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

extract_f32_value is dead code — never called.

dataframe_to_float_features (lines 124–161) uses series.cast(&DataType::Float32) directly instead of this function. The 75-line function has no callers in this file. Either remove it or refactor dataframe_to_float_features to use it if per-type control is desired.

♻️ Remove unused function
-/// Extract an f32 value from a Series at the given index
-fn extract_f32_value(series: &Series, idx: usize) -> CatBoostResult<f32> {
-    use DataType::*;
-
-    match series.dtype() {
-        Float32 => {
-            let ca = series.f32().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to f32: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx))
-        }
-        Float64 => {
-            let ca = series.f64().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to f64: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        Int8 => {
-            let ca = series.i8().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to i8: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        Int16 => {
-            let ca = series.i16().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to i16: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        Int32 => {
-            let ca = series.i32().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to i32: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        Int64 => {
-            let ca = series.i64().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to i64: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        UInt8 => {
-            let ca = series.u8().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to u8: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        UInt16 => {
-            let ca = series.u16().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to u16: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        UInt32 => {
-            let ca = series.u32().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to u32: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        UInt64 => {
-            let ca = series.u64().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to u64: {}", e),
-            })?;
-            Ok(get_checked_value!(ca, idx) as f32)
-        }
-        Boolean => {
-            let ca = series.bool().map_err(|e| CatBoostError {
-                description: format!("Failed to cast to bool: {}", e),
-            })?;
-            let val = get_checked_value!(ca, idx);
-            Ok(if val { 1.0 } else { 0.0 })
-        }
-        dt => Err(CatBoostError {
-            description: format!("Unsupported data type for float conversion: {}", dt),
-        }),
-    }
-}
🤖 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/polars_ext.rs` around lines 234 - 309, Remove the unused
extract_f32_value function and its associated conversion logic, since
dataframe_to_float_features performs Float32 casting directly and has no callers
for this helper.
.github/workflows/upstream-release.yml (1)

95-95: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin actions to a commit SHA for supply-chain security.

Both anthropics/claude-code-action@v1 and peter-evans/create-pull-request@v6 are pinned to mutable version tags. A compromised tag would execute arbitrary code in a workflow with id-token: write, contents: write, and pull-requests: write permissions. Pin to a specific commit SHA and optionally add a comment with the version for readability.

🔒️ Example pinning
-        uses: anthropics/claude-code-action@v1
+        uses: anthropics/claude-code-action@<full-commit-sha> # v1
-        uses: peter-evans/create-pull-request@v6
+        uses: peter-evans/create-pull-request@<full-commit-sha> # v6

Also applies to: 165-165

🤖 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 @.github/workflows/upstream-release.yml at line 95, Update the action
references in the workflow steps using anthropics/claude-code-action and
peter-evans/create-pull-request to immutable commit SHA pins instead of mutable
version tags, optionally retaining the human-readable version in comments.
🤖 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 @.github/workflows/upstream-release.yml:
- Line 77: Update the shell options in the affected run block of the upstream
release workflow from set -uo pipefail to set -euo pipefail. Preserve the
existing || true guards for expected curl and diff failures while ensuring
unexpected command failures stop the step.
- Around line 40-44: Validate LATEST in the upstream release-version step before
writing the GitHub output, allowing only the expected CatBoost version format
after removing the leading v; reject empty or any value containing
shell/template metacharacters with an error and nonzero exit. Keep the existing
current/latest outputs unchanged for valid versions so downstream uses of
steps.versions.outputs.latest remain safe.

In `@src/polars_ext.rs`:
- Around line 312-387: Update extract_string_value to handle Float32 and Float64
series by retrieving values through the corresponding Polars accessors and
converting them to strings, consistent with the existing integer arms. Preserve
the existing String, integer, Boolean, and unsupported-type behavior.

---

Outside diff comments:
In `@build.rs`:
- Around line 178-184: Update the Windows aarch64 branch in the build
target-selection logic to mirror the Windows x86_64 handling: download both
catboostmodel.dll and catboostmodel.lib, then return Ok(()) before the shared
download path. Preserve the existing aarch64 DLL URL and use the corresponding
library URL and filenames expected by the linker.

---

Nitpick comments:
In @.github/workflows/upstream-release.yml:
- Line 95: Update the action references in the workflow steps using
anthropics/claude-code-action and peter-evans/create-pull-request to immutable
commit SHA pins instead of mutable version tags, optionally retaining the
human-readable version in comments.

In `@src/polars_ext.rs`:
- Around line 234-309: Remove the unused extract_f32_value function and its
associated conversion logic, since dataframe_to_float_features performs Float32
casting directly and has no callers for this helper.
🪄 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: 2d35438e-7d46-425e-9483-233398df7f00

📥 Commits

Reviewing files that changed from the base of the PR and between 8449f37 and 97e9cff.

📒 Files selected for processing (5)
  • .github/workflows/upstream-release.yml
  • Cargo.toml
  • build.rs
  • src/lib.rs
  • src/polars_ext.rs

Comment on lines +40 to +44
LATEST=$(gh api "repos/catboost/catboost/releases/latest" --jq '.tag_name' | sed 's/^v//')
[ -n "$LATEST" ] || { echo "::error::Could not fetch latest release"; exit 1; }

echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validate version format to prevent shell template injection.

LATEST is derived from the upstream catboost/catboost release tag name via gh api ... --jq '.tag_name' | sed 's/^v//'. The sed only strips a leading v — it does not sanitize other characters. This value is then injected directly into a shell script at line 63 (BRANCH="chore/catboost-${{ steps.versions.outputs.latest }}"), where an attacker-controlled tag containing shell metacharacters (e.g., $(...) or backticks) would execute arbitrary commands with contents: write and pull-requests: write permissions.

Add a strict version-format validation before setting the output:

🔒️ Proposed fix: validate version format
          LATEST=$(gh api "repos/catboost/catboost/releases/latest" --jq '.tag_name' | sed 's/^v//')
          [ -n "$LATEST" ] || { echo "::error::Could not fetch latest release"; exit 1; }
+
+         # Reject anything that isn't a clean semver — prevents template injection downstream
+         echo "$LATEST" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
+           echo "::error::Latest upstream tag is not a clean semver: '$LATEST'"
+           exit 1
+         }
+         echo "$CURRENT" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
+           echo "::error::Pinned version is not a clean semver: '$CURRENT'"
+           exit 1
+         }

This protects all downstream uses of steps.versions.outputs.latest (lines 63, 115, 120, 138, 147, 168–170).

📝 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.

Suggested change
LATEST=$(gh api "repos/catboost/catboost/releases/latest" --jq '.tag_name' | sed 's/^v//')
[ -n "$LATEST" ] || { echo "::error::Could not fetch latest release"; exit 1; }
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
LATEST=$(gh api "repos/catboost/catboost/releases/latest" --jq '.tag_name' | sed 's/^v//')
[ -n "$LATEST" ] || { echo "::error::Could not fetch latest release"; exit 1; }
# Reject anything that isn't a clean semver — prevents template injection downstream
echo "$LATEST" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
echo "::error::Latest upstream tag is not a clean semver: '$LATEST'"
exit 1
}
echo "$CURRENT" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
echo "::error::Pinned version is not a clean semver: '$CURRENT'"
exit 1
}
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
🤖 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 @.github/workflows/upstream-release.yml around lines 40 - 44, Validate LATEST
in the upstream release-version step before writing the GitHub output, allowing
only the expected CatBoost version format after removing the leading v; reject
empty or any value containing shell/template metacharacters with an error and
nonzero exit. Keep the existing current/latest outputs unchanged for valid
versions so downstream uses of steps.versions.outputs.latest remain safe.

Source: Linters/SAST tools

CURRENT: ${{ steps.versions.outputs.current }}
LATEST: ${{ steps.versions.outputs.latest }}
run: |
set -uo pipefail

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add set -e for consistency with other steps.

This step uses set -uo pipefail while all other run blocks use set -euo pipefail. Without -e, unexpected failures (e.g., printf at line 88) are silently ignored, potentially producing an incomplete diff or PR body. The existing || true guards on curl and diff already handle expected non-zero exits, so adding -e is safe.

✏️ Proposed fix
-          set -uo pipefail
+          set -euo pipefail
📝 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.

Suggested change
set -uo pipefail
set -euo pipefail
🤖 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 @.github/workflows/upstream-release.yml at line 77, Update the shell options
in the affected run block of the upstream release workflow from set -uo pipefail
to set -euo pipefail. Preserve the existing || true guards for expected curl and
diff failures while ensuring unexpected command failures stop the step.

Comment thread src/polars_ext.rs
Comment on lines +312 to +387
fn extract_string_value(series: &Series, idx: usize) -> CatBoostResult<String> {
use DataType::*;

match series.dtype() {
String => {
let ca = series.str().map_err(|e| CatBoostError {
description: format!("Failed to cast to String: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
// Convert numeric types to strings for categorical features
// Handle each integer type directly to avoid precision loss
Int8 => {
let ca = series.i8().map_err(|e| CatBoostError {
description: format!("Failed to cast to i8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int16 => {
let ca = series.i16().map_err(|e| CatBoostError {
description: format!("Failed to cast to i16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int32 => {
let ca = series.i32().map_err(|e| CatBoostError {
description: format!("Failed to cast to i32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int64 => {
let ca = series.i64().map_err(|e| CatBoostError {
description: format!("Failed to cast to i64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt8 => {
let ca = series.u8().map_err(|e| CatBoostError {
description: format!("Failed to cast to u8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt16 => {
let ca = series.u16().map_err(|e| CatBoostError {
description: format!("Failed to cast to u16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt32 => {
let ca = series.u32().map_err(|e| CatBoostError {
description: format!("Failed to cast to u32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt64 => {
let ca = series.u64().map_err(|e| CatBoostError {
description: format!("Failed to cast to u64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Boolean => {
let ca = series.bool().map_err(|e| CatBoostError {
description: format!("Failed to cast to bool: {}", e),
})?;
let val = get_checked_value!(ca, idx);
Ok(if val {
"true".to_string()
} else {
"false".to_string()
})
}
dt => Err(CatBoostError {
description: format!("Unsupported data type for categorical conversion: {}", dt),
}),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

extract_string_value rejects Float32/Float64 columns for categorical features.

The function handles String, all integer types, and Boolean, but returns "Unsupported data type for categorical conversion" for Float32/Float64. A user passing a float column as a categorical feature gets a confusing error. Consider adding Float32/Float64 arms that convert to string.

🛡️ Add float handling
         Boolean => {
             let ca = series.bool().map_err(|e| CatBoostError {
                 description: format!("Failed to cast to bool: {}", e),
             })?;
             let val = get_checked_value!(ca, idx);
             Ok(if val {
                 "true".to_string()
             } else {
                 "false".to_string()
             })
         }
+        Float32 => {
+            let ca = series.f32().map_err(|e| CatBoostError {
+                description: format!("Failed to cast to f32: {}", e),
+            })?;
+            Ok(get_checked_value!(ca, idx).to_string())
+        }
+        Float64 => {
+            let ca = series.f64().map_err(|e| CatBoostError {
+                description: format!("Failed to cast to f64: {}", e),
+            })?;
+            Ok(get_checked_value!(ca, idx).to_string())
+        }
         dt => Err(CatBoostError {
📝 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.

Suggested change
fn extract_string_value(series: &Series, idx: usize) -> CatBoostResult<String> {
use DataType::*;
match series.dtype() {
String => {
let ca = series.str().map_err(|e| CatBoostError {
description: format!("Failed to cast to String: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
// Convert numeric types to strings for categorical features
// Handle each integer type directly to avoid precision loss
Int8 => {
let ca = series.i8().map_err(|e| CatBoostError {
description: format!("Failed to cast to i8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int16 => {
let ca = series.i16().map_err(|e| CatBoostError {
description: format!("Failed to cast to i16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int32 => {
let ca = series.i32().map_err(|e| CatBoostError {
description: format!("Failed to cast to i32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int64 => {
let ca = series.i64().map_err(|e| CatBoostError {
description: format!("Failed to cast to i64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt8 => {
let ca = series.u8().map_err(|e| CatBoostError {
description: format!("Failed to cast to u8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt16 => {
let ca = series.u16().map_err(|e| CatBoostError {
description: format!("Failed to cast to u16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt32 => {
let ca = series.u32().map_err(|e| CatBoostError {
description: format!("Failed to cast to u32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt64 => {
let ca = series.u64().map_err(|e| CatBoostError {
description: format!("Failed to cast to u64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Boolean => {
let ca = series.bool().map_err(|e| CatBoostError {
description: format!("Failed to cast to bool: {}", e),
})?;
let val = get_checked_value!(ca, idx);
Ok(if val {
"true".to_string()
} else {
"false".to_string()
})
}
dt => Err(CatBoostError {
description: format!("Unsupported data type for categorical conversion: {}", dt),
}),
}
}
fn extract_string_value(series: &Series, idx: usize) -> CatBoostResult<String> {
use DataType::*;
match series.dtype() {
String => {
let ca = series.str().map_err(|e| CatBoostError {
description: format!("Failed to cast to String: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
// Convert numeric types to strings for categorical features
// Handle each integer type directly to avoid precision loss
Int8 => {
let ca = series.i8().map_err(|e| CatBoostError {
description: format!("Failed to cast to i8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int16 => {
let ca = series.i16().map_err(|e| CatBoostError {
description: format!("Failed to cast to i16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int32 => {
let ca = series.i32().map_err(|e| CatBoostError {
description: format!("Failed to cast to i32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Int64 => {
let ca = series.i64().map_err(|e| CatBoostError {
description: format!("Failed to cast to i64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt8 => {
let ca = series.u8().map_err(|e| CatBoostError {
description: format!("Failed to cast to u8: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt16 => {
let ca = series.u16().map_err(|e| CatBoostError {
description: format!("Failed to cast to u16: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt32 => {
let ca = series.u32().map_err(|e| CatBoostError {
description: format!("Failed to cast to u32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
UInt64 => {
let ca = series.u64().map_err(|e| CatBoostError {
description: format!("Failed to cast to u64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Boolean => {
let ca = series.bool().map_err(|e| CatBoostError {
description: format!("Failed to cast to bool: {}", e),
})?;
let val = get_checked_value!(ca, idx);
Ok(if val {
"true".to_string()
} else {
"false".to_string()
})
}
Float32 => {
let ca = series.f32().map_err(|e| CatBoostError {
description: format!("Failed to cast to f32: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
Float64 => {
let ca = series.f64().map_err(|e| CatBoostError {
description: format!("Failed to cast to f64: {}", e),
})?;
Ok(get_checked_value!(ca, idx).to_string())
}
dt => Err(CatBoostError {
description: format!("Unsupported data type for categorical conversion: {}", dt),
}),
}
}
🤖 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/polars_ext.rs` around lines 312 - 387, Update extract_string_value to
handle Float32 and Float64 series by retrieving values through the corresponding
Polars accessors and converting them to strings, consistent with the existing
integer arms. Preserve the existing String, integer, Boolean, and
unsupported-type behavior.

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