diff --git a/api/apps/services/provider_api_service.py b/api/apps/services/provider_api_service.py index 8896558cb5..2cb926425e 100644 --- a/api/apps/services/provider_api_service.py +++ b/api/apps/services/provider_api_service.py @@ -184,7 +184,14 @@ async def list_provider_models(provider_name: str, api_key: str = None, base_url "name": llm["name"], "max_tokens": llm["max_tokens"], "model_types": [llm["model_type"]], - "features": None + "features": ( + llm.get("features") + if llm.get("features") is not None + else ( + (["is_tools"] if llm.get("is_tools") else []) + + (["thinking"] if llm.get("thinking") else []) + ) + ) } for llm in factory_info[0]["llm"]] model_base_url = base_url or factory_info[0].get("url", "") diff --git a/conf/llm_factories.json b/conf/llm_factories.json index 83ab0480ab..1669ec52e9 100644 --- a/conf/llm_factories.json +++ b/conf/llm_factories.json @@ -3916,7 +3916,56 @@ "logo": "", "tags": "LLM,TTS", "status": "1", - "llm": [] + "llm": [ + { + "llm_name": "Spark-Max", + "tags": "LLM,CHAT,8K", + "max_tokens": 8192, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + }, + { + "llm_name": "Spark-Max-32K", + "tags": "LLM,CHAT,32K", + "max_tokens": 32768, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + }, + { + "llm_name": "Spark-Lite", + "tags": "LLM,CHAT,8K", + "max_tokens": 8192, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + }, + { + "llm_name": "Spark-Pro", + "tags": "LLM,CHAT,8K", + "max_tokens": 8192, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + }, + { + "llm_name": "Spark-Pro-128K", + "tags": "LLM,CHAT,128K", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + }, + { + "llm_name": "Spark-4.0-Ultra", + "tags": "LLM,CHAT,128K", + "max_tokens": 131072, + "model_type": "chat", + "is_tools": true, + "features": ["is_tools", "thinking"] + } + ] }, { "name": "BaiduYiyan", diff --git a/rag/llm/model_meta.py b/rag/llm/model_meta.py index 41399456cb..78f37d2ab4 100644 --- a/rag/llm/model_meta.py +++ b/rag/llm/model_meta.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json import aiohttp from abc import ABC @@ -110,3 +111,107 @@ class Ollama(Base): "max_tokens": model_info["model_info"].get(max_tokens_key, 8192) }) return res + + +class FishAudio(Base): + _FACTORY_NAME = "Fish Audio" + + def _get_access_token(self): + api_key = self._get_api_key() + if not api_key: + return "" + try: + payload = json.loads(api_key) + except Exception: + return api_key + if isinstance(payload, dict): + return payload.get("fish_audio_ak") or payload.get("access_token") or payload.get("api_key") or api_key + return api_key + + def _get_model_list_url(self): + if not self.base_url: + return "https://api.fish.audio/model" + base_url = self.base_url.rstrip("/") + if "/v1/" in base_url: + return base_url.split("/v1")[0].rstrip("/") + "/model" + if base_url.endswith("/v1"): + return base_url[:-3] + "/model" + return base_url + "/model" + + async def get_model_list(self): + url = self._get_model_list_url() + access_token = self._get_access_token() + if not url or not access_token: + return [] + + async with aiohttp.ClientSession() as session: + async with session.get(url, headers={"Authorization": f"Bearer {access_token}"}) as resp: + if resp.status != 200: + return [] + raw_model_list = await resp.json() + if not isinstance(raw_model_list, dict): + return [] + models = raw_model_list.get("items") or [] + if not isinstance(models, list): + return [] + + model_list = [] + for model in models: + if not isinstance(model, dict): + continue + model_name = model.get("title") or model.get("_id") + if not model_name: + continue + model_list.append({ + "name": model_name, + "model_types": [LLMType.TTS.value], + "features": [], + "max_tokens": 8192, + }) + return model_list + +class MinerU(Base): + _FACTORY_NAME = "MinerU" + + def _get_access_token(self): + api_key = self._get_api_key() + if not api_key: + return "" + try: + payload = json.loads(api_key) + except Exception: + return api_key + if isinstance(payload, dict): + return payload.get("access_token") or payload.get("api_key") or api_key + return api_key + + async def get_model_list(self): + url = self._get_model_list_url() + access_token = self._get_access_token() + if not url or not access_token: + return [] + + async with aiohttp.ClientSession() as session: + async with session.get(url, headers={"Authorization": f"Bearer {access_token}"}) as resp: + if resp.status != 200: + return [] + raw_model_list = await resp.json() + if isinstance(raw_model_list, dict): + raw_model_list = raw_model_list.get("data") or raw_model_list.get("models") or raw_model_list.get("items") or [] + if not isinstance(raw_model_list, list): + return [] + + model_list = [] + for model in raw_model_list: + if not isinstance(model, dict): + continue + model_name = model.get("title") or model.get("name") or model.get("id") or model.get("_id") + if not model_name: + continue + model_list.append({ + "name": model_name, + "model_types": [LLMType.OCR.value], + "features": [], + "max_tokens": model.get("max_tokens", 8192), + }) + return model_list