mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-11 22:25:41 +08:00
feat: add Ragcon provider (#13425)
### What problem does this PR solve? This PR aims to extend the list of possible providers. Adds new Provider "RAGcon" within the Ollama Modal. It provides all model types except OCR via Openai-compatible endpoints. ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Co-authored-by: Jakob <16180662+hauberj@users.noreply.github.com>
This commit is contained in:
@@ -6267,6 +6267,14 @@
|
||||
"is_tools": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "RAGcon",
|
||||
"logo": "",
|
||||
"tags": "LLM,TEXT EMBEDDING,TTS,TEXT RE-RANK,SPEECH2TEXT,IMAGE2TEXT",
|
||||
"status": "1",
|
||||
"rank": "100",
|
||||
"llm": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1658,3 +1658,17 @@ class LiteLLMBase(ABC):
|
||||
completion_args["extra_headers"] = extra_headers
|
||||
return completion_args
|
||||
|
||||
class RAGconChat(Base):
|
||||
"""
|
||||
RAGcon Chat Provider - routes through LiteLLM proxy
|
||||
|
||||
All model types are handled through a unified LiteLLM endpoint.
|
||||
Default Base URL: https://connect.ragcon.com/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name, base_url=None, **kwargs):
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
super().__init__(key, model_name, base_url, **kwargs)
|
||||
|
||||
@@ -1252,3 +1252,26 @@ class MoonshotCV(GptV4):
|
||||
if not base_url:
|
||||
base_url = "https://api.moonshot.cn/v1"
|
||||
super().__init__(key, model_name, lang=lang, base_url=base_url, **kwargs)
|
||||
|
||||
|
||||
class RAGconCV(GptV4):
|
||||
"""
|
||||
RAGcon CV Provider - routes through LiteLLM proxy
|
||||
|
||||
Supports vision models through LiteLLM.
|
||||
Default Base URL: https://connect.ragcon.ai/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name, lang="Chinese", base_url="", **kwargs):
|
||||
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
# Initialize client
|
||||
self.client = OpenAI(api_key=key, base_url=base_url)
|
||||
self.async_client = AsyncOpenAI(api_key=key, base_url=base_url)
|
||||
self.model_name = model_name
|
||||
self.lang = lang
|
||||
|
||||
Base.__init__(self, **kwargs)
|
||||
@@ -1080,3 +1080,17 @@ class JiekouAIEmbed(OpenAIEmbed):
|
||||
if not base_url:
|
||||
base_url = "https://api.jiekou.ai/openai/v1/embeddings"
|
||||
super().__init__(key, model_name, base_url)
|
||||
|
||||
class RAGconEmbed(OpenAIEmbed):
|
||||
"""
|
||||
RAGcon Embedding Provider - routes through LiteLLM proxy
|
||||
|
||||
Default Base URL: https://connect.ragcon.ai/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name="text-embedding-3-small", base_url=None):
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
super().__init__(key, model_name, base_url)
|
||||
@@ -506,3 +506,47 @@ class JiekouAIRerank(JinaRerank):
|
||||
if not base_url:
|
||||
base_url = "https://api.jiekou.ai/openai/v1/rerank"
|
||||
super().__init__(key, model_name, base_url)
|
||||
|
||||
class RAGconRerank(Base):
|
||||
"""
|
||||
RAGcon Rerank Provider - routes through LiteLLM proxy
|
||||
|
||||
Assumes LiteLLM proxy supports /rerank endpoint.
|
||||
Default Base URL: https://connect.ragcon.ai/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name, base_url=None, **kwargs):
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
self._api_key = key
|
||||
self._base_url = base_url
|
||||
|
||||
self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"}
|
||||
self.model_name = model_name
|
||||
|
||||
|
||||
def similarity(self, query: str, texts: list):
|
||||
# noway to config Ragflow , use fix setting
|
||||
texts = [truncate(t, 500) for t in texts]
|
||||
data = {
|
||||
"model": self.model_name,
|
||||
"query": query,
|
||||
"documents": texts,
|
||||
"top_n": len(texts),
|
||||
}
|
||||
token_count = 0
|
||||
for t in texts:
|
||||
token_count += num_tokens_from_string(t)
|
||||
res = requests.post(self._base_url + "/rerank", headers=self.headers, json=data).json()
|
||||
rank = np.zeros(len(texts), dtype=float)
|
||||
try:
|
||||
for d in res["results"]:
|
||||
rank[d["index"]] = d["relevance_score"]
|
||||
except Exception as _e:
|
||||
log_exception(_e, res)
|
||||
|
||||
rank = Base._normalize_rank(rank)
|
||||
|
||||
return rank, token_count
|
||||
@@ -376,3 +376,48 @@ class ZhipuSeq2txt(Base):
|
||||
return f"**ERROR**: code: {error['code']}, message: {error['message']}", 0
|
||||
except Exception as e:
|
||||
return "**ERROR**: " + str(e), 0
|
||||
|
||||
|
||||
class RAGconSeq2txt(Base):
|
||||
"""
|
||||
RAGcon Sequence2Text Provider - routes through LiteLLM proxy
|
||||
|
||||
Speech-to-text models routed through LiteLLM.
|
||||
Default Base URL: https://connect.ragcon.com/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name, base_url=None, lang="English", **kwargs):
|
||||
# Use provided base_url or fallback to default
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
self.base_url = base_url
|
||||
self.model_name = model_name
|
||||
self.key = key
|
||||
self.lang = lang
|
||||
|
||||
self.client = OpenAI(api_key=key, base_url=self.base_url)
|
||||
|
||||
def transcription(self, audio_path, **kwargs):
|
||||
"""
|
||||
Transcribe audio file using RAGcon's OpenAI-compatible API.
|
||||
Uses Whisper's automatic language detection for German and English audio.
|
||||
|
||||
Args:
|
||||
audio_path: Path to the audio file
|
||||
**kwargs: Additional parameters (currently unused but maintained for compatibility)
|
||||
|
||||
Returns:
|
||||
tuple: (transcribed_text, token_count)
|
||||
"""
|
||||
with open(audio_path, "rb") as audio_file:
|
||||
# Call RAGcon API - Whisper will auto-detect language
|
||||
transcription = self.client.audio.transcriptions.create(
|
||||
model=self.model_name,
|
||||
file=audio_file
|
||||
)
|
||||
|
||||
# Return text and token count
|
||||
text = transcription.text.strip()
|
||||
return text, num_tokens_from_string(text)
|
||||
|
||||
@@ -482,3 +482,51 @@ class StepFunTTS(OpenAITTS):
|
||||
yield chunk
|
||||
|
||||
yield num_tokens_from_string(text)
|
||||
|
||||
|
||||
class RAGconTTS(Base):
|
||||
"""
|
||||
RAGcon TTS Provider - routes through LiteLLM proxy
|
||||
|
||||
Text-to-speech models routed through LiteLLM.
|
||||
Default Base URL: https://connect.ragcon.ai/v1
|
||||
"""
|
||||
_FACTORY_NAME = "RAGcon"
|
||||
|
||||
def __init__(self, key, model_name, base_url=None, **kwargs):
|
||||
if not base_url:
|
||||
base_url = "https://connect.ragcon.com/v1"
|
||||
|
||||
self.base_url = base_url
|
||||
self.api_key = key
|
||||
self.model_name = model_name
|
||||
self.headers = {
|
||||
"accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.api_key}"
|
||||
}
|
||||
|
||||
def tts(self, text, voice="English Female", stream=True):
|
||||
"""
|
||||
Uses LiteLLM's /v1/audio/speech endpoint
|
||||
"""
|
||||
|
||||
payload = {
|
||||
"model": self.model_name,
|
||||
"input": text,
|
||||
"voice": voice
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
f"{self.base_url}/audio/speech",
|
||||
headers=self.headers,
|
||||
json=payload,
|
||||
stream=stream
|
||||
)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"**Error**: {response.status_code}, {response.text}")
|
||||
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
yield chunk
|
||||
|
||||
24
web/src/assets/svg/llm/ragcon.svg
Normal file
24
web/src/assets/svg/llm/ragcon.svg
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="75" height="75" version="1.1" viewBox="0 0 75 75">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #63c3d1;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #194870;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<!-- Generator: Adobe Illustrator 28.7.7, SVG Export Plug-In . SVG Version: 1.2.0 Build 194) -->
|
||||
<g>
|
||||
<g id="Ebene_1">
|
||||
<g>
|
||||
<path class="cls-2" d="M8.889.148C4.043.148.115,4.078.115,8.923s3.928,8.774,8.774,8.774,8.774-3.929,8.774-8.774S13.735.148,8.889.148ZM8.889,13.179c-2.351,0-4.256-1.905-4.256-4.256s1.905-4.256,4.256-4.256,4.256,1.905,4.256,4.256-1.905,4.256-4.256,4.256Z"/>
|
||||
<path class="cls-2" d="M25.866,33.473c-4.846,0-8.774,3.93-8.774,8.775s3.928,8.774,8.774,8.774,8.774-3.929,8.774-8.774-3.928-8.775-8.774-8.775ZM25.866,46.503c-2.351,0-4.256-1.905-4.256-4.256s1.905-4.256,4.256-4.256,4.256,1.905,4.256,4.256-1.905,4.256-4.256,4.256Z"/>
|
||||
</g>
|
||||
<path class="cls-1" d="M73.319,63.835l-10.95-21.456c4.362-4.335,7.063-10.261,7.063-16.796,0-13.242-11.058-24.015-24.651-24.015l-31.11.003c2.402,1.566,3.992,4.271,3.992,7.352s-1.591,5.786-3.994,7.351l31.112-.003c5.485,0,9.947,4.177,9.947,9.312s-4.462,9.311-9.948,9.311l-13.302.002c-.258,0-.513.014-.764.04,2.365,1.572,3.926,4.259,3.926,7.311s-1.561,5.74-3.927,7.311c.253.026.509.04.768.04h13.301c1.555,0,3.07-.159,4.546-.428l10.895,21.347c1.3,2.547,3.879,4.012,6.554,4.012,1.124,0,2.265-.259,3.336-.806,3.616-1.845,5.051-6.272,3.206-9.889Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -85,6 +85,7 @@ const svgIcons = [
|
||||
LLMFactory.N1n,
|
||||
// LLMFactory.DeerAPI,
|
||||
LLMFactory.Avian,
|
||||
LLMFactory.RAGcon,
|
||||
];
|
||||
|
||||
export const LlmIcon = ({
|
||||
|
||||
@@ -64,6 +64,7 @@ export enum LLMFactory {
|
||||
PaddleOCR = 'PaddleOCR',
|
||||
N1n = 'n1n',
|
||||
Avian = 'Avian',
|
||||
RAGcon = 'RAGcon',
|
||||
}
|
||||
|
||||
// Please lowercase the file name
|
||||
@@ -133,6 +134,7 @@ export const IconMap = {
|
||||
[LLMFactory.PaddleOCR]: 'paddleocr',
|
||||
[LLMFactory.N1n]: 'n1n',
|
||||
[LLMFactory.Avian]: 'avian',
|
||||
[LLMFactory.RAGcon]: 'ragcon',
|
||||
};
|
||||
|
||||
export const APIMapUrl = {
|
||||
|
||||
@@ -39,6 +39,7 @@ export const LocalLlmFactories = [
|
||||
LLMFactory.GPUStack,
|
||||
LLMFactory.ModelScope,
|
||||
LLMFactory.VLLM,
|
||||
LLMFactory.RAGcon,
|
||||
];
|
||||
|
||||
export enum TenantRole {
|
||||
|
||||
@@ -27,6 +27,7 @@ const llmFactoryToUrlMap: Partial<Record<LLMFactory, string>> = {
|
||||
[LLMFactory.LMStudio]: 'https://lmstudio.ai/docs/basics',
|
||||
[LLMFactory.OpenAiAPICompatible]:
|
||||
'https://platform.openai.com/docs/models/gpt-4',
|
||||
[LLMFactory.RAGcon]: 'https://www.ragcon.ai/erste-schritte-mit-ragflow/',
|
||||
[LLMFactory.TogetherAI]: 'https://docs.together.ai/docs/deployment-options',
|
||||
[LLMFactory.Replicate]: 'https://replicate.com/docs/topics/deployments',
|
||||
[LLMFactory.OpenRouter]: 'https://openrouter.ai/docs',
|
||||
@@ -81,6 +82,14 @@ const OllamaModal = ({
|
||||
'speech2text',
|
||||
'tts',
|
||||
]),
|
||||
[LLMFactory.RAGcon]: buildModelTypeOptions([
|
||||
'chat',
|
||||
'embedding',
|
||||
'rerank',
|
||||
'image2text',
|
||||
'speech2text',
|
||||
'tts',
|
||||
]),
|
||||
[LLMFactory.ModelScope]: buildModelTypeOptions(['chat']),
|
||||
[LLMFactory.GPUStack]: buildModelTypeOptions([
|
||||
'chat',
|
||||
|
||||
Reference in New Issue
Block a user