257 Commits

  • feat: permission to limit sub-agents to specific users and groups (#34040)
    Sub-agents could only be switched on or off for the whole instance, so once they were on every user could spawn them and an admin had no way to keep the extra model usage to a pilot group. The user permissions now have a Sub-agents switch under Features, both in the default permissions and in each group, next to Memories and working the same way. Users without the permission no longer get the sub-agent tool in their chats, nor the timer tool that is part of the same feature. Admins are not restricted by it, and with sub-agents off in Admin Settings nobody gets them, admins included. The permission is on by default, so existing instances behave as before until an admin turns it off; the default can also be set with the USER_PERMISSIONS_FEATURES_SUBAGENTS environment variable.
    
    Closes #34038
  • fix: files and knowledge bases sent through the API reach the model again (#33815)
    API clients that attach a file or knowledge collection in the files field of a chat completion got an answer to the bare question. File Context now defaults to off unless function calling is set to legacy, and API requests that do not come from a chat in the web UI get no tools to open the files either. File Context now defaults to on for those requests, so the file content is used in the answer as before. Chats in the web UI keep letting the model open the files itself.
    
    Fixes #33808
  • fix: terminal connections using Session auth fail Verify Connection and never load the terminal tools (#33699)
    A terminal connection with Auth set to Session had no API key field, so Verify Connection, the policy settings and the tool list request all went out without the orchestrator's key and were rejected as unauthorized. That left the policy dropdown empty and chats failing with "Terminal server is unavailable". Session connections now have the API key field, and the key is sent for those admin and discovery calls the same way Bearer connections already do. Terminals that users open still sign in with the user's own session.
    
    Fixes #30001
    Fixes #33579
  • fix: chat stops with no answer when an MCP server request fails mid-chat (#33272)
    When an MCP tool server could not be reached for a tool call (server restarted, connection reset by a proxy or load balancer, a request timing out), the whole chat was cancelled as if the user had pressed stop, and the log only said "Task was cancelled!". Now only that tool call fails, the model gets the connection error as the tool result and the reply finishes, and the underlying connection error is logged. The MCP SDK cancels whatever task opened the session when a single request fails, so each MCP session now runs in its own task instead of the chat's.
    
    Fixes #33268
  • fix: tool server tools change their schema on every restart and break prompt caching (#33000)
    The required parameters of OpenAPI tool server tools, Open Terminal included, were sent to the model in a random order that could change every time Open WebUI restarted. The tool list sits at the start of every prompt, so a different order made every prefix cache on the model side miss, and long chats were processed again from scratch after a restart. The required parameters now keep the order the tool server declares, so the tool list stays identical across restarts.
  • feat: tools come back on their own after all OpenAPI tool servers were unreachable (#32533)
    With Redis, if every tool server was unreachable when Open WebUI loaded the tool server list, it cached an empty list and served it forever. The tools stayed missing from chats and the tool list after the servers came back, until a restart, a save in the tool server settings or a manual delete of the cached Redis key. An empty cached list is now only trusted while no OpenAPI tool server is enabled, so the next request that loads tools fetches the servers again, the same way terminal servers already behave. While an enabled server stays unreachable, each such request tries it again and can wait for its timeout.
    
    Fixes #29573
  • fix: temporary chats save sub-agent conversations on the server (#31573)
    In a temporary chat, when the model handed a task to a sub-agent, the sub-agent's conversation with the task and its answer was saved on the server, although a temporary chat should leave nothing behind. The model is no longer offered sub-agents in temporary chats.
    
    Fixes #31567
  • fix: stop sending OpenAPI tool server path and query parameters in the request body (#29717)
    Tool calls to an OpenAPI tool server put every argument the model returned into the JSON request body, including the parameters that were already substituted into the URL. Servers that validate their input strictly (additionalProperties: false) answered 422 "unexpected property", so reads worked and every write through an endpoint with a path or query parameter failed.
    
    The body is now built from the model's arguments minus the operation's declared parameters, keeping any name the requestBody schema declares as a property of its own, so an endpoint that wants the resource id in the body as well as in the path still gets it.
    
    The filter only runs when the resolved body schema lists its properties. A free-form, composed or non-JSON body offers nothing to check a name against, so those requests go out exactly as before.
    
    src/lib/apis/index.ts carries the same request builder for direct tool server connections and had the same bug, so it gets the same fix.
    
    Fixes #29716
  • perf: cache the serialized builtin tool spec instead of deep-copying it per request (#28860)
    Every chat request hands each builtin tool a fresh copy of its cached spec, because callers mutate what they get. That copy was a full deepcopy of a nested dict, repeated per tool per message.
    
    The builder now caches the spec already serialized, so a request only parses it back. Parsing is what produces the independent tree callers mutate, and the cached value becomes an immutable string, so a request can no longer reach the cached object at all.
    
    Measured on CPython 3.12 with a 1.1 KB spec and 20 builtin tools per request:
    
    | | before | after |
    |---|---|---|
    | stdlib json, the default | 276.2 us | 66.4 us |
    | orjson | 279.5 us | 37.5 us |
    
    Builtin specs are plain JSON by construction: pydantic normalizes every default before it reaches the schema, so a tuple, set, enum or datetime cannot appear in one, and an unserializable default is dropped rather than embedded.