Metadata-Version: 2.4
Name: openwebui-sdk
Version: 0.1.1
Summary: Library for programmatic access to an Open WebUI server (auth, models, tools, chat).
Author: Alexey @vedmaka
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: python-socketio>=5.11
Requires-Dist: aiohttp>=3.8

# openwebui-sdk

[![CI](https://github.com/vedmaka/openwebui-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/vedmaka/openwebui-sdk/actions/workflows/ci.yml)
[![skills.sh](https://skills.sh/b/vedmaka/openwebui-sdk)](https://skills.sh/vedmaka/openwebui-sdk)

> [!WARNING]
> **Work in progress.** This SDK is incomplete and under active development.
> The API may change, some features are missing, and it is not yet stable or
> ready for production use.

A Python library for talking to an [Open WebUI](https://github.com/open-webui/open-webui) server. `OpenWebUIClient` gives you the full tool-calling loop, not just plain chat, as a callable library. Use it from scripts, services and other apps, without a terminal or a browser.

> [!IMPORTANT]
> **Looking for the CLI?** This is the *library* (``openwebui-sdk``). The
> ready-to-use terminal tool built on top of it ships as the separate
> ``openwebui-cli`` package. Install it with ``pip install openwebui-cli`` or
> read its **[README](cli/README.md)**.

- **Full tool-calling loop.** `run_chat` wires `resolve_tools` → Socket.IO tool execution → `save_chat`, so a non-CLI app gets real tool runs, not just text.
- **Structured results.** `run_chat` returns a `ChatResult` (`answer`, `reasoning`, `tool_calls`, `raw_content`) instead of a raw stream.
- **Server access carried for it.** Auth (email/password or API key), models, tools and functions CRUD and valves, all through `Authorization: Bearer` against real Open WebUI routes (verified against 0.6.5).
- **Thin surface.** One client class and a handful of result dataclasses; no framework, no server dependencies.

```python
from openwebui_sdk import OpenWebUIClient

client = OpenWebUIClient(base_url="http://localhost:8080", token="sk-...")
result = client.run_chat(
    model="sample-workspace-model-1",
    messages=[{"role": "user", "content": "What time is it?"}],
    tool_ids=client.resolve_tools("sample-workspace-model-1"),
)
result.answer  # "The current time is 7:33 PM."  (tools ran; reasoning + tool_calls also filled)
```

## Install

Install the SDK from the registry (PyPI-compatible; works with `pip` and `uv`):

```bash
pip install openwebui-sdk
# or with uv
uv add openwebui-sdk
```

The CLI is a separate published package that depends on the SDK:

```bash
pip install openwebui-cli
# or with uv
uv add openwebui-cli
```

Installing the SDK pulls in `python-socketio` and `aiohttp`, which the
Socket.IO tool-execution runner requires.

## Guide

### Streaming a plain chat

`run_chat` picks the transport for you. Socket.IO when `tool_ids` is set,
plain HTTP streaming otherwise, and returns a structured `ChatResult`:

```python
result = client.run_chat(
    model="sample-workspace-model-1",
    messages=[{"role": "user", "content": "What time is it?"}],
    on_text=lambda fragment: print(fragment, end=""),  # stream to stdout
)
print(result.answer)   # ChatResult: answer, reasoning, tool_calls, raw_content
```

Lower-level callers can use `chat_stream` (per-fragment iterator),
`chat_once` (buffered string) and `chat_json` (raw OpenAI-compatible dict)
directly.

### Using tools

Resolve the tools **attached** to a model, then run a chat with them.
The Socket.IO runner executes the tool-call loop and streams reasoning / tool
activity / the final answer through callbacks:

```python
tool_ids = client.resolve_tools("sample-workspace-model-1")   # list[str]
result = client.run_chat(
    model="sample-workspace-model-1",
    messages=[{"role": "user", "content": "What time is it?"}],
    tool_ids=tool_ids,
    on_reasoning=lambda f: None,      # chain-of-thought
    on_tool=lambda line: None,        # tool activity, e.g. "↳ get_time ..."
    on_status=lambda line: None,
)
```

`resolve_tools` reads the model's attached `info.meta.toolIds` (the same field
the web UI reads), merges any explicit extras (deduped), and honours `--no-tools`
via `no_tools=True` (returns `[]`).

### Persisting a chat

Create a chat row, run the completion, and save it so it appears in the web UI
sidebar with a generated title:

```python
chat_id = client.create_chat(title="New Chat", model=model)
result = client.run_chat(model=model, messages=[...])
client.save_chat(
    chat_id=chat_id,
    message_id=str(uuid.uuid4()),
    model=model,
    prompt_text="What time is it?",
    answer=result.answer,
    raw_content=result.raw_content,
)
```

### Authentication

`OpenWebUIClient` accepts either an API key or a JWT bearer token, or exchanges
email + password for one:

```python
client = OpenWebUIClient(base_url)
session = client.signin(email, password)   # -> Session (token, user_id, ...)
print(session.token)                       # use it to build an API-key client

client.session()   # validate the current token + refresh it; returns Session
```

Both JWTs and `sk-...` API keys are sent as `Authorization: Bearer <token>`.
`session()` also mints a refreshed token, which the client adopts.

### Managing models

The client manages the server's workspace models (Settings → Workspace in the web
UI). Methods return the parsed `Model` / `ModelConfig` objects.

```python
client.list_models()                       # -> list[Model]
cfg = client.get_model_config("my-model")  # full editable config (ModelConfig)

client.create_model(
    id="my-model",
    base_model_id="gpt-4o",
    name="My Model",
    system="You are a helpful assistant.",
    tools=["dummytools"],
    functions=["my_filter"],
    capabilities={"vision": True, "web_search": True},
    function_calling="native",
)

client.update_model("my-model", system="New prompt", name="Renamed")   # partial
client.delete_model("my-model")

client.add_model_tools("my-model", ["dummytools"])     # enable a tool
client.remove_model_tools("my-model", ["dummytools"])   # disable a tool
client.add_model_functions("my-model", ["my_filter"])   # enable a function
client.remove_model_functions("my-model", ["my_filter"]) # disable a function
```

`update_model` fetches the current config first and round-trips the full
`meta`/`params`, so fields you don't touch (temperature, tags, profile image,
…) survive. Tools are stored as `meta.toolIds`; functions are split into
filter/action by their server type.

### Managing tools

The client manages workspace tools (Settings → Workspace in the web UI): CRUD
plus admin valves for the `python-socketio` tool execution.

```python
client.list_tools()
client.get_tool("my_tool")
client.create_tool(
    id="my_tool", name="My Tool", content="def ...", description="does X"
)
client.update_tool("my_tool", description="new desc")   # None fields keep current
client.delete_tool("my_tool")

client.set_tool_valves("my_tool", {"api_key": "..."})
client.get_tool_valves("my_tool")
```

### Managing functions

Functions attach to models as filters or actions (Settings → Workspace →
Functions in the web UI). The client manages them like tools (admin required):

```python
client.list_functions()                       # -> list[Function]
client.get_function("my_filter")              # includes source code (FunctionModel)
client.create_function(
    id="my_filter", name="My Filter",
    content="class Filter:\n ...", description="does X",
)
client.update_function("my_filter", description="new desc")  # None fields keep current
client.delete_function("my_filter")

# admin valves for the function's Valves class
client.set_function_valves("my_filter", {"api_key": "..."})
client.get_function_valves("my_filter")
client.get_function_valves_spec("my_filter")
```

`Function` carries `type` (filter/action) plus `is_active`/`is_global` and the
parsed `manifest`; the server derives the type from the source on create.

## Layout

```text
src/openwebui_sdk/
  __init__.py     # version, re-exports (OpenWebUIClient, Model, Tool, Session, ChatResult)
  client.py       # OpenWebUIClient: auth / models / tools / functions / chats / run_chat
  models.py       # Model, ModelConfig (editable workspace-model config)
  tools.py        # Tool
  functions.py    # Function
  sessions.py     # Session (sign-in / identity)
  chat.py         # ChatResult + parse_title (chat-title helper)
  http.py         # urllib request + SSE streaming (proxy-aware via env)
  sse.py          # decode OpenAI chat-completion chunks into text
  sockets.py      # Socket.IO chat runner (tool execution path)
  render.py       # render serialized content blocks (answer / reasoning / tools) for a terminal
  errors.py       # exception types
```

The command-line wrapper built on top of this library lives in
[`cli/`](cli/README.md).
