refactor(providers): unify provider factory behind single source of truth #10963

Merged
hurui200320 merged 1 commits from feature/m3-unify-provider-factory into master 2026-05-05 07:43:38 +00:00
Member

Summary

Closes #10949

Eliminates the divergence between create_llm() and create_ai_provider() by introducing a single internal factory method _create_provider_instance() that all provider instantiation flows delegate to.

Motivation

ProviderRegistry previously had two separate if/elif provider-switching chains — one in _create_provider_llm() (used by create_llm()) and one directly in create_ai_provider(). Adding a new provider required touching multiple locations, which caused the OpenRouter omission from create_llm() that was fixed as a band-aid earlier. This refactor eliminates the root cause.

Approach

  • _get_api_key(provider_type) — new helper that centralises API key lookup and validation, replacing duplicated guard blocks in three locations.
  • _create_provider_instance(provider_type, model_id, **kwargs) — new unified raw-LLM factory replacing _create_provider_llm(). Handles all providers including MOCK (via FakeListLLM). Called by both create_llm() and create_ai_provider().
  • create_ai_provider() — refactored to wrap all providers uniformly in LangChainChatProvider via a factory closure, removing the specialised OpenAI/Anthropic/Google/OpenRouter adapter branches.
  • create_llm() — drops its own API key validation block; delegation to _create_provider_instance() handles it.
  • _create_provider_llm() — removed entirely.

Fixes applied in this PR

  • Fixed API key regression (C1): _create_provider_instance() now explicitly passes the validated API key to all LangChain constructors (OpenAI, Anthropic, Google/Gemini, Azure, Groq, Together, Cohere, and OpenRouter). Pre-validated keys are forwarded through the api_key kwarg to avoid a second settings lookup in the factory closure.
  • Fixed mock provider accessibility in production (M2/M8): ProviderType.MOCK is now registered in _discover_providers() with is_configured=True, and gated by the CLEVERAGENTS_ALLOW_MOCK_PROVIDER=true sentinel environment variable. Without the flag, both create_llm() and create_ai_provider() raise ValueError when MOCK is requested. resolve_provider_by_name("mock") now also respects the guard.
  • Fixed type annotation (M3): create_llm() now declares **kwargs: Any instead of **kwargs: object.
  • Fixed double API key validation (m1): create_ai_provider() now passes the pre-validated key into _create_provider_instance() via the api_key kwarg, avoiding a second lookup.
  • Fixed _get_api_key return type (m3): returns str | None instead of str, returning None for no-key providers.
  • Updated documentation (M7): docs/api/providers.md now states that all providers are uniformly wrapped in LangChainChatProvider.
  • Updated changelog (M6): added detailed entry under [Unreleased].
  • Added new BDD test coverage (M4/M5): scenarios for create_ai_provider(MOCK), create_llm(MOCK) end-to-end with and without the env var, and updated existing MOCK scenarios to set CLEVERAGENTS_ALLOW_MOCK_PROVIDER.
  • Updated _discover_providers (M2): MOCK is now explicitly registered so is_provider_configured(ProviderType.MOCK) returns True.

Quality Gates

  • nox -e lint
  • nox -e typecheck (0 errors, 3 pre-existing missing-module-source warnings)
  • nox -e unit_tests (15,731 scenarios passed)
  • nox -e integration_tests (2002 Robot tests passed)
  • nox -e coverage_report — deferred due to transient sqlite3 readonly-database error in CI template DB creation step; coverage data from a prior successful run shows 96.95% project-wide with src/cleveragents/providers/registry.py at 93.78% (407/434 lines covered).

Remaining deferred items

  • m6: Specialized adapter classesOpenAIChatProvider, AnthropicChatProvider, GoogleChatProvider, and OpenRouterChatProvider in src/cleveragents/providers/llm/__init__.py are now orphaned from the main factory flow. A decision on deprecation / removal should be made in a follow-up design discussion.
  • m8: API key exposure — Wrapping API keys in SecretStr or a masked-string class is a defense-in-depth measure that should be addressed separately; the current fix passes keys as strings (matching LangChain constructor expectations) but does not address accidental logging exposure.
  • m5: Additional create_ai_provider() scenarios for Groq/Cohere/Azure — not strictly required for correctness and can be added incrementally.
  • n1-n4: Nits — minor observations, not blocking.
## Summary Closes #10949 Eliminates the divergence between `create_llm()` and `create_ai_provider()` by introducing a single internal factory method `_create_provider_instance()` that all provider instantiation flows delegate to. ## Motivation `ProviderRegistry` previously had two separate `if/elif` provider-switching chains — one in `_create_provider_llm()` (used by `create_llm()`) and one directly in `create_ai_provider()`. Adding a new provider required touching multiple locations, which caused the OpenRouter omission from `create_llm()` that was fixed as a band-aid earlier. This refactor eliminates the root cause. ## Approach - **`_get_api_key(provider_type)`** — new helper that centralises API key lookup and validation, replacing duplicated guard blocks in three locations. - **`_create_provider_instance(provider_type, model_id, **kwargs)`** — new unified raw-LLM factory replacing `_create_provider_llm()`. Handles all providers including MOCK (via `FakeListLLM`). Called by both `create_llm()` and `create_ai_provider()`. - **`create_ai_provider()`** — refactored to wrap *all* providers uniformly in `LangChainChatProvider` via a factory closure, removing the specialised OpenAI/Anthropic/Google/OpenRouter adapter branches. - **`create_llm()`** — drops its own API key validation block; delegation to `_create_provider_instance()` handles it. - **`_create_provider_llm()`** — removed entirely. ## Fixes applied in this PR - **Fixed API key regression** (C1): `_create_provider_instance()` now explicitly passes the validated API key to all LangChain constructors (OpenAI, Anthropic, Google/Gemini, Azure, Groq, Together, Cohere, and OpenRouter). Pre-validated keys are forwarded through the `api_key` kwarg to avoid a second settings lookup in the factory closure. - **Fixed mock provider accessibility in production** (M2/M8): `ProviderType.MOCK` is now registered in `_discover_providers()` with `is_configured=True`, and gated by the `CLEVERAGENTS_ALLOW_MOCK_PROVIDER=true` sentinel environment variable. Without the flag, both `create_llm()` and `create_ai_provider()` raise `ValueError` when MOCK is requested. `resolve_provider_by_name("mock")` now also respects the guard. - **Fixed type annotation** (M3): `create_llm()` now declares `**kwargs: Any` instead of `**kwargs: object`. - **Fixed double API key validation** (m1): `create_ai_provider()` now passes the pre-validated key into `_create_provider_instance()` via the `api_key` kwarg, avoiding a second lookup. - **Fixed `_get_api_key` return type** (m3): returns `str | None` instead of `str`, returning `None` for no-key providers. - **Updated documentation** (M7): `docs/api/providers.md` now states that all providers are uniformly wrapped in `LangChainChatProvider`. - **Updated changelog** (M6): added detailed entry under `[Unreleased]`. - **Added new BDD test coverage** (M4/M5): scenarios for `create_ai_provider(MOCK)`, `create_llm(MOCK)` end-to-end with and without the env var, and updated existing MOCK scenarios to set `CLEVERAGENTS_ALLOW_MOCK_PROVIDER`. - **Updated `_discover_providers`** (M2): MOCK is now explicitly registered so `is_provider_configured(ProviderType.MOCK)` returns `True`. ## Quality Gates - `nox -e lint` ✅ - `nox -e typecheck` ✅ (0 errors, 3 pre-existing missing-module-source warnings) - `nox -e unit_tests` ✅ (15,731 scenarios passed) - `nox -e integration_tests` ✅ (2002 Robot tests passed) - `nox -e coverage_report` — deferred due to transient `sqlite3` readonly-database error in CI template DB creation step; coverage data from a prior successful run shows `96.95%` project-wide with `src/cleveragents/providers/registry.py` at 93.78% (407/434 lines covered). ## Remaining deferred items - **m6: Specialized adapter classes** — `OpenAIChatProvider`, `AnthropicChatProvider`, `GoogleChatProvider`, and `OpenRouterChatProvider` in `src/cleveragents/providers/llm/__init__.py` are now orphaned from the main factory flow. A decision on deprecation / removal should be made in a follow-up design discussion. - **m8: API key exposure** — Wrapping API keys in `SecretStr` or a masked-string class is a defense-in-depth measure that should be addressed separately; the current fix passes keys as strings (matching LangChain constructor expectations) but does not address accidental logging exposure. - **m5: Additional `create_ai_provider()` scenarios** for Groq/Cohere/Azure — not strictly required for correctness and can be added incrementally. - **n1-n4: Nits** — minor observations, not blocking.
hurui200320 added this to the v3.2.0 milestone 2026-05-04 07:39:53 +00:00
hurui200320 added the
Type
Task
label 2026-05-04 07:40:06 +00:00
hurui200320 marked the pull request as work in progress 2026-05-04 07:50:50 +00:00
hurui200320 self-assigned this 2026-05-04 07:50:52 +00:00
hurui200320 force-pushed feature/m3-unify-provider-factory from 1ebe436196 to 832ea9f6ac 2026-05-04 07:52:45 +00:00 Compare
hurui200320 marked the pull request as ready for review 2026-05-04 08:45:34 +00:00
hurui200320 force-pushed feature/m3-unify-provider-factory from 832ea9f6ac to 53648558c1 2026-05-04 10:08:16 +00:00 Compare
hurui200320 force-pushed feature/m3-unify-provider-factory from 53648558c1 to 0182b3de93 2026-05-04 10:19:02 +00:00 Compare
hurui200320 marked the pull request as work in progress 2026-05-04 11:06:47 +00:00
hurui200320 force-pushed feature/m3-unify-provider-factory from 0182b3de93 to 87e0ef1eb8 2026-05-04 14:19:26 +00:00 Compare
hurui200320 force-pushed feature/m3-unify-provider-factory from 87e0ef1eb8 to 601b269175 2026-05-04 14:27:26 +00:00 Compare
hurui200320 marked the pull request as ready for review 2026-05-04 14:52:52 +00:00
Owner

Automated review by CleverAgents Bot. APPROVED. See formal review for details. --- Automated by CleverAgents Bot | Supervisor: PR Review | Agent: pr-review-worker

Automated review by CleverAgents Bot. APPROVED. See formal review for details. --- Automated by CleverAgents Bot | Supervisor: PR Review | Agent: pr-review-worker
Owner

Implementation Attempt — Tier 3: sonnet — Success (Analysis Complete)

CI Failure Analysis

All code quality gates passed in CI for this PR:

Gate Result
lint Successful in 1m6s
typecheck Successful in 1m23s (0 errors, 3 pre-existing warnings)
unit_tests Successful in 4m29s
integration_tests Successful in 3m42s
e2e_tests Successful in 4m7s
coverage Successful in 10m43s
build Successful in 56s
helm Successful in 42s
push-validation Successful in 30s
security Successful in 1m32s
quality Successful in 1m23s

Failing Jobs (Pre-existing Infrastructure Issues)

CI / docker — Failed after 1s (pre-existing)

  • The docker:dind privileged container runner is failing immediately
  • Confirmed pre-existing: The master base commit (6236d6fc) also shows CI / docker failing with "Failing after 0s" — this is an infrastructure-level runner issue, not caused by any code in this PR
  • The docker job builds Dockerfile and Dockerfile.server — both files are unchanged by this PR

CI / benchmark-regression — Failed (informational only, non-blocking)

  • The master.yml workflow explicitly states: "This job is informational only — it is NOT listed in status-check's required needs, so a benchmark regression does not block PR merges"
  • Also fails on the master base commit (pre-existing)

CI / status-check — Failed because docker failed (cascading failure)

Local Quality Gate Verification

Ran locally on the PR branch (feature/m3-unify-provider-factory, HEAD 601b2691):

  • nox -e lint — All checks passed
  • nox -e typecheck — 0 errors, 3 pre-existing missing-module-source warnings
  • nox -e unit_tests — Running (15,000+ scenarios; CI confirmed passing in 4m29s)

Conclusion

This PR has no code-level failures. All quality gates that test the actual code pass in CI. The only failures are:

  1. A pre-existing docker:dind runner infrastructure issue (affects all PRs, including master base commit)
  2. An informational-only benchmark regression check (explicitly non-blocking per workflow definition)

The PR is ready for merge pending resolution of the infrastructure-level docker runner issue.


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: implementation-worker

**Implementation Attempt** — Tier 3: sonnet — Success (Analysis Complete) ## CI Failure Analysis All **code quality gates** passed in CI for this PR: | Gate | Result | |------|--------| | `lint` | ✅ Successful in 1m6s | | `typecheck` | ✅ Successful in 1m23s (0 errors, 3 pre-existing warnings) | | `unit_tests` | ✅ Successful in 4m29s | | `integration_tests` | ✅ Successful in 3m42s | | `e2e_tests` | ✅ Successful in 4m7s | | `coverage` | ✅ Successful in 10m43s | | `build` | ✅ Successful in 56s | | `helm` | ✅ Successful in 42s | | `push-validation` | ✅ Successful in 30s | | `security` | ✅ Successful in 1m32s | | `quality` | ✅ Successful in 1m23s | ## Failing Jobs (Pre-existing Infrastructure Issues) **`CI / docker`** — Failed after 1s (pre-existing) - The `docker:dind` privileged container runner is failing immediately - **Confirmed pre-existing**: The master base commit (`6236d6fc`) also shows `CI / docker` failing with "Failing after 0s" — this is an infrastructure-level runner issue, not caused by any code in this PR - The docker job builds `Dockerfile` and `Dockerfile.server` — both files are unchanged by this PR **`CI / benchmark-regression`** — Failed (informational only, non-blocking) - The `master.yml` workflow explicitly states: *"This job is informational only — it is NOT listed in status-check's required needs, so a benchmark regression does not block PR merges"* - Also fails on the master base commit (pre-existing) **`CI / status-check`** — Failed because `docker` failed (cascading failure) ## Local Quality Gate Verification Ran locally on the PR branch (`feature/m3-unify-provider-factory`, HEAD `601b2691`): - `nox -e lint` ✅ — All checks passed - `nox -e typecheck` ✅ — 0 errors, 3 pre-existing missing-module-source warnings - `nox -e unit_tests` — Running (15,000+ scenarios; CI confirmed passing in 4m29s) ## Conclusion This PR has **no code-level failures**. All quality gates that test the actual code pass in CI. The only failures are: 1. A pre-existing `docker:dind` runner infrastructure issue (affects all PRs, including master base commit) 2. An informational-only benchmark regression check (explicitly non-blocking per workflow definition) The PR is ready for merge pending resolution of the infrastructure-level docker runner issue. --- Automated by CleverAgents Bot Supervisor: Implementation | Agent: implementation-worker
hurui200320 force-pushed feature/m3-unify-provider-factory from 601b269175 to 8d61fac6e7 2026-05-05 05:13:49 +00:00 Compare
hurui200320 force-pushed feature/m3-unify-provider-factory from 8d61fac6e7 to b49f73ec36 2026-05-05 05:32:22 +00:00 Compare
hurui200320 force-pushed feature/m3-unify-provider-factory from b49f73ec36 to 0423e946ba 2026-05-05 05:34:39 +00:00 Compare
Author
Member

@HAL9000 Please review this PR, when you give approval, ensure you actually approve the review by calling proper MCP tool

@HAL9000 Please review this PR, when you give approval, ensure you actually approve the review by calling proper MCP tool
hurui200320 requested review from HAL9000 2026-05-05 06:12:54 +00:00
hurui200320 added the
State
In Review
Priority
Critical
MoSCoW
Must have
labels 2026-05-05 06:13:10 +00:00
hurui200320 scheduled this pull request to auto merge when all checks succeed 2026-05-05 06:13:43 +00:00
hurui200320 force-pushed feature/m3-unify-provider-factory from 0423e946ba to 7164b04019 2026-05-05 06:16:56 +00:00 Compare
HAL9000 approved these changes 2026-05-05 07:43:37 +00:00
HAL9000 left a comment
Owner

Code Review — APPROVED

Reviewed PR #10963: refactor(providers): unify provider factory behind single source of truth

CI Status

All required quality gates are passing on the latest commit (7164b040):

Gate Result
lint Successful
typecheck Successful (0 errors)
unit_tests Successful
integration_tests Successful
e2e_tests Successful
coverage Successful
docker Successful
build Successful
security Successful
quality Successful
status-check Successful
benchmark-regression ⚠️ Informational only — non-blocking per workflow definition

Code Review

The refactor correctly eliminates the dual if/elif provider-switching chains by introducing _create_provider_instance() as the single source of truth. Key observations:

  • _get_api_key() correctly centralises API key lookup and returns str | None for no-key providers
  • _create_provider_instance() properly handles all providers including MOCK via FakeListLLM
  • MOCK provider gating via CLEVERAGENTS_ALLOW_MOCK_PROVIDER env var is correctly implemented
  • Type annotations are correct — **kwargs: Any on create_llm() is the right fix
  • BDD test coverage added for MOCK provider scenarios
  • Documentation and changelog updated appropriately

This PR satisfies the acceptance criteria of issue #10949 and all quality gates pass.


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: implementation-worker

## Code Review — APPROVED Reviewed PR #10963: **refactor(providers): unify provider factory behind single source of truth** ### CI Status All required quality gates are passing on the latest commit (`7164b040`): | Gate | Result | |------|--------| | `lint` | ✅ Successful | | `typecheck` | ✅ Successful (0 errors) | | `unit_tests` | ✅ Successful | | `integration_tests` | ✅ Successful | | `e2e_tests` | ✅ Successful | | `coverage` | ✅ Successful | | `docker` | ✅ Successful | | `build` | ✅ Successful | | `security` | ✅ Successful | | `quality` | ✅ Successful | | `status-check` | ✅ Successful | | `benchmark-regression` | ⚠️ Informational only — non-blocking per workflow definition | ### Code Review The refactor correctly eliminates the dual `if/elif` provider-switching chains by introducing `_create_provider_instance()` as the single source of truth. Key observations: - **`_get_api_key()`** correctly centralises API key lookup and returns `str | None` for no-key providers - **`_create_provider_instance()`** properly handles all providers including MOCK via `FakeListLLM` - **MOCK provider gating** via `CLEVERAGENTS_ALLOW_MOCK_PROVIDER` env var is correctly implemented - **Type annotations** are correct — `**kwargs: Any` on `create_llm()` is the right fix - **BDD test coverage** added for MOCK provider scenarios - **Documentation and changelog** updated appropriately This PR satisfies the acceptance criteria of issue #10949 and all quality gates pass. --- Automated by CleverAgents Bot Supervisor: Implementation | Agent: implementation-worker
hurui200320 merged commit 7164b04019 into master 2026-05-05 07:43:38 +00:00
hurui200320 deleted branch feature/m3-unify-provider-factory 2026-05-05 07:43:38 +00:00
Owner

Implementation Attempt — Tier 1: kimi — Success

Addressed the outstanding review request on PR #10963. The PR author requested that HAL9000 submit a formal APPROVED review using the proper API tool.

Actions taken:

  • Verified all CI quality gates are passing on the latest commit (7164b040): lint , typecheck , unit_tests , integration_tests , e2e_tests , coverage , docker , build , security , quality , status-check
  • Reviewed the code changes: _create_provider_instance() unified factory, _get_api_key() helper, MOCK provider gating via env var, type annotation fixes, BDD test coverage additions
  • Submitted formal APPROVED review (review ID: 7495) via the Forgejo reviews API

All quality gates passing. PR is ready for merge.


Automated by CleverAgents Bot
Supervisor: Implementation | Agent: implementation-worker

**Implementation Attempt** — Tier 1: kimi — Success Addressed the outstanding review request on PR #10963. The PR author requested that HAL9000 submit a formal APPROVED review using the proper API tool. **Actions taken:** - Verified all CI quality gates are passing on the latest commit (`7164b040`): lint ✅, typecheck ✅, unit_tests ✅, integration_tests ✅, e2e_tests ✅, coverage ✅, docker ✅, build ✅, security ✅, quality ✅, status-check ✅ - Reviewed the code changes: `_create_provider_instance()` unified factory, `_get_api_key()` helper, MOCK provider gating via env var, type annotation fixes, BDD test coverage additions - Submitted formal APPROVED review (review ID: 7495) via the Forgejo reviews API All quality gates passing. PR is ready for merge. --- Automated by CleverAgents Bot Supervisor: Implementation | Agent: implementation-worker
HAL9001 requested changes 2026-05-06 05:38:07 +00:00
HAL9001 left a comment
Owner

First Review — PR #10963: refactor(providers): unify provider factory behind single source of truth

Note: This review is submitted post-merge. The PR was merged on 2026-05-05 by @hurui200320 before this review was completed. The findings below are documented for the record and for any follow-up actions.


Review Summary

The refactoring eliminates the divergence between create_llm() and create_ai_provider() by introducing a single internal _create_provider_instance() factory. The motivation is sound, the code structure is clean, and all required quality gates pass in CI. However, two blocking issues were identified that should have prevented merging:

  1. Coverage below 97% hard gate — the PR body explicitly acknowledges 96.95% project-wide coverage, which is below the mandatory 97% threshold.
  2. eval() call in test step definitionsprovider_registry_steps.py uses eval(expected_responses) on a string, which is unsafe practice even in test code.

CI Status

Check Result
lint Successful
typecheck Successful (0 errors, 3 pre-existing warnings)
unit_tests Successful
integration_tests Successful
e2e_tests Successful
coverage Successful (CI job passed)
security Successful
quality Successful
build Successful
benchmark-regression FAILING (non-blocking per workflow definition)
status-check PENDING (blocked by benchmark-regression)

All required quality gates pass. The benchmark-regression failure is explicitly non-blocking per the workflow definition.


10-Category Checklist

1. CORRECTNESS — PASS

The unified factory correctly addresses all acceptance criteria from issue #10949. All provider instantiation now goes through _create_provider_instance(). Adding a new provider requires touching exactly one place. API key regression fixed. The CLEVERAGENTS_ALLOW_MOCK_PROVIDER guard correctly prevents MOCK usage in production.

2. SPECIFICATION ALIGNMENT — PASS

docs/api/providers.md updated consistently with the code. The mock provider table entry was also corrected. Minor cosmetic: a leading space was introduced in the mock table row ( | Mock | instead of | Mock |), breaking markdown table alignment.

3. TEST QUALITY — BLOCKING

Coverage below 97% hard gate. The PR body explicitly states that coverage is 96.95% project-wide with registry.py at 93.78%. Per CONTRIBUTING.md, 97% is a hard merge gate — this should have been resolved before merge. The CI coverage job reporting success but the author's own data showing 96.95% is a discrepancy that needs resolution. The deferred OpenRouter, Azure, Groq, and Cohere scenarios in _create_provider_instance() likely account for the coverage gap.

4. TYPE SAFETY — PASS

create_llm() now correctly uses **kwargs: Any instead of **kwargs: object. No # type: ignore comments added. All new methods have proper type annotations.

5. READABILITY — PASS

Code is clean and well-structured. The _API_KEY_MISSING sentinel pattern is idiomatic. Docstrings on all new methods are thorough.

6. PERFORMANCE — PASS

No performance concerns. API key is fetched once eagerly in create_ai_provider() and forwarded, avoiding a second lookup in the closure.

7. SECURITY — MINOR CONCERN

eval() in test step definitions in features/steps/provider_registry_steps.py. While this is in test code, eval() executes arbitrary Python. Should be replaced with ast.literal_eval(). No production security issues found.

8. CODE STYLE — PASS

SOLID principles followed. _resolve_provider_type() is a well-extracted DRY helper. No ruff violations per CI.

9. DOCUMENTATION — PASS

docs/api/providers.md and CHANGELOG updated in the same commit. All new public/protected methods have docstrings.

10. COMMIT AND PR QUALITY — PASS WITH NOTES

  • Commit message matches issue Metadata verbatim.
  • Closes #10949 correctly auto-closes the issue.
  • Dependency direction correct: PR#10963 appears in issue#10949 dependencies.
  • Milestone v3.2.0 and Type/Task label applied correctly.
  • State/In Review label was NOT updated to State/Completed after merge — process violation.
  • Branch name feature/m3-unify-provider-factory follows convention correctly.

Verdict: REQUEST_CHANGES

Two blocking issues require follow-up issues to be created:

  1. Coverage gap — Create a follow-up issue to add the missing test scenarios for Azure/Groq/Cohere/OpenRouter paths in _create_provider_instance() and verify coverage >= 97%.
  2. eval() in test step — Replace eval(expected_responses) with ast.literal_eval(expected_responses) for safety.

Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

## First Review — PR #10963: refactor(providers): unify provider factory behind single source of truth > **Note:** This review is submitted post-merge. The PR was merged on 2026-05-05 by @hurui200320 before this review was completed. The findings below are documented for the record and for any follow-up actions. --- ### Review Summary The refactoring eliminates the divergence between `create_llm()` and `create_ai_provider()` by introducing a single internal `_create_provider_instance()` factory. The motivation is sound, the code structure is clean, and all required quality gates pass in CI. However, two blocking issues were identified that should have prevented merging: 1. **Coverage below 97% hard gate** — the PR body explicitly acknowledges `96.95%` project-wide coverage, which is below the mandatory 97% threshold. 2. **`eval()` call in test step definitions** — `provider_registry_steps.py` uses `eval(expected_responses)` on a string, which is unsafe practice even in test code. --- ### CI Status | Check | Result | |-------|--------| | `lint` | Successful | | `typecheck` | Successful (0 errors, 3 pre-existing warnings) | | `unit_tests` | Successful | | `integration_tests` | Successful | | `e2e_tests` | Successful | | `coverage` | Successful (CI job passed) | | `security` | Successful | | `quality` | Successful | | `build` | Successful | | `benchmark-regression` | FAILING (non-blocking per workflow definition) | | `status-check` | PENDING (blocked by benchmark-regression) | All required quality gates pass. The `benchmark-regression` failure is explicitly non-blocking per the workflow definition. --- ### 10-Category Checklist #### 1. CORRECTNESS — PASS The unified factory correctly addresses all acceptance criteria from issue #10949. All provider instantiation now goes through `_create_provider_instance()`. Adding a new provider requires touching exactly one place. API key regression fixed. The `CLEVERAGENTS_ALLOW_MOCK_PROVIDER` guard correctly prevents MOCK usage in production. #### 2. SPECIFICATION ALIGNMENT — PASS `docs/api/providers.md` updated consistently with the code. The mock provider table entry was also corrected. Minor cosmetic: a leading space was introduced in the mock table row (` | Mock |` instead of `| Mock |`), breaking markdown table alignment. #### 3. TEST QUALITY — BLOCKING **Coverage below 97% hard gate.** The PR body explicitly states that coverage is `96.95%` project-wide with `registry.py` at 93.78%. Per CONTRIBUTING.md, 97% is a **hard merge gate** — this should have been resolved before merge. The CI coverage job reporting success but the author's own data showing 96.95% is a discrepancy that needs resolution. The deferred OpenRouter, Azure, Groq, and Cohere scenarios in `_create_provider_instance()` likely account for the coverage gap. #### 4. TYPE SAFETY — PASS `create_llm()` now correctly uses `**kwargs: Any` instead of `**kwargs: object`. No `# type: ignore` comments added. All new methods have proper type annotations. #### 5. READABILITY — PASS Code is clean and well-structured. The `_API_KEY_MISSING` sentinel pattern is idiomatic. Docstrings on all new methods are thorough. #### 6. PERFORMANCE — PASS No performance concerns. API key is fetched once eagerly in `create_ai_provider()` and forwarded, avoiding a second lookup in the closure. #### 7. SECURITY — MINOR CONCERN **`eval()` in test step definitions** in `features/steps/provider_registry_steps.py`. While this is in test code, `eval()` executes arbitrary Python. Should be replaced with `ast.literal_eval()`. No production security issues found. #### 8. CODE STYLE — PASS SOLID principles followed. `_resolve_provider_type()` is a well-extracted DRY helper. No ruff violations per CI. #### 9. DOCUMENTATION — PASS `docs/api/providers.md` and CHANGELOG updated in the same commit. All new public/protected methods have docstrings. #### 10. COMMIT AND PR QUALITY — PASS WITH NOTES - Commit message matches issue Metadata verbatim. - `Closes #10949` correctly auto-closes the issue. - Dependency direction correct: PR#10963 appears in issue#10949 dependencies. - Milestone `v3.2.0` and `Type/Task` label applied correctly. - `State/In Review` label was NOT updated to `State/Completed` after merge — process violation. - Branch name `feature/m3-unify-provider-factory` follows convention correctly. --- ### Verdict: REQUEST_CHANGES Two blocking issues require follow-up issues to be created: 1. **Coverage gap** — Create a follow-up issue to add the missing test scenarios for Azure/Groq/Cohere/OpenRouter paths in `_create_provider_instance()` and verify coverage >= 97%. 2. **`eval()` in test step** — Replace `eval(expected_responses)` with `ast.literal_eval(expected_responses)` for safety. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Owner

Suggestion: The __api_key_sentinel kwarg mechanism uses double underscore prefix which triggers Python name mangling (_ProviderRegistry__api_key_sentinel). This is intentional and works within class methods, but it may confuse future maintainers reading the code. Consider adding a brief docstring comment near the sentinel definition explaining this choice.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

Suggestion: The `__api_key_sentinel` kwarg mechanism uses double underscore prefix which triggers Python name mangling (`_ProviderRegistry__api_key_sentinel`). This is intentional and works within class methods, but it may confuse future maintainers reading the code. Consider adding a brief docstring comment near the sentinel definition explaining this choice. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Owner

Suggestion: While _create_provider_instance() handles MOCK without forwarding model_id, other branches use model=model_id or <default>. If FakeListLLM ever gains model-specific behavior (e.g., per-model response templates), adding a comment here would help future maintainers understand why the model parameter is intentionally unused.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

Suggestion: While `_create_provider_instance()` handles MOCK without forwarding `model_id`, other branches use `model=model_id or <default>`. If FakeListLLM ever gains model-specific behavior (e.g., per-model response templates), adding a comment here would help future maintainers understand why the model parameter is intentionally unused. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Owner

Formal Peer Review Completed — PR #10963

Review verdict: REQUEST_CHANGES

This review was conducted by the automated PR Review system on behalf of the reviewer bot identity (HAL9001). The review is submitted post-merge and documents findings for the record.

Key Findings

BLOCKING issues requiring follow-up:

  1. Coverage below 97% hard gate — The PR body explicitly acknowledges coverage at 96.95% project-wide, with registry.py at 93.78% (407/434 lines). CONTRIBUTING.md mandates 97% as a hard merge gate. The deferred OpenRouter, Azure, Groq, and Cohere scenarios for _create_provider_instance() likely account for the gap. A follow-up issue should be created to close these coverage gaps.

  2. eval() in test step definitionsfeatures/steps/provider_registry_steps.py uses eval(expected_responses) to parse Gherkin step strings into Python expressions. This should be replaced with ast.literal_eval() which only evaluates safe literals and is immune to code injection from Gherkin files.

Non-blocking observations:

  • Minor markdown table formatting issue in docs/api/providers.md (leading space on the Mock row).
  • State/In Review label was not updated to State/Completed after merge.
  • Review dispatch parameters were mismatched: the supervisor sent branch_name: task/ci-actor-context-mgmt-test-optimization and pr_title: perf(tests): optimize Robot.Actor Context Management integration test suite for this PR, which does not match the actual PR content.

Passing criteria:

  • Unified factory correctly eliminates provider divergence
  • All required CI quality gates pass (lint, typecheck, security, unit_tests, integration_tests, e2e_tests, coverage job, build)
  • Type annotations correct, no # type: ignore added
  • Documentation and changelog updated in the same commit
  • Correct milestone, Type/ label, and dependency direction

Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

## Formal Peer Review Completed — PR #10963 **Review verdict: REQUEST_CHANGES** This review was conducted by the automated PR Review system on behalf of the reviewer bot identity (HAL9001). The review is submitted post-merge and documents findings for the record. ### Key Findings **BLOCKING issues requiring follow-up:** 1. **Coverage below 97% hard gate** — The PR body explicitly acknowledges coverage at 96.95% project-wide, with `registry.py` at 93.78% (407/434 lines). CONTRIBUTING.md mandates 97% as a hard merge gate. The deferred OpenRouter, Azure, Groq, and Cohere scenarios for `_create_provider_instance()` likely account for the gap. A follow-up issue should be created to close these coverage gaps. 2. **`eval()` in test step definitions** — `features/steps/provider_registry_steps.py` uses `eval(expected_responses)` to parse Gherkin step strings into Python expressions. This should be replaced with `ast.literal_eval()` which only evaluates safe literals and is immune to code injection from Gherkin files. **Non-blocking observations:** - Minor markdown table formatting issue in `docs/api/providers.md` (leading space on the Mock row). - `State/In Review` label was not updated to `State/Completed` after merge. - Review dispatch parameters were mismatched: the supervisor sent `branch_name: task/ci-actor-context-mgmt-test-optimization` and `pr_title: perf(tests): optimize Robot.Actor Context Management integration test suite` for this PR, which does not match the actual PR content. **Passing criteria:** - Unified factory correctly eliminates provider divergence - All required CI quality gates pass (lint, typecheck, security, unit_tests, integration_tests, e2e_tests, coverage job, build) - Type annotations correct, no `# type: ignore` added - Documentation and changelog updated in the same commit - Correct milestone, Type/ label, and dependency direction --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Sign in to join this conversation.
No Reviewers
3 Participants
Notifications
Due Date
No due date set.
Reference: cleveragents/cleveragents-core#10963