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