Skip to content

fix: pre-existing issues in worker health config, bootstrap parsing, and model card cloning - #415

Merged
slin1237 merged 1 commit into
mainfrom
open-fix-n/2
Feb 16, 2026
Merged

slin1237 merged 1 commit into
mainfrom
open-fix-n/2

Conversation

@slin1237

@slin1237 slin1237 commented Feb 12, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes several pre-existing issues identified during PR #412 review:

  • Per-worker health config overrides were silently ignored — WorkerSpec.health was HealthCheckConfig (all required fields), so any unspecified fields reset to defaults instead of inheriting router config. Changed to HealthCheckUpdate (all Option<T>) with proper merge via apply_to().
  • parse_bootstrap_host broke on DP-aware URLs — URLs like http://host:8080@3 had @ misinterpreted as RFC 3986 userinfo delimiter, extracting "3" as host instead of "host".
  • max_connection_attempts could be 0 — when success_threshold is 0, 0 * N = 0 prevented any connection attempts.
  • build_model_card dropped user-supplied fields — selectively copied only 5 fields from proto_card, silently losing display_name, provider, context_length, etc.

What changed

File Change
protocols/src/worker.rs WorkerSpec.health: HealthCheckConfig → HealthCheckUpdate; added Default + is_empty() on HealthCheckUpdate
model_gateway/src/core/worker.rs Added health_config: HealthCheckConfig to WorkerMetadata; updated all runtime access sites (~8)
model_gateway/src/core/worker_builder.rs Builder resolves health config via apply_to(); fixed parse_bootstrap_host for DP URLs and bare hosts; added 4 tests
model_gateway/src/core/job_queue.rs Removed incorrect spec.health assignment; clamped max_connection_attempts with .max(1)
model_gateway/src/service_discovery.rs Same clamp + removed spec.health assignment
model_gateway/src/core/steps/worker/local/create_worker.rs build_health_config uses apply_to(); build_model_card clones full proto_card
model_gateway/src/core/steps/worker/external/create_workers.rs Same apply_to() pattern for health merge
model_gateway/src/core/steps/worker/local/detect_connection.rs Falls back to router timeout when per-worker override not set
model_gateway/src/core/steps/worker/local/update_worker_properties.rs Merges updates against resolved health_config
model_gateway/src/core/worker_registry.rs Health check filter uses resolved health_config
bindings/golang/src/policy.rs Added health_config field to WorkerMetadata construction

Test plan

  • cargo build — compiles cleanly
  • cargo clippy --all-targets — no warnings
  • cargo test — 344+ tests pass, 0 failures
  • New tests: test_parse_bootstrap_host_normal_url, test_parse_bootstrap_host_dp_aware_url, test_parse_bootstrap_host_bare_host, test_dp_aware_worker_bootstrap_host
  • Existing health config tests updated to assert against health_config (resolved) instead of spec.health (partial)
  • Wire format unchanged — HealthCheckUpdate serializes with skip_serializing_none, WorkerSpec.health skips when empty

Summary by CodeRabbit

  • New Features

    • Partial per-worker health overrides; per-worker timeout control and improved model card reuse (architectures, id2label).
    • DP-aware bootstrap host parsing (handles rank suffixes).
    • Workers now carry a resolved runtime health configuration.
  • Bug Fixes

    • Enforced minimums for health thresholds and connection-attempt calculations.
    • External workers default to health checks disabled unless overridden.
  • Refactor

    • Health config resolution moved to worker build time; health scheduler rewritten to deadline-driven, lower-CPU behavior with graceful shutdown.

@coderabbitai

coderabbitai Bot commented Feb 12, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Worker health is split: specs carry optional HealthCheckUpdate overrides, and runtime WorkerMetadata contains the resolved HealthCheckConfig. Health checks are scheduled per-worker with a deadline-driven loop using tokio::Notify; runtime logic reads health_config for enablement, timeouts, and thresholds.

Changes

Cohort / File(s) Summary
Protocols
protocols/src/worker.rs
Introduce HealthCheckUpdate (optional fields, Default, is_empty) and change WorkerSpec.health from HealthCheckConfig to HealthCheckUpdate with conditional serialization.
Worker metadata & builder
model_gateway/src/core/worker.rs, model_gateway/src/core/worker_builder.rs, bindings/golang/src/policy.rs
Add public health_config: HealthCheckConfig to WorkerMetadata; BasicWorkerBuilder gains health_config: Option<HealthCheckConfig> and resolves/embeds the final config during build. Go binding imports and initializes health_config.
Health-check scheduler & runtime
model_gateway/src/core/worker_registry.rs, model_gateway/src/core/worker.rs
Replace fixed-interval polling with per-worker deadline-driven scheduling; swap AtomicBool shutdown for tokio::Notify; health checks consult per-worker health_config for disable flag, timeouts, intervals, thresholds.
Worker creation & config merging
model_gateway/src/core/steps/worker/external/create_workers.rs, model_gateway/src/core/steps/worker/local/create_worker.rs
Assemble runtime health via router_config.health_check.to_protocol_config() then HealthCheckUpdate::apply_to(); external workers default to disabled health checks unless overridden. Model card and id2label parsing/synthesis improved.
Detection & property updates
model_gateway/src/core/steps/worker/local/detect_connection.rs, model_gateway/src/core/steps/worker/local/update_worker_properties.rs
Per-worker timeout resolution and update logic now prefer worker.metadata().health_config (router fallback).
Job queue & service discovery
model_gateway/src/core/job_queue.rs, model_gateway/src/service_discovery.rs
Stop assigning runtime spec.health from router config; use guarded computations success_threshold.max(1) when deriving max connection attempts.
API surface & registry
model_gateway/src/core/worker_registry.rs
Change start_health_checker signature to accept default_interval_secs and update HealthChecker shutdown mechanism to tokio::Notify (public API surface change within module).
Tests & misc
...tests/..., model_gateway/..., model_gateway/src/core/steps/...
Tests updated for new health_config wiring; added DP-aware bootstrap parsing tests; minor logging/format tweaks across steps.

Sequence Diagram(s)

sequenceDiagram
    participant Registry as WorkerRegistry
    participant Checker as HealthChecker
    participant Worker as Worker (metadata + endpoint)
    participant Notify as tokio::Notify

    Registry->>Registry: compute next-deadlines per worker from health_config
    Registry->>Checker: wait for earliest deadline or Notify
    Note over Checker,Notify: Checker awaits Notify or sleep-until(deadline)
    Checker->>Worker: perform health probe (timeout from health_config)
    Worker-->>Checker: probe result (success/failure)
    Checker->>Registry: report result -> update next-deadline/metrics
    Registry->>Notify: notify Checker if earlier deadline requires wakeup
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

tests

Suggested reviewers

  • CatherineSue
  • whybeyoung
  • key4ng

Poem

🐰 I nibble defaults and stitch each thread,
Specs whisper overrides, runtime’s led,
Deadlines hum, the notifier wakes,
Builders merge what each worker takes,
Hop—health configured, cozy bed.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (22 files):

⚔️ bindings/golang/src/policy.rs (content)
⚔️ model_gateway/src/core/job_queue.rs (content)
⚔️ model_gateway/src/core/mod.rs (content)
⚔️ model_gateway/src/core/steps/worker/external/create_workers.rs (content)
⚔️ model_gateway/src/core/steps/worker/local/create_worker.rs (content)
⚔️ model_gateway/src/core/steps/worker/local/detect_connection.rs (content)
⚔️ model_gateway/src/core/steps/worker/local/update_worker_properties.rs (content)
⚔️ model_gateway/src/core/worker.rs (content)
⚔️ model_gateway/src/core/worker_builder.rs (content)
⚔️ model_gateway/src/core/worker_registry.rs (content)
⚔️ model_gateway/src/routers/anthropic/context.rs (content)
⚔️ model_gateway/src/routers/anthropic/mod.rs (content)
⚔️ model_gateway/src/routers/anthropic/models.rs (content)
⚔️ model_gateway/src/routers/anthropic/router.rs (content)
⚔️ model_gateway/src/routers/anthropic/utils.rs (content)
⚔️ model_gateway/src/routers/factory.rs (content)
⚔️ model_gateway/src/routers/http/router.rs (content)
⚔️ model_gateway/src/routers/mcp_utils.rs (content)
⚔️ model_gateway/src/server.rs (content)
⚔️ model_gateway/src/service_discovery.rs (content)
⚔️ protocols/src/messages.rs (content)
⚔️ protocols/src/worker.rs (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically addresses the main changes: fixing pre-existing issues in worker health config (per-worker overrides, spec.health refactoring), bootstrap parsing (DP-aware URL handling), and model card cloning (preserving user-supplied fields).
Docstring Coverage ✅ Passed Docstring coverage is 95.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 unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch open-fix-n/2

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

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @slin1237, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves several identified issues to enhance the robustness and correctness of worker management. It improves how per-worker health check configurations are applied, ensures accurate parsing of bootstrap host URLs, guarantees that worker connection attempts are always made, and prevents the loss of user-defined model card metadata during worker initialization. These changes contribute to more reliable worker registration and operation within the system.

Highlights

  • Refactored Worker Health Configuration: The WorkerSpec.health field was changed from HealthCheckConfig to HealthCheckUpdate (using Option<T> for fields) to allow partial overrides of router health settings without silently resetting unspecified fields to defaults. A new apply_to() method facilitates proper merging.
  • Improved Bootstrap Host Parsing: The parse_bootstrap_host function was fixed to correctly extract hostnames from DP-aware URLs (e.g., http://host:8080@3) by stripping the @rank suffix and to handle bare hostnames without a scheme.
  • Ensured Minimum Connection Attempts: The max_connection_attempts calculation was updated to ensure it is always at least 1, preventing scenarios where a success_threshold of 0 would lead to zero connection attempts.
  • Preserved Model Card Fields: The build_model_card function was modified to fully clone user-supplied proto_card fields, ensuring that all custom metadata like display_name, provider, and context_length are preserved.
Changelog
  • bindings/golang/src/policy.rs
    • Imported HealthCheckConfig for use in worker metadata.
    • Initialized health_config with a default HealthCheckConfig when constructing WorkerMetadata.
  • model_gateway/src/core/job_queue.rs
    • Removed direct assignment of router health check configuration to spec.health.
    • Clamped max_connection_attempts to a minimum value of 1 to prevent zero attempts.
  • model_gateway/src/core/steps/worker/external/create_workers.rs
    • Removed an unused import of HealthCheckConfig.
    • Updated health configuration merging logic to use the apply_to() method for combining worker-specific overrides with router defaults.
  • model_gateway/src/core/steps/worker/local/create_worker.rs
    • Modified build_model_card to clone the entire proto_card if a matching model is found, preserving all user-supplied fields.
    • Refactored build_health_config to utilize the apply_to() method for merging health configurations.
  • model_gateway/src/core/steps/worker/local/detect_connection.rs
    • Adjusted the health check timeout to prioritize per-worker overrides, falling back to the router's default if not specified.
  • model_gateway/src/core/steps/worker/local/update_worker_properties.rs
    • Changed the source of existing_health from worker.metadata().spec.health to worker.metadata().health_config for merging updates.
  • model_gateway/src/core/worker.rs
    • Imported HealthCheckConfig for WorkerMetadata.
    • Added a health_config: HealthCheckConfig field to the WorkerMetadata struct to store the resolved health configuration.
    • Updated check_health_async, grpc_health_check, and http_health_check methods to use self.metadata.health_config.
    • Adjusted test assertions to check health_config instead of spec.health.
    • Added health_config: HealthCheckConfig::default() to WorkerMetadata initializations in tests.
  • model_gateway/src/core/worker_builder.rs
    • Introduced health_config: Option<HealthCheckConfig> to BasicWorkerBuilder.
    • Initialized health_config to None in builder constructors (new, from_spec, from_worker_type).
    • Modified the health_config builder method to set self.health_config.
    • Updated the build method to resolve the final health_config by applying spec.health overrides to defaults.
    • Refined parse_bootstrap_host to correctly handle DP-aware URLs by stripping @rank suffixes and to parse bare hostnames.
    • Updated test assertions to check health_config instead of spec.health.
    • Added new tests for parse_bootstrap_host functionality and DP-aware worker bootstrap hosts.
  • model_gateway/src/core/worker_registry.rs
    • Modified the health check filter to use worker.metadata().health_config.disable_health_check.
  • model_gateway/src/service_discovery.rs
    • Removed direct assignment of router health check configuration to spec.health.
    • Clamped max_connection_attempts to a minimum value of 1.
  • protocols/src/worker.rs
    • Changed the type of WorkerSpec.health from HealthCheckConfig to HealthCheckUpdate.
    • Added skip_serializing_if = "HealthCheckUpdate::is_empty" to WorkerSpec.health for serialization.
    • Updated WorkerSpec::new to initialize health with HealthCheckUpdate::default().
    • Derived Default for the HealthCheckUpdate struct.
    • Added an is_empty() method to HealthCheckUpdate to check if all its fields are None.
Activity
  • Identified and addressed several pre-existing issues during a review of PR refactor(protocols,gateway): redesign worker type hierarchy and consolidate protocol layer #412.
  • Implemented fixes for worker health configuration, URL parsing for bootstrap hosts, connection attempt logic, and model card cloning.
  • Added new unit tests for the parse_bootstrap_host function to cover normal, DP-aware, and bare host scenarios.
  • Updated existing health configuration tests to assert against the newly introduced health_config field in WorkerMetadata.
  • Verified successful compilation, static analysis (cargo clippy), and all 344+ tests passing.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces several important fixes for pre-existing issues, enhancing the robustness and correctness of the worker configuration and management. The changes to handle per-worker health config overrides are well-implemented using HealthCheckUpdate. The fix for parsing DP-aware bootstrap URLs is a great improvement, and the correction for max_connection_attempts prevents potential stalls. Additionally, the refactoring of build_model_card to correctly clone model card data is a clean and effective solution. Overall, these are excellent and much-needed fixes. I have one minor suggestion to simplify the implementation of parse_bootstrap_host for better maintainability.

Comment on lines +244 to 269
// Strip DP rank suffix (e.g., "http://host:8080@3" -> "http://host:8080")
let clean_url = match url.rfind('@') {
Some(at_pos) if url[at_pos + 1..].chars().all(|c| c.is_ascii_digit()) => &url[..at_pos],
_ => url,
};

// Try parsing as-is first. If the URL lacks a scheme (e.g., "worker1:8080"),
// Url::parse may treat the host as a scheme — detect this via missing host_str()
// and fall back to prefixing "http://".
let try_parse = |u: &str| -> Option<String> {
url::Url::parse(u)
.ok()
.and_then(|p| p.host_str().map(|h| h.to_string()))
};

if let Some(host) = try_parse(clean_url) {
host
} else if !clean_url.contains("://") {
try_parse(&format!("http://{}", clean_url)).unwrap_or_else(|| {
tracing::warn!("Failed to parse URL '{}', defaulting to localhost", url);
"localhost".to_string()
}
})
} else {
tracing::warn!("Failed to parse URL '{}', defaulting to localhost", url);
"localhost".to_string()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for parsing the bootstrap host is correct and handles the described edge cases well. However, the implementation can be slightly simplified to reduce code duplication, specifically the tracing::warn! and default to "localhost" part.

This refactoring consolidates the error handling path, making the function's flow a bit more linear and easier to follow.

    // Strip DP rank suffix (e.g., "http://host:8080@3" -> "http://host:8080")
    let clean_url = match url.rfind('@') {
        Some(at_pos) if url[at_pos + 1..].chars().all(|c| c.is_ascii_digit()) => &url[..at_pos],
        _ => url,
    };

    // Try parsing as-is first. If the URL lacks a scheme (e.g., "worker1:8080"),
    // Url::parse may treat the host as a scheme — detect this via missing host_str()
    // and fall back to prefixing "http://".
    let try_parse = |u: &str| -> Option<String> {
        url::Url::parse(u)
            .ok()
            .and_then(|p| p.host_str().map(|h| h.to_string()))
    };

    if let Some(host) = try_parse(clean_url) {
        return host;
    }

    if !clean_url.contains("://") {
        if let Some(host) = try_parse(&format!("http://{}", clean_url)) {
            return host;
        }
    }

    tracing::warn!("Failed to parse URL '{}', defaulting to localhost", url);
    "localhost".to_string()

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
model_gateway/src/core/steps/worker/local/create_worker.rs (1)

191-223: ⚠️ Potential issue | 🟡 Minor

Fallback to num_labels is skipped when id2label_json is empty/invalid.
If id2label_json is present but empty or malformed, the else if prevents the fallback mapping from being created, despite the comment saying “if id2label wasn’t parsed.”

🔧 Proposed fix: track whether id2label was successfully parsed
-    if let Some(id2label_json) = labels.get("id2label_json") {
-        if !id2label_json.is_empty() {
-            // Parse JSON: keys are string indices, values are label names
-            if let Ok(string_map) = serde_json::from_str::<HashMap<String, String>>(id2label_json) {
-                // Convert string keys ("0", "1") to u32 keys (0, 1)
-                let id2label: HashMap<u32, String> = string_map
-                    .into_iter()
-                    .filter_map(|(k, v)| k.parse::<u32>().ok().map(|idx| (idx, v)))
-                    .collect();
-
-                if !id2label.is_empty() {
-                    card = card.with_id2label(id2label);
-                    debug!("Parsed id2label with {} classes", card.num_labels);
-                }
-            }
-        }
-    }
-    // Fallback: if num_labels is set but id2label wasn't parsed, create default labels
-    // Match logic in serving_classify.py::_get_id2label_mapping
-    else if let Some(num_labels_str) = labels.get("num_labels") {
+    let mut parsed_id2label = false;
+    if let Some(id2label_json) = labels.get("id2label_json") {
+        if !id2label_json.is_empty() {
+            // Parse JSON: keys are string indices, values are label names
+            if let Ok(string_map) = serde_json::from_str::<HashMap<String, String>>(id2label_json) {
+                // Convert string keys ("0", "1") to u32 keys (0, 1)
+                let id2label: HashMap<u32, String> = string_map
+                    .into_iter()
+                    .filter_map(|(k, v)| k.parse::<u32>().ok().map(|idx| (idx, v)))
+                    .collect();
+
+                if !id2label.is_empty() {
+                    card = card.with_id2label(id2label);
+                    debug!("Parsed id2label with {} classes", card.num_labels);
+                    parsed_id2label = true;
+                }
+            }
+        }
+    }
+    // Fallback: if num_labels is set but id2label wasn't parsed, create default labels
+    // Match logic in serving_classify.py::_get_id2label_mapping
+    if !parsed_id2label {
+        if let Some(num_labels_str) = labels.get("num_labels") {
             if let Ok(num_labels) = num_labels_str.parse::<u32>() {
                 if num_labels > 0 {
                     // Create default mapping: {0: "LABEL_0", 1: "LABEL_1", ...}
                     let id2label: HashMap<u32, String> = (0..num_labels)
                         .map(|i| (i, format!("LABEL_{}", i)))
                         .collect();
                     card = card.with_id2label(id2label);
                     debug!("Created default id2label with {} classes", num_labels);
                 }
             }
-    }
+        }
+    }
🤖 Fix all issues with AI agents
In `@model_gateway/src/core/steps/worker/local/detect_connection.rs`:
- Around line 117-129: The debug log currently prints the optional override
config.health.timeout_secs which can be None; compute the resolved timeout first
(the `timeout` value derived from
`config.health.timeout_secs.unwrap_or(app_context.router_config.health_check.timeout_secs)`)
and update the `debug!` call in detect_connection.rs to log that resolved
`timeout` (and optionally which source was used) instead of the raw optional so
the message reflects the actual timeout used by the health check.

Comment thread model_gateway/src/core/steps/worker/local/detect_connection.rs
@github-actions github-actions Bot added protocols Protocols crate changes model-gateway Model gateway crate changes labels Feb 12, 2026
@CatherineSue

CatherineSue commented Feb 13, 2026 •

Copy link
Copy Markdown
Member

@Mergifyio refresh

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

refresh

✅ Pull request refreshed

@CatherineSue

CatherineSue commented Feb 13, 2026 •

Copy link
Copy Markdown
Member

@Mergifyio update

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

update

⚠️ Cannot use the command update

Details

⚠ The product Workflow Automation needs to be activated to enable this feature.

@CatherineSue

Copy link
Copy Markdown
Member

@Mergifyio rebase

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

rebase

⚠️ Cannot use the command rebase

Details

⚠ The product Workflow Automation needs to be activated to enable this feature.

@CatherineSue

Copy link
Copy Markdown
Member

@Mergifyio rebase

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

rebase

☑️ Nothing to do, the required conditions are not met

Details
  • -conflict [📌 rebase requirement]
  • -closed [📌 rebase requirement]
  • queue-position = -1 [📌 rebase requirement]
  • any of:
    • #commits-behind > 0 [📌 rebase requirement]
    • -linear-history [📌 rebase requirement]

@CatherineSue

Copy link
Copy Markdown
Member

@Mergifyio update

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

update

☑️ Nothing to do, the required conditions are not met

Details
  • -conflict [📌 update requirement]
  • #commits-behind > 0 [📌 update requirement]
  • -closed [📌 update requirement]
  • queue-position = -1 [📌 update requirement]

@mergify

mergify Bot commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Hi @slin1237, this PR has merge conflicts that must be resolved before it can be merged. Please rebase your branch:

git fetch origin main
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease

@mergify mergify Bot added the needs-rebase PR has merge conflicts that need to be resolved label Feb 13, 2026
@mergify

mergify Bot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

Hi @slin1237, the DCO sign-off check has failed. All commits must include a Signed-off-by line.

To fix existing commits:

# Sign off the last N commits (replace N with the number of unsigned commits)
git rebase HEAD~N --signoff
git push --force-with-lease

To sign off future commits automatically:

  • Use git commit -s every time, or
  • VSCode: enable Git: Always Sign Off in Settings
  • PyCharm: enable Sign-off commit in the Commit tool window

@mergify mergify Bot removed the needs-rebase PR has merge conflicts that need to be resolved label Feb 16, 2026

@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: 2

🤖 Fix all issues with AI agents
In `@model_gateway/src/core/steps/worker/external/create_workers.rs`:
- Around line 53-64: The code unconditionally forces merged.disable_health_check
= true after applying per-worker config (via config.health.apply_to), which
prevents user overrides; change the logic to only set
merged.disable_health_check = true when the user has not provided an explicit
value (i.e., treat true as the default). Locate the block building
(health_config, health_endpoint), check the structure that holds the user's
disable_health_check (the result of config.health.apply_to and/or the config
type backing it), and set merged.disable_health_check only when that field is
unset/None/has the zero/default value; if the config type has no “unset” state,
instead adjust apply_to or the precedence so user-provided values win, or if
override must be forbidden, update the comment to remove the “Users can still
override” line.

In `@model_gateway/src/core/worker_builder.rs`:
- Around line 242-246: The current DP-rank stripping uses url.rfind('@') and
then checks url[at_pos + 1..].chars().all(|c| c.is_ascii_digit()), which returns
true for an empty suffix and implicitly strips a trailing '@'; update the
condition used when computing clean_url to ensure the suffix after '@' is
non-empty (e.g., check at_pos + 1 < url.len() or that the suffix is not empty)
in addition to all characters being ASCII digits, so only valid numeric suffixes
are stripped; update the logic around clean_url (and any code that relies on it)
and add a unit test for the edge case URL ending with '@' (e.g.,
"http://host:8080@") to assert the trailing '@' handling is explicit and
correct.

Comment thread model_gateway/src/core/steps/worker/external/create_workers.rs
Comment thread model_gateway/src/core/worker_builder.rs

@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: 1

🤖 Fix all issues with AI agents
In `@model_gateway/src/core/worker_registry.rs`:
- Around line 665-673: The scheduled-check map next_check currently retains
entries for workers that later set disable_health_check=true; build a set of
disabled worker URLs (e.g., iterate workers and collect worker.url().to_string()
where worker.metadata().health_config.disable_health_check is true) and change
the retain call to drop entries that are either not in active_urls or are in
that disabled set (e.g., next_check.retain(|url,_| active_urls.contains(url) &&
!disabled_urls.contains(url))). Keep the existing loop that inserts
next_check.entry(...) only for workers that are not disabled.

Comment thread model_gateway/src/core/worker_registry.rs Outdated

@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: 2

🤖 Fix all issues with AI agents
In `@model_gateway/src/core/worker_builder.rs`:
- Around line 564-599: Add tests covering userinfo and trailing-@ edge cases for
parse_bootstrap_host and, if failing, adjust parse_bootstrap_host logic: add a
test `test_parse_bootstrap_host_edge_cases` asserting
parse_bootstrap_host("http://user@host:8080") == "host" and
parse_bootstrap_host("http://host:8080@") == "host"; ensure the function treats
an '@' inside userinfo (before hostname) correctly by extracting the hostname
after userinfo, and do not strip a trailing '@' with an empty suffix as a DP
rank removal step unless a numeric rank follows; run/update DPAwareWorkerBuilder
tests (e.g., test_dp_aware_worker_bootstrap_host) to confirm behavior remains
unchanged.

In `@model_gateway/src/service_discovery.rs`:
- Around line 455-462: Add a clarifying comment above the assignment to
spec.max_connection_attempts explaining why we multiply success_threshold by 20
here (instead of the 10x used in job_queue.rs): note that service discovery
(location: service_discovery.rs around spec.max_connection_attempts and
app_context.router_config.health_check.success_threshold) handles workers with
per-worker health overrides and so needs extra retry headroom, hence the 20x
multiplier.

Comment thread model_gateway/src/core/worker_builder.rs
Comment thread model_gateway/src/service_discovery.rs
…and model card cloning

What changed:
- protocols/src/worker.rs: Changed WorkerSpec.health from HealthCheckConfig (all
  required fields) to HealthCheckUpdate (all Option<T>) for proper PATCH semantics.
  Added Default derive and is_empty() helper on HealthCheckUpdate.
- model_gateway/src/core/worker.rs: Added health_config: HealthCheckConfig to
  WorkerMetadata for resolved runtime config. Updated all runtime access sites
  (check_health_async, grpc/http health checks, worker_registry health filter)
  from spec.health.X to health_config.X.
- model_gateway/src/core/worker_builder.rs: Builder now stores resolved
  health_config on WorkerMetadata via apply_to(). Fixed parse_bootstrap_host to
  strip @rank suffix from DP-aware URLs and handle bare host:port inputs.
- model_gateway/src/core/job_queue.rs: Removed incorrect assignment of full
  HealthCheckConfig to spec.health. Clamped max_connection_attempts with .max(1)
  to prevent 0 attempts when success_threshold is 0.
- model_gateway/src/service_discovery.rs: Same max_connection_attempts clamp and
  removed spec.health assignment.
- model_gateway/src/core/steps/worker/local/create_worker.rs: Changed
  build_health_config to use config.health.apply_to(&base). Changed
  build_model_card to clone full proto_card preserving all user-supplied fields.
- model_gateway/src/core/steps/worker/external/create_workers.rs: Same
  apply_to() pattern for health config merge.
- model_gateway/src/core/steps/worker/local/detect_connection.rs: Fall back to
  router timeout when per-worker override not set.
- model_gateway/src/core/steps/worker/local/update_worker_properties.rs: Merge
  health updates against resolved health_config instead of spec.health.
- bindings/golang/src/policy.rs: Added health_config field to WorkerMetadata
  construction.

Why:
- WorkerSpec.health as HealthCheckConfig silently reset unspecified fields to
  defaults, making per-worker health overrides impossible. Changing to
  HealthCheckUpdate gives proper partial-update semantics where only Some fields
  override router defaults.
- parse_bootstrap_host misinterpreted DP-aware URLs (e.g. http://host:8080@3)
  because @ is an RFC 3986 userinfo delimiter, extracting "3" as host instead
  of "host". Also failed on bare host:port inputs.
- max_connection_attempts was 0 when success_threshold was 0 (0 * N = 0),
  preventing any connection attempts.
- build_model_card selectively copied only 5 fields from proto_card, silently
  dropping user-supplied fields like display_name, provider, and context_length.

How:
- WorkerSpec.health is now HealthCheckUpdate (partial overrides). The resolved
  config lives on WorkerMetadata.health_config, populated at build time via
  HealthCheckUpdate::apply_to(&base). Runtime code reads health_config directly.
- parse_bootstrap_host strips trailing @digits before parsing and uses a
  try_parse helper that detects missing host_str() to handle schemeless URLs.
- .max(1) clamp on all 3 success_threshold multiplication sites.
- build_model_card now does config.models.find(id).cloned() to get the full card.

Signed-off-by: Simo Lin <simo.lin@oracle.com>
@slin1237
slin1237 merged commit 393e81a into main Feb 16, 2026
24 checks passed
@slin1237
slin1237 deleted the open-fix-n/2 branch February 16, 2026 16:13
ppraneth pushed a commit that referenced this pull request Feb 18, 2026
…and model card cloning (#415)

Signed-off-by: Simo Lin <linsimo.mark@gmail.com>
Signed-off-by: ppraneth <pranethparuchuri@gmail.com>
@coderabbitai coderabbitai Bot mentioned this pull request Mar 10, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model-gateway Model gateway crate changes protocols Protocols crate changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants