mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-24 14:34:51 -06:00
773 lines
22 KiB
Python
773 lines
22 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
|
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
|
from open_webui.constants import ERROR_MESSAGES
|
|
from open_webui.events import EVENTS, publish_event
|
|
from open_webui.internal.db import get_async_session
|
|
from open_webui.models.access_grants import AccessGrants
|
|
from open_webui.models.config import Config
|
|
from open_webui.models.groups import Groups
|
|
from open_webui.models.prompt_history import (
|
|
PromptHistories,
|
|
PromptHistoryModel,
|
|
PromptHistoryResponse,
|
|
)
|
|
from open_webui.models.prompts import (
|
|
PromptAccessListResponse,
|
|
PromptAccessResponse,
|
|
PromptForm,
|
|
PromptModel,
|
|
Prompts,
|
|
PromptUserResponse,
|
|
)
|
|
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
|
from open_webui.utils.auth import get_admin_user, get_verified_user
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
class PromptVersionUpdateForm(BaseModel):
|
|
version_id: str
|
|
|
|
|
|
class PromptMetadataForm(BaseModel):
|
|
name: str
|
|
command: str
|
|
tags: list[str | None] = None
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
PAGE_ITEM_COUNT = 30
|
|
|
|
|
|
############################
|
|
# GetPrompts
|
|
# The hardest part is knowing what to ask. Let the right
|
|
# question already be here when it is needed.
|
|
############################
|
|
|
|
|
|
@router.get('/', response_model=list[PromptModel])
|
|
async def get_prompts(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
|
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
|
prompts = await Prompts.get_prompts(db=db)
|
|
else:
|
|
prompts = await Prompts.get_prompts_by_user_id(user.id, 'read', db=db)
|
|
|
|
return prompts
|
|
|
|
|
|
@router.get('/tags', response_model=list[str])
|
|
async def get_prompt_tags(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
|
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
|
return await Prompts.get_tags(db=db)
|
|
return await Prompts.get_tags_by_user_id(user.id, db=db)
|
|
|
|
|
|
@router.get('/list', response_model=PromptAccessListResponse)
|
|
async def get_prompt_list(
|
|
query: str | None = None,
|
|
view_option: str | None = None,
|
|
tag: str | None = None,
|
|
order_by: str | None = None,
|
|
direction: str | None = None,
|
|
page: int | None = 1,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
limit = PAGE_ITEM_COUNT
|
|
|
|
page = max(1, page)
|
|
skip = (page - 1) * limit
|
|
|
|
filter = {}
|
|
if query:
|
|
filter['query'] = query
|
|
if view_option:
|
|
filter['view_option'] = view_option
|
|
if tag:
|
|
filter['tag'] = tag
|
|
if order_by:
|
|
filter['order_by'] = order_by
|
|
if direction:
|
|
filter['direction'] = direction
|
|
|
|
# Pre-fetch user group IDs once - used for both filter and write_access check
|
|
groups = await Groups.get_groups_by_member_id(user.id, db=db)
|
|
user_group_ids = {group.id for group in groups}
|
|
|
|
if not (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL):
|
|
if groups:
|
|
filter['group_ids'] = [group.id for group in groups]
|
|
|
|
filter['user_id'] = user.id
|
|
|
|
result = await Prompts.search_prompts(user.id, filter=filter, skip=skip, limit=limit, db=db)
|
|
|
|
# Batch-fetch writable prompt IDs in a single query instead of N has_access calls
|
|
prompt_ids = [prompt.id for prompt in result.items]
|
|
writable_prompt_ids = await AccessGrants.get_accessible_resource_ids(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_ids=prompt_ids,
|
|
permission='write',
|
|
user_group_ids=user_group_ids,
|
|
db=db,
|
|
)
|
|
|
|
return PromptAccessListResponse(
|
|
items=[
|
|
PromptAccessResponse(
|
|
**prompt.model_dump(),
|
|
write_access=(
|
|
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
|
or user.id == prompt.user_id
|
|
or prompt.id in writable_prompt_ids
|
|
),
|
|
)
|
|
for prompt in result.items
|
|
],
|
|
total=result.total,
|
|
)
|
|
|
|
|
|
############################
|
|
# CreateNewPrompt
|
|
############################
|
|
|
|
|
|
@router.post('/create', response_model=PromptModel | None)
|
|
async def create_new_prompt(
|
|
request: Request,
|
|
form_data: PromptForm,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
if user.role != 'admin' and not (
|
|
await has_permission(
|
|
user.id,
|
|
'workspace.prompts',
|
|
await Config.get('user.permissions'),
|
|
db=db,
|
|
)
|
|
or await has_permission(
|
|
user.id,
|
|
'workspace.prompts_import',
|
|
await Config.get('user.permissions'),
|
|
db=db,
|
|
)
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.UNAUTHORIZED,
|
|
)
|
|
|
|
form_data.access_grants = await filter_allowed_access_grants(
|
|
await Config.get('user.permissions'),
|
|
user.id,
|
|
user.role,
|
|
form_data.access_grants,
|
|
'sharing.public_prompts',
|
|
)
|
|
|
|
prompt = await Prompts.get_prompt_by_command(form_data.command, db=db)
|
|
if prompt is None:
|
|
prompt = await Prompts.insert_new_prompt(user.id, form_data, db=db)
|
|
|
|
if prompt:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_CREATED,
|
|
actor=user,
|
|
subject_id=prompt.id,
|
|
data={'name': prompt.name, 'command': prompt.command},
|
|
)
|
|
return prompt
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.COMMAND_TAKEN,
|
|
)
|
|
|
|
|
|
############################
|
|
# GetPromptById
|
|
############################
|
|
|
|
|
|
@router.get('/id/{prompt_id}', response_model=PromptAccessResponse | None)
|
|
async def get_prompt_by_id(
|
|
prompt_id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
|
):
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if prompt:
|
|
if (
|
|
user.role == 'admin'
|
|
or prompt.user_id == user.id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='read',
|
|
db=db,
|
|
)
|
|
):
|
|
return PromptAccessResponse(
|
|
**prompt.model_dump(),
|
|
write_access=(
|
|
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
|
or user.id == prompt.user_id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
),
|
|
)
|
|
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
|
|
############################
|
|
# UpdatePromptById
|
|
############################
|
|
|
|
|
|
@router.post('/id/{prompt_id}/update', response_model=PromptModel | None)
|
|
async def update_prompt_by_id(
|
|
request: Request,
|
|
prompt_id: str,
|
|
form_data: PromptForm,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Update a prompt's content, creating a new history entry if changed."""
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
# Is the user the original creator, in a group with write access, or an admin
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
# Check for command collision if command is being changed
|
|
if form_data.command != prompt.command:
|
|
existing_prompt = await Prompts.get_prompt_by_command(form_data.command, db=db)
|
|
if existing_prompt and existing_prompt.id != prompt.id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.COMMAND_TAKEN,
|
|
)
|
|
|
|
form_data.access_grants = await filter_allowed_access_grants(
|
|
await Config.get('user.permissions'),
|
|
user.id,
|
|
user.role,
|
|
form_data.access_grants,
|
|
'sharing.public_prompts',
|
|
)
|
|
|
|
# Use the ID from the found prompt
|
|
updated_prompt = await Prompts.update_prompt_by_id(prompt.id, form_data, user.id, db=db)
|
|
if updated_prompt:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_UPDATED,
|
|
actor=user,
|
|
subject_id=updated_prompt.id,
|
|
data={'name': updated_prompt.name, 'command': updated_prompt.command},
|
|
)
|
|
return updated_prompt
|
|
else:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
)
|
|
|
|
|
|
############################
|
|
# UpdatePromptMetadata
|
|
############################
|
|
|
|
|
|
@router.post('/id/{prompt_id}/update/meta', response_model=PromptModel | None)
|
|
async def update_prompt_metadata(
|
|
request: Request,
|
|
prompt_id: str,
|
|
form_data: PromptMetadataForm,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Update prompt name and command only (no history created)."""
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
# Check for command collision if command is being changed
|
|
if form_data.command != prompt.command:
|
|
existing_prompt = await Prompts.get_prompt_by_command(form_data.command, db=db)
|
|
if existing_prompt and existing_prompt.id != prompt.id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.COMMAND_TAKEN,
|
|
)
|
|
|
|
updated_prompt = await Prompts.update_prompt_metadata(
|
|
prompt.id, form_data.name, form_data.command, form_data.tags, db=db
|
|
)
|
|
if updated_prompt:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_UPDATED,
|
|
actor=user,
|
|
subject_id=updated_prompt.id,
|
|
data={'name': updated_prompt.name, 'command': updated_prompt.command},
|
|
)
|
|
return updated_prompt
|
|
else:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
)
|
|
|
|
|
|
@router.post('/id/{prompt_id}/update/version', response_model=PromptModel | None)
|
|
async def set_prompt_version(
|
|
request: Request,
|
|
prompt_id: str,
|
|
form_data: PromptVersionUpdateForm,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
updated_prompt = await Prompts.update_prompt_version(prompt.id, form_data.version_id, db=db)
|
|
if updated_prompt:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_VERSION_UPDATED,
|
|
actor=user,
|
|
subject_id=updated_prompt.id,
|
|
data={'version_id': updated_prompt.version_id},
|
|
)
|
|
return updated_prompt
|
|
else:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
)
|
|
|
|
|
|
############################
|
|
# UpdatePromptAccessById
|
|
############################
|
|
|
|
|
|
class PromptAccessGrantsForm(BaseModel):
|
|
access_grants: list[dict]
|
|
|
|
|
|
@router.post('/id/{prompt_id}/access/update', response_model=PromptModel | None)
|
|
async def update_prompt_access_by_id(
|
|
request: Request,
|
|
prompt_id: str,
|
|
form_data: PromptAccessGrantsForm,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
form_data.access_grants = await filter_allowed_access_grants(
|
|
await Config.get('user.permissions'),
|
|
user.id,
|
|
user.role,
|
|
form_data.access_grants,
|
|
'sharing.public_prompts',
|
|
)
|
|
|
|
await AccessGrants.set_access_grants('prompt', prompt_id, form_data.access_grants, db=db)
|
|
|
|
updated_prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_ACCESS_UPDATED,
|
|
actor=user,
|
|
subject_id=prompt_id,
|
|
data={'name': updated_prompt.name if updated_prompt else None},
|
|
)
|
|
return updated_prompt
|
|
|
|
|
|
############################
|
|
# TogglePromptActiveById
|
|
############################
|
|
|
|
|
|
@router.post('/id/{prompt_id}/toggle', response_model=PromptModel | None)
|
|
async def toggle_prompt_active(
|
|
request: Request,
|
|
prompt_id: str,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
result = await Prompts.toggle_prompt_active(prompt.id, db=db)
|
|
if result:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_ENABLED if result.is_active else EVENTS.PROMPT_DISABLED,
|
|
actor=user,
|
|
subject_id=result.id,
|
|
subject_type='prompt',
|
|
data={'name': result.name, 'command': result.command},
|
|
)
|
|
return result
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=ERROR_MESSAGES.DEFAULT(),
|
|
)
|
|
|
|
|
|
############################
|
|
# DeletePromptById
|
|
############################
|
|
|
|
|
|
@router.delete('/id/{prompt_id}/delete', response_model=bool)
|
|
async def delete_prompt_by_id(
|
|
request: Request,
|
|
prompt_id: str,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
if (
|
|
prompt.user_id != user.id
|
|
and not await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
and user.role != 'admin'
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
result = await Prompts.delete_prompt_by_id(prompt.id, db=db)
|
|
if result:
|
|
await publish_event(
|
|
request,
|
|
EVENTS.PROMPT_DELETED,
|
|
actor=user,
|
|
subject_id=prompt.id,
|
|
data={'name': prompt.name, 'command': prompt.command},
|
|
)
|
|
return result
|
|
|
|
|
|
############################
|
|
# Prompt History Endpoints
|
|
############################
|
|
|
|
|
|
@router.get('/id/{prompt_id}/history', response_model=list[PromptHistoryResponse])
|
|
async def get_prompt_history(
|
|
prompt_id: str,
|
|
page: int = 0,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Get version history for a prompt."""
|
|
PAGE_SIZE = 20
|
|
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
# Check read access
|
|
if not (
|
|
user.role == 'admin'
|
|
or prompt.user_id == user.id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='read',
|
|
db=db,
|
|
)
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
history = await PromptHistories.get_history_by_prompt_id(prompt.id, limit=PAGE_SIZE, offset=page * PAGE_SIZE, db=db)
|
|
return history
|
|
|
|
|
|
@router.get('/id/{prompt_id}/history/{history_id}', response_model=PromptHistoryModel)
|
|
async def get_prompt_history_entry(
|
|
prompt_id: str,
|
|
history_id: str,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Get a specific version from history."""
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
# Check read access
|
|
if not (
|
|
user.role == 'admin'
|
|
or prompt.user_id == user.id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='read',
|
|
db=db,
|
|
)
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
history_entry = await PromptHistories.get_history_entry_by_id(history_id, db=db)
|
|
if not history_entry or history_entry.prompt_id != prompt.id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
return history_entry
|
|
|
|
|
|
@router.delete('/id/{prompt_id}/history/{history_id}', response_model=bool)
|
|
async def delete_prompt_history_entry(
|
|
prompt_id: str,
|
|
history_id: str,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Delete a history entry. Cannot delete the active production version."""
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
# Check write access
|
|
if not (
|
|
user.role == 'admin'
|
|
or prompt.user_id == user.id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='write',
|
|
db=db,
|
|
)
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
# Cannot delete active production version
|
|
if prompt.version_id == history_id:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail='Cannot delete the active production version',
|
|
)
|
|
|
|
success = await PromptHistories.delete_history_entry(history_id, prompt.id, db=db)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
return success
|
|
|
|
|
|
@router.get('/id/{prompt_id}/history/diff')
|
|
async def get_prompt_diff(
|
|
prompt_id: str,
|
|
from_id: str,
|
|
to_id: str,
|
|
user=Depends(get_verified_user),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""Get diff between two versions."""
|
|
prompt = await Prompts.get_prompt_by_id(prompt_id, db=db)
|
|
|
|
if not prompt:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
# Check read access
|
|
if not (
|
|
user.role == 'admin'
|
|
or prompt.user_id == user.id
|
|
or await AccessGrants.has_access(
|
|
user_id=user.id,
|
|
resource_type='prompt',
|
|
resource_id=prompt.id,
|
|
permission='read',
|
|
db=db,
|
|
)
|
|
):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
|
|
)
|
|
|
|
diff = await PromptHistories.compute_diff(from_id, to_id, prompt.id, db=db)
|
|
if not diff:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=ERROR_MESSAGES.NOT_FOUND,
|
|
)
|
|
|
|
return diff
|