Skip to content

Commit fcedeb9

Browse files
authored
feat: add /v1/responses proxy endpoint for Ollama (open-webui#23483)
Ollama recently added Responses API support via its OpenAI-compatible endpoint (/v1/responses). This adds a proxy endpoint to the Ollama router that forwards requests to Ollama's /v1/responses, applying the same model resolution, access control, and prefix_id handling used by the existing /v1/chat/completions and /v1/messages proxies. Uses a typed ResponsesForm Pydantic model with required model field and extra='allow' for forward compatibility, consistent with other endpoint schemas in the file. This allows API consumers (Codex, Claude Code, etc.) to use the Responses API directly with Ollama-hosted models without requiring a separate OpenAI-compatible connection.
1 parent 99f3c55 commit fcedeb9

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

backend/open_webui/routers/ollama.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,84 @@ async def generate_anthropic_messages(
15961596
)
15971597

15981598

1599+
class ResponsesForm(BaseModel):
1600+
model: str
1601+
1602+
model_config = ConfigDict(extra='allow')
1603+
1604+
1605+
@router.post('/v1/responses')
1606+
@router.post('/v1/responses/{url_idx}')
1607+
async def generate_responses(
1608+
request: Request,
1609+
form_data: ResponsesForm,
1610+
url_idx: Optional[int] = None,
1611+
user=Depends(get_verified_user),
1612+
):
1613+
"""
1614+
Proxy for Ollama's OpenAI-compatible /v1/responses endpoint.
1615+
1616+
Forwards the request as-is to the Ollama backend, applying the same
1617+
model resolution, access control, and prefix_id handling used by
1618+
the OpenAI-compatible /v1/chat/completions proxy.
1619+
1620+
See https://ollama.com/blog/responses-api
1621+
"""
1622+
if not request.app.state.config.ENABLE_OLLAMA_API:
1623+
raise HTTPException(status_code=503, detail='Ollama API is disabled')
1624+
1625+
payload = form_data.model_dump()
1626+
model_id = form_data.model
1627+
1628+
model_info = Models.get_model_by_id(model_id)
1629+
if model_info:
1630+
if model_info.base_model_id:
1631+
payload['model'] = model_info.base_model_id
1632+
1633+
# Check if user has access to the model
1634+
if user.role == 'user':
1635+
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
1636+
if not (
1637+
user.id == model_info.user_id
1638+
or AccessGrants.has_access(
1639+
user_id=user.id,
1640+
resource_type='model',
1641+
resource_id=model_info.id,
1642+
permission='read',
1643+
user_group_ids=user_group_ids,
1644+
)
1645+
):
1646+
raise HTTPException(
1647+
status_code=403,
1648+
detail='Model not found',
1649+
)
1650+
else:
1651+
if user.role != 'admin':
1652+
raise HTTPException(
1653+
status_code=403,
1654+
detail='Model not found',
1655+
)
1656+
1657+
url, url_idx = await get_ollama_url(request, payload['model'], url_idx)
1658+
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
1659+
str(url_idx),
1660+
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}), # Legacy support
1661+
)
1662+
1663+
prefix_id = api_config.get('prefix_id', None)
1664+
if prefix_id:
1665+
payload['model'] = payload['model'].replace(f'{prefix_id}.', '')
1666+
1667+
return await send_post_request(
1668+
url=f'{url}/v1/responses',
1669+
payload=json.dumps(payload),
1670+
stream=payload.get('stream', False),
1671+
content_type='text/event-stream' if payload.get('stream', False) else None,
1672+
key=get_api_key(url_idx, url, request.app.state.config.OLLAMA_API_CONFIGS),
1673+
user=user,
1674+
)
1675+
1676+
15991677
@router.get('/v1/models')
16001678
@router.get('/v1/models/{url_idx}')
16011679
async def get_openai_models(

0 commit comments

Comments
 (0)