Skip to content

Commit 4ab20cd

Browse files
authored
Add #18
1 parent db8d10b commit 4ab20cd

2 files changed

Lines changed: 70 additions & 24 deletions

File tree

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Pipelines are processing functions that extend Open WebUI with **custom AI model
7878
### **1. [Azure AI Foundry Pipeline](./pipelines/azure/azure_ai_foundry.py)**
7979

8080
- Enables interaction with **Azure OpenAI** and other **Azure AI** models.
81-
- Supports dynamic model selection via `x-ms-model-mesh-model-name` headers.
81+
- Supports multiple Azure AI models selection via the `AZURE_AI_MODEL` environment variable (e.g. `gpt-4o, gpt-4o-mini`).
8282
- Filters valid parameters to ensure clean requests.
8383
- Handles both streaming and non-streaming responses.
8484
- Provides configurable error handling and timeouts.
@@ -87,6 +87,7 @@ Pipelines are processing functions that extend Open WebUI with **custom AI model
8787

8888
🔗 [Azure AI Pipeline in Open WebUI](https://openwebui.com/f/owndev/azure_ai/)
8989

90+
9091
### **2. [N8N Pipeline](./pipelines/n8n/n8n.py)**
9192

9293
- Integrates **Open WebUI** with **N8N**, an automation and workflow platform.
@@ -100,16 +101,14 @@ Pipelines are processing functions that extend Open WebUI with **custom AI model
100101

101102
🔗 [Learn More About N8N](https://n8n.io/)
102103

104+
103105
### **3. [Infomaniak](./pipelines/infomaniak/infomaniak.py)**
104106

105107
- Integrates **Open WebUI** with **Infomaniak**, a Swiss web hosting and cloud services provider.
106108
- Sends messages from Open WebUI to an **Infomaniak AI Tool**.
107109
- Supports encryption of sensitive information like API keys.
108110

109-
> **Important**: The function ID in Open WebUI must not contain the name `infomaniak`. Because of a [bug](https://github.com/open-webui/open-webui/discussions/10914) in Open WebUI, the function will not work if the id contains `infomaniak`.
110-
111-
112-
🔗 [Infomaniak Pipeline in Open WebUI](https://openwebui.com/f/owndev/im_ai_tools/)
111+
🔗 [Infomaniak Pipeline in Open WebUI](https://openwebui.com/f/owndev/infomaniak_ai_tools)
113112

114113
🔗 [Learn More About Infomaniak](https://www.infomaniak.com/en/hosting/ai-tools)
115114

@@ -135,17 +134,16 @@ Filters allow for **preprocessing and postprocessing** of data within Open WebUI
135134
The repository includes functions specifically designed for **Azure AI**, supporting both **Azure OpenAI** models and general **Azure AI** services.
136135

137136
### Features:
138-
- **Azure OpenAI API Support**: Access models like **GPT-4, GPT-3.5**, and **other fine-tuned AI models** via Azure.
137+
- **Azure OpenAI API Support**: Access models like **GPT-4o, o3**, and **other fine-tuned AI models** via Azure.
139138
- **Azure AI Model Deployment**: Connect to **custom models** hosted on Azure AI.
140-
- **Dynamic Model Selection**: Choose models via the `x-ms-model-mesh-model-name` header or environment variables.
141139
- **Secure API Requests**: Supports API key authentication and environment variable configurations.
142140

143141
### Environment Variables:
144142
For Azure AI-based functions, set the following:
145143
```bash
146144
AZURE_AI_API_KEY="your-api-key"
147145
AZURE_AI_ENDPOINT="https://your-service.openai.azure.com/chat/completions?api-version=2024-05-01-preview"
148-
AZURE_AI_MODEL="gpt-4o" # Optional model name, only necessary if not Azure OpenAI or if model name not in URL (e.g. "https://<your-endpoint>/openai/deployments/<model-name>/chat/completions").
146+
AZURE_AI_MODEL="gpt-4o, gpt-4o-mini" # Optional model name, only necessary if not Azure OpenAI or if model name not in URL (e.g. "https://<your-endpoint>/openai/deployments/<model-name>/chat/completions").
149147
```
150148

151149
---

pipelines/azure/azure_ai_foundry.py

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,11 @@ class Valves(BaseModel):
151151
)
152152

153153
# Optional model name, only necessary if not Azure OpenAI or if model name not in URL (e.g. "https://<your-endpoint>/openai/deployments/<model-name>/chat/completions")
154+
# Multiple models can be specified as a semicolon-separated list (e.g. "gpt-4o;gpt-4o-mini")
155+
# or a comma-separated list (e.g. "gpt-4o,gpt-4o-mini").
154156
AZURE_AI_MODEL: str = Field(
155157
default=os.getenv("AZURE_AI_MODEL", ""),
156-
description="Optional model name for Azure AI",
158+
description="Optional model names for Azure AI (e.g. gpt-4o, gpt-4o-mini)",
157159
)
158160

159161
# Switch for sending model name in request body
@@ -192,10 +194,13 @@ def validate_environment(self) -> None:
192194
if not self.valves.AZURE_AI_ENDPOINT:
193195
raise ValueError("AZURE_AI_ENDPOINT is not set!")
194196

195-
def get_headers(self) -> Dict[str, str]:
197+
def get_headers(self, model_name: str = None) -> Dict[str, str]:
196198
"""
197199
Constructs the headers for the API request, including the model name if defined.
198200
201+
Args:
202+
model_name: Optional model name to use instead of the default one
203+
199204
Returns:
200205
Dictionary containing the required headers for the API request.
201206
"""
@@ -209,10 +214,14 @@ def get_headers(self) -> Dict[str, str]:
209214
else:
210215
headers = {"api-key": api_key, "Content-Type": "application/json"}
211216

212-
# If the valve indicates that the model name should be in the body,
213-
# add it to the filtered body.
214-
if self.valves.AZURE_AI_MODEL and not self.valves.AZURE_AI_MODEL_IN_BODY:
215-
headers["x-ms-model-mesh-model-name"] = self.valves.AZURE_AI_MODEL
217+
# If we have a model name and it shouldn't be in the body, add it to headers
218+
if not self.valves.AZURE_AI_MODEL_IN_BODY:
219+
# If specific model name provided, use it
220+
if model_name:
221+
headers["x-ms-model-mesh-model-name"] = model_name
222+
# Otherwise, if AZURE_AI_MODEL has a single value, use that
223+
elif self.valves.AZURE_AI_MODEL and ";" not in self.valves.AZURE_AI_MODEL and "," not in self.valves.AZURE_AI_MODEL and " " not in self.valves.AZURE_AI_MODEL:
224+
headers["x-ms-model-mesh-model-name"] = self.valves.AZURE_AI_MODEL
216225
return headers
217226

218227
def validate_body(self, body: Dict[str, Any]) -> None:
@@ -228,6 +237,27 @@ def validate_body(self, body: Dict[str, Any]) -> None:
228237
if "messages" not in body or not isinstance(body["messages"], list):
229238
raise ValueError("The 'messages' field is required and must be a list.")
230239

240+
def parse_models(self, models_str: str) -> List[str]:
241+
"""
242+
Parses a string of models separated by commas, semicolons, or spaces.
243+
244+
Args:
245+
models_str: String containing model names separated by commas, semicolons, or spaces
246+
247+
Returns:
248+
List of individual model names
249+
"""
250+
if not models_str:
251+
return []
252+
253+
# Replace semicolons and commas with spaces, then split by spaces and filter empty strings
254+
models = []
255+
for model in models_str.replace(';', ' ').replace(',', ' ').split():
256+
if model.strip():
257+
models.append(model.strip())
258+
259+
return models
260+
231261
def get_azure_models(self) -> List[Dict[str, str]]:
232262
"""
233263
Returns a list of predefined Azure AI models.
@@ -296,12 +326,15 @@ def pipes(self) -> List[Dict[str, str]]:
296326
"""
297327
self.validate_environment()
298328

299-
# If a custom model is provided, use it exclusively.
329+
# If custom models are provided, parse them and return as pipes
300330
if self.valves.AZURE_AI_MODEL:
301331
self.name = "Azure AI: "
302-
return [
303-
{"id": self.valves.AZURE_AI_MODEL, "name": self.valves.AZURE_AI_MODEL}
304-
]
332+
models = self.parse_models(self.valves.AZURE_AI_MODEL)
333+
if models:
334+
return [{"id": model, "name": model} for model in models]
335+
else:
336+
# Fallback for backward compatibility
337+
return [{"id": self.valves.AZURE_AI_MODEL, "name": self.valves.AZURE_AI_MODEL}]
305338

306339
# If custom model is not provided but predefined models are enabled, return those.
307340
if self.valves.USE_PREDEFINED_AZURE_AI_MODELS:
@@ -329,9 +362,15 @@ async def pipe(
329362

330363
# Validate the request body
331364
self.validate_body(body)
365+
selected_model = None
332366

333-
# Construct headers
334-
headers = self.get_headers()
367+
if "model" in body and body["model"]:
368+
selected_model = body["model"]
369+
# Safer model extraction with split
370+
selected_model = selected_model.split(".", 1)[1] if "." in selected_model else selected_model
371+
372+
# Construct headers with selected model
373+
headers = self.get_headers(selected_model)
335374

336375
# Filter allowed parameters
337376
allowed_params = {
@@ -350,11 +389,20 @@ async def pipe(
350389
"top_p",
351390
}
352391
filtered_body = {k: v for k, v in body.items() if k in allowed_params}
353-
354-
# If the valve indicates that the model name should be in the body,
355-
# add it to the filtered body.
392+
393+
356394
if self.valves.AZURE_AI_MODEL and self.valves.AZURE_AI_MODEL_IN_BODY:
357-
filtered_body["model"] = self.valves.AZURE_AI_MODEL
395+
# If a model was explicitly selected in the request, use that
396+
if selected_model:
397+
filtered_body["model"] = selected_model
398+
else:
399+
# Otherwise, if AZURE_AI_MODEL contains multiple models, only use the first one to avoid errors
400+
models = self.parse_models(self.valves.AZURE_AI_MODEL)
401+
if models and len(models) > 0:
402+
filtered_body["model"] = models[0]
403+
else:
404+
# Fallback to the original value
405+
filtered_body["model"] = self.valves.AZURE_AI_MODEL
358406
elif "model" in filtered_body and filtered_body["model"]:
359407
# Safer model extraction with split
360408
filtered_body["model"] = (

0 commit comments

Comments
 (0)