diff --git a/config.toml b/config.toml index 89e94ed52..a407989fd 100644 --- a/config.toml +++ b/config.toml @@ -26,6 +26,21 @@ simple_model = "gemini-3-flash" complex_model = "gemini-3-pro" embeddings_model = "text-embedding-004" +[minimax] +simple_model = "MiniMax-M2.7" +complex_model = "MiniMax-M3" +embeddings_model = "" +protocol = "openai" +region = "global_en" + +[minimax.endpoints.global_en] +openai_base_url = "https://api.minimax.io/v1" +anthropic_base_url = "https://api.minimax.io/anthropic" + +[minimax.endpoints.cn_zh] +openai_base_url = "https://api.minimaxi.com/v1" +anthropic_base_url = "https://api.minimaxi.com/anthropic" + [tracing] project = "rai" diff --git a/docs/setup/vendors.md b/docs/setup/vendors.md index 855f0add7..e17167829 100644 --- a/docs/setup/vendors.md +++ b/docs/setup/vendors.md @@ -9,9 +9,9 @@ Alternatively vendors can be configured manually in `config.toml` file. The table summarizes vendor alternative for core AI service and optional RAI modules: -| Module | Open source | Alternative | Why to consider alternative? | More information | -| ----------------------------------------------- | ------------------ | ----------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [LLM service](#llm-model-configuration-in-rai) | Ollama | OpenAI, Bedrock | Overall performance of the LLM models, supported modalities and features | [LangChain models](https://docs.langchain4j.dev/integrations/language-models/) | +| Module | Open source | Alternative | Why to consider alternative? | More information | +| ----------------------------------------------- | ------------------ | -------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [LLM service](#llm-model-configuration-in-rai) | Ollama | OpenAI, Bedrock, MiniMax | Overall performance of the LLM models, supported modalities and features | [LangChain models](https://docs.langchain4j.dev/integrations/language-models/) | | **Optional:** [Tracing tool](./tracing.md) | Langfuse | LangSmith | Better integration with LangChain | [Comparison](https://langfuse.com/faq/all/langsmith-alternative) | | **Optional:** [Text to speech](#text-to-speech) | KokoroTTS, OpenTTS | ElevenLabs | Arguably, significantly better voice synthesis |
  • [KokoroTTS](https://huggingface.co/hexgrad/Kokoro-82M#usage)
  • [OpenTTS GitHub](https://github.com/synesthesiam/opentts)
  • [RAI voice interface][s2s]
  • | | **Optional:** [Speech to text](#speech-to-text) | Whisper | OpenAI Whisper (hosted) | When suitable local GPU is not an option |
  • [Whisper GitHub](https://github.com/openai/whisper)
  • [RAI voice interface][s2s]
  • | @@ -67,6 +67,47 @@ Ollama can be used to host models locally. 2. Use [RAI Configurator][configurator] -> `Model Selection` -> `bedrock` vendor +### MiniMax + +MiniMax can be configured through either its OpenAI-compatible or Anthropic-compatible +chat API. Set the API key before starting RAI: + +```bash +export MINIMAX_API_KEY="your-api-key" +``` + +The generated `config.toml` contains both supported regions and both protocols. Select the +active combination with `protocol` and `region`: + +```toml +[vendor] +simple_model = "minimax" +complex_model = "minimax" + +[minimax] +simple_model = "MiniMax-M2.7" +complex_model = "MiniMax-M3" +embeddings_model = "" +protocol = "openai" +region = "global_en" + +[minimax.endpoints.global_en] +openai_base_url = "https://api.minimax.io/v1" +anthropic_base_url = "https://api.minimax.io/anthropic" + +[minimax.endpoints.cn_zh] +openai_base_url = "https://api.minimaxi.com/v1" +anthropic_base_url = "https://api.minimaxi.com/anthropic" +``` + +The Anthropic-compatible base URL must end in `/anthropic`; the client appends the +`/v1/messages` request path. MiniMax does not provide an embeddings model in this +configuration, so keep `embeddings_model` assigned to a separate supported vendor. + +See the [global API documentation](https://platform.minimax.io/docs/api-reference/api-overview) +or the [China API documentation](https://platform.minimaxi.com/docs/api-reference/api-overview) +for service-specific details. + ## Complex LLM Model Configuration For custom setups please use LangChain API. diff --git a/src/rai_core/pyproject.toml b/src/rai_core/pyproject.toml index d7c3e5ae8..51c78bdbc 100644 --- a/src/rai_core/pyproject.toml +++ b/src/rai_core/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "rai_core" -version = "2.12.1" +version = "2.13.0" description = "Core functionality for RAI framework" readme = "README.md" requires-python = ">=3.10,<3.13" @@ -21,6 +21,7 @@ dependencies = [ "langchain>=1.0.0,<2.0.0", "langchain-aws", "langchain-openai", + "langchain-anthropic", "langchain-ollama", "langchain-google-genai", "langchain-community", diff --git a/src/rai_core/rai/initialization/config_initialization.py b/src/rai_core/rai/initialization/config_initialization.py index 94028b252..5b16ff690 100644 --- a/src/rai_core/rai/initialization/config_initialization.py +++ b/src/rai_core/rai/initialization/config_initialization.py @@ -47,6 +47,21 @@ complex_model = "gemini-3-pro" embeddings_model = "text-embedding-004" +[minimax] +simple_model = "MiniMax-M2.7" +complex_model = "MiniMax-M3" +embeddings_model = "" +protocol = "openai" +region = "global_en" + +[minimax.endpoints.global_en] +openai_base_url = "https://api.minimax.io/v1" +anthropic_base_url = "https://api.minimax.io/anthropic" + +[minimax.endpoints.cn_zh] +openai_base_url = "https://api.minimaxi.com/v1" +anthropic_base_url = "https://api.minimaxi.com/anthropic" + [tracing] project = "rai" diff --git a/src/rai_core/rai/initialization/model_initialization.py b/src/rai_core/rai/initialization/model_initialization.py index 6cd5d7e42..22fc8d6bb 100644 --- a/src/rai_core/rai/initialization/model_initialization.py +++ b/src/rai_core/rai/initialization/model_initialization.py @@ -67,6 +67,19 @@ class GoogleConfig(ModelConfig): pass +@dataclass +class MiniMaxEndpointConfig: + openai_base_url: str + anthropic_base_url: str + + +@dataclass +class MiniMaxConfig(ModelConfig): + protocol: Literal["openai", "anthropic"] + region: str + endpoints: Dict[str, MiniMaxEndpointConfig] + + @dataclass class LangfuseConfig: use_langfuse: bool @@ -93,6 +106,7 @@ class RAIConfig: openai: OpenAIConfig ollama: OllamaConfig google: GoogleConfig + minimax: MiniMaxConfig tracing: TracingConfig @@ -107,6 +121,14 @@ class RAIConfig: simple_model="", complex_model="", embeddings_model="", base_url="" ) _DEFAULT_GOOGLE = GoogleConfig(simple_model="", complex_model="", embeddings_model="") +_DEFAULT_MINIMAX = MiniMaxConfig( + simple_model="", + complex_model="", + embeddings_model="", + protocol="openai", + region="global_en", + endpoints={}, +) _DEFAULT_TRACING = TracingConfig( project="", langfuse=LangfuseConfig(use_langfuse=False, host=""), @@ -140,6 +162,21 @@ def load_config(config_path: Optional[str] = None) -> RAIConfig: if "google" in config_dict else _DEFAULT_GOOGLE ) + if "minimax" in config_dict: + minimax_dict = config_dict["minimax"] + minimax = MiniMaxConfig( + simple_model=minimax_dict["simple_model"], + complex_model=minimax_dict["complex_model"], + embeddings_model=minimax_dict.get("embeddings_model", ""), + protocol=minimax_dict.get("protocol", "openai"), + region=minimax_dict.get("region", "global_en"), + endpoints={ + region: MiniMaxEndpointConfig(**endpoint) + for region, endpoint in minimax_dict.get("endpoints", {}).items() + }, + ) + else: + minimax = _DEFAULT_MINIMAX if "tracing" in config_dict: tracing = TracingConfig( @@ -156,10 +193,48 @@ def load_config(config_path: Optional[str] = None) -> RAIConfig: openai=openai, ollama=ollama, google=google, + minimax=minimax, tracing=tracing, ) +def _create_minimax_chat_model( + model: str, + model_config: MiniMaxConfig, + kwargs: Dict[str, Any], +) -> Any: + try: + endpoint = model_config.endpoints[model_config.region] + except KeyError as exc: + raise ValueError( + f"MiniMax endpoint is not configured for region: {model_config.region}" + ) from exc + + model_kwargs = dict(kwargs) + if "api_key" not in model_kwargs: + api_key = os.getenv("MINIMAX_API_KEY") + if api_key: + model_kwargs["api_key"] = api_key + + if model_config.protocol == "openai": + from langchain_openai import ChatOpenAI + + return ChatOpenAI( + model=model, + base_url=endpoint.openai_base_url, + **model_kwargs, + ) + if model_config.protocol == "anthropic": + from langchain_anthropic import ChatAnthropic + + return ChatAnthropic( + model=model, + base_url=endpoint.anthropic_base_url, + **model_kwargs, + ) + raise ValueError(f"Unknown MiniMax protocol: {model_config.protocol}") + + def get_llm_model_config_and_vendor( model_type: Literal["simple_model", "complex_model"], vendor: Optional[str] = None, @@ -213,6 +288,12 @@ def get_llm_model( model_config = cast(GoogleConfig, model_config) return ChatGoogleGenerativeAI(model=model, **kwargs) + elif vendor == "minimax": + return _create_minimax_chat_model( + model, + cast(MiniMaxConfig, model_config), + kwargs, + ) else: raise ValueError(f"Unknown LLM vendor: {vendor}") @@ -255,6 +336,12 @@ def get_llm_model_direct( model_config = cast(GoogleConfig, model_config) return ChatGoogleGenerativeAI(model=model_name, **kwargs) + elif vendor == "minimax": + return _create_minimax_chat_model( + model_name, + cast(MiniMaxConfig, model_config), + kwargs, + ) else: raise ValueError(f"Unknown LLM vendor: {vendor}") @@ -268,6 +355,12 @@ def get_embeddings_model( model_config = getattr(config, vendor) + if vendor == "minimax": + raise ValueError( + "MiniMax does not provide an embeddings model. " + "Configure embeddings with a separate supported vendor." + ) + logger.info(f"Using embeddings model: {vendor}-{model_config.embeddings_model}") if vendor == "openai": from langchain_openai import OpenAIEmbeddings diff --git a/tests/initialization/test_minimax_initialization.py b/tests/initialization/test_minimax_initialization.py new file mode 100644 index 000000000..a6e779c20 --- /dev/null +++ b/tests/initialization/test_minimax_initialization.py @@ -0,0 +1,183 @@ +# Copyright (C) 2026 Robotec.AI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path + +import pytest +from rai.initialization import model_initialization + +MINIMAX_CONFIG_TEMPLATE = """ +[vendor] +simple_model = "minimax" +complex_model = "minimax" +embeddings_model = "openai" + +[openai] +simple_model = "gpt-4o-mini" +complex_model = "gpt-4o" +embeddings_model = "text-embedding-3-small" +base_url = "https://api.openai.com/v1" + +[minimax] +simple_model = "MiniMax-M2.7" +complex_model = "MiniMax-M3" +embeddings_model = "" +protocol = "openai" +region = "global_en" + +[minimax.endpoints.global_en] +openai_base_url = "https://api.minimax.io/v1" +anthropic_base_url = "https://api.minimax.io/anthropic" + +[minimax.endpoints.cn_zh] +openai_base_url = "https://api.minimaxi.com/v1" +anthropic_base_url = "https://api.minimaxi.com/anthropic" +""" + + +class DummyModel: + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +def write_config(path: Path, config: str = MINIMAX_CONFIG_TEMPLATE) -> Path: + path.write_text(config, encoding="utf-8") + return path + + +def test_load_config_preserves_models_and_endpoints(tmp_path): + config = model_initialization.load_config( + str(write_config(tmp_path / "config.toml")) + ) + + assert config.minimax.simple_model == "MiniMax-M2.7" + assert config.minimax.complex_model == "MiniMax-M3" + assert config.minimax.endpoints["global_en"].openai_base_url.endswith("/v1") + assert config.minimax.endpoints["global_en"].anthropic_base_url.endswith( + "/anthropic" + ) + assert config.minimax.endpoints["cn_zh"].openai_base_url.endswith("/v1") + assert config.minimax.endpoints["cn_zh"].anthropic_base_url.endswith("/anthropic") + + +def test_get_llm_model_uses_openai_protocol_and_selected_region(monkeypatch, tmp_path): + config_path = write_config(tmp_path / "config.toml") + monkeypatch.setenv("MINIMAX_API_KEY", "test-key") + monkeypatch.setattr("langchain_openai.ChatOpenAI", DummyModel) + + model = model_initialization.get_llm_model( + "complex_model", vendor="minimax", config_path=str(config_path) + ) + + assert isinstance(model, DummyModel) + assert model.kwargs["model"] == "MiniMax-M3" + assert model.kwargs["base_url"] == "https://api.minimax.io/v1" + assert model.kwargs["api_key"] == "test-key" + + +def test_get_llm_model_uses_anthropic_protocol(monkeypatch, tmp_path): + config = MINIMAX_CONFIG_TEMPLATE.replace( + 'protocol = "openai"', 'protocol = "anthropic"' + ) + config_path = write_config(tmp_path / "config.toml", config) + monkeypatch.setattr("langchain_anthropic.ChatAnthropic", DummyModel) + + model = model_initialization.get_llm_model( + "simple_model", vendor="minimax", config_path=str(config_path) + ) + + assert isinstance(model, DummyModel) + assert model.kwargs["model"] == "MiniMax-M2.7" + assert model.kwargs["base_url"] == "https://api.minimax.io/anthropic" + + +def test_get_llm_model_direct_preserves_explicit_api_key(monkeypatch, tmp_path): + config_path = write_config(tmp_path / "config.toml") + monkeypatch.setenv("MINIMAX_API_KEY", "environment-key") + monkeypatch.setattr("langchain_openai.ChatOpenAI", DummyModel) + + model = model_initialization.get_llm_model_direct( + "MiniMax-M3", + vendor="minimax", + config_path=str(config_path), + api_key="explicit-key", + ) + + assert model.kwargs["api_key"] == "explicit-key" + + +def test_get_embeddings_model_rejects_minimax(tmp_path): + config = MINIMAX_CONFIG_TEMPLATE.replace( + 'embeddings_model = "openai"', 'embeddings_model = "minimax"' + ) + config_path = write_config(tmp_path / "config.toml", config) + + with pytest.raises(ValueError, match="does not provide an embeddings model"): + model_initialization.get_embeddings_model(config_path=str(config_path)) + + +class CaptureHandler(BaseHTTPRequestHandler): + request_path = "" + + def do_POST(self): # noqa: N802 + self.__class__.request_path = self.path + content_length = int(self.headers.get("content-length", "0")) + self.rfile.read(content_length) + response = { + "id": "message-test", + "type": "message", + "role": "assistant", + "model": "MiniMax-M3", + "content": [{"type": "text", "text": "ok"}], + "stop_reason": "end_turn", + "stop_sequence": None, + "usage": {"input_tokens": 1, "output_tokens": 1}, + } + payload = json.dumps(response).encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(payload))) + self.end_headers() + self.wfile.write(payload) + + def log_message(self, format, *args): # noqa: A002 + return + + +def test_anthropic_client_appends_messages_path(tmp_path, monkeypatch): + server = ThreadingHTTPServer(("127.0.0.1", 0), CaptureHandler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + base_url = f"http://127.0.0.1:{server.server_port}/anthropic" + config = MINIMAX_CONFIG_TEMPLATE.replace( + 'protocol = "openai"', 'protocol = "anthropic"' + ).replace("https://api.minimax.io/anthropic", base_url) + config_path = write_config(tmp_path / "config.toml", config) + monkeypatch.setenv("MINIMAX_API_KEY", "test-key") + + model = model_initialization.get_llm_model( + "complex_model", vendor="minimax", config_path=str(config_path) + ) + response = model.invoke("Say hello") + + assert response.content == "ok" + assert CaptureHandler.request_path == "/anthropic/v1/messages" + finally: + server.shutdown() + thread.join(timeout=5) diff --git a/uv.lock b/uv.lock index a430dfcd9..5218b1228 100644 --- a/uv.lock +++ b/uv.lock @@ -186,6 +186,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "anthropic" +version = "0.116.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/a2/d31f14e28d49bae983a3634e38dfb4b31c50110b5e403596c5c6a20b23f8/anthropic-0.116.0.tar.gz", hash = "sha256:5fc248fbb9fe03ef686f8a774f81586bca31a043260aab88b387ea3660f4a396", size = 949149, upload-time = "2026-07-02T19:08:10.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/dd/2a1e81cf1b163acc340afc4ec74ed1d86f5eed1a809fabdeed3e0997b346/anthropic-0.116.0-py3-none-any.whl", hash = "sha256:6c0a7698e8d652455da3499978279bb2588c7264d0a35be3666009a4258c8256", size = 956896, upload-time = "2026-07-02T19:08:08.756Z" }, +] + [[package]] name = "antlr4-python3-runtime" version = "4.9.3" @@ -1088,6 +1107,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/90/022c79d6e5e6f843268c10b84d4a021ee3afba0621d3c176d3ff2024bfc8/dlinfo-2.0.0-py3-none-any.whl", hash = "sha256:b32cc18e3ea67c0ca9ca409e5b41eed863bd1363dbc9dd3de90fedf11b61e7bc", size = 3654, upload-time = "2025-01-16T15:43:09.474Z" }, ] +[[package]] +name = "docstring-parser" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/4d/f332313098c1de1b2d2ff91cf2674415cc7cddab2ca1b01ae29774bd5fdf/docstring_parser-0.18.0.tar.gz", hash = "sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015", size = 29341, upload-time = "2026-04-14T04:09:19.867Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, +] + [[package]] name = "donfig" version = "0.8.1.post1" @@ -2307,6 +2335,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/e8/a3b8cb0005553f6a876865073c81ef93bd7c5b18381bcb9ba4013af96ebc/langchain-1.2.15-py3-none-any.whl", hash = "sha256:e349db349cb3e9550c4044077cf90a1717691756cc236438404b23500e615874", size = 112714, upload-time = "2026-04-03T14:26:02.557Z" }, ] +[[package]] +name = "langchain-anthropic" +version = "1.4.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anthropic" }, + { name = "langchain-core" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/22/40ab129b08329ca295b391aa1d48267692b42594757084c6918e22b655ac/langchain_anthropic-1.4.8.tar.gz", hash = "sha256:c76891b2044d56105ff13c106ed12650637b53bd598a4bdf15b4796eefa2a4ec", size = 708524, upload-time = "2026-06-26T21:28:46.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/14/746235c4da89d9bc6a608c5f489f628e03feb8f697195c146e452c8f23c8/langchain_anthropic-1.4.8-py3-none-any.whl", hash = "sha256:778e9301b6fd517824f76ec1776975ce8add97a1f6a36c50ae3c2f4b03a66f7f", size = 52366, upload-time = "2026-06-26T21:28:45.535Z" }, +] + [[package]] name = "langchain-aws" version = "1.4.3" @@ -2347,10 +2389,11 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.2.28" +version = "1.4.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, + { name = "langchain-protocol" }, { name = "langsmith" }, { name = "packaging" }, { name = "pydantic" }, @@ -2359,9 +2402,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/317a1a3ac1df33a64adb3670bf88bbe3b3d5baa274db6863a979db472897/langchain_core-1.2.28.tar.gz", hash = "sha256:271a3d8bd618f795fdeba112b0753980457fc90537c46a0c11998516a74dc2cb", size = 846119, upload-time = "2026-04-08T18:19:34.867Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/b9/e937d0a90b26540bff07e7a7c64349f3b29c2dcc36257cd1cd3fdce17f2a/langchain_core-1.4.9.tar.gz", hash = "sha256:f8078901145bed0466755277500a5a22822a7b628808c4c0a28d4fc88895fcf2", size = 967294, upload-time = "2026-07-08T20:06:54.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/92/32f785f077c7e898da97064f113c73fbd9ad55d1e2169cf3a391b183dedb/langchain_core-1.2.28-py3-none-any.whl", hash = "sha256:80764232581eaf8057bcefa71dbf8adc1f6a28d257ebd8b95ba9b8b452e8c6ac", size = 508727, upload-time = "2026-04-08T18:19:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/84/70/ade2fada52772798ef815b6352b59e71b116aa0c32c3aef5be3dc2cbed12/langchain_core-1.4.9-py3-none-any.whl", hash = "sha256:28e3909e2a10cc81504952d795ac0a9e014c0018121ef89d48dd396fa09ec624", size = 558293, upload-time = "2026-07-08T20:06:52.382Z" }, ] [[package]] @@ -2406,6 +2449,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/a6/68fb22e3604015e6f546fa1d3677d24378b482855ae74710cbf4aec44132/langchain_openai-1.1.12-py3-none-any.whl", hash = "sha256:da71ca3f2d18c16f7a2443cc306aa195ad2a07054335ac9b0626dcae02b6a0c5", size = 88487, upload-time = "2026-03-23T18:59:17.978Z" }, ] +[[package]] +name = "langchain-protocol" +version = "0.0.18" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d2/59/b5959aea96faa9146e2e49a7a22882b3528c62efafe9a6a95beab30c2305/langchain_protocol-0.0.18.tar.gz", hash = "sha256:ec3e11782f1ed0c9db38e5a9ed01b0e7a0d3fba406faa8aef6594b73c56a63e6", size = 6150, upload-time = "2026-06-18T17:08:26.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/2e/d82db9eec13ad0f72e7aaad5c4bc730ab111934fdc83c85523206eb9b0a0/langchain_protocol-0.0.18-py3-none-any.whl", hash = "sha256:70b53a86fbf9cedc863555effe44da192ab02d556ddbf2cf95b8873adcf41b5a", size = 7221, upload-time = "2026-06-18T17:08:25.996Z" }, +] + [[package]] name = "langfuse" version = "3.14.6" @@ -4714,12 +4769,13 @@ requires-dist = [ [[package]] name = "rai-core" -version = "2.12.1" +version = "2.13.0" source = { editable = "src/rai_core" } dependencies = [ { name = "coloredlogs" }, { name = "deprecated" }, { name = "langchain" }, + { name = "langchain-anthropic" }, { name = "langchain-aws" }, { name = "langchain-community" }, { name = "langchain-core" }, @@ -4748,6 +4804,7 @@ requires-dist = [ { name = "coloredlogs", specifier = ">=15.0.1,<16.0.0" }, { name = "deprecated", specifier = ">=1.2.14,<2.0.0" }, { name = "langchain", specifier = ">=1.0.0,<2.0.0" }, + { name = "langchain-anthropic" }, { name = "langchain-aws" }, { name = "langchain-community" }, { name = "langchain-core", specifier = ">=1.2.10,<2.0.0" },