Skip to content

feat(llm): add OrcaRouter as a named gateway provider - #3584

Open
jinhaosong-source wants to merge 3 commits into
MervinPraison:mainfrom
jinhaosong-source:feat/orcarouter-provider
Open

feat(llm): add OrcaRouter as a named gateway provider#3584
jinhaosong-source wants to merge 3 commits into
MervinPraison:mainfrom
jinhaosong-source:feat/orcarouter-provider

Conversation

@jinhaosong-source

@jinhaosong-source jinhaosong-source commented Aug 1, 2026

Copy link
Copy Markdown

Summary

Adds OrcaRouter as a named gateway provider, alongside the existing openrouter / litellm-proxy / custom-gateway entries in praisonai/llm/gateways.py. OrcaRouter is an OpenAI-compatible LLM gateway that fronts ~190 models from OpenAI, Anthropic, Google, DeepSeek, Qwen, MiniMax and others behind one key and one base URL.

Users can reach it today via custom-gateway plus a hand-typed base URL, but that path gives no credential discovery, no key-creation hint in praisonai setup / praisonai auth login, and no entry in the built-in model ladder. This wires it up the same way OpenRouter already is, so llm="orcarouter/openai/gpt-5.5" just works.

It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

I'm an engineer on the OrcaRouter team.

Changes

src/praisonai (gateway provider + built-in ladder)

  • praisonai/llm/gateways.pyOrcaRouterProvider (base URL https://api.orcarouter.ai/v1, ORCAROUTER_API_KEY), registered as orcarouter with an orca alias.
  • praisonai/llm/__init__.py — lazy export + __all__.
  • praisonai/inc/models.py — a branch in the PraisonAIModel ladder and orcarouter in _BUILTIN_MODEL_PREFIXES, so every framework adapter that goes through PraisonAIModel resolves it on the fast path.
  • praisonai/auto.py — the mirrored fallback copy of that prefix set.

src/praisonai-code (CLI resolution + onboarding)

  • llm/env.pyorcarouter/ in _PROVIDER_MAP, ORCAROUTER_API_KEY in the key-var → stored-provider map and _ALL_FALLBACK_PROVIDERS, and a _PROVIDER_DEFAULTS row so a bare ORCAROUTER_API_KEY picks an OrcaRouter default model.
  • llm/credentials.py — env mapping, model → key-var, key-var → stored provider, and the is_configured var list.
  • llm/catalogue.pyPROVIDER_KEY_URLS entry, plus four FALLBACK_MODELS rows. The rows are the reason the provider is selectable: the onboarding picker is catalogue-driven via list_providers(), and unlike OpenRouter, litellm ships no catalogue for this gateway, so without them praisonai setup could never offer it.
  • cli/commands/auth.py_PROVIDER_ENV_KEYS entry so auth list / auth status show it.
  • cli/main.py — the provider list in the "no LLM provider configured" help.

Docs / examples

  • README.md + src/praisonai/README.md — provider badge and a row in the providers table.
  • examples/python/providers/orcarouter/orcarouter_example.py — mirrors the OpenRouter example.

One thing worth reviewing closely

OrcaRouterProvider does not rewrite the model id the way OpenRouterProvider does. OrcaRouter's own model ids are namespaced (openai/gpt-5.5, anthropic/claude-sonnet-5, orcarouter/auto) and the gateway rejects bare names, so the id has to survive intact. LiteLLM strips only the leading openai/ and forwards the rest, so the provider prefixes openai/ unconditionally — including for ids that already start with openai/:

llm= LiteLLM model reaches gateway as
orcarouter/openai/gpt-5.5 openai/openai/gpt-5.5 openai/gpt-5.5
orcarouter/anthropic/claude-sonnet-5 openai/anthropic/claude-sonnet-5 anthropic/claude-sonnet-5
orcarouter/orcarouter/auto openai/orcarouter/auto orcarouter/auto

I tried the tidier-looking custom_llm_provider="openai" first and it is wrong: with openai/gpt-5.5 LiteLLM consumes the id's own namespace and the gateway returns 503 No available channel for model gpt. Verified live against the real API; there's a test pinning this.

Also note orcarouter/auto (the adaptive router) picks an upstream per request, so its structured-output behaviour varies. The example file and docstring point at pinned models for anything schema-dependent.

Testing

New tests — added to the existing files rather than new ones:

  • tests/unit/llm/test_gateway_providers.py — 5 OrcaRouter tests (init, env var, the namespace-preservation table above, base-URL override, and a litellm.completion call assertion), plus OrcaRouter added to the registration and create_llm_provider tests.
  • tests/unit/llm/test_env_resolver.pyorcarouter/ added to the exhaustive _PROVIDER_MAP URL assertion (which would otherwise KeyError on the new entry), plus 2 resolution tests.

Results — run with the same env and marker exclusions as test-core.yml:

Check Result
collection gate (tests/unit/ --collect-only, CI's own gate) 4805 collected, exit 0
subdirs shard subset — tests/unit/llm/ tests/unit/code/ tests/unit/integrations/ 396 passed, 5 skipped
tests/unit/llm/ alone 151 passed
root-shard tests touching the changed ladder (test_model_routing, test_auto_generator, test_auto_lazy_loading, test_enhanced_auto, test_framework_adapter_simple, test_hybrid_workflow, test_agents_schema_publish) 132 passed

I could not get the full root shard (tests/unit/*.py) to finish locally — it stalls partway on test_logging_regression.py on this Windows box. That file passes in isolation on both this branch and clean main (7 passed each), so it's a cross-test interaction in my environment rather than something this change introduced. CI will cover the rest.

Note for reviewers: the _PROVIDER_MAP assertion in test_env_resolver.py enumerates every entry against an expected_urls dict, so adding a provider without updating it raises KeyError. That's why the test file is touched.

Live verification against the real gateway — 17/17, driven through PraisonAI's own entry points (create_llm_provider, PraisonAIModel, praisonai_code.llm.env.resolve_llm_endpoint) rather than a hand-built client:

  • registry resolves orcarouter and the orca alias and lists it beside openrouter
  • the three model-id shapes above route to the expected LiteLLM ids with the gateway base URL and key attached
  • real completions on openai/gpt-5.5, anthropic/claude-sonnet-5, google/gemini-3.5-flash, and orcarouter/auto
  • PraisonAIModel resolves ORCAROUTER_API_KEY + base URL + stripped model name, its OpenAI client completes, and orcarouter/auto keeps its namespace through the ladder
  • resolve_llm_endpoint routes an orcarouter/ model to the gateway with the right key
  • key_url_for_provider("orcarouter") returns the console URL and ModelCatalogue().list_providers() includes orcarouter (the onboarding picker)
  • a bad key raises a clean AuthenticationError

How a new user gets started

  1. Sign up at orcarouter.ai and create a key (keys start with sk-orca-).
  2. export ORCAROUTER_API_KEY=sk-orca-... — or run praisonai setup and pick orcarouter, which prints the key page and stores the credential.
  3. Use it: Agent(instructions="...", llm="orcarouter/openai/gpt-5.5"), or praisonai --model orcarouter/openai/gpt-5.5.
  4. Any id from the catalog works; orcarouter/orcarouter/auto uses the adaptive router.

Summary by CodeRabbit

  • New Features

    • Added OrcaRouter as a supported AI provider.
    • Supports adaptive routing and OpenAI, Anthropic, and Google models.
    • Added API-key configuration, automatic model detection, provider aliases, and custom endpoint support.
    • Added an OrcaRouter example with conversational, creative-writing, reasoning, and adaptive-routing prompts.
  • Documentation

    • Updated provider listings, badges, setup guidance, examples, and provider counts to include OrcaRouter.

OrcaRouter is an OpenAI-compatible LLM gateway. Register it alongside the
existing openrouter / litellm-proxy / custom-gateway entries so
llm="orcarouter/openai/gpt-5.5" works without routing users through a
hand-configured custom gateway.

- llm/gateways.py: OrcaRouterProvider (ORCAROUTER_API_KEY, base URL
  https://api.orcarouter.ai/v1), registered as "orcarouter" with an "orca"
  alias; llm/__init__.py exports it lazily
- inc/models.py: a branch in the PraisonAIModel ladder plus "orcarouter" in
  _BUILTIN_MODEL_PREFIXES, and the mirrored copy of that set in auto.py
- praisonai_code llm/env.py, llm/credentials.py, llm/catalogue.py: provider
  map, credential lookup, key-creation URL, and catalogue entries so the
  catalogue-driven picker in `praisonai setup` can offer it
- praisonai_code cli/commands/auth.py, cli/main.py: auth listing and help text
- README (both) and examples/python/providers/orcarouter/

Unlike OpenRouterProvider this does not rewrite the model id. OrcaRouter model
ids are themselves namespaced (openai/gpt-5.5, orcarouter/auto) and the
gateway rejects bare names, so the provider prefixes "openai/"
unconditionally and LiteLLM strips only that segment, leaving the id intact.
Using custom_llm_provider instead makes LiteLLM consume the id's own
namespace and the gateway returns 503 No available channel.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet pipeline/reviews-pending Waiting for CodeRabbit/Qodo/Copilot reviews labels Aug 1, 2026
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

OrcaRouter is added as an OpenAI-compatible provider. The change includes gateway registration, model and credential resolution, catalogue metadata, CLI support, tests, documentation, and a usage example.

Changes

OrcaRouter support

Layer / File(s) Summary
Gateway provider and registration
src/praisonai/praisonai/llm/*, src/praisonai/tests/unit/llm/test_gateway_providers.py
Adds OrcaRouterProvider, public exports, orcarouter registration, the orca alias, model normalization, and gateway tests.
Model resolution and credentials
src/praisonai/praisonai/inc/models.py, src/praisonai-code/praisonai_code/llm/*, src/praisonai-code/praisonai_code/cli/*, src/praisonai/tests/unit/llm/test_env_resolver.py
Adds ORCAROUTER_API_KEY, endpoint resolution, fallback models, catalogue entries, CLI provider messages, and resolver tests.
Structured completion normalization
src/praisonai/praisonai/auto.py, src/praisonai/tests/unit/llm/test_registered_provider_hot_paths.py
Normalizes OrcaRouter model identifiers for LiteLLM and OpenAI SDK fallback paths.
Documentation and usage example
README.md, src/praisonai/README.md, examples/python/providers/orcarouter/*
Adds provider badges, example links, and a complete OrcaRouter usage example.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant PraisonAIModel
  participant OrcaRouterProvider
  participant LiteLLM
  Application->>PraisonAIModel: select orcarouter model
  PraisonAIModel->>OrcaRouterProvider: resolve model and credentials
  OrcaRouterProvider->>LiteLLM: send completion request
  LiteLLM-->>OrcaRouterProvider: return response
  OrcaRouterProvider-->>Application: return generated output
Loading

Suggested labels: pipeline/blocked:manual-review

Suggested reviewers: mervinpraison

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes adding OrcaRouter as a named LLM gateway provider.
Docstring Coverage ✅ Passed Docstring coverage is 96.55% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds OrcaRouter as a named OpenAI-compatible gateway and integrates it across provider registration, model resolution, credentials, onboarding, documentation, and tests.

  • Registers orcarouter and its orca alias with the gateway-provider registry.
  • Adds OrcaRouter endpoint, credential, catalogue, and CLI onboarding support.
  • Preserves vendor-qualified model IDs across LiteLLM and direct OpenAI SDK execution paths.
  • Adds documentation, an example, and focused routing tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported adaptive-router namespace issue is addressed across catalogue storage and the relevant execution normalizers.

Important Files Changed

Filename Overview
src/praisonai/praisonai/llm/gateways.py Adds the OrcaRouter gateway provider, alias registration, endpoint defaults, and LiteLLM-compatible model rewriting.
src/praisonai/praisonai/auto.py Normalizes OrcaRouter model IDs separately for LiteLLM and direct OpenAI SDK structured-completion paths.
src/praisonai/praisonai/inc/models.py Adds OrcaRouter to built-in model handling while stripping only its outer routing prefix.
src/praisonai-code/praisonai_code/llm/catalogue.py Adds selectable OrcaRouter fallback models, including the fully qualified adaptive-router ID, and its credential URL.
src/praisonai-code/praisonai_code/llm/env.py Adds OrcaRouter endpoint, credential, provider detection, and default-model resolution.
src/praisonai-code/praisonai_code/llm/credentials.py Integrates OrcaRouter with credential injection, model-specific key lookup, and configuration detection.
src/praisonai/tests/unit/llm/test_registered_provider_hot_paths.py Verifies the per-runtime model normalization that preserves OrcaRouter namespaces.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  User["orcarouter/vendor/model"] --> Resolve["Provider and credential resolution"]
  Resolve --> Catalogue["Stored model remains fully qualified"]
  Catalogue --> Runtime{"Execution path"}
  Runtime --> LiteLLM["LiteLLM: openai/vendor/model"]
  Runtime --> OpenAI["OpenAI SDK: vendor/model"]
  LiteLLM --> Gateway["OrcaRouter API"]
  OpenAI --> Gateway
Loading

Reviews (3): Last reviewed commit: "fix(llm): route orcarouter through struc..." | Re-trigger Greptile

Comment thread src/praisonai-code/praisonai_code/llm/catalogue.py Outdated
@MervinPraison

Copy link
Copy Markdown
Owner

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions
  4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep
  5. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params.
  6. Repo routing: agent-callable tools → PraisonAI-Tools; lifecycle plugins → PraisonAI-Plugins; optional sandbox backends → PraisonAI-Plugins (praisonai.sandbox entry point) — request changes if wrongly added to praisonaiagents/

Phase 2: FIX Valid Issues
7. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
8. Also independently identify and fix any gaps or issues you find in the changed code — do not rely only on prior reviewer feedback
9. Push all code fixes directly to THIS branch (do NOT create a new PR)
10. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
11. If all issues are resolved, approve the PR / close the Issue
12. If blocking issues remain, request changes / leave clear action items

@MervinPraison MervinPraison added pipeline/blocked:cooldown Blocked: post-push or @claude cooldown and removed pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet labels Aug 1, 2026

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

Actionable comments posted: 4

🤖 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 `@README.md`:
- Line 172: Update the provider-count summary from 24 to 25 in both README.md
(lines 172-172) and src/praisonai/README.md (lines 172-172), leaving the
provider tables unchanged.

In `@src/praisonai/praisonai/auto.py`:
- Line 472: Remove "orcarouter" from the built-in structured-completion prefix
collection near the completion configuration, leaving the other provider
prefixes unchanged so orcarouter models follow the normal Litellm model
normalization path.

In `@src/praisonai/README.md`:
- Line 172: Update the OrcaRouter link in the provider examples table of the
README to use the repository-relative path
../../examples/python/providers/orcarouter/orcarouter_example.py, leaving the
link text unchanged.

In `@src/praisonai/tests/unit/llm/test_env_resolver.py`:
- Line 283: In the loop over _PROVIDER_MAP, rename the unused key_var binding to
_key_var while preserving the existing prefix and base_url behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a489d4f-a225-47fa-987f-3a4a7664e9a6

📥 Commits

Reviewing files that changed from the base of the PR and between ce0a38d and ad9d897.

📒 Files selected for processing (14)
  • README.md
  • examples/python/providers/orcarouter/orcarouter_example.py
  • src/praisonai-code/praisonai_code/cli/commands/auth.py
  • src/praisonai-code/praisonai_code/cli/main.py
  • src/praisonai-code/praisonai_code/llm/catalogue.py
  • src/praisonai-code/praisonai_code/llm/credentials.py
  • src/praisonai-code/praisonai_code/llm/env.py
  • src/praisonai/README.md
  • src/praisonai/praisonai/auto.py
  • src/praisonai/praisonai/inc/models.py
  • src/praisonai/praisonai/llm/__init__.py
  • src/praisonai/praisonai/llm/gateways.py
  • src/praisonai/tests/unit/llm/test_env_resolver.py
  • src/praisonai/tests/unit/llm/test_gateway_providers.py

Comment thread README.md
Comment thread src/praisonai/praisonai/auto.py
Comment thread src/praisonai/README.md
| Fireworks | [Example](examples/python/providers/fireworks/fireworks_example.py) |
| Together AI | [Example](examples/python/providers/together/together_ai_example.py) |
| OpenRouter | [Example](examples/python/providers/openrouter/openrouter_example.py) |
| OrcaRouter | [Example](examples/python/providers/orcarouter/orcarouter_example.py) |

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files of interest =="
git ls-files | rg '(^|/)README\.md$|src/praisonai/README.md|examples/python/providers/orcarouter/orcarouter_example.py|mkdocs|docs|README' | sed -n '1,200p'

echo
echo "== target file exists =="
test -f examples/python/providers/orcarouter/orcarouter_example.py && echo "exists: examples/python/providers/orcarouter/orcarouter_example.py" || echo "missing target"

echo
echo "== link line in src/praisonai/README.md =="
sed -n '165,178p' src/praisonai/README.md

echo
echo "== doc build/config references =="
for f in $(git ls-files | rg '(^|/)(mkdocs\.ya?ml|docs|README\.md)$'); do
  case "$f" in
    *README.md)
      if rg -n '\[\|.*OrcaRouter|OrcaRouter|\[\*\].*OrcaRouter|src/praisonai/README|README.md' "$f" >/dev/null; then
        echo "--- $f"
        rg -n '\[\|.*OrcaRouter|OrcaRouter|src/praisonai/README|README|link' "$f" | sed -n '1,80p'
      fi
      ;;
    mkdocs.yml|mkdocs.yaml)
      echo "--- $f"
      cat "$f"
      ;;
  esac
done

echo
echo "== relative link resolution check =="
python3 - <<'PY'
from pathlib import Path
readme = Path("src/praisonai/README.md")
link = Path("examples/python/providers/orcarouter/orcarouter_example.py")
absolute = (readme.parent / link).resolve()
print("readme path:", readme)
print("current link target:", link)
print("readme-relative resolution:", absolute)
print("target exists:", Path(link).exists())
print("current link resolves to existing file:", absolute.exists())
print("recommended sibling-relative link exists:", (Path("src/praisonai") / "../../examples/python/providers/orcarouter/orcarouter_example.py").resolve().exists())
PY

Repository: MervinPraison/PraisonAI

Length of output: 7708


Fix the OrcaRouter example link.

When src/praisonai/README.md is rendered from its repository path, this link resolves at src/praisonai/examples/..., but the file is at examples/python/providers/orcarouter/orcarouter_example.py. Use ../../examples/python/providers/orcarouter/orcarouter_example.py.

🤖 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/praisonai/README.md` at line 172, Update the OrcaRouter link in the
provider examples table of the README to use the repository-relative path
../../examples/python/providers/orcarouter/orcarouter_example.py, leaving the
link text unchanged.

Comment thread src/praisonai/tests/unit/llm/test_env_resolver.py Outdated
@praisonai-triage-agent

praisonai-triage-agent Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MervinPraison's task —— View job


Final Architecture Review — PR #3584 (OrcaRouter provider) ✅

Pushed 0c77ab366 to the fork branch jinhaosong-source:feat/orcarouter-provider.

Architecture (AGENTS.md): Additive gateway wiring — no new Agent params, no new deps, protocol-driven (provider registered via the existing registry seam), backward-compatible, no hot-path regression. The provider adds real SDK value (a working named gateway + fast path). No scope creep.


Issues verified & fixed

# Reviewer Finding Verdict Action
1 Greptile Catalogue adaptive-router loses namespace Valid — and broader Fixed all 4 ids
2 CodeRabbit auto.py structured completion sends wrong id Valid — real bug; fix location differs Fixed via per-leg normalization
3 CodeRabbit README count 24→25 Valid Fixed (both files; fork author also bumped it — merged cleanly)
4 CodeRabbit B007 unused key_var Valid Renamed _key_var
5 CodeRabbit src/praisonai/README.md example link ../../ Skipped See below

#1 — catalogue (broader than reported). _catalogue_default_model("orcarouter") returns models[0].id verbatim as the stored/default model, and only an outer orcarouter/ prefix routes a model to the gateway (resolution strips it, preserving the vendor namespace the gateway requires). All four catalogue ids lacked it, so the default auto reached the gateway bare (503) and openai/gpt-5.5 would have routed to OpenAI. Gave all four the full orcarouter/<vendor>/<model> shape (list_providers() groups on the provider= field, so grouping is unaffected — verified).

#2 — structured completion. Confirmed live that config_list[0]['model'] is the raw orcarouter/openai/gpt-5.5. Since orcarouter ∈ _BUILTIN_MODEL_PREFIXES (needed for the working PraisonAIModel fast path) and the gateway provider has no generate_structured, both _completion_impl legs sent the raw id → LiteLLM has no orcarouter/ route and the OpenAI-SDK leg sends orcarouter/... to a gateway that wants openai/gpt-5.5. Both legs failed. CodeRabbit's suggested fix (drop orcarouter from the prefix set) does not fix it and would break the working fast path. Instead I added BaseAutoGenerator._normalize_gateway_model() — LiteLLM leg → openai/<gateway-id>, OpenAI-SDK leg → <gateway-id> — mirroring OrcaRouterProvider/PraisonAIModel. Every other provider (incl. LiteLLM-native openrouter/) passes through unchanged.

#5 — skipped (with reason). All 24 existing rows in src/praisonai/README.md use examples/..., not ../../examples/.... Fixing only OrcaRouter makes it the lone outlier; rewriting all 25 is out of scope for this PR.


Files modified

  • src/praisonai-code/praisonai_code/llm/catalogue.py — 4 OrcaRouter ids namespaced orcarouter/<vendor>/<model>
  • src/praisonai/praisonai/auto.py_normalize_gateway_model() + per-leg use in _completion_impl
  • src/praisonai/tests/unit/llm/test_registered_provider_hot_paths.py — 3 regression tests
  • src/praisonai/tests/unit/llm/test_env_resolver.py — B007 fix
  • README.md, src/praisonai/README.md — provider count 25

Tests

  • tests/unit/llm/ (praisonai): 154 passed (was 151 → +3)
  • catalogue/setup/credentials/env/provider (praisonai-code): 46 passed
  • ladder regression (test_model_routing, test_auto_generator, test_auto_lazy_loading, test_enhanced_auto): 102 passed
  • collection gate (tests/unit/ --collect-only): 4734 collected, exit 0

Verdict

All blocking issues resolved. Approve — the two functional bugs (setup default + structured completion) are fixed with regression coverage, and the change is a clean, additive, backward-compatible gateway integration.

(Note: I can't submit a formal GitHub PR approval for security reasons — this comment is the review.)


Job run · branch feat/orcarouter-provider · commit 0c77ab366

The providers table now has 25 rows after adding OrcaRouter.
@MervinPraison MervinPraison added the pipeline/blocked:stale-final Blocked: FINAL stale after new commits label Aug 1, 2026
@jinhaosong-source

Copy link
Copy Markdown
Author

Thanks for the review — went through all four.

1. Provider count 24 → 25 — fixed (d8d6bfc). Confirmed by counting the table: it really is 25 rows now. Updated in both README.md and src/praisonai/README.md.

2. Remove "orcarouter" from the prefix set in auto.py — skipping, I believe this one is incorrect.

That literal is the except ImportError fallback for _BUILTIN_MODEL_PREFIXES, and the comment immediately above it asks for exactly the opposite:

reuse the SAME set as PraisonAIModel._is_registered_provider() so both hot paths agree on which providers are "registered"

Dropping orcarouter from only the fallback copy would make the two sets disagree whenever the praisonai.inc.models import fails, which is the one situation the fallback exists for.

It also can't be dropped from the primary set in inc/models.py, because this PR adds an orcarouter/ branch to the PraisonAIModel ladder. _is_registered_provider() returns True for any prefix that is registered and not in the built-in set — so removing it would route PraisonAIModel through _resolve_registered_provider() instead, making that new ladder branch dead code. get_model() would then hit getattr(registered, "get_client", None), find nothing callable, and hand back the OrcaRouterProvider instance where framework adapters expect a native client. Verified live: with the current shape, PraisonAIModel(model="orcarouter/openai/gpt-5.5") resolves ORCAROUTER_API_KEY + the gateway base URL + the stripped model name, and the OpenAI client it returns completes against the gateway.

One real limitation your comment does point at, which I'd rather state than paper over: AutoGenerator._structured_completion passes model_name straight to litellm.completion, and LiteLLM has no native orcarouter/ route, so structured generation in praisonai auto won't work for OrcaRouter models. Removing the prefix wouldn't fix that either — OrcaRouterProvider doesn't expose generate_structured, so _structured_via_registered_provider returns None and lands on the same ladder. It's the same gap litellm-proxy and custom-gateway have today. Happy to follow up separately if you'd like that path generalised for non-LiteLLM-native gateways; it felt out of scope for adding one provider.

3. Use ../../examples/... in src/praisonai/README.md — skipping for consistency. All 24 pre-existing rows in that table use the repo-root-relative examples/python/providers/... form (0 use ../../); that file is a copy of the root README. Changing only the OrcaRouter row would make it the odd one out. If the paths there should be fixed, that's a separate sweep over all 25 rows.

4. Rename key_var_key_var in the _PROVIDER_MAP loop — skipping as out of scope. That loop is pre-existing; this PR only adds a dict entry to _PROVIDER_MAP (which the exhaustive expected_urls assertion in that test forces, otherwise it KeyErrors). Happy to fold it in if you'd prefer.

… catalogue defaults

Fixes two OrcaRouter gaps found in review:

- catalogue: the setup wizard stores a catalogue id verbatim and only an
  outer "orcarouter/" prefix routes a model to the gateway (resolution strips
  it, preserving the vendor namespace the gateway requires). The four
  OrcaRouter catalogue ids lacked it, so the picker default "auto" reached the
  gateway bare (503) and "openai/gpt-5.5" would even route to OpenAI. Give all
  four the full "orcarouter/<vendor>/<model>" shape.

- auto.py structured completion: orcarouter is a built-in prefix (so it never
  delegates to the registry) but LiteLLM has no native "orcarouter/" route and
  the gateway provider has no generate_structured, so both ladder legs sent the
  raw id and failed. Normalize per leg (LiteLLM -> openai/<gateway-id>, OpenAI
  SDK -> <gateway-id>) mirroring OrcaRouterProvider/PraisonAIModel; other
  providers pass through unchanged.

Also: README provider count 24->25, and B007 unused loop var in test_env_resolver.

Adds regression tests for _normalize_gateway_model and the LiteLLM-leg id.

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
@MervinPraison MervinPraison removed the pipeline/blocked:stale-final Blocked: FINAL stale after new commits label Aug 1, 2026

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/praisonai/praisonai/auto.py`:
- Around line 527-531: Update _normalize_gateway_model to recognize the
registered “orca/” alias as well as “orcarouter/”, rewriting either prefix to
the same openai gateway model and gateway ID while preserving unrelated model
names. Add coverage verifying that an alias such as “orca/openai/gpt-5.5” is
normalized before structured completion/config_list construction.
🪄 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: 2f45b9d4-eead-44bd-ac8b-1d2fd05de760

📥 Commits

Reviewing files that changed from the base of the PR and between d8d6bfc and 0c77ab3.

📒 Files selected for processing (4)
  • src/praisonai-code/praisonai_code/llm/catalogue.py
  • src/praisonai/praisonai/auto.py
  • src/praisonai/tests/unit/llm/test_env_resolver.py
  • src/praisonai/tests/unit/llm/test_registered_provider_hot_paths.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/praisonai-code/praisonai_code/llm/catalogue.py
  • src/praisonai/tests/unit/llm/test_env_resolver.py

Comment on lines +527 to +531
prefix = "orcarouter/"
if not model_name.lower().startswith(prefix):
return model_name, model_name
gateway_id = model_name[len(prefix):]
return f"openai/{gateway_id}", gateway_id

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect provider alias registration and built-in-prefix handling.
rg -n -C 5 --glob '*.py' \
  'OrcaRouterProvider|register_llm_provider|_BUILTIN_MODEL_PREFIXES|orcarouter|["'\'']orca["'\'']' \
  src/praisonai

# Inspect all structured-completion normalization call sites and tests.
rg -n -C 5 --glob '*.py' \
  '_normalize_gateway_model|_structured_via_registered_provider|orca/' \
  src/praisonai

Repository: MervinPraison/PraisonAI

Length of output: 50379


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant auto.py =="
sed -n '440,585p' src/praisonai/praisonai/auto.py

echo "== relevant registry resolution =="
sed -n '240,450p' src/praisonai/praisonai/llm/registry.py

echo "== relevant models.py =="
sed -n '86,170p' src/praisonai/praisonai/inc/models.py

echo "== model string docs/usages =="
rg -n -C 3 --glob '*.py' 'parse_model_string|model_path|model\s*=|model:"|model=' src/praisonai/tests src/praisonai/praisonai | head -n 200

echo "== tests for orca/ prefix =="
rg -n 'orca/' src/praisonai/tests src/praisonai/praisonai || true

Repository: MervinPraison/PraisonAI

Length of output: 34731


Normalize the orca/ model-ID alias.

orcarouter is registered with the orca alias, but _normalize_gateway_model only rewrites orcarouter/..., leaving orca/openai/gpt-5.5 unchanged for structured completion. Treat orca/... as a built-in and rewrite it the same way, or canonicalize aliases before building config_list; add coverage for the alias form.

🤖 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/praisonai/praisonai/auto.py` around lines 527 - 531, Update
_normalize_gateway_model to recognize the registered “orca/” alias as well as
“orcarouter/”, rewriting either prefix to the same openai gateway model and
gateway ID while preserving unrelated model names. Add coverage verifying that
an alias such as “orca/openai/gpt-5.5” is normalized before structured
completion/config_list construction.

@codecov

codecov Bot commented Aug 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.35%. Comparing base (61de56b) to head (0c77ab3).
⚠️ Report is 205 commits behind head on main.

Files with missing lines Patch % Lines
src/praisonai/praisonai/inc/models.py 25.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3584      +/-   ##
==========================================
- Coverage   32.73%   32.35%   -0.39%     
==========================================
  Files         542      531      -11     
  Lines       57463    56207    -1256     
==========================================
- Hits        18813    18187     -626     
+ Misses      38650    38020     -630     
Flag Coverage Δ
main-tests 32.35% <83.33%> (-0.39%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown pipeline/reviews-pending Waiting for CodeRabbit/Qodo/Copilot reviews

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants