OpenWebUIのAPI経由でchat機能を試してみた。(+web検索も試す)
目的
ローカルにollama + OpenWebUIを構築したので、API経由でchat機能を試してみる。
あと、せっかくOpenWebUIからweb検索機能を使えるようにしたので、API経由からできないか試してみる。
構築は以下を参照。
ollama + OpenWebUI過去記事
Web検索 searxng(OpenWebUI公式ドキュメント)
環境
- OpenWebUI 0.6.21
- ollama 0.10.1
# OpenWebUIのバージョン
root@sv# curl -H "Authorization: bearer YOUR_API_KEY" http://<llm_container-IP>:<OpenWebUI-PORT>/api/version
{"version":"0.6.21"}
# ollamaのバージョン
root@sv# curl http://<llm_container_IP>:<ollama-PORT>/api/version
{"version":"0.10.1"}
公式ドキュメントの確認
OpenWebUIのAPIドキュメントは以下にある。
OpenWebUI APIドキュメント
ドキュメントの中にはモデル取得やchatのAPIエンドポイントが記載されている。
APIキーの取得
どこにAPIキーがあるか、少し迷ったので、以下に記載しておく。
- OpenWebUIのWebUIにアクセスする
- 左下のアカウントアイコンをクリックし、設定を開く
- アカウントの中にAPIキーがあるので、そこから新規作成する
注意:管理者側でAPIキーを有効にしていない場合は、APIキーを作成できない。
その場合は、管理者設定->一般->APIキーを有効にするをONにして保存する。
なぜか、APIキーをコピーしようとしてもクリップボードにコピーされない。コピーマークあるのに..
APIを実際に叩いてみる
モデルの取得
まずは、APIを叩いてモデルの一覧を取得してみる。
# モデル一覧の取得
root@sv# curl -H "Authorization: bearer YOUR_API_KEY" http://<llm_container-IP>:<OpenWebUI-PORT>/api/models
# 出力例
{
"data": [
{
"id": "gemma3n:e2b",
"name": "gemma3n:e2b",
"object": "model",
"created": 1755166869,
"owned_by": "ollama",
"ollama": {
"name": "gemma3n:e2b",
"model": "gemma3n:e2b",
"modified_at": "2025-08-11T08:36:12.916637887Z",
"size": 5621616562,
"digest": "719372f8c7deee188821a4dcbaf75efa13a342d7e88a79d4fc2412b24947f6fd",
"details": {
"parent_model": "",
"format": "gguf",
"family": "gemma3n",
"families": ["gemma3n"],
"parameter_size": "4.5B",
"quantization_level": "Q4_K_M"
},
"connection_type": "local",
"urls": [0]
},
"connection_type": "local",
"tags": [],
"actions": [],
"filters": []
},
# 各モデルの情報が出力 長いので省略
Chatの実行
次に、chatを実行してみる。
# Chatの実行
root@sv# curl -X POST http://<llm_container-IP>:<OpenWebUI-PORT>/api/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma3n:e2b",
"messages": [
{
"role": "user",
"content": "Why is the sky blue?"
}
]
}'
# 出力例
{
"id": "gemma3n:******************",
"created": 1755167635,
"model": "gemma3n:e2b",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "The sky is blue because of a phenomenon called **Rayleigh scattering**. Here's a breakdown of what that means:\n\n* **Sunlight is made of all colors:** Sunlight actually contains all the colors of the rainbow – red, orange, yellow, green, blue, indigo, and violet.\n* **Light travels in waves:** Light travels as waves. Different colors have different wavelengths. Blue and violet have shorter wavelengths, while red and orange have longer wavelengths.\n* **Scattering by air molecules:** As sunlight enters the Earth's atmosphere, it bumps into tiny air molecules (mostly nitrogen and oxygen). This causes the light to scatter in different directions.\n* **Rayleigh scattering favors blue light:** Rayleigh scattering is more effective at scattering shorter wavelengths (blue and violet light) than longer wavelengths (red and orange light). \n* **Why blue, not violet?** While violet light is scattered even *more* than blue light, our eyes are more sensitive to blue. Also, the sun emits slightly less violet light than blue light. This combination makes the sky appear blue.\n\n**In simpler terms:**\n\nImagine throwing a bunch of small balls (light waves) at a bumpy surface (air molecules). The smaller balls (blue light) are more likely to bounce off in all directions, while the larger balls (red light) are more likely to travel straight through. That's why we see blue light scattered all over the sky.\n\n\n\n**Sunrise and Sunset:**\n\nAt sunrise and sunset, the sunlight has to travel through *more* of the atmosphere. This means the blue light gets scattered away even more, leaving the longer wavelengths like red and orange to reach our eyes. That's why sunrises and sunsets often have beautiful red and orange colors.\n\n\n\n"
}
}
],
"object": "chat.completion",
"usage": {
"response_token/s": 17.55,
"prompt_token/s": 57.6,
"total_duration": 36423518206,
"load_duration": 14856136287,
"prompt_eval_count": 15,
"prompt_tokens": 15,
"prompt_eval_duration": 260400382,
"eval_count": 374,
"completion_tokens": 374,
"eval_duration": 21305968662,
"approximate_total": "0h0m36s",
"total_tokens": 389,
"completion_tokens_details": {
"reasoning_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
}
}
ここまではドキュメントに記載されている通りに実行できた。
Web検索を行うための調査
OpenWebUIとsearxngを連携させている状態なので、せっかくだからAPI経由でweb検索を試してみる。
公式ドキュメントには記載されているないのでソースから追ってみる
web検索のパラメータを確認
とりあえず、ソース中にweb_search
の文言とAPIエンドポイントである/api/chat/completions
を検索してみる。
root@llm_container# ls
data dev.sh open_webui requirements.txt start.sh start_windows.bat
root@llm_container# grep -rn "web_search" .
# 重要なものだけ記載
./open_webui/routers/retrieval.py:1912:async def process_web_search(
./open_webui/utils/middleware.py:40:from open_webui.routers.retrieval import process_web_search, SearchForm
./open_webui/utils/middleware.py:345:async def chat_web_search_handler(
./open_webui/utils/middleware.py:353: "action": "web_search",
./open_webui/utils/middleware.py:371: "type": "web_search",
./open_webui/utils/middleware.py:405: "action": "web_search",
./open_webui/utils/middleware.py:417: "action": "web_search",
./open_webui/utils/middleware.py:425: results = await process_web_search(
./open_webui/utils/middleware.py:442: "type": "web_search",
./open_webui/utils/middleware.py:454: "type": "web_search",
./open_webui/utils/middleware.py:466: "action": "web_search",
./open_webui/utils/middleware.py:478: "action": "web_search",
./open_webui/utils/middleware.py:492: "action": "web_search",
./open_webui/utils/middleware.py:866: if "web_search" in features and features["web_search"]:
./open_webui/utils/middleware.py:867: form_data = await chat_web_search_handler(
root@llm_container# grep -rn "/api/chat/completions" .
./open_webui/main.py:1369:@app.post("/api/chat/completions")
結果として下記についてわかった。
-
open_webui/utils/middleware.py
にchat_web_search_handler
が定義されている -
chat_web_search_handler
はopen_webui/routers/retrieval.py
のprocess_web_search
を呼び出している -
/api/chat/completions
のAPIエンドポイントはopen_webui/main.py
に定義されている
なので、これらファイルのソースを確認してみる。
open_webui/main.py
の確認
まずはAPIを実際に定義しているところから確認してみる。
metadataで色々パラメータを設定しているので、ここでweb検索のパラメータも設定できそうな気がする。
@app.post("/api/chat/completions")
async def chat_completion(
request: Request,
form_data: dict,
user=Depends(get_verified_user),
):
if not request.app.state.MODELS:
await get_all_models(request, user=user)
model_id = form_data.get("model", None)
model_item = form_data.pop("model_item", {})
tasks = form_data.pop("background_tasks", None)
metadata = {}
try:
if not model_item.get("direct", False):
if model_id not in request.app.state.MODELS:
raise Exception("Model not found")
model = request.app.state.MODELS[model_id]
model_info = Models.get_model_by_id(model_id)
# Check if user has access to the model
if not BYPASS_MODEL_ACCESS_CONTROL and user.role == "user":
try:
check_model_access(user, model)
except Exception as e:
raise e
else:
model = model_item
model_info = None
request.state.direct = True
request.state.model = model
model_info_params = (
model_info.params.model_dump() if model_info and model_info.params else {}
)
# Chat Params
stream_delta_chunk_size = form_data.get("params", {}).get(
"stream_delta_chunk_size"
)
# Model Params
if model_info_params.get("stream_delta_chunk_size"):
stream_delta_chunk_size = model_info_params.get("stream_delta_chunk_size")
# -----------------------
# 多分ここら辺のパラメータでweb検索を有効にできそう
# -----------------------
metadata = {
"user_id": user.id,
"chat_id": form_data.pop("chat_id", None),
"message_id": form_data.pop("id", None),
"session_id": form_data.pop("session_id", None),
"filter_ids": form_data.pop("filter_ids", []),
"tool_ids": form_data.get("tool_ids", None),
"tool_servers": form_data.pop("tool_servers", None),
"files": form_data.get("files", None),
"features": form_data.get("features", {}),
"variables": form_data.get("variables", {}),
"model": model,
"direct": model_item.get("direct", False),
"params": {
"stream_delta_chunk_size": stream_delta_chunk_size,
"function_calling": (
"native"
if (
form_data.get("params", {}).get("function_calling") == "native"
or model_info_params.get("function_calling") == "native"
)
else "default"
),
},
}
if metadata.get("chat_id") and (user and user.role != "admin"):
chat = Chats.get_chat_by_id_and_user_id(metadata["chat_id"], user.id)
if chat is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=ERROR_MESSAGES.DEFAULT(),
)
request.state.metadata = metadata
form_data["metadata"] = metadata
form_data, metadata, events = await process_chat_payload(
request, form_data, user, metadata, model
)
except Exception as e:
log.debug(f"Error processing chat payload: {e}")
if metadata.get("chat_id") and metadata.get("message_id"):
# Update the chat message with the error
Chats.upsert_message_to_chat_by_id_and_message_id(
metadata["chat_id"],
metadata["message_id"],
{
"error": {"content": str(e)},
},
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
try:
response = await chat_completion_handler(request, form_data, user)
if metadata.get("chat_id") and metadata.get("message_id"):
Chats.upsert_message_to_chat_by_id_and_message_id(
metadata["chat_id"],
metadata["message_id"],
{
"model": model_id,
},
)
return await process_chat_response(
request, response, form_data, user, metadata, model, events, tasks
)
except Exception as e:
log.debug(f"Error in chat completion: {e}")
if metadata.get("chat_id") and metadata.get("message_id"):
# Update the chat message with the error
Chats.upsert_message_to_chat_by_id_and_message_id(
metadata["chat_id"],
metadata["message_id"],
{
"error": {"content": str(e)},
},
)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
open_webui/utils/middleware.py
の確認
chat_web_search_handlerはあったが、肝心なのはこのchat_web_search_handlerがどのように呼び出されているかなので、他の関数で呼び出している箇所を確認してみる。
async def chat_web_search_handler(
request: Request, form_data: dict, extra_params: dict, user
):
event_emitter = extra_params["__event_emitter__"]
await event_emitter(
{
"type": "status",
"data": {
"action": "web_search",
"description": "Generating search query",
"done": False,
},
}
)
同じファイル内のprocess_chat_payload
関数内で呼び出されている。
featuresにweb_search
が含まれている場合に呼び出されるように見える。
process_chat_payload
は/api/chat/completions
のAPIエンドポイントで呼び出されているので、
chatのリクエストにweb_searchのパラメータを含めれば、web検索が実行されるように見える。
features = form_data.pop("features", None)
if features:
if "memory" in features and features["memory"]:
form_data = await chat_memory_handler(
request, form_data, extra_params, user
)
if "web_search" in features and features["web_search"]:
form_data = await chat_web_search_handler(
request, form_data, extra_params, user
)
open_webui/routers/retrieval.py
の確認
不要な気がするので省略
API経由でweb検索を実行してみる
では、実際にAPI経由でweb検索を実行してみる。
# web検索の実行
root@sv# curl -X POST http://<llm_container-IP>:<OpenWebUI-PORT>/api/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemma3n:e2b",
"features": {"web_search": true},
"messages": [
{
"role": "user",
"content": "Why is the sky blue?"
}
]
}'
# 出力例
{
"sources": [
{
"source": {
"collection_name": "web-search-c021f0cfe617437d395c544af243494d70eb66e7b6a1428a69f3",
"name": "Why is the sky blue?, Rayleigh scattering explanation",
"type": "web_search",
"urls": [
"https://spaceplace.nasa.gov/blue-sky/en/",
"https://scijinks.gov/blue-sky/",
"https://www.weather.gov/fgz/SkyBlue",
"https://en.wikipedia.org/wiki/Rayleigh_scattering",
"https://www.sciencedirect.com/topics/medicine-and-dentistry/rayleigh-scattering",
"https://www.britannica.com/science/Rayleigh-scattering"
],
"queries": [
"Why is the sky blue?",
"Rayleigh scattering explanation"
]
},
"document": [
"Why Is the Sky Blue? | NASA Space Place – NASA Science for Kids\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nblue-sky\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nVea en Español\n\n\n\n\n\n\n\n\n\n\n\nEarth\nSun\nSolar System\nUniverse\nScience and Tech\nEducators\n\n\n\nmisr\n\n\n\n\nWhy Is the Sky Blue?\n\n\n\n\n\nThe Short Answer:\n\r\n Sunlight reaches Earth's atmosphere and is scattered in all directions by all the gases and particles in the air. Blue light is scattered more than the other colors because it travels as shorter, smaller waves. This is why we see a blue sky most of the time.\r\n \n\n\n\n\n\n\n\nClick above to watch this video about why the sky is blue! Voiceover provided by NASA scientist Dr. Moogega Stricker. Click here to download this video (1920x1080, 87 MB, video/mp4).\n\nIt's easy to see that the sky is blue. Have you ever wondered why?\nA lot of other smart people have, too. And it took a long time to figure it out!\n\n\n\nThe light from the Sun looks white. But it is really made up of all the colors of the rainbow.",
"Rayleigh scattering, dispersion of electromagnetic radiation by particles that have a radius less than approximately 1/10 the wavelength of the radiation. The process has been named in honour of Lord Rayleigh, who in 1871 published a paper describing this phenomenon.",
"Why Is the Sky Blue? | NOAA SciJinks – All About Weather\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nEducators\nGOES-R\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nHome\nVideos\nGames\nTOPICS\nStorms\nTides and Oceans\nAtmosphere\nWater and Ice\nSatellites and Technology\nWeather Forecasting\nSpace Weather\nLast Updated August 11th, 2025\nAbout Us\nContact\nNewsletter\nRequest Materials\nEducators\nGOES-R\nnoaa.gov\n\n\n\n\n\n\nAtmosphere\n\nWhy Is the Sky Blue?\n\n\n\n\n\n\n\n\n\nThe Short Answer:\n\r\n\t\t\tGases and particles in Earth's atmosphere scatter sunlight in all directions. Blue light is scattered more than other colors because it travels as shorter, smaller waves. This is why we see a blue sky most of the time.\r\n\t\t\n\n\n\n\n\n\nClick above to watch this video about why the sky is blue! Voiceover provided by NASA scientist Dr. Moogega Stricker."
],
"metadata": [
{
"description": "Learn the answer and impress your friends!",
"embedding_config": "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}",
"language": "en",
"source": "https://spaceplace.nasa.gov/blue-sky/en/",
"start_index": 5,
"title": "Why Is the Sky Blue? | NASA Space Place – NASA Science for Kids"
},
{
"description": "Rayleigh scattering, dispersion of electromagnetic radiation by particles that have a radius less than approximately 110 the wavelength of the radiation. The process has been named in honour of Lord Rayleigh, who in 1871 published a paper describing this phenomenon. The angle through which sunlight",
"embedding_config": "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}",
"language": "en",
"source": "https://www.britannica.com/science/Rayleigh-scattering",
"start_index": 4600,
"title": "Rayleigh scattering | Molecules, Light, Wavelength | Britannica"
},
{
"description": "This may sound like an easy question... it's not! ",
"embedding_config": "{'engine': '', 'model': 'sentence-transformers/all-MiniLM-L6-v2'}",
"language": "en",
"source": "https://scijinks.gov/blue-sky/",
"start_index": 10,
"title": "Why Is the Sky Blue? | NOAA SciJinks – All About Weather"
}
],
"distances": [
0.8779539880039114,
0.8764416974031537,
0.8733746445891861
]
}
],
"id": "gemma3n:e2b-e59a4753-b481-4de3-8e5a-88abff212010",
"created": 1755240270,
"model": "gemma3n:e2b",
"choices": [
{
"index": 0,
"logprobs": null,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "The sky appears blue due to Rayleigh scattering, where shorter, smaller waves of blue light are scattered more than other colors [2] [3]. This is because sunlight is made up of all the colors of the rainbow [1]."
}
}
],
"object": "chat.completion",
"usage": {
"response_token/s": 17.28,
"prompt_token/s": 63.6,
"total_duration": 21612488154,
"load_duration": 86884758,
"prompt_eval_count": 1196,
"prompt_tokens": 1196,
"prompt_eval_duration": 18805638926,
"eval_count": 47,
"completion_tokens": 47,
"eval_duration": 2719320167,
"approximate_total": "0h0m21s",
"total_tokens": 1243,
"completion_tokens_details": {
"reasoning_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
}
}
出力結果にweb検索の結果が含まれている。
できているような気がする。
まとめ
OpenWebUIのAPI経由でchat機能を試してみた。
また、web検索機能もAPI経由で実行できることを確認した。
open_webui/utils/middleware.py
には他のパラメータも指定できそうなので、色々試してみると良いかも。
open_webui/main.py
にはドキュメントに載っていないAPIエンドポイントもあるので、そちらも試してみると面白いかも。
あと個人的な話になるが、初めてAPIを叩いてみる感覚を味わった。