add supprot for OpenAI-API-Compatible llm (#1787)
Browse files### What problem does this PR solve?
#1771 add supprot for OpenAI-API-Compatible
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Zhedong Cen <[email protected]>
- api/apps/llm_app.py +6 -3
- conf/llm_factories.json +7 -0
- rag/llm/__init__.py +8 -4
- rag/llm/chat_model.py +12 -2
- rag/llm/cv_model.py +11 -0
- rag/llm/embedding_model.py +10 -0
- rag/llm/rerank_model.py +8 -0
- web/src/assets/svg/llm/openai-api.svg +1 -0
- web/src/interfaces/request/llm.ts +1 -0
- web/src/pages/user-setting/constants.tsx +1 -1
- web/src/pages/user-setting/setting-model/constant.ts +2 -1
- web/src/pages/user-setting/setting-model/ollama-modal/index.tsx +7 -0
api/apps/llm_app.py
CHANGED
@@ -129,6 +129,9 @@ def add_llm():
|
|
129 |
elif factory == "LocalAI":
|
130 |
llm_name = req["llm_name"]+"___LocalAI"
|
131 |
api_key = "xxxxxxxxxxxxxxx"
|
|
|
|
|
|
|
132 |
else:
|
133 |
llm_name = req["llm_name"]
|
134 |
api_key = "xxxxxxxxxxxxxxx"
|
@@ -145,7 +148,7 @@ def add_llm():
|
|
145 |
msg = ""
|
146 |
if llm["model_type"] == LLMType.EMBEDDING.value:
|
147 |
mdl = EmbeddingModel[factory](
|
148 |
-
key=llm['api_key'] if factory in ["VolcEngine", "Bedrock"] else None,
|
149 |
model_name=llm["llm_name"],
|
150 |
base_url=llm["api_base"])
|
151 |
try:
|
@@ -156,7 +159,7 @@ def add_llm():
|
|
156 |
msg += f"\nFail to access embedding model({llm['llm_name']})." + str(e)
|
157 |
elif llm["model_type"] == LLMType.CHAT.value:
|
158 |
mdl = ChatModel[factory](
|
159 |
-
key=llm['api_key'] if factory in ["VolcEngine", "Bedrock"] else None,
|
160 |
model_name=llm["llm_name"],
|
161 |
base_url=llm["api_base"]
|
162 |
)
|
@@ -181,7 +184,7 @@ def add_llm():
|
|
181 |
e)
|
182 |
elif llm["model_type"] == LLMType.IMAGE2TEXT.value:
|
183 |
mdl = CvModel[factory](
|
184 |
-
key=None, model_name=llm["llm_name"], base_url=llm["api_base"]
|
185 |
)
|
186 |
try:
|
187 |
img_url = (
|
|
|
129 |
elif factory == "LocalAI":
|
130 |
llm_name = req["llm_name"]+"___LocalAI"
|
131 |
api_key = "xxxxxxxxxxxxxxx"
|
132 |
+
elif factory == "OpenAI-API-Compatible":
|
133 |
+
llm_name = req["llm_name"]+"___OpenAI-API"
|
134 |
+
api_key = req["api_key"]
|
135 |
else:
|
136 |
llm_name = req["llm_name"]
|
137 |
api_key = "xxxxxxxxxxxxxxx"
|
|
|
148 |
msg = ""
|
149 |
if llm["model_type"] == LLMType.EMBEDDING.value:
|
150 |
mdl = EmbeddingModel[factory](
|
151 |
+
key=llm['api_key'] if factory in ["VolcEngine", "Bedrock","OpenAI-API-Compatible"] else None,
|
152 |
model_name=llm["llm_name"],
|
153 |
base_url=llm["api_base"])
|
154 |
try:
|
|
|
159 |
msg += f"\nFail to access embedding model({llm['llm_name']})." + str(e)
|
160 |
elif llm["model_type"] == LLMType.CHAT.value:
|
161 |
mdl = ChatModel[factory](
|
162 |
+
key=llm['api_key'] if factory in ["VolcEngine", "Bedrock","OpenAI-API-Compatible"] else None,
|
163 |
model_name=llm["llm_name"],
|
164 |
base_url=llm["api_base"]
|
165 |
)
|
|
|
184 |
e)
|
185 |
elif llm["model_type"] == LLMType.IMAGE2TEXT.value:
|
186 |
mdl = CvModel[factory](
|
187 |
+
key=llm["api_key"] if factory in ["OpenAI-API-Compatible"] else None, model_name=llm["llm_name"], base_url=llm["api_base"]
|
188 |
)
|
189 |
try:
|
190 |
img_url = (
|
conf/llm_factories.json
CHANGED
@@ -158,6 +158,13 @@
|
|
158 |
"status": "1",
|
159 |
"llm": []
|
160 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
{
|
162 |
"name": "Moonshot",
|
163 |
"logo": "",
|
|
|
158 |
"status": "1",
|
159 |
"llm": []
|
160 |
},
|
161 |
+
{
|
162 |
+
"name": "OpenAI-API-Compatible",
|
163 |
+
"logo": "",
|
164 |
+
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
165 |
+
"status": "1",
|
166 |
+
"llm": []
|
167 |
+
},
|
168 |
{
|
169 |
"name": "Moonshot",
|
170 |
"logo": "",
|
rag/llm/__init__.py
CHANGED
@@ -36,7 +36,8 @@ EmbeddingModel = {
|
|
36 |
"Bedrock": BedrockEmbed,
|
37 |
"Gemini": GeminiEmbed,
|
38 |
"NVIDIA": NvidiaEmbed,
|
39 |
-
"LM-Studio": LmStudioEmbed
|
|
|
40 |
}
|
41 |
|
42 |
|
@@ -53,7 +54,8 @@ CvModel = {
|
|
53 |
"LocalAI": LocalAICV,
|
54 |
"NVIDIA": NvidiaCV,
|
55 |
"LM-Studio": LmStudioCV,
|
56 |
-
"StepFun":StepFunCV
|
|
|
57 |
}
|
58 |
|
59 |
|
@@ -78,7 +80,8 @@ ChatModel = {
|
|
78 |
"OpenRouter": OpenRouterChat,
|
79 |
"StepFun": StepFunChat,
|
80 |
"NVIDIA": NvidiaChat,
|
81 |
-
"LM-Studio": LmStudioChat
|
|
|
82 |
}
|
83 |
|
84 |
|
@@ -88,7 +91,8 @@ RerankModel = {
|
|
88 |
"Youdao": YoudaoRerank,
|
89 |
"Xinference": XInferenceRerank,
|
90 |
"NVIDIA": NvidiaRerank,
|
91 |
-
"LM-Studio": LmStudioRerank
|
|
|
92 |
}
|
93 |
|
94 |
|
|
|
36 |
"Bedrock": BedrockEmbed,
|
37 |
"Gemini": GeminiEmbed,
|
38 |
"NVIDIA": NvidiaEmbed,
|
39 |
+
"LM-Studio": LmStudioEmbed,
|
40 |
+
"OpenAI-API-Compatible": OpenAI_APIEmbed
|
41 |
}
|
42 |
|
43 |
|
|
|
54 |
"LocalAI": LocalAICV,
|
55 |
"NVIDIA": NvidiaCV,
|
56 |
"LM-Studio": LmStudioCV,
|
57 |
+
"StepFun":StepFunCV,
|
58 |
+
"OpenAI-API-Compatible": OpenAI_APICV
|
59 |
}
|
60 |
|
61 |
|
|
|
80 |
"OpenRouter": OpenRouterChat,
|
81 |
"StepFun": StepFunChat,
|
82 |
"NVIDIA": NvidiaChat,
|
83 |
+
"LM-Studio": LmStudioChat,
|
84 |
+
"OpenAI-API-Compatible": OpenAI_APIChat
|
85 |
}
|
86 |
|
87 |
|
|
|
91 |
"Youdao": YoudaoRerank,
|
92 |
"Xinference": XInferenceRerank,
|
93 |
"NVIDIA": NvidiaRerank,
|
94 |
+
"LM-Studio": LmStudioRerank,
|
95 |
+
"OpenAI-API-Compatible": OpenAI_APIRerank
|
96 |
}
|
97 |
|
98 |
|
rag/llm/chat_model.py
CHANGED
@@ -887,6 +887,16 @@ class LmStudioChat(Base):
|
|
887 |
if not base_url:
|
888 |
raise ValueError("Local llm url cannot be None")
|
889 |
if base_url.split("/")[-1] != "v1":
|
890 |
-
|
891 |
-
self.client = OpenAI(api_key="lm-studio", base_url=
|
892 |
self.model_name = model_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
887 |
if not base_url:
|
888 |
raise ValueError("Local llm url cannot be None")
|
889 |
if base_url.split("/")[-1] != "v1":
|
890 |
+
base_url = os.path.join(base_url, "v1")
|
891 |
+
self.client = OpenAI(api_key="lm-studio", base_url=base_url)
|
892 |
self.model_name = model_name
|
893 |
+
|
894 |
+
|
895 |
+
class OpenAI_APIChat(Base):
|
896 |
+
def __init__(self, key, model_name, base_url):
|
897 |
+
if not base_url:
|
898 |
+
raise ValueError("url cannot be None")
|
899 |
+
if base_url.split("/")[-1] != "v1":
|
900 |
+
base_url = os.path.join(base_url, "v1")
|
901 |
+
model_name = model_name.split("___")[0]
|
902 |
+
super().__init__(key, model_name, base_url)
|
rag/llm/cv_model.py
CHANGED
@@ -638,3 +638,14 @@ class LmStudioCV(GptV4):
|
|
638 |
self.client = OpenAI(api_key="lm-studio", base_url=base_url)
|
639 |
self.model_name = model_name
|
640 |
self.lang = lang
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
638 |
self.client = OpenAI(api_key="lm-studio", base_url=base_url)
|
639 |
self.model_name = model_name
|
640 |
self.lang = lang
|
641 |
+
|
642 |
+
|
643 |
+
class OpenAI_APICV(GptV4):
|
644 |
+
def __init__(self, key, model_name, base_url, lang="Chinese"):
|
645 |
+
if not base_url:
|
646 |
+
raise ValueError("url cannot be None")
|
647 |
+
if base_url.split("/")[-1] != "v1":
|
648 |
+
base_url = os.path.join(base_url, "v1")
|
649 |
+
self.client = OpenAI(api_key=key, base_url=base_url)
|
650 |
+
self.model_name = model_name.split("___")[0]
|
651 |
+
self.lang = lang
|
rag/llm/embedding_model.py
CHANGED
@@ -513,3 +513,13 @@ class LmStudioEmbed(LocalAIEmbed):
|
|
513 |
self.base_url = os.path.join(base_url, "v1")
|
514 |
self.client = OpenAI(api_key="lm-studio", base_url=self.base_url)
|
515 |
self.model_name = model_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
self.base_url = os.path.join(base_url, "v1")
|
514 |
self.client = OpenAI(api_key="lm-studio", base_url=self.base_url)
|
515 |
self.model_name = model_name
|
516 |
+
|
517 |
+
|
518 |
+
class OpenAI_APIEmbed(OpenAIEmbed):
|
519 |
+
def __init__(self, key, model_name, base_url):
|
520 |
+
if not base_url:
|
521 |
+
raise ValueError("url cannot be None")
|
522 |
+
if base_url.split("/")[-1] != "v1":
|
523 |
+
self.base_url = os.path.join(base_url, "v1")
|
524 |
+
self.client = OpenAI(api_key=key, base_url=base_url)
|
525 |
+
self.model_name = model_name.split("___")[0]
|
rag/llm/rerank_model.py
CHANGED
@@ -212,3 +212,11 @@ class LmStudioRerank(Base):
|
|
212 |
|
213 |
def similarity(self, query: str, texts: list):
|
214 |
raise NotImplementedError("The LmStudioRerank has not been implement")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
def similarity(self, query: str, texts: list):
|
214 |
raise NotImplementedError("The LmStudioRerank has not been implement")
|
215 |
+
|
216 |
+
|
217 |
+
class OpenAI_APIRerank(Base):
|
218 |
+
def __init__(self, key, model_name, base_url):
|
219 |
+
pass
|
220 |
+
|
221 |
+
def similarity(self, query: str, texts: list):
|
222 |
+
raise NotImplementedError("The api has not been implement")
|
web/src/assets/svg/llm/openai-api.svg
ADDED
|
web/src/interfaces/request/llm.ts
CHANGED
@@ -3,6 +3,7 @@ export interface IAddLlmRequestBody {
|
|
3 |
llm_name: string;
|
4 |
model_type: string;
|
5 |
api_base?: string; // chat|embedding|speech2text|image2text
|
|
|
6 |
}
|
7 |
|
8 |
export interface IDeleteLlmRequestBody {
|
|
|
3 |
llm_name: string;
|
4 |
model_type: string;
|
5 |
api_base?: string; // chat|embedding|speech2text|image2text
|
6 |
+
api_key: string;
|
7 |
}
|
8 |
|
9 |
export interface IDeleteLlmRequestBody {
|
web/src/pages/user-setting/constants.tsx
CHANGED
@@ -17,4 +17,4 @@ export const UserSettingIconMap = {
|
|
17 |
|
18 |
export * from '@/constants/setting';
|
19 |
|
20 |
-
export const LocalLlmFactories = ['Ollama', 'Xinference','LocalAI','LM-Studio'];
|
|
|
17 |
|
18 |
export * from '@/constants/setting';
|
19 |
|
20 |
+
export const LocalLlmFactories = ['Ollama', 'Xinference','LocalAI','LM-Studio',"OpenAI-API-Compatible"];
|
web/src/pages/user-setting/setting-model/constant.ts
CHANGED
@@ -21,7 +21,8 @@ export const IconMap = {
|
|
21 |
LocalAI: 'local-ai',
|
22 |
StepFun: 'stepfun',
|
23 |
NVIDIA:'nvidia',
|
24 |
-
'LM-Studio':'lm-studio'
|
|
|
25 |
};
|
26 |
|
27 |
export const BedrockRegionList = [
|
|
|
21 |
LocalAI: 'local-ai',
|
22 |
StepFun: 'stepfun',
|
23 |
NVIDIA:'nvidia',
|
24 |
+
'LM-Studio':'lm-studio',
|
25 |
+
'OpenAI-API-Compatible':'openai-api'
|
26 |
};
|
27 |
|
28 |
export const BedrockRegionList = [
|
web/src/pages/user-setting/setting-model/ollama-modal/index.tsx
CHANGED
@@ -92,6 +92,13 @@ const OllamaModal = ({
|
|
92 |
>
|
93 |
<Input placeholder={t('baseUrlNameMessage')} />
|
94 |
</Form.Item>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
<Form.Item noStyle dependencies={['model_type']}>
|
96 |
{({ getFieldValue }) =>
|
97 |
getFieldValue('model_type') === 'chat' && (
|
|
|
92 |
>
|
93 |
<Input placeholder={t('baseUrlNameMessage')} />
|
94 |
</Form.Item>
|
95 |
+
<Form.Item<FieldType>
|
96 |
+
label={t('apiKey')}
|
97 |
+
name="api_key"
|
98 |
+
rules={[{ required: false, message: t('apiKeyMessage') }]}
|
99 |
+
>
|
100 |
+
<Input placeholder={t('apiKeyMessage')} />
|
101 |
+
</Form.Item>
|
102 |
<Form.Item noStyle dependencies={['model_type']}>
|
103 |
{({ getFieldValue }) =>
|
104 |
getFieldValue('model_type') === 'chat' && (
|