Spaces:
Sleeping
Sleeping
| from open_webui.utils.task import prompt_template | |
| from open_webui.utils.misc import ( | |
| add_or_update_system_message, | |
| ) | |
| from typing import Callable, Optional | |
| # inplace function: form_data is modified | |
| def apply_model_system_prompt_to_body(params: dict, form_data: dict, user) -> dict: | |
| system = params.get("system", None) | |
| if not system: | |
| return form_data | |
| if user: | |
| template_params = { | |
| "user_name": user.name, | |
| "user_location": user.info.get("location") if user.info else None, | |
| } | |
| else: | |
| template_params = {} | |
| system = prompt_template(system, **template_params) | |
| form_data["messages"] = add_or_update_system_message( | |
| system, form_data.get("messages", []) | |
| ) | |
| return form_data | |
| # inplace function: form_data is modified | |
| def apply_model_params_to_body( | |
| params: dict, form_data: dict, mappings: dict[str, Callable] | |
| ) -> dict: | |
| if not params: | |
| return form_data | |
| for key, cast_func in mappings.items(): | |
| if (value := params.get(key)) is not None: | |
| form_data[key] = cast_func(value) | |
| return form_data | |
| # inplace function: form_data is modified | |
| def apply_model_params_to_body_openai(params: dict, form_data: dict) -> dict: | |
| mappings = { | |
| "temperature": float, | |
| "top_p": float, | |
| "max_tokens": int, | |
| "frequency_penalty": float, | |
| "seed": lambda x: x, | |
| "stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x], | |
| } | |
| return apply_model_params_to_body(params, form_data, mappings) | |
| def apply_model_params_to_body_ollama(params: dict, form_data: dict) -> dict: | |
| opts = [ | |
| "temperature", | |
| "top_p", | |
| "seed", | |
| "mirostat", | |
| "mirostat_eta", | |
| "mirostat_tau", | |
| "num_ctx", | |
| "num_batch", | |
| "num_keep", | |
| "repeat_last_n", | |
| "tfs_z", | |
| "top_k", | |
| "min_p", | |
| "use_mmap", | |
| "use_mlock", | |
| "num_thread", | |
| "num_gpu", | |
| ] | |
| mappings = {i: lambda x: x for i in opts} | |
| form_data = apply_model_params_to_body(params, form_data, mappings) | |
| name_differences = { | |
| "max_tokens": "num_predict", | |
| "frequency_penalty": "repeat_penalty", | |
| } | |
| for key, value in name_differences.items(): | |
| if (param := params.get(key, None)) is not None: | |
| form_data[value] = param | |
| return form_data | |
| def convert_messages_openai_to_ollama(messages: list[dict]) -> list[dict]: | |
| ollama_messages = [] | |
| for message in messages: | |
| # Initialize the new message structure with the role | |
| new_message = {"role": message["role"]} | |
| content = message.get("content", []) | |
| # Check if the content is a string (just a simple message) | |
| if isinstance(content, str): | |
| # If the content is a string, it's pure text | |
| new_message["content"] = content | |
| else: | |
| # Otherwise, assume the content is a list of dicts, e.g., text followed by an image URL | |
| content_text = "" | |
| images = [] | |
| # Iterate through the list of content items | |
| for item in content: | |
| # Check if it's a text type | |
| if item.get("type") == "text": | |
| content_text += item.get("text", "") | |
| # Check if it's an image URL type | |
| elif item.get("type") == "image_url": | |
| img_url = item.get("image_url", {}).get("url", "") | |
| if img_url: | |
| # If the image url starts with data:, it's a base64 image and should be trimmed | |
| if img_url.startswith("data:"): | |
| img_url = img_url.split(",")[-1] | |
| images.append(img_url) | |
| # Add content text (if any) | |
| if content_text: | |
| new_message["content"] = content_text.strip() | |
| # Add images (if any) | |
| if images: | |
| new_message["images"] = images | |
| # Append the new formatted message to the result | |
| ollama_messages.append(new_message) | |
| return ollama_messages | |
| def convert_payload_openai_to_ollama(openai_payload: dict) -> dict: | |
| """ | |
| Converts a payload formatted for OpenAI's API to be compatible with Ollama's API endpoint for chat completions. | |
| Args: | |
| openai_payload (dict): The payload originally designed for OpenAI API usage. | |
| Returns: | |
| dict: A modified payload compatible with the Ollama API. | |
| """ | |
| ollama_payload = {} | |
| # Mapping basic model and message details | |
| ollama_payload["model"] = openai_payload.get("model") | |
| ollama_payload["messages"] = convert_messages_openai_to_ollama( | |
| openai_payload.get("messages") | |
| ) | |
| ollama_payload["stream"] = openai_payload.get("stream", False) | |
| # If there are advanced parameters in the payload, format them in Ollama's options field | |
| ollama_options = {} | |
| # Handle parameters which map directly | |
| for param in ["temperature", "top_p", "seed"]: | |
| if param in openai_payload: | |
| ollama_options[param] = openai_payload[param] | |
| # Mapping OpenAI's `max_tokens` -> Ollama's `num_predict` | |
| if "max_completion_tokens" in openai_payload: | |
| ollama_options["num_predict"] = openai_payload["max_completion_tokens"] | |
| elif "max_tokens" in openai_payload: | |
| ollama_options["num_predict"] = openai_payload["max_tokens"] | |
| # Handle frequency / presence_penalty, which needs renaming and checking | |
| if "frequency_penalty" in openai_payload: | |
| ollama_options["repeat_penalty"] = openai_payload["frequency_penalty"] | |
| if "presence_penalty" in openai_payload and "penalty" not in ollama_options: | |
| # We are assuming presence penalty uses a similar concept in Ollama, which needs custom handling if exists. | |
| ollama_options["new_topic_penalty"] = openai_payload["presence_penalty"] | |
| # Add options to payload if any have been set | |
| if ollama_options: | |
| ollama_payload["options"] = ollama_options | |
| return ollama_payload | |