From 2548c28d65b71a142698711014675255b28f7b7b Mon Sep 17 00:00:00 2001 From: FuturMix Date: Thu, 30 Apr 2026 10:59:37 +0800 Subject: [PATCH] feat: add FuturMix as model provider (#14419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Add [FuturMix](https://futurmix.ai) as a new model provider. FuturMix is an OpenAI-compatible unified AI gateway that provides access to 22+ models (GPT, Claude, Gemini, DeepSeek, and more) through a single API endpoint and key. - **API Base**: `https://futurmix.ai/v1` (OpenAI-compatible) - **Supported capabilities**: Chat, Embedding, Image2Text, TTS, Speech2Text, Rerank ### Changes | File | Change | |------|--------| | `rag/llm/__init__.py` | Add `FuturMix` to `SupportedLiteLLMProvider` enum, `FACTORY_DEFAULT_BASE_URL`, and `LITELLM_PROVIDER_PREFIX` | | `rag/llm/chat_model.py` | Add `FuturMixChat(Base)` — follows Astraflow/Avian pattern | | `rag/llm/embedding_model.py` | Add `FuturMixEmbed(OpenAIEmbed)` — follows Astraflow pattern | | `rag/llm/cv_model.py` | Add `FuturMixCV(GptV4)` — follows SILICONFLOW/OpenRouter pattern | | `rag/llm/tts_model.py` | Add `FuturMixTTS(OpenAITTS)` — follows CometAPI/DeerAPI pattern | | `rag/llm/sequence2txt_model.py` | Add `FuturMixSeq2txt(GPTSeq2txt)` — follows StepFun pattern | | `rag/llm/rerank_model.py` | Add `FuturMixRerank(OpenAI_APIRerank)` | | `conf/llm_factories.json` | Add factory config with 8 chat, 2 embedding, 1 image2text, 2 TTS, 1 speech2text models | | `docs/guides/models/supported_models.mdx` | Add FuturMix to supported models table | ### Models included - **Chat**: claude-sonnet-4-20250514, claude-3.5-haiku, gpt-4o, gpt-4o-mini, gemini-2.5-flash, gemini-2.0-flash, deepseek-chat, deepseek-reasoner - **Embedding**: text-embedding-3-small, text-embedding-3-large - **Image2Text**: gpt-4o - **TTS**: tts-1, tts-1-hd - **Speech2Text**: whisper-1 ## Test plan - [ ] Verify FuturMix appears in the model provider list in RAGFlow UI - [ ] Configure FuturMix with API key and test chat completion - [ ] Test embedding model with document indexing - [ ] Test image2text with a sample image 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 --- conf/llm_factories.json | 117 +++++++++++++++++++++++- docs/guides/models/supported_models.mdx | 1 + rag/llm/__init__.py | 3 + rag/llm/chat_model.py | 10 ++ rag/llm/cv_model.py | 10 ++ rag/llm/embedding_model.py | 10 ++ rag/llm/rerank_model.py | 12 +++ rag/llm/sequence2txt_model.py | 11 +++ rag/llm/tts_model.py | 11 +++ 9 files changed, 184 insertions(+), 1 deletion(-) diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 7ac9808518..ee74bb8a50 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -6457,6 +6457,121 @@ } ] }, + { + "name": "FuturMix", + "logo": "", + "tags": "LLM,TEXT EMBEDDING,IMAGE2TEXT,SPEECH2TEXT,TTS,TEXT RE-RANK", + "status": "1", + "rank": "248", + "url": "https://futurmix.ai/v1", + "llm": [ + { + "llm_name": "claude-sonnet-4-20250514", + "tags": "LLM,CHAT,200k", + "max_tokens": 200000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "claude-3.5-haiku", + "tags": "LLM,CHAT,200k", + "max_tokens": 200000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "gpt-4o", + "tags": "LLM,CHAT,128k", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "gpt-4o-mini", + "tags": "LLM,CHAT,128k", + "max_tokens": 128000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "gemini-2.5-flash", + "tags": "LLM,CHAT,1M", + "max_tokens": 1000000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "gemini-2.0-flash", + "tags": "LLM,CHAT,1M", + "max_tokens": 1000000, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "deepseek-chat", + "tags": "LLM,CHAT,64k", + "max_tokens": 65536, + "model_type": "chat", + "is_tools": true + }, + { + "llm_name": "deepseek-reasoner", + "tags": "LLM,CHAT,64k", + "max_tokens": 65536, + "model_type": "chat", + "is_tools": false + }, + { + "llm_name": "gpt-4o", + "tags": "IMAGE2TEXT,CHAT,128k", + "max_tokens": 128000, + "model_type": "image2text", + "is_tools": true + }, + { + "llm_name": "text-embedding-3-small", + "tags": "TEXT EMBEDDING,8K", + "max_tokens": 8191, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "text-embedding-3-large", + "tags": "TEXT EMBEDDING,8K", + "max_tokens": 8191, + "model_type": "embedding", + "is_tools": false + }, + { + "llm_name": "tts-1", + "tags": "TTS", + "max_tokens": 4096, + "model_type": "tts", + "is_tools": false + }, + { + "llm_name": "tts-1-hd", + "tags": "TTS", + "max_tokens": 4096, + "model_type": "tts", + "is_tools": false + }, + { + "llm_name": "whisper-1", + "tags": "SPEECH2TEXT", + "max_tokens": 25000000, + "model_type": "speech2text", + "is_tools": false + }, + { + "llm_name": "jina-reranker-v2-base-multilingual", + "tags": "RE-RANK,8k", + "max_tokens": 8192, + "model_type": "rerank", + "is_tools": false + } + ] + }, { "name": "Astraflow-CN", "logo": "", @@ -6691,4 +6806,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/docs/guides/models/supported_models.mdx b/docs/guides/models/supported_models.mdx index 95290424d7..d3747cdadb 100644 --- a/docs/guides/models/supported_models.mdx +++ b/docs/guides/models/supported_models.mdx @@ -28,6 +28,7 @@ A complete list of models supported by RAGFlow, which will continue to expand. | Cohere | :heavy_check_mark: | :heavy_check_mark: | | | :heavy_check_mark: | :heavy_check_mark: | | | DeepSeek | :heavy_check_mark: | | | | | | | | Fish Audio | | | | :heavy_check_mark: | | | | +| FuturMix | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | | Gemini | :heavy_check_mark: | :heavy_check_mark: | | | :heavy_check_mark: | | | | Google Cloud | :heavy_check_mark: | | | | | | | | GPUStack | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | diff --git a/rag/llm/__init__.py b/rag/llm/__init__.py index aeb1748876..8d6db359ce 100644 --- a/rag/llm/__init__.py +++ b/rag/llm/__init__.py @@ -61,6 +61,7 @@ class SupportedLiteLLMProvider(StrEnum): Avian = "Avian" Astraflow = "Astraflow" Astraflow_CN = "Astraflow-CN" + FuturMix = "FuturMix" FACTORY_DEFAULT_BASE_URL = { @@ -91,6 +92,7 @@ FACTORY_DEFAULT_BASE_URL = { SupportedLiteLLMProvider.Avian: "https://api.avian.io/v1", SupportedLiteLLMProvider.Astraflow: "https://api-us-ca.umodelverse.ai/v1", SupportedLiteLLMProvider.Astraflow_CN: "https://api.modelverse.cn/v1", + SupportedLiteLLMProvider.FuturMix: "https://futurmix.ai/v1", } @@ -133,6 +135,7 @@ LITELLM_PROVIDER_PREFIX = { SupportedLiteLLMProvider.Avian: "openai/", SupportedLiteLLMProvider.Astraflow: "openai/", SupportedLiteLLMProvider.Astraflow_CN: "openai/", + SupportedLiteLLMProvider.FuturMix: "openai/", } ChatModel = globals().get("ChatModel", {}) diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py index 3aa13d03d8..d31587e33c 100644 --- a/rag/llm/chat_model.py +++ b/rag/llm/chat_model.py @@ -1227,6 +1227,16 @@ class AstraflowCNChat(Base): super().__init__(key, model_name, base_url, **kwargs) +class FuturMixChat(Base): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name, base_url="https://futurmix.ai/v1", **kwargs): + if not base_url: + base_url = "https://futurmix.ai/v1" + super().__init__(key, model_name, base_url, **kwargs) + logging.info("[FuturMix] Chat initialized with model %s", model_name) + + class LiteLLMBase(ABC): _FACTORY_NAME = [ "Tongyi-Qianwen", diff --git a/rag/llm/cv_model.py b/rag/llm/cv_model.py index ff868d6bdb..3d23c0a32e 100644 --- a/rag/llm/cv_model.py +++ b/rag/llm/cv_model.py @@ -1254,6 +1254,16 @@ class MoonshotCV(GptV4): super().__init__(key, model_name, lang=lang, base_url=base_url, **kwargs) +class FuturMixCV(GptV4): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name, lang="Chinese", base_url="https://futurmix.ai/v1", **kwargs): + if not base_url: + base_url = "https://futurmix.ai/v1" + super().__init__(key, model_name, lang=lang, base_url=base_url, **kwargs) + logging.info("[FuturMix] CV initialized with model %s", model_name) + + class RAGconCV(GptV4): """ RAGcon CV Provider - routes through LiteLLM proxy diff --git a/rag/llm/embedding_model.py b/rag/llm/embedding_model.py index 23b9105558..2d9f0b322c 100644 --- a/rag/llm/embedding_model.py +++ b/rag/llm/embedding_model.py @@ -179,6 +179,16 @@ class AstraflowCNEmbed(OpenAIEmbed): super().__init__(key, model_name, base_url) +class FuturMixEmbed(OpenAIEmbed): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name="text-embedding-3-small", base_url="https://futurmix.ai/v1"): + if not base_url: + base_url = "https://futurmix.ai/v1" + super().__init__(key, model_name, base_url) + logging.info("[FuturMix] Embedding initialized with model %s", model_name) + + class BaiChuanEmbed(OpenAIEmbed): _FACTORY_NAME = "BaiChuan" diff --git a/rag/llm/rerank_model.py b/rag/llm/rerank_model.py index a1ec3a17ea..ed569d6bdc 100644 --- a/rag/llm/rerank_model.py +++ b/rag/llm/rerank_model.py @@ -14,6 +14,7 @@ # limitations under the License. # import json +import logging from abc import ABC from urllib.parse import urljoin @@ -521,6 +522,17 @@ class JiekouAIRerank(JinaRerank): base_url = "https://api.jiekou.ai/openai/v1/rerank" super().__init__(key, model_name, base_url) + +class FuturMixRerank(OpenAI_APIRerank): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name, base_url="https://futurmix.ai/v1/rerank"): + if not base_url: + base_url = "https://futurmix.ai/v1/rerank" + super().__init__(key, model_name, base_url) + logging.info("[FuturMix] Rerank initialized with model %s", model_name) + + class RAGconRerank(Base): """ RAGcon Rerank Provider - routes through LiteLLM proxy diff --git a/rag/llm/sequence2txt_model.py b/rag/llm/sequence2txt_model.py index 5919f43467..563dd47fc1 100644 --- a/rag/llm/sequence2txt_model.py +++ b/rag/llm/sequence2txt_model.py @@ -20,6 +20,7 @@ import os import re from abc import ABC import tempfile +import logging import requests from openai import OpenAI @@ -68,6 +69,16 @@ class StepFunSeq2txt(GPTSeq2txt): super().__init__(key, model_name=model_name, base_url=base_url, **kwargs) +class FuturMixSeq2txt(GPTSeq2txt): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name="whisper-1", base_url="https://futurmix.ai/v1", **kwargs): + if not base_url: + base_url = "https://futurmix.ai/v1" + super().__init__(key, model_name=model_name, base_url=base_url, **kwargs) + logging.info("[FuturMix] Speech2Text initialized with model %s", model_name) + + class QWenSeq2txt(Base): _FACTORY_NAME = "Tongyi-Qianwen" diff --git a/rag/llm/tts_model.py b/rag/llm/tts_model.py index b39b6a8c7b..94a81ceba2 100644 --- a/rag/llm/tts_model.py +++ b/rag/llm/tts_model.py @@ -28,6 +28,7 @@ from abc import ABC from datetime import datetime from time import mktime from typing import Annotated, Literal +import logging from urllib.parse import urlencode from wsgiref.handlers import format_date_time @@ -450,6 +451,16 @@ class DeerAPITTS(OpenAITTS): super().__init__(key, model_name, base_url, **kwargs) +class FuturMixTTS(OpenAITTS): + _FACTORY_NAME = "FuturMix" + + def __init__(self, key, model_name, base_url="https://futurmix.ai/v1", **kwargs): + if not base_url: + base_url = "https://futurmix.ai/v1" + super().__init__(key, model_name, base_url, **kwargs) + logging.info("[FuturMix] TTS initialized with model %s", model_name) + + class StepFunTTS(OpenAITTS): _FACTORY_NAME = "StepFun" _SUPPORTED_RESPONSE_FORMATS = {"wav", "mp3", "flac", "opus", "pcm"}