Skip to content

Fix Issue #166: Dynamic Native LLM Provider Unavailable During Model …#167

Merged
Telli merged 8 commits into
mainfrom
Dynamic-Native-LLM-Provider
Jun 27, 2026
Merged

Fix Issue #166: Dynamic Native LLM Provider Unavailable During Model …#167
Telli merged 8 commits into
mainfrom
Dynamic-Native-LLM-Provider

Conversation

@geffzhang

@geffzhang geffzhang commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Description

[Provide a brief description of the changes in this PR. What problem do they solve?]

Summary

Root cause: ConfiguredModelProfileRegistry called BuildRegistrations() in its constructor during DI container resolution — well before LoadPluginCompositionAsync registers dynamic native providers. Custom providers were never found, causing startup to fail with Configured provider '' is not available.

Changes

  1. ConfiguredModelProfileRegistry.cs

Added _config backing field to store configuration for later use Removed BuildRegistrations(config) call from the constructor (too early — dynamic providers not yet registered) Changed DefaultProfileId from { get; } to { get; private set; } so it can be set outside the constructor Added SetDefaultProfileId() method that defers BuildRegistrations(_config) to the correct point in startup
2. RuntimeInitializationExtensions.cs

Added services.ModelProfiles.SetDefaultProfileId() call after LoadPluginCompositionAsync (which registers dynamic native providers) and after MarkDefault() — ensuring all providers are available when profiles are built

Related Issues

Fixes #166

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Tests
  • Build/CI
  • Governance/process
  • Refactoring (no functional changes)

Validation

  • dotnet restore OpenClaw.Net.slnx
  • dotnet build OpenClaw.Net.slnx --configuration Release --no-restore
  • dotnet test OpenClaw.Net.slnx --configuration Release --no-build
  • dotnet run --project samples/OpenClaw.HelloAgent -c Release --no-build

Review Notes

  • I considered NativeAOT compatibility
  • I considered security posture and unsafe defaults
  • I updated docs/tests where needed
  • This PR is scoped and does not mix unrelated changes

Checklist

  • I have read the CONTRIBUTING guidelines
  • My code follows the code style implementation of this project
  • I have added tests that prove my fix is effective or that my feature works
  • All new and existing tests passed locally (dotnet test)
  • I have updated the documentation (README.md, comments) if required
  • I have checked for security implications (input validation, authorization)
  • I have checked the relevant maintainer review checklist
  • I have disclosed whether this directly supports a company or customer use case

Summary by CodeRabbit

  • Bug Fixes

    • Ensured the default model profile ID is set consistently during runtime startup, compatibility setup, and admin/diagnostics endpoint fallback initialization.
    • Fixed scenarios where the default profile wasn’t available when providers become available after construction.
  • Tests

    • Added/updated coverage for dynamic/native provider availability, implicit default profiles, idempotent initialization, and handling of unavailable referenced providers.
  • Documentation

    • Added new guide pages comparing MetaSKILL with Mode-Step grids and Claude Code workflows, and updated site navigation accordingly.

…Profile Construction

Root cause: ConfiguredModelProfileRegistry called BuildRegistrations() in its constructor during DI container resolution — well before LoadPluginCompositionAsync registers dynamic native providers. Custom providers were never found, causing startup to fail with Configured provider '<id>' is not available.

Changes
1. ConfiguredModelProfileRegistry.cs

Added _config backing field to store configuration for later use
Removed BuildRegistrations(config) call from the constructor (too early — dynamic providers not yet registered)
Changed DefaultProfileId from { get; } to { get; private set; } so it can be set outside the constructor
Added SetDefaultProfileId() method that defers BuildRegistrations(_config) to the correct point in startup
2. RuntimeInitializationExtensions.cs

Added services.ModelProfiles.SetDefaultProfileId() call after LoadPluginCompositionAsync (which registers dynamic native providers) and after MarkDefault() — ensuring all providers are available when profiles are built
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

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

The runtime now defers model profile registration until SetDefaultProfileId() runs after provider setup. ConfiguredModelProfileRegistry stores its config for later use, startup and endpoint wiring call the new method, tests were updated around delayed initialization, and new comparison docs were added in English and Chinese.

Changes

Deferred model profile initialization

Layer / File(s) Summary
Registry computes default later
src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs
The registry keeps the constructor config in a field, makes DefaultProfileId privately settable, and adds SetDefaultProfileId() to build registrations after construction.
Startup wiring calls registry hook
src/OpenClaw.Gateway/Composition/RuntimeInitializationExtensions.cs, src/OpenClaw.Gateway/GatewayLlmExecutionService.cs
Runtime initialization and compatibility service creation both call SetDefaultProfileId() after provider setup and before chat client selection.
Endpoint registry initialization
src/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs, src/OpenClaw.Gateway/Endpoints/DiagnosticsEndpoints.cs, src/OpenClaw.Tests/GatewayAdminEndpointTests.cs
Admin and diagnostics endpoint fallbacks now use CreateInitialized(startup.Config), and the admin test helper builds the registry through a helper that also calls SetDefaultProfileId().
Registry behavior tests
src/OpenClaw.Tests/ConfiguredModelProfileRegistryTests.cs, src/OpenClaw.Tests/ModelProfileSelectionTests.cs, src/OpenClaw.Tests/PromptCachingTests.cs
New registry tests cover deferred construction, dynamic provider resolution, registered clients, missing providers, repeated calls, and implicit defaults, and existing selection and prompt-caching tests now initialize the registry before using it.

Documentation comparison pages

Layer / File(s) Summary
Comparison pages
docs/meta-skill-mode-step-grid-comparison.md, docs/meta-skill-vs-claude-code-workflows.md, docs/zh-CN/meta-skill-mode-step-grid-comparison.md, docs/zh-CN/meta-skill-vs-claude-code-workflows.md
The PR adds English and Chinese pages comparing Mode-Step Grid with MetaSKILL, and Claude Code workflows with OpenClaw MetaSKILL.
Site map entries
docs/SITE_MAP.md, docs/zh-CN/SITE_MAP.md
The English and Chinese site maps add navigation entries for the new comparison pages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A bunny hopped through startup light,
and set the defaults just right.
New profiles bloom, thump-thump hooray,
the registry waits until the day.
My whiskers twitch with code delight.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds unrelated documentation pages and sitemap updates that do not support the issue #166 fix. Move the new docs and sitemap changes into a separate PR so this change stays focused on the gateway startup fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 clearly references issue #166 and the Dynamic Native LLM provider startup fix.
Linked Issues check ✅ Passed The code defers profile registration until after plugin loading and covers the dynamic provider path, matching issue #166.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Dynamic-Native-LLM-Provider

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.

Comment thread src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs Fixed

@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

🤖 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/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs`:
- Around line 50-53: Repeated calls to SetDefaultProfileId() currently rebuild
registrations and overwrite owned IChatClient instances in _registrations
without disposing them, causing leaks. Update
ConfiguredModelProfileRegistry.SetDefaultProfileId to either short-circuit when
registrations are already built or dispose any previously owned clients before
calling BuildRegistrations, and make sure the cleanup covers registrations
created with ownsClient = true.
- Around line 48-53: Fallback ConfiguredModelProfileRegistry instances created
in the AdminEndpoints and DiagnosticsEndpoints fallback branches are never
initialized, leaving DefaultProfileId null and ListStatuses() empty. Update the
fallback construction path where
app.Services.GetService<IModelProfileRegistry>() and
runtime.Operations.ModelProfiles both miss by creating the
ConfiguredModelProfileRegistry and immediately calling SetDefaultProfileId()
before it is used, matching the initialization done in
RuntimeInitializationExtensions and other compatibility paths.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ca007ddb-bebb-4afa-947d-61cbfc9d26cc

📥 Commits

Reviewing files that changed from the base of the PR and between 6d03e2e and ab99d53.

📒 Files selected for processing (7)
  • src/OpenClaw.Gateway/Composition/RuntimeInitializationExtensions.cs
  • src/OpenClaw.Gateway/GatewayLlmExecutionService.cs
  • src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs
  • src/OpenClaw.Tests/ConfiguredModelProfileRegistryTests.cs
  • src/OpenClaw.Tests/GatewayAdminEndpointTests.cs
  • src/OpenClaw.Tests/ModelProfileSelectionTests.cs
  • src/OpenClaw.Tests/PromptCachingTests.cs

Comment thread src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs
Comment thread src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs
… client leaks

- Collapse suffix ternary in RuntimeInitializationExtensions.cs to stay
  within the GatewayStructureTests 252-line budget (Issue #166 follow-up).

- Add null-guard to SetDefaultProfileId() so repeated calls short-circuit
  instead of overwriting _registrations and leaking owned IChatClient
  instances left undisposed from prior BuildRegistrations runs.

- Introduce CreateInitialized() factory and use it in all three endpoint
  fallback paths (AdminEndpoints ×2, DiagnosticsEndpoints ×1) so fallback
  registries are initialized with SetDefaultProfileId() rather than left
  with a null DefaultProfileId and empty ListStatuses().

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

🧹 Nitpick comments (1)
src/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs (1)

87-95: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Reuse the fallback registry here. CreateInitialized(startup.Config) is invoked twice on the fallback path, which can create two separate registries with owned chat clients that are never disposed. Initialize once and pass the same instance to both locals, or dispose the fallback with the app lifetime.

🤖 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/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs` around lines 87 - 95, The
fallback registry is being created twice in AdminEndpoints when resolving
modelProfiles and modelEvaluationRunner, which can duplicate owned chat clients
and leak resources. Update the initialization flow around the
IModelProfileRegistry and ModelEvaluationRunner setup to create the
ConfiguredModelProfileRegistry once on the fallback path, reuse that same
instance for both locals, and ensure its lifetime is tied to the app if it is
not provided by the container.
🤖 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.

Nitpick comments:
In `@src/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs`:
- Around line 87-95: The fallback registry is being created twice in
AdminEndpoints when resolving modelProfiles and modelEvaluationRunner, which can
duplicate owned chat clients and leak resources. Update the initialization flow
around the IModelProfileRegistry and ModelEvaluationRunner setup to create the
ConfiguredModelProfileRegistry once on the fallback path, reuse that same
instance for both locals, and ensure its lifetime is tied to the app if it is
not provided by the container.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b33759ff-d229-4a1c-8bbc-12939e86d4c2

📥 Commits

Reviewing files that changed from the base of the PR and between ab99d53 and e9f4559.

📒 Files selected for processing (4)
  • src/OpenClaw.Gateway/Composition/RuntimeInitializationExtensions.cs
  • src/OpenClaw.Gateway/Endpoints/AdminEndpoints.cs
  • src/OpenClaw.Gateway/Endpoints/DiagnosticsEndpoints.cs
  • src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/OpenClaw.Gateway/Models/ConfiguredModelProfileRegistry.cs

…ModelProfileRegistry as the second fallback in the modelEvaluationRunner resolution chain

// Before: two independent CreateInitialized calls could each build owned clients
    ?? ConfiguredModelProfileRegistry.CreateInitialized(startup.Config)
// After: reuses modelProfiles if it's a ConfiguredModelProfileRegistry
    ?? modelProfiles as ConfiguredModelProfileRegistry
    ?? ConfiguredModelProfileRegistry.CreateInitialized(startup.Config)
Now when both app.Services and runtime.Operations.ModelProfiles miss, only one fallback registry is created (for modelProfiles), and the same instance is reused for modelEvaluationRunner. The third ?? CreateInitialized is kept as a safety net for the edge case where DI provides a non-ConfiguredModelProfileRegistry implementation.
Add a comprehensive comparison between the Mode-Step Grid pattern
(from cnblogs / AI老六) and OpenClaw's native MetaSKILL DAG
orchestration engine — covering state modeling, execution model,
failure handling, directory structure, suitability, and a universal
design checklist.  Available in both English and Simplified Chinese.
SITE_MAP entries added for both languages.
…-CN)

Add a systematic comparison between Claude Code's Dynamic Workflows
(JavaScript-based executable orchestration scripts) and OpenClaw's
MetaSKILL (YAML-declared DAG orchestration engine) — covering
orchestration model, expressiveness, security, auditability, human-
in-the-loop support, and suitability.  Available in English and
Simplified Chinese with SITE_MAP entries.

@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

🧹 Nitpick comments (1)
docs/SITE_MAP.md (1)

88-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sidebar Grouping omits new comparison pages.

The "Sidebar Grouping" section does not include the newly added comparison pages (MetaSkill vs Mode-Step Grid Comparison, MetaSKILL vs Claude Code Workflows, and their Chinese counterparts). Verify whether this omission is intentional — if the sidebar grouping is meant to be a curated subset, this is fine; if it should stay in sync with the full navigation table, add the missing entries.

🤖 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 `@docs/SITE_MAP.md` around lines 88 - 156, The Sidebar Grouping in SITE_MAP is
missing the newly added comparison pages, so first confirm whether this section
is meant to be a curated subset or a mirror of the full navigation. If it should
stay in sync, add the missing comparison entries to the sidebar grouping
alongside the existing page names, including the English and Chinese variants,
and keep the list aligned with the corresponding navigation symbols in the same
document.
🤖 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 `@docs/meta-skill-vs-claude-code-workflows.md`:
- Line 153: Update the phrasing in the docs where the compound modifier is used
so it reads with proper hyphenation, replacing “7 step kinds” with “7-step step
kinds” or rephrasing it to “7 kinds of steps”; make this wording change in the
relevant documentation section identified by the “Learning YAML structure and 7
step kinds” text.

---

Nitpick comments:
In `@docs/SITE_MAP.md`:
- Around line 88-156: The Sidebar Grouping in SITE_MAP is missing the newly
added comparison pages, so first confirm whether this section is meant to be a
curated subset or a mirror of the full navigation. If it should stay in sync,
add the missing comparison entries to the sidebar grouping alongside the
existing page names, including the English and Chinese variants, and keep the
list aligned with the corresponding navigation symbols in the same document.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d27dee7-7ee4-4a48-82bf-d8d8273d1d37

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8798d and f631607.

📒 Files selected for processing (6)
  • docs/SITE_MAP.md
  • docs/meta-skill-mode-step-grid-comparison.md
  • docs/meta-skill-vs-claude-code-workflows.md
  • docs/zh-CN/SITE_MAP.md
  • docs/zh-CN/meta-skill-mode-step-grid-comparison.md
  • docs/zh-CN/meta-skill-vs-claude-code-workflows.md
✅ Files skipped from review due to trivial changes (1)
  • docs/zh-CN/SITE_MAP.md

Comment thread docs/meta-skill-vs-claude-code-workflows.md Outdated
geffzhang and others added 3 commits June 27, 2026 07:29
 make the _config field immutable by adding the readonly modifier to its declaration
Remove unrelated comparison documentation from the issue #166 fix so the pull request only carries the runtime startup change and its tests.

Co-authored-by: geffzhang <geffzhang@qq.com>

@Telli Telli 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.

Reviewed the focused runtime change after removing unrelated docs scope. Verified the deferred profile initialization path, fallback initialization, idempotency guard, and tests. Local Release test suite passed (2,265 tests), and GitHub checks are green.

@Telli Telli merged commit dbbf5d2 into main Jun 27, 2026
18 checks passed
@Telli Telli deleted the Dynamic-Native-LLM-Provider branch June 27, 2026 06:51
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.

Dynamic Native LLM Provider Unavailable During Model Profile Construction

2 participants