This commit is contained in:
Timothy Jaeryang Baek committed 2026-06-29 13:03:14 -05:00
1 parent 7ea7680f56
commit 517cd8d102
45 files changed
+1293 -969

No files matched your search

+3 -8
View File
@@ -292,8 +292,7 @@ async def update_audio_config(request: Request, form_data: AudioConfigUpdateForm
if form_data.stt.ENGINE == '':
request.app.state.faster_whisper_model = await asyncio.to_thread(
set_faster_whisper_model,
form_data.stt.WHISPER_MODEL, WHISPER_MODEL_AUTO_UPDATE
set_faster_whisper_model, form_data.stt.WHISPER_MODEL, WHISPER_MODEL_AUTO_UPDATE
)
else:
request.app.state.faster_whisper_model = None
@@ -560,9 +559,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
detail=ERROR_MESSAGES.NOT_FOUND,
)
if user.role != 'admin' and not await has_permission(
user.id, 'chat.tts', await Config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'chat.tts', await Config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -1152,9 +1149,7 @@ async def transcription(
language: Optional[str] = Form(None),
user=Depends(get_verified_user),
):
if user.role != 'admin' and not await has_permission(
user.id, 'chat.stt', await Config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'chat.stt', await Config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+10 -10
View File
@@ -186,7 +186,8 @@ async def create_session_response(
request,
EVENTS.AUTH_LOGIN,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
source=source,
data={'auth_method': source},
)
@@ -386,7 +387,8 @@ async def update_password(
request,
EVENTS.AUTH_PASSWORD_CHANGED,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return success
else:
@@ -1372,9 +1374,7 @@ async def update_oauth_config(request: Request, form_data: OAuthConfigForm, user
async def _check_api_key_permission(request: Request, user, db: AsyncSession):
if not await Config.get('auth.enable_api_keys') or (
user.role != 'admin'
and not await has_permission(
user.id, 'features.api_keys', await Config.get('user.permissions'), db=db
)
and not await has_permission(user.id, 'features.api_keys', await Config.get('user.permissions'), db=db)
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
@@ -1397,7 +1397,8 @@ async def generate_api_key(
request,
EVENTS.AUTH_API_KEY_CREATED,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return {
'api_key': api_key,
@@ -1418,16 +1419,15 @@ async def delete_api_key(
request,
EVENTS.AUTH_API_KEY_DELETED,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return success
# get api key
@router.get('/api_key', response_model=ApiKey)
async def get_api_key(
request: Request, user=Depends(get_current_user), db: AsyncSession = Depends(get_async_session)
):
async def get_api_key(request: Request, user=Depends(get_current_user), db: AsyncSession = Depends(get_async_session)):
await _check_api_key_permission(request, user, db)
api_key = await Users.get_user_api_key_by_id(user.id, db=db)
if api_key:
+2 -1
View File
@@ -266,7 +266,8 @@ async def toggle_automation_by_id(
request,
EVENTS.AUTOMATION_ENABLED if toggled.is_active else EVENTS.AUTOMATION_DISABLED,
actor=user,
subject_id=toggled.id, subject_type='automation',
subject_id=toggled.id,
subject_type='automation',
data={'name': toggled.name},
)
return response
+1 -3
View File
@@ -42,9 +42,7 @@ async def check_calendar_permission(request: Request, user):
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.UNAUTHORIZED,
)
if user.role != 'admin' and not await has_permission(
user.id, 'features.calendar', config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'features.calendar', config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.UNAUTHORIZED,
+18 -12
View File
@@ -575,9 +575,7 @@ async def delete_all_user_chats(
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
if user.role == 'user' and not await has_permission(
user.id, 'chat.delete', await Config.get('user.permissions')
):
if user.role == 'user' and not await has_permission(user.id, 'chat.delete', await Config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -589,7 +587,8 @@ async def delete_all_user_chats(
request,
EVENTS.CHAT_DELETED_ALL,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return result
@@ -1001,7 +1000,8 @@ async def unshare_all_chats(
request,
EVENTS.CHAT_UNSHARED,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
data={'count': len(chat_ids), 'chat_ids': chat_ids},
)
return result
@@ -1215,6 +1215,12 @@ async def update_chat_by_id(
chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db)
if chat:
updated_chat = {**chat.chat, **form_data.chat}
if 'history' in form_data.chat:
updated_chat['history'] = Chats.merge_history(
chat.chat.get('history'),
form_data.chat.get('history'),
form_data.deleted_message_ids,
)
# Re-derive content from output for assistant messages so that frontend
# edits to output items are reflected in content. Only when output
@@ -1222,7 +1228,7 @@ async def update_chat_by_id(
# (e.g. a `replace` event or an outlet filter footer) would be reverted.
existing_messages = (chat.chat.get('history') or {}).get('messages') or {}
for msg_id, msg in updated_chat.get('history', {}).get('messages', {}).items():
if msg.get('role') == 'assistant' and msg.get('output'):
if isinstance(msg, dict) and msg.get('role') == 'assistant' and msg.get('output'):
if msg.get('output') != existing_messages.get(msg_id, {}).get('output'):
msg['content'] = serialize_output(msg['output'])
@@ -1233,7 +1239,7 @@ async def update_chat_by_id(
# history with potential edits, deletions, or new branches.
messages = (updated_chat.get('history') or {}).get('messages') or {}
if messages:
await Chats.reconcile_messages_by_chat_id(id, user.id, messages)
await Chats.reconcile_messages_by_chat_id(id, user.id, messages, form_data.deleted_message_ids)
await publish_event(
request,
@@ -1470,7 +1476,8 @@ async def pin_chat_by_id(
request,
EVENTS.CHAT_PINNED if chat.pinned else EVENTS.CHAT_UNPINNED,
actor=user,
subject_id=id, subject_type='chat',
subject_id=id,
subject_type='chat',
)
return chat
else:
@@ -1643,7 +1650,8 @@ async def archive_chat_by_id(
request,
EVENTS.CHAT_ARCHIVED if chat.archived else EVENTS.CHAT_UNARCHIVED,
actor=user,
subject_id=id, subject_type='chat',
subject_id=id,
subject_type='chat',
)
return ChatResponse(**chat.model_dump())
else:
@@ -1660,9 +1668,7 @@ async def share_chat_by_id(
user=Depends(get_verified_user),
db: AsyncSession = Depends(get_async_session),
):
if user.role != 'admin' and not await has_permission(
user.id, 'chat.share', await Config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'chat.share', await Config.get('user.permissions')):
raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.ACCESS_PROHIBITED)
chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db)
+15 -10
View File
@@ -144,7 +144,8 @@ async def set_connections_config(
request,
EVENTS.CONFIG_CONNECTIONS_UPDATED,
actor=user,
subject_id='connections', subject_type='config',
subject_id='connections',
subject_type='config',
data=values,
)
return values
@@ -262,9 +263,7 @@ async def set_tool_servers_config(
if auth_type in ('oauth_2.1', 'oauth_2.1_static') and server_id:
try:
oauth_client_info = resolve_oauth_client_info(connection)
oauth_client_info = await recover_static_oauth_client_metadata(
connection, oauth_client_info
)
oauth_client_info = await recover_static_oauth_client_metadata(connection, oauth_client_info)
oauth_client_info = apply_connection_oauth_options(connection, oauth_client_info)
request.app.state.oauth_client_manager.add_client(
f'{server_type}:{server_id}',
@@ -278,7 +277,8 @@ async def set_tool_servers_config(
request,
EVENTS.CONFIG_TOOL_SERVERS_UPDATED,
actor=user,
subject_id='tool_server.connections', subject_type='config',
subject_id='tool_server.connections',
subject_type='config',
data={'count': len(connections), 'types': [connection.get('type', 'openapi') for connection in connections]},
)
return {'TOOL_SERVER_CONNECTIONS': connections}
@@ -330,7 +330,8 @@ async def set_terminal_servers_config(
request,
EVENTS.CONFIG_TERMINAL_SERVERS_UPDATED,
actor=user,
subject_id='terminal_server.connections', subject_type='config',
subject_id='terminal_server.connections',
subject_type='config',
data={'count': len(connections)},
)
return {'TERMINAL_SERVER_CONNECTIONS': connections}
@@ -699,7 +700,8 @@ async def set_code_execution_config(
request,
EVENTS.CONFIG_CODE_EXECUTION_UPDATED,
actor=user,
subject_id='code_execution', subject_type='config',
subject_id='code_execution',
subject_type='config',
data={
'code_execution_enabled': values.get('ENABLE_CODE_EXECUTION'),
'code_execution_engine': values.get('CODE_EXECUTION_ENGINE'),
@@ -741,7 +743,8 @@ async def set_models_config(request: Request, form_data: ModelsConfigForm, user=
request,
EVENTS.CONFIG_MODELS_UPDATED,
actor=user,
subject_id='models', subject_type='config',
subject_id='models',
subject_type='config',
data={
'default_models': values.get('DEFAULT_MODELS'),
'default_pinned_models': values.get('DEFAULT_PINNED_MODELS'),
@@ -773,7 +776,8 @@ async def set_default_suggestions(
request,
EVENTS.CONFIG_SUGGESTIONS_UPDATED,
actor=user,
subject_id='ui.prompt_suggestions', subject_type='config',
subject_id='ui.prompt_suggestions',
subject_type='config',
data={'count': len(suggestions or [])},
)
return suggestions
@@ -801,7 +805,8 @@ async def set_banners(
request,
EVENTS.CONFIG_BANNERS_UPDATED,
actor=user,
subject_id='ui.banners', subject_type='config',
subject_id='ui.banners',
subject_type='config',
data={'count': len(banners or [])},
)
return banners
+2 -1
View File
@@ -373,7 +373,8 @@ async def delete_feedbacks(
request,
EVENTS.FEEDBACK_DELETED_ALL,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return success
+4 -4
View File
@@ -270,9 +270,7 @@ async def upload_file(
result_meta = result.meta or {}
result_content_type = (
result_meta.get('content_type')
if isinstance(result_meta, dict)
else getattr(result_meta, 'content_type', None)
result_meta.get('content_type') if isinstance(result_meta, dict) else getattr(result_meta, 'content_type', None)
)
await publish_event(
request,
@@ -532,7 +530,9 @@ async def count_files(
@router.delete('/all')
async def delete_all_files(request: Request, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)):
async def delete_all_files(
request: Request, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)
):
result = await Files.delete_all_files(db=db)
if result:
try:
+22 -22
View File
@@ -182,9 +182,7 @@ async def get_shared_folders(
groups = await Groups.get_groups_by_member_id(user.id, db=db)
group_ids = {g.id for g in groups}
folder_perms = await Folders.get_shared_folder_ids_for_user(
user.id, group_ids, db=db
)
folder_perms = await Folders.get_shared_folder_ids_for_user(user.id, group_ids, db=db)
# Filter out folders owned by the user
results = []
@@ -199,28 +197,30 @@ async def get_shared_folders(
owner = await Users.get_user_by_id(folder.user_id, db=db)
owner_cache[folder.user_id] = owner.name if owner else 'Unknown'
results.append({
**folder.model_dump(),
'owner_name': owner_cache[folder.user_id],
'permission': permission,
})
results.append(
{
**folder.model_dump(),
'owner_name': owner_cache[folder.user_id],
'permission': permission,
}
)
# Also include child folders of shared folders (inheritance)
shared_root_ids = {r['id'] for r in results}
for root_id in list(shared_root_ids):
root_folder = await Folders.get_folder_by_id(root_id, db=db)
if root_folder:
children = await Folders.get_children_folders_by_id_and_user_id(
root_id, root_folder.user_id, db=db
)
children = await Folders.get_children_folders_by_id_and_user_id(root_id, root_folder.user_id, db=db)
if children:
for child in children:
if child.id not in {r['id'] for r in results}:
results.append({
**child.model_dump(),
'owner_name': owner_cache.get(child.user_id, 'Unknown'),
'permission': folder_perms.get(root_id, 'read'),
})
results.append(
{
**child.model_dump(),
'owner_name': owner_cache.get(child.user_id, 'Unknown'),
'permission': folder_perms.get(root_id, 'read'),
}
)
return results
@@ -231,7 +231,9 @@ async def get_shared_folders(
@router.get('/{id}', response_model=None)
async def get_folder_by_id(request: Request, id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
async def get_folder_by_id(
request: Request, id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
):
await check_folders_permission(request, user, db=db)
folder = await Folders.get_folder_by_id_and_user_id(id, user.id, db=db)
if folder:
@@ -443,7 +445,8 @@ async def update_folder_access_by_id(
form_data.access_grants = await filter_allowed_access_grants(
await Config.get('user.permissions'),
user.id, user.role,
user.id,
user.role,
form_data.access_grants,
None,
db=db,
@@ -509,10 +512,7 @@ async def get_shared_folder_chats(
chat['owner_name'] = owner_cache[uid]
return {
'chats': [
{**chat, 'readonly': chat['user_id'] != user.id}
for chat in chats
],
'chats': [{**chat, 'readonly': chat['user_id'] != user.id} for chat in chats],
'folder_permission': 'write' if has_write else 'read',
}
+2 -1
View File
@@ -291,7 +291,8 @@ async def toggle_function_by_id(
request,
EVENTS.FUNCTION_ENABLED if function.is_active else EVENTS.FUNCTION_DISABLED,
actor=user,
subject_id=function.id, subject_type='function',
subject_id=function.id,
subject_type='function',
data={'type': function.type, 'name': function.name},
)
return function
+11 -32
View File
@@ -201,25 +201,12 @@ async def set_image_model(request: Request, model: str):
async def get_image_model(request):
image_config = await get_image_config()
if image_config.IMAGE_GENERATION_ENGINE == 'openai':
return (
image_config.IMAGE_GENERATION_MODEL
if image_config.IMAGE_GENERATION_MODEL
else 'dall-e-2'
)
return image_config.IMAGE_GENERATION_MODEL if image_config.IMAGE_GENERATION_MODEL else 'dall-e-2'
elif image_config.IMAGE_GENERATION_ENGINE == 'gemini':
return (
image_config.IMAGE_GENERATION_MODEL
if image_config.IMAGE_GENERATION_MODEL
else 'imagen-3.0-generate-002'
)
return image_config.IMAGE_GENERATION_MODEL if image_config.IMAGE_GENERATION_MODEL else 'imagen-3.0-generate-002'
elif image_config.IMAGE_GENERATION_ENGINE == 'comfyui':
return (
image_config.IMAGE_GENERATION_MODEL if image_config.IMAGE_GENERATION_MODEL else ''
)
elif (
image_config.IMAGE_GENERATION_ENGINE == 'automatic1111'
or image_config.IMAGE_GENERATION_ENGINE == ''
):
return image_config.IMAGE_GENERATION_MODEL if image_config.IMAGE_GENERATION_MODEL else ''
elif image_config.IMAGE_GENERATION_ENGINE == 'automatic1111' or image_config.IMAGE_GENERATION_ENGINE == '':
try:
session = await get_session()
async with session.get(
@@ -433,10 +420,7 @@ async def get_models(request: Request, user=Depends(get_verified_user)):
info['CheckpointLoaderSimple']['input']['required']['ckpt_name'][0],
)
)
elif (
image_config.IMAGE_GENERATION_ENGINE == 'automatic1111'
or image_config.IMAGE_GENERATION_ENGINE == ''
):
elif image_config.IMAGE_GENERATION_ENGINE == 'automatic1111' or image_config.IMAGE_GENERATION_ENGINE == '':
session = await get_session()
async with session.get(
url=f'{image_config.AUTOMATIC1111_BASE_URL}/sdapi/v1/sd-models',
@@ -592,7 +576,8 @@ async def generate_images(request: Request, form_data: CreateImageForm, user=Dep
request,
EVENTS.IMAGE_GENERATED,
actor=user,
subject_id=None, subject_type='image',
subject_id=None,
subject_type='image',
data={
'model': form_data.model,
'size': form_data.size,
@@ -658,11 +643,7 @@ async def image_generations(
)
else {'response_format': 'b64_json'}
),
**(
{}
if not image_config.IMAGES_OPENAI_API_PARAMS
else image_config.IMAGES_OPENAI_API_PARAMS
),
**({} if not image_config.IMAGES_OPENAI_API_PARAMS else image_config.IMAGES_OPENAI_API_PARAMS),
}
session = await get_session()
@@ -803,10 +784,7 @@ async def image_generations(
)
images.append({'url': url})
return images
elif (
image_config.IMAGE_GENERATION_ENGINE == 'automatic1111'
or image_config.IMAGE_GENERATION_ENGINE == ''
):
elif image_config.IMAGE_GENERATION_ENGINE == 'automatic1111' or image_config.IMAGE_GENERATION_ENGINE == '':
if form_data.model:
await set_image_model(request, form_data.model)
@@ -892,7 +870,8 @@ async def edit_images(request: Request, form_data: EditImageForm, user=Depends(g
request,
EVENTS.IMAGE_EDITED,
actor=user,
subject_id=None, subject_type='image',
subject_id=None,
subject_type='image',
data={
'model': form_data.model,
'size': form_data.size,
+4 -4
View File
@@ -570,7 +570,9 @@ async def _set_external_connections(connections: list[dict]) -> None:
await Config.upsert({EXTERNAL_KNOWLEDGE_CONNECTIONS_CONFIG_KEY: connections})
def _external_connection_dict(form_data: ExternalKnowledgeConnectionForm, user_id: str, id: Optional[str] = None) -> dict:
def _external_connection_dict(
form_data: ExternalKnowledgeConnectionForm, user_id: str, id: Optional[str] = None
) -> dict:
provider, config = _validate_external_connection_form(form_data)
now = int(time.time())
return {
@@ -1703,9 +1705,7 @@ async def delete_knowledge_by_id(
connection_id = (knowledge.meta or {}).get('external', {}).get('connection_id')
if connection_id:
connections = [
connection
for connection in await _get_external_connections()
if connection.get('id') != connection_id
connection for connection in await _get_external_connections() if connection.get('id') != connection_id
]
await _set_external_connections(connections)
else:
+5 -5
View File
@@ -39,9 +39,7 @@ async def check_memories_permission(user):
detail=ERROR_MESSAGES.NOT_FOUND,
)
if user.role != 'admin' and not await has_permission(
user.id, 'features.memories', config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'features.memories', config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -431,7 +429,8 @@ async def reset_memory_from_vector_db(
request,
EVENTS.MEMORY_RESET,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
data={'count': len(memories)},
)
return True
@@ -461,7 +460,8 @@ async def delete_memory_by_user_id(
request,
EVENTS.MEMORY_DELETED,
actor=user,
subject_id=user.id, subject_type='user',
subject_id=user.id,
subject_type='user',
)
return True
+5 -2
View File
@@ -654,7 +654,8 @@ async def toggle_model_by_id(
request,
EVENTS.MODEL_ENABLED if model.is_active else EVENTS.MODEL_DISABLED,
actor=user,
subject_id=model.id, subject_type='model',
subject_id=model.id,
subject_type='model',
data={'name': model.name},
)
return model
@@ -867,7 +868,9 @@ async def delete_model_by_id(
@router.delete('/delete/all', response_model=bool)
async def delete_all_models(request: Request, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)):
async def delete_all_models(
request: Request, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)
):
result = await Models.delete_all_models(db=db)
if result:
await publish_event(request, EVENTS.MODEL_DELETED, actor=user, subject_type='model')
+2 -1
View File
@@ -479,7 +479,8 @@ async def pin_note_by_id(
request,
EVENTS.NOTE_PINNED if note.is_pinned else EVENTS.NOTE_UNPINNED,
actor=user,
subject_id=note.id, subject_type='note',
subject_id=note.id,
subject_type='note',
)
return note
+1 -3
View File
@@ -356,9 +356,7 @@ async def update_config(request: Request, form_data: OpenAIConfigForm, user=Depe
@router.post('/audio/speech')
async def speech(request: Request, user=Depends(get_verified_user)):
if user.role != 'admin' and not await has_permission(
user.id, 'chat.tts', await Config.get('user.permissions')
):
if user.role != 'admin' and not await has_permission(user.id, 'chat.tts', await Config.get('user.permissions')):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+2 -1
View File
@@ -532,7 +532,8 @@ async def toggle_prompt_active(
request,
EVENTS.PROMPT_ENABLED if result.is_active else EVENTS.PROMPT_DISABLED,
actor=user,
subject_id=result.id, subject_type='prompt',
subject_id=result.id,
subject_type='prompt',
data={'name': result.name, 'command': result.command},
)
return result
+40 -98
View File
@@ -196,9 +196,7 @@ def get_rf(
)
except Exception as e:
log.error(f'ExternalReranking: {e}')
raise Exception(
ERROR_MESSAGES.DEFAULT(e, 'Error loading reranking model')
)
raise Exception(ERROR_MESSAGES.DEFAULT(e, 'Error loading reranking model'))
else:
import sentence_transformers
import torch
@@ -516,9 +514,7 @@ async def unload_embedding_model(request: Request):
@router.post('/embedding/update')
async def update_embedding_config(request: Request, form_data: EmbeddingModelUpdateForm, user=Depends(get_admin_user)):
config = await get_retrieval_config()
log.info(
f'Updating embedding model: {config.RAG_EMBEDDING_MODEL} to {form_data.RAG_EMBEDDING_MODEL}'
)
log.info(f'Updating embedding model: {config.RAG_EMBEDDING_MODEL} to {form_data.RAG_EMBEDDING_MODEL}')
await unload_embedding_model(request)
try:
config.RAG_EMBEDDING_ENGINE = form_data.RAG_EMBEDDING_ENGINE
@@ -574,9 +570,7 @@ async def update_embedding_config(request: Request, form_data: EmbeddingModelUpd
),
config.RAG_EMBEDDING_BATCH_SIZE,
azure_api_version=(
config.RAG_AZURE_OPENAI_API_VERSION
if config.RAG_EMBEDDING_ENGINE == 'azure_openai'
else None
config.RAG_AZURE_OPENAI_API_VERSION if config.RAG_EMBEDDING_ENGINE == 'azure_openai' else None
),
enable_async=config.ENABLE_ASYNC_EMBEDDING,
concurrent_requests=config.RAG_EMBEDDING_CONCURRENT_REQUESTS,
@@ -933,9 +927,7 @@ class ConfigForm(BaseModel):
async def update_rag_config(request: Request, form_data: ConfigForm, user=Depends(get_admin_user)):
# RAG settings
config = await get_retrieval_config()
config.RAG_TEMPLATE = (
form_data.RAG_TEMPLATE if form_data.RAG_TEMPLATE is not None else config.RAG_TEMPLATE
)
config.RAG_TEMPLATE = form_data.RAG_TEMPLATE if form_data.RAG_TEMPLATE is not None else config.RAG_TEMPLATE
config.TOP_K = form_data.TOP_K if form_data.TOP_K is not None else config.TOP_K
config.BYPASS_EMBEDDING_AND_RETRIEVAL = (
form_data.BYPASS_EMBEDDING_AND_RETRIEVAL
@@ -943,9 +935,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.BYPASS_EMBEDDING_AND_RETRIEVAL
)
config.RAG_FULL_CONTEXT = (
form_data.RAG_FULL_CONTEXT
if form_data.RAG_FULL_CONTEXT is not None
else config.RAG_FULL_CONTEXT
form_data.RAG_FULL_CONTEXT if form_data.RAG_FULL_CONTEXT is not None else config.RAG_FULL_CONTEXT
)
# Hybrid search settings
@@ -960,18 +950,12 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS
)
config.TOP_K_RERANKER = (
form_data.TOP_K_RERANKER if form_data.TOP_K_RERANKER is not None else config.TOP_K_RERANKER
)
config.TOP_K_RERANKER = form_data.TOP_K_RERANKER if form_data.TOP_K_RERANKER is not None else config.TOP_K_RERANKER
config.RELEVANCE_THRESHOLD = (
form_data.RELEVANCE_THRESHOLD
if form_data.RELEVANCE_THRESHOLD is not None
else config.RELEVANCE_THRESHOLD
form_data.RELEVANCE_THRESHOLD if form_data.RELEVANCE_THRESHOLD is not None else config.RELEVANCE_THRESHOLD
)
config.HYBRID_BM25_WEIGHT = (
form_data.HYBRID_BM25_WEIGHT
if form_data.HYBRID_BM25_WEIGHT is not None
else config.HYBRID_BM25_WEIGHT
form_data.HYBRID_BM25_WEIGHT if form_data.HYBRID_BM25_WEIGHT is not None else config.HYBRID_BM25_WEIGHT
)
# Content extraction settings
@@ -981,9 +965,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.CONTENT_EXTRACTION_ENGINE
)
config.PDF_EXTRACT_IMAGES = (
form_data.PDF_EXTRACT_IMAGES
if form_data.PDF_EXTRACT_IMAGES is not None
else config.PDF_EXTRACT_IMAGES
form_data.PDF_EXTRACT_IMAGES if form_data.PDF_EXTRACT_IMAGES is not None else config.PDF_EXTRACT_IMAGES
)
config.PDF_LOADER_MODE = (
form_data.PDF_LOADER_MODE if form_data.PDF_LOADER_MODE is not None else config.PDF_LOADER_MODE
@@ -1062,16 +1044,12 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
form_data.TIKA_SERVER_URL if form_data.TIKA_SERVER_URL is not None else config.TIKA_SERVER_URL
)
config.DOCLING_SERVER_URL = (
form_data.DOCLING_SERVER_URL
if form_data.DOCLING_SERVER_URL is not None
else config.DOCLING_SERVER_URL
form_data.DOCLING_SERVER_URL if form_data.DOCLING_SERVER_URL is not None else config.DOCLING_SERVER_URL
)
config.DOCLING_API_KEY = (
form_data.DOCLING_API_KEY if form_data.DOCLING_API_KEY is not None else config.DOCLING_API_KEY
)
config.DOCLING_PARAMS = (
form_data.DOCLING_PARAMS if form_data.DOCLING_PARAMS is not None else config.DOCLING_PARAMS
)
config.DOCLING_PARAMS = form_data.DOCLING_PARAMS if form_data.DOCLING_PARAMS is not None else config.DOCLING_PARAMS
config.DOCUMENT_INTELLIGENCE_ENDPOINT = (
form_data.DOCUMENT_INTELLIGENCE_ENDPOINT
if form_data.DOCUMENT_INTELLIGENCE_ENDPOINT is not None
@@ -1094,39 +1072,25 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.MISTRAL_OCR_API_BASE_URL
)
config.MISTRAL_OCR_API_KEY = (
form_data.MISTRAL_OCR_API_KEY
if form_data.MISTRAL_OCR_API_KEY is not None
else config.MISTRAL_OCR_API_KEY
form_data.MISTRAL_OCR_API_KEY if form_data.MISTRAL_OCR_API_KEY is not None else config.MISTRAL_OCR_API_KEY
)
config.PADDLEOCR_VL_BASE_URL = (
form_data.PADDLEOCR_VL_BASE_URL
if form_data.PADDLEOCR_VL_BASE_URL is not None
else config.PADDLEOCR_VL_BASE_URL
form_data.PADDLEOCR_VL_BASE_URL if form_data.PADDLEOCR_VL_BASE_URL is not None else config.PADDLEOCR_VL_BASE_URL
)
config.PADDLEOCR_VL_TOKEN = (
form_data.PADDLEOCR_VL_TOKEN
if form_data.PADDLEOCR_VL_TOKEN is not None
else config.PADDLEOCR_VL_TOKEN
form_data.PADDLEOCR_VL_TOKEN if form_data.PADDLEOCR_VL_TOKEN is not None else config.PADDLEOCR_VL_TOKEN
)
# MinerU settings
config.MINERU_API_MODE = (
form_data.MINERU_API_MODE if form_data.MINERU_API_MODE is not None else config.MINERU_API_MODE
)
config.MINERU_API_URL = (
form_data.MINERU_API_URL if form_data.MINERU_API_URL is not None else config.MINERU_API_URL
)
config.MINERU_API_KEY = (
form_data.MINERU_API_KEY if form_data.MINERU_API_KEY is not None else config.MINERU_API_KEY
)
config.MINERU_API_URL = form_data.MINERU_API_URL if form_data.MINERU_API_URL is not None else config.MINERU_API_URL
config.MINERU_API_KEY = form_data.MINERU_API_KEY if form_data.MINERU_API_KEY is not None else config.MINERU_API_KEY
config.MINERU_API_TIMEOUT = (
form_data.MINERU_API_TIMEOUT
if form_data.MINERU_API_TIMEOUT is not None
else config.MINERU_API_TIMEOUT
)
config.MINERU_PARAMS = (
form_data.MINERU_PARAMS if form_data.MINERU_PARAMS is not None else config.MINERU_PARAMS
form_data.MINERU_API_TIMEOUT if form_data.MINERU_API_TIMEOUT is not None else config.MINERU_API_TIMEOUT
)
config.MINERU_PARAMS = form_data.MINERU_PARAMS if form_data.MINERU_PARAMS is not None else config.MINERU_PARAMS
config.MINERU_FILE_EXTENSIONS = (
form_data.MINERU_FILE_EXTENSIONS
if form_data.MINERU_FILE_EXTENSIONS is not None
@@ -1147,9 +1111,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
if torch.cuda.is_available():
torch.cuda.empty_cache()
config.RAG_RERANKING_ENGINE = (
form_data.RAG_RERANKING_ENGINE
if form_data.RAG_RERANKING_ENGINE is not None
else config.RAG_RERANKING_ENGINE
form_data.RAG_RERANKING_ENGINE if form_data.RAG_RERANKING_ENGINE is not None else config.RAG_RERANKING_ENGINE
)
config.RAG_EXTERNAL_RERANKER_URL = (
@@ -1176,21 +1138,14 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.RAG_RERANKING_BATCH_SIZE
)
log.info(
f'Updating reranking model: {config.RAG_RERANKING_MODEL} to {form_data.RAG_RERANKING_MODEL}'
)
log.info(f'Updating reranking model: {config.RAG_RERANKING_MODEL} to {form_data.RAG_RERANKING_MODEL}')
try:
config.RAG_RERANKING_MODEL = (
form_data.RAG_RERANKING_MODEL
if form_data.RAG_RERANKING_MODEL is not None
else config.RAG_RERANKING_MODEL
form_data.RAG_RERANKING_MODEL if form_data.RAG_RERANKING_MODEL is not None else config.RAG_RERANKING_MODEL
)
try:
if (
config.ENABLE_RAG_HYBRID_SEARCH
and not config.BYPASS_EMBEDDING_AND_RETRIEVAL
):
if config.ENABLE_RAG_HYBRID_SEARCH and not config.BYPASS_EMBEDDING_AND_RETRIEVAL:
request.app.state.rf = get_rf(
config.RAG_RERANKING_ENGINE,
config.RAG_RERANKING_MODEL,
@@ -1216,25 +1171,17 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
)
# Chunking settings
config.TEXT_SPLITTER = (
form_data.TEXT_SPLITTER if form_data.TEXT_SPLITTER is not None else config.TEXT_SPLITTER
)
config.TEXT_SPLITTER = form_data.TEXT_SPLITTER if form_data.TEXT_SPLITTER is not None else config.TEXT_SPLITTER
config.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER = (
form_data.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER
if form_data.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER is not None
else config.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER
)
config.CHUNK_SIZE = (
form_data.CHUNK_SIZE if form_data.CHUNK_SIZE is not None else config.CHUNK_SIZE
)
config.CHUNK_SIZE = form_data.CHUNK_SIZE if form_data.CHUNK_SIZE is not None else config.CHUNK_SIZE
config.CHUNK_MIN_SIZE_TARGET = (
form_data.CHUNK_MIN_SIZE_TARGET
if form_data.CHUNK_MIN_SIZE_TARGET is not None
else config.CHUNK_MIN_SIZE_TARGET
)
config.CHUNK_OVERLAP = (
form_data.CHUNK_OVERLAP if form_data.CHUNK_OVERLAP is not None else config.CHUNK_OVERLAP
form_data.CHUNK_MIN_SIZE_TARGET if form_data.CHUNK_MIN_SIZE_TARGET is not None else config.CHUNK_MIN_SIZE_TARGET
)
config.CHUNK_OVERLAP = form_data.CHUNK_OVERLAP if form_data.CHUNK_OVERLAP is not None else config.CHUNK_OVERLAP
config.RAG_TOKENIZER_MODEL = (
form_data.RAG_TOKENIZER_MODEL.strip()
if form_data.RAG_TOKENIZER_MODEL is not None
@@ -1287,9 +1234,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
config.WEB_FETCH_MAX_CONTENT_LENGTH = form_data.web.WEB_FETCH_MAX_CONTENT_LENGTH
config.WEB_LOADER_CONCURRENT_REQUESTS = form_data.web.WEB_LOADER_CONCURRENT_REQUESTS
config.WEB_SEARCH_DOMAIN_FILTER_LIST = form_data.web.WEB_SEARCH_DOMAIN_FILTER_LIST
config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (
form_data.web.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
)
config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = form_data.web.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
config.BYPASS_WEB_SEARCH_WEB_LOADER = form_data.web.BYPASS_WEB_SEARCH_WEB_LOADER
config.OLLAMA_CLOUD_WEB_SEARCH_API_KEY = form_data.web.OLLAMA_CLOUD_WEB_SEARCH_API_KEY
config.SEARXNG_QUERY_URL = form_data.web.SEARXNG_QUERY_URL
@@ -1802,9 +1747,7 @@ def save_docs_to_vector_db(
),
config.RAG_EMBEDDING_BATCH_SIZE,
azure_api_version=(
config.RAG_AZURE_OPENAI_API_VERSION
if config.RAG_EMBEDDING_ENGINE == 'azure_openai'
else None
config.RAG_AZURE_OPENAI_API_VERSION if config.RAG_EMBEDDING_ENGINE == 'azure_openai' else None
),
enable_async=config.ENABLE_ASYNC_EMBEDDING,
concurrent_requests=config.RAG_EMBEDDING_CONCURRENT_REQUESTS,
@@ -1997,7 +1940,8 @@ async def process_file(
request,
EVENTS.RETRIEVAL_CONTENT_PROCESSED,
actor=user,
subject_id=file.id, subject_type='file',
subject_id=file.id,
subject_type='file',
data={'collection_name': None, 'filename': file.filename},
)
return {
@@ -2056,7 +2000,8 @@ async def process_file(
request,
EVENTS.RETRIEVAL_CONTENT_PROCESSED,
actor=user,
subject_id=file.id, subject_type='file',
subject_id=file.id,
subject_type='file',
data={'collection_name': collection_name, 'filename': file.filename},
)
return {
@@ -2131,7 +2076,8 @@ async def process_text(
request,
EVENTS.RETRIEVAL_CONTENT_PROCESSED,
actor=user,
subject_id=collection_name, subject_type='retrieval.collection',
subject_id=collection_name,
subject_type='retrieval.collection',
data={'name': form_data.name, 'content_preview': text_content[:300]},
)
return {
@@ -2451,11 +2397,7 @@ async def search_web(request: Request, engine: str, query: str, user=None) -> li
config.WEB_SEARCH_DOMAIN_FILTER_LIST,
)
elif engine == 'azure':
if (
config.AZURE_AI_SEARCH_API_KEY
and config.AZURE_AI_SEARCH_ENDPOINT
and config.AZURE_AI_SEARCH_INDEX_NAME
):
if config.AZURE_AI_SEARCH_API_KEY and config.AZURE_AI_SEARCH_ENDPOINT and config.AZURE_AI_SEARCH_INDEX_NAME:
return await asyncio.to_thread(
search_azure,
config.AZURE_AI_SEARCH_API_KEY,
@@ -2570,9 +2512,7 @@ async def process_web_search(request: Request, form_data: SearchForm, user=Depen
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
if user.role != 'admin' and not await has_permission(
user.id, 'features.web_search', config.USER_PERMISSIONS
):
if user.role != 'admin' and not await has_permission(user.id, 'features.web_search', config.USER_PERMISSIONS):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
@@ -2980,7 +2920,8 @@ async def reset_upload_dir(request: Request, user=Depends(get_admin_user)) -> bo
request,
EVENTS.RETRIEVAL_UPLOADS_RESET,
actor=user,
subject_id='all', subject_type='file.uploads',
subject_id='all',
subject_type='file.uploads',
)
return True
@@ -3117,7 +3058,8 @@ async def process_files_batch(
request,
EVENTS.RETRIEVAL_CONTENT_PROCESSED,
actor=user,
subject_id=collection_name, subject_type='retrieval.collection',
subject_id=collection_name,
subject_type='retrieval.collection',
data={
'count': len([item for item in file_results if item.status == 'completed']),
'errors': len(file_errors),
+2 -6
View File
@@ -159,12 +159,8 @@ async def create_new_skill(
db: AsyncSession = Depends(get_async_session),
):
if user.role != 'admin' and not (
await has_permission(
user.id, 'workspace.skills', await Config.get('user.permissions'), db=db
)
or await has_permission(
user.id, 'workspace.skills_import', await Config.get('user.permissions'), db=db
)
await has_permission(user.id, 'workspace.skills', await Config.get('user.permissions'), db=db)
or await has_permission(user.id, 'workspace.skills_import', await Config.get('user.permissions'), db=db)
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
+1 -2
View File
@@ -76,8 +76,7 @@ async def get_tools(
for tool in await Tools.get_tools(defer_content=True, db=db):
tool_module = tools_cache.get(tool.id)
has_user_valves = (
hasattr(tool_module, 'UserValves') if tool_module
else (tool.meta.has_user_valves if tool.meta else False)
hasattr(tool_module, 'UserValves') if tool_module else (tool.meta.has_user_valves if tool.meta else False)
)
tools.append(
ToolUserResponse(
+4 -2
View File
@@ -280,7 +280,8 @@ async def update_default_user_permissions(request: Request, form_data: UserPermi
request,
EVENTS.USER_PERMISSIONS_UPDATED,
actor=user,
subject_id='user.permissions', subject_type='config',
subject_id='user.permissions',
subject_type='config',
)
return user_permissions
@@ -691,7 +692,8 @@ async def update_user_by_id(
request,
EVENTS.AUTH_PASSWORD_CHANGED,
actor=session_user,
subject_id=user_id, subject_type='user',
subject_id=user_id,
subject_type='user',
source='admin',
)
return updated_user