Skip to content

Commit f23296b

Browse files
committed
refac
1 parent 10f06a6 commit f23296b

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

backend/open_webui/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,7 @@ async def check_url(request: Request, call_next):
14401440
# Fallback to x-api-key header for Anthropic Messages API routes
14411441
if request.state.token is None and request.headers.get('x-api-key'):
14421442
request_path = request.url.path
1443-
if request_path in ('/api/message', '/api/v1/messages'):
1443+
if request_path in ('/api/message', '/api/v1/messages') or request_path.startswith('/ollama/v1/messages'):
14441444
from fastapi.security import HTTPAuthorizationCredentials
14451445

14461446
request.state.token = HTTPAuthorizationCredentials(

backend/open_webui/routers/ollama.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,78 @@ async def generate_openai_chat_completion(
15221522
)
15231523

15241524

1525+
@router.post('/v1/messages')
1526+
@router.post('/v1/messages/{url_idx}')
1527+
async def generate_anthropic_messages(
1528+
request: Request,
1529+
form_data: dict,
1530+
url_idx: Optional[int] = None,
1531+
user=Depends(get_verified_user),
1532+
):
1533+
"""
1534+
Proxy for Ollama's Anthropic-compatible /v1/messages endpoint.
1535+
1536+
Forwards the request as-is to the Ollama backend, applying the same
1537+
model resolution, access control, and prefix_id handling used by
1538+
the OpenAI-compatible /v1/chat/completions proxy.
1539+
1540+
See https://docs.ollama.com/api/anthropic-compatibility
1541+
"""
1542+
if not request.app.state.config.ENABLE_OLLAMA_API:
1543+
raise HTTPException(status_code=503, detail='Ollama API is disabled')
1544+
1545+
payload = {**form_data}
1546+
model_id = payload.get('model', '')
1547+
1548+
model_info = Models.get_model_by_id(model_id)
1549+
if model_info:
1550+
if model_info.base_model_id:
1551+
payload['model'] = model_info.base_model_id
1552+
1553+
# Check if user has access to the model
1554+
if user.role == 'user':
1555+
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
1556+
if not (
1557+
user.id == model_info.user_id
1558+
or AccessGrants.has_access(
1559+
user_id=user.id,
1560+
resource_type='model',
1561+
resource_id=model_info.id,
1562+
permission='read',
1563+
user_group_ids=user_group_ids,
1564+
)
1565+
):
1566+
raise HTTPException(
1567+
status_code=403,
1568+
detail='Model not found',
1569+
)
1570+
else:
1571+
if user.role != 'admin':
1572+
raise HTTPException(
1573+
status_code=403,
1574+
detail='Model not found',
1575+
)
1576+
1577+
url, url_idx = await get_ollama_url(request, payload['model'], url_idx)
1578+
api_config = request.app.state.config.OLLAMA_API_CONFIGS.get(
1579+
str(url_idx),
1580+
request.app.state.config.OLLAMA_API_CONFIGS.get(url, {}), # Legacy support
1581+
)
1582+
1583+
prefix_id = api_config.get('prefix_id', None)
1584+
if prefix_id:
1585+
payload['model'] = payload['model'].replace(f'{prefix_id}.', '')
1586+
1587+
return await send_post_request(
1588+
url=f'{url}/v1/messages',
1589+
payload=json.dumps(payload),
1590+
stream=payload.get('stream', False),
1591+
content_type='text/event-stream' if payload.get('stream', False) else None,
1592+
key=get_api_key(url_idx, url, request.app.state.config.OLLAMA_API_CONFIGS),
1593+
user=user,
1594+
)
1595+
1596+
15251597
@router.get('/v1/models')
15261598
@router.get('/v1/models/{url_idx}')
15271599
async def get_openai_models(

0 commit comments

Comments
 (0)