From 521d9354e56a924996f3224f734f21a42fcb9143 Mon Sep 17 00:00:00 2001 From: XiaoHuo888 Date: Fri, 14 Aug 2026 20:04:06 +0800 Subject: [PATCH 1/2] feat(sdk1): add OrcaRouter as a named LLM adapter Adds OrcaRouter as a branded OpenAI-compatible LLM adapter, mirroring the existing NVIDIA Build adapter: users pick a model and supply an OrcaRouter API key, and the endpoint (https://api.orcarouter.ai/v1) is preconfigured and overridable. - base1.py: OrcaRouterLLMParameters reusing the shared branded OpenAI-compatible validation helper - llm1/orcarouter.py: OrcaRouterLLMAdapter + schema + icon - llm1/__init__.py: export the new adapter - tests: registration, model prefixing, api_base default/override, and schema checks for the OrcaRouter adapter Co-Authored-By: Claude --- .../public/icons/adapter-icons/OrcaRouter.png | Bin 0 -> 25544 bytes .../sdk1/src/unstract/sdk1/adapters/base1.py | 14 +++ .../unstract/sdk1/adapters/llm1/__init__.py | 2 + .../unstract/sdk1/adapters/llm1/orcarouter.py | 46 +++++++ .../sdk1/adapters/llm1/static/orcarouter.json | 112 ++++++++++++++++++ .../tests/test_branded_openai_adapters.py | 28 ++++- 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 frontend/public/icons/adapter-icons/OrcaRouter.png create mode 100644 unstract/sdk1/src/unstract/sdk1/adapters/llm1/orcarouter.py create mode 100644 unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/orcarouter.json diff --git a/frontend/public/icons/adapter-icons/OrcaRouter.png b/frontend/public/icons/adapter-icons/OrcaRouter.png new file mode 100644 index 0000000000000000000000000000000000000000..11ce68e1eb226ab14e638729f6b9328fc405acd7 GIT binary patch literal 25544 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 literal 0 HcmV?d00001 diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py index f396bd618c..6d4efbcf6c 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py @@ -534,6 +534,7 @@ def _validate_branded_openai_compatible( _NVIDIA_BUILD_API_BASE = "https://integrate.api.nvidia.com/v1" _OPENROUTER_API_BASE = "https://openrouter.ai/api/v1" +_ORCAROUTER_API_BASE = "https://api.orcarouter.ai/v1" _MINIMAX_API_BASE = "https://api.minimax.io/v1" _OPENROUTER_PROVIDER_PREFIX = "openrouter/" _MINIMAX_PROVIDER_PREFIX = "minimax/" @@ -578,6 +579,19 @@ def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: ) +class OrcaRouterLLMParameters(OpenAICompatibleLLMParameters): + """OpenAI-compatible adapter for OrcaRouter's model routing gateway.""" + + # Required str so a directly-constructed instance stays valid. + api_base: str = _ORCAROUTER_API_BASE + + @staticmethod + def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: + return _validate_branded_openai_compatible( + adapter_metadata, _ORCAROUTER_API_BASE + ) + + class MiniMaxLLMParameters(BaseChatCompletionParameters): """Adapter for MiniMax's OpenAI- and Anthropic-compatible APIs.""" diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py index a3a03c7da3..d3f58ff4a8 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/__init__.py @@ -12,6 +12,7 @@ from unstract.sdk1.adapters.llm1.openai import OpenAILLMAdapter from unstract.sdk1.adapters.llm1.openai_compatible import OpenAICompatibleLLMAdapter from unstract.sdk1.adapters.llm1.openrouter import OpenRouterLLMAdapter +from unstract.sdk1.adapters.llm1.orcarouter import OrcaRouterLLMAdapter from unstract.sdk1.adapters.llm1.vertexai import VertexAILLMAdapter adapters: dict[str, dict[str, Any]] = {} @@ -30,5 +31,6 @@ "OpenAILLMAdapter", "OpenAICompatibleLLMAdapter", "OpenRouterLLMAdapter", + "OrcaRouterLLMAdapter", "VertexAILLMAdapter", ] diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/orcarouter.py b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/orcarouter.py new file mode 100644 index 0000000000..1df0bfdc68 --- /dev/null +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/orcarouter.py @@ -0,0 +1,46 @@ +from typing import Any + +from unstract.sdk1.adapters.base1 import BaseAdapter, OrcaRouterLLMParameters +from unstract.sdk1.adapters.enums import AdapterTypes + +DESCRIPTION = ( + "Adapter for OrcaRouter's OpenAI-compatible model routing gateway " + "(orcarouter.ai). Supply a model name and your OrcaRouter API key; the " + "endpoint is preconfigured." +) + + +class OrcaRouterLLMAdapter(OrcaRouterLLMParameters, BaseAdapter): + @staticmethod + def get_id() -> str: + return "orcarouter|cc86ba36-f289-46a9-b60e-b2e8940c4385" + + @staticmethod + def get_metadata() -> dict[str, Any]: + return { + "name": "OrcaRouter", + "version": "1.0.0", + "adapter": OrcaRouterLLMAdapter, + "description": DESCRIPTION, + "is_active": True, + } + + @staticmethod + def get_name() -> str: + return "OrcaRouter" + + @staticmethod + def get_description() -> str: + return DESCRIPTION + + @staticmethod + def get_provider() -> str: + return "orcarouter" + + @staticmethod + def get_icon() -> str: + return "/icons/adapter-icons/OrcaRouter.png" + + @staticmethod + def get_adapter_type() -> AdapterTypes: + return AdapterTypes.LLM diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/orcarouter.json b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/orcarouter.json new file mode 100644 index 0000000000..4c94a1e9f4 --- /dev/null +++ b/unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/orcarouter.json @@ -0,0 +1,112 @@ +{ + "title": "OrcaRouter", + "type": "object", + "required": [ + "adapter_name", + "api_key", + "model" + ], + "properties": { + "adapter_name": { + "type": "string", + "title": "Name", + "default": "", + "description": "Provide a unique name for this adapter instance. Example: orcarouter-1" + }, + "api_key": { + "type": "string", + "title": "API Key", + "format": "password", + "description": "Your OrcaRouter API key from [orcarouter.ai](https://www.orcarouter.ai). Keys begin with `sk-orca-`." + }, + "model": { + "type": "string", + "title": "Model", + "description": "The model slug as listed on [orcarouter.ai/models](https://www.orcarouter.ai/models). Examples: openai/gpt-5.5, anthropic/claude-opus-4.8, deepseek/deepseek-v4-flash" + }, + "api_base": { + "type": "string", + "format": "url", + "title": "API Base", + "default": "https://api.orcarouter.ai/v1", + "description": "OrcaRouter endpoint. Pre-filled with the default; change only if OrcaRouter moves the base URL." + }, + "max_tokens": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Maximum Output Tokens", + "default": 4096, + "description": "Maximum number of output tokens to limit LLM replies. Leave it empty to use the provider default. Sent as `max_completion_tokens` when Enable Reasoning is on." + }, + "max_retries": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Max Retries", + "default": 5, + "description": "The maximum number of times to retry a request if it fails." + }, + "timeout": { + "type": "number", + "minimum": 0, + "multipleOf": 1, + "title": "Timeout", + "default": 900, + "description": "Timeout in seconds." + }, + "enable_reasoning": { + "type": "boolean", + "title": "Enable Reasoning", + "default": false, + "description": "Toggle on for reasoning models to drop `temperature` and send `max_completion_tokens` and `reasoning_effort` via `extra_body` so the upstream API accepts them." + } + }, + "allOf": [ + { + "if": { + "properties": { + "enable_reasoning": { + "const": true + } + }, + "required": [ + "enable_reasoning" + ] + }, + "then": { + "properties": { + "reasoning_effort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "default": "medium", + "title": "Reasoning Effort", + "description": "Sets the reasoning strength when Enable Reasoning is on." + } + }, + "required": [ + "reasoning_effort" + ] + } + }, + { + "if": { + "properties": { + "enable_reasoning": { + "const": false + } + }, + "required": [ + "enable_reasoning" + ] + }, + "then": { + "properties": {} + } + } + ] +} diff --git a/unstract/sdk1/tests/test_branded_openai_adapters.py b/unstract/sdk1/tests/test_branded_openai_adapters.py index c1556a3dd7..d40c91a492 100644 --- a/unstract/sdk1/tests/test_branded_openai_adapters.py +++ b/unstract/sdk1/tests/test_branded_openai_adapters.py @@ -7,6 +7,7 @@ NvidiaBuildLLMParameters, OpenAICompatibleEmbeddingParameters, OpenRouterLLMParameters, + OrcaRouterLLMParameters, ) from unstract.sdk1.adapters.constants import Common from unstract.sdk1.adapters.embedding1 import adapters as embedding_adapters @@ -18,9 +19,11 @@ from unstract.sdk1.adapters.llm1.minimax import MiniMaxLLMAdapter from unstract.sdk1.adapters.llm1.nvidia_build import NvidiaBuildLLMAdapter from unstract.sdk1.adapters.llm1.openrouter import OpenRouterLLMAdapter +from unstract.sdk1.adapters.llm1.orcarouter import OrcaRouterLLMAdapter _NVIDIA_BUILD_API_BASE = "https://integrate.api.nvidia.com/v1" _OPENROUTER_API_BASE = "https://openrouter.ai/api/v1" +_ORCAROUTER_API_BASE = "https://api.orcarouter.ai/v1" _MINIMAX_API_BASE = "https://api.minimax.io/v1" _MINIMAX_ANTHROPIC_API_BASE = "https://api.minimax.io/anthropic" _MINIMAX_CN_API_BASE = "https://api.minimaxi.com/v1" @@ -32,7 +35,12 @@ @pytest.mark.parametrize( "adapter", - [MiniMaxLLMAdapter, NvidiaBuildLLMAdapter, OpenRouterLLMAdapter], + [ + MiniMaxLLMAdapter, + NvidiaBuildLLMAdapter, + OpenRouterLLMAdapter, + OrcaRouterLLMAdapter, + ], ) def test_branded_llm_adapter_is_registered(adapter: type) -> None: adapter_id = adapter.get_id() @@ -47,6 +55,15 @@ def test_nvidia_llm_prefixes_model_via_custom_openai() -> None: assert validated["api_base"] == _NVIDIA_BUILD_API_BASE +def test_orcarouter_llm_prefixes_model_via_custom_openai() -> None: + validated = OrcaRouterLLMParameters.validate( + {"model": "deepseek/deepseek-v4-flash", "api_key": "k"} + ) + + assert validated["model"] == "custom_openai/deepseek/deepseek-v4-flash" + assert validated["api_base"] == _ORCAROUTER_API_BASE + + @pytest.mark.parametrize("model", ["MiniMax-M3", "MiniMax-M2.7"]) @pytest.mark.parametrize( ("api_base", "provider"), @@ -268,6 +285,7 @@ def test_openrouter_reasoning_survives_revalidation() -> None: (MiniMaxLLMParameters, _MINIMAX_API_BASE), (NvidiaBuildLLMParameters, _NVIDIA_BUILD_API_BASE), (OpenRouterLLMParameters, _OPENROUTER_API_BASE), + (OrcaRouterLLMParameters, _ORCAROUTER_API_BASE), ], ) def test_branded_llm_blank_api_base_falls_back_to_default( @@ -280,7 +298,12 @@ def test_branded_llm_blank_api_base_falls_back_to_default( @pytest.mark.parametrize( "params", - [MiniMaxLLMParameters, NvidiaBuildLLMParameters, OpenRouterLLMParameters], + [ + MiniMaxLLMParameters, + NvidiaBuildLLMParameters, + OpenRouterLLMParameters, + OrcaRouterLLMParameters, + ], ) def test_branded_llm_honours_api_base_override(params: type) -> None: validated = params.validate( @@ -296,6 +319,7 @@ def test_branded_llm_honours_api_base_override(params: type) -> None: (MiniMaxLLMAdapter, _MINIMAX_API_BASE), (NvidiaBuildLLMAdapter, _NVIDIA_BUILD_API_BASE), (OpenRouterLLMAdapter, _OPENROUTER_API_BASE), + (OrcaRouterLLMAdapter, _ORCAROUTER_API_BASE), ], ) def test_branded_llm_schema_exposes_api_base_with_default( From 026f6942c05bf9412a01c0d0a3e2fbde83a7184e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:05:06 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- unstract/sdk1/src/unstract/sdk1/adapters/base1.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py index 6d4efbcf6c..70c302bce2 100644 --- a/unstract/sdk1/src/unstract/sdk1/adapters/base1.py +++ b/unstract/sdk1/src/unstract/sdk1/adapters/base1.py @@ -587,9 +587,7 @@ class OrcaRouterLLMParameters(OpenAICompatibleLLMParameters): @staticmethod def validate(adapter_metadata: dict[str, "Any"]) -> dict[str, "Any"]: - return _validate_branded_openai_compatible( - adapter_metadata, _ORCAROUTER_API_BASE - ) + return _validate_branded_openai_compatible(adapter_metadata, _ORCAROUTER_API_BASE) class MiniMaxLLMParameters(BaseChatCompletionParameters):