Compare commits

...

1 Commits

Author SHA1 Message Date
Nik
e0c957fdb2 refactor: replace HTTPException with OnyxError in CE features API files
Convert 11 files in onyx/server/features/ (excluding build/ and mcp/)
from HTTPException to OnyxError with appropriate error codes.
2026-03-04 22:51:05 -08:00
11 changed files with 145 additions and 139 deletions

View File

@@ -2,7 +2,6 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_admin_user
@@ -10,6 +9,8 @@ from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.db.persona import get_default_assistant
from onyx.db.persona import update_default_assistant_configuration
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.prompts.chat_prompts import DEFAULT_SYSTEM_PROMPT
from onyx.server.features.default_assistant.models import DefaultAssistantConfiguration
from onyx.server.features.default_assistant.models import DefaultAssistantUpdateRequest
@@ -32,7 +33,7 @@ def get_default_assistant_configuration(
"""
persona = get_default_assistant(db_session)
if not persona:
raise HTTPException(status_code=404, detail="Default assistant not found")
raise OnyxError(OnyxErrorCode.PERSONA_NOT_FOUND, "Default assistant not found")
# Extract DB tool IDs from the persona's tools
tool_ids = [tool.id for tool in persona.tools]
@@ -86,5 +87,5 @@ def update_default_assistant(
except ValueError as e:
if "Default assistant not found" in str(e):
raise HTTPException(status_code=404, detail=str(e))
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.PERSONA_NOT_FOUND, str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi import Query
from sqlalchemy.orm import Session
@@ -19,6 +18,8 @@ from onyx.db.document_set import mark_document_set_as_to_be_deleted
from onyx.db.document_set import update_document_set
from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.document_set.models import CheckDocSetPublicRequest
from onyx.server.features.document_set.models import CheckDocSetPublicResponse
from onyx.server.features.document_set.models import DocumentSetCreationRequest
@@ -54,7 +55,7 @@ def create_document_set(
db_session=db_session,
)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
if not DISABLE_VECTOR_DB:
client_app.send_task(
@@ -75,9 +76,9 @@ def patch_document_set(
) -> None:
document_set = get_document_set_by_id(db_session, document_set_update_request.id)
if document_set is None:
raise HTTPException(
status_code=404,
detail=f"Document set {document_set_update_request.id} does not exist",
raise OnyxError(
OnyxErrorCode.NOT_FOUND,
f"Document set {document_set_update_request.id} does not exist",
)
fetch_ee_implementation_or_noop(
@@ -97,7 +98,7 @@ def patch_document_set(
user=user,
)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
if not DISABLE_VECTOR_DB:
client_app.send_task(
@@ -116,9 +117,9 @@ def delete_document_set(
) -> None:
document_set = get_document_set_by_id(db_session, document_set_id)
if document_set is None:
raise HTTPException(
status_code=404,
detail=f"Document set {document_set_id} does not exist",
raise OnyxError(
OnyxErrorCode.NOT_FOUND,
f"Document set {document_set_id} does not exist",
)
# check if the user has "edit" access to the document set.
@@ -141,7 +142,7 @@ def delete_document_set(
user=user,
)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
if DISABLE_VECTOR_DB:
db_session.refresh(document_set)

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.access.hierarchy_access import get_user_external_group_ids
@@ -12,6 +11,8 @@ from onyx.db.engine.sql_engine import get_session
from onyx.db.hierarchy import get_accessible_hierarchy_nodes_for_source
from onyx.db.models import User
from onyx.db.opensearch_migration import get_opensearch_retrieval_state
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.hierarchy.constants import DOCUMENT_PAGE_SIZE
from onyx.server.features.hierarchy.constants import HIERARCHY_NODE_DOCUMENTS_PATH
from onyx.server.features.hierarchy.constants import HIERARCHY_NODES_LIST_PATH
@@ -43,14 +44,14 @@ router = APIRouter(prefix=HIERARCHY_NODES_PREFIX)
def _require_opensearch(db_session: Session) -> None:
if not ENABLE_OPENSEARCH_INDEXING_FOR_ONYX:
raise HTTPException(
status_code=403,
detail=OPENSEARCH_NOT_ENABLED_MESSAGE,
raise OnyxError(
OnyxErrorCode.UNAUTHORIZED,
OPENSEARCH_NOT_ENABLED_MESSAGE,
)
if not get_opensearch_retrieval_state(db_session):
raise HTTPException(
status_code=403,
detail=MIGRATION_STATUS_MESSAGE,
raise OnyxError(
OnyxErrorCode.UNAUTHORIZED,
MIGRATION_STATUS_MESSAGE,
)

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_admin_user
@@ -15,6 +14,8 @@ from onyx.db.input_prompt import remove_public_input_prompt
from onyx.db.input_prompt import update_input_prompt
from onyx.db.models import InputPrompt__User
from onyx.db.models import User
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.input_prompt.models import CreateInputPromptRequest
from onyx.server.features.input_prompt.models import InputPromptSnapshot
from onyx.server.features.input_prompt.models import UpdateInputPromptRequest
@@ -97,7 +98,7 @@ def patch_input_prompt(
except ValueError as e:
error_msg = "Error occurred while updated input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
return InputPromptSnapshot.from_model(updated_input_prompt)
@@ -117,7 +118,7 @@ def delete_input_prompt(
except ValueError as e:
error_msg = "Error occurred while deleting input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
@admin_router.delete("/{input_prompt_id}")
@@ -132,7 +133,7 @@ def delete_public_input_prompt(
except ValueError as e:
error_msg = "Error occurred while deleting input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
@basic_router.post("/{input_prompt_id}/hide")

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
@@ -9,6 +8,8 @@ from onyx.db.models import User
from onyx.db.notification import dismiss_notification
from onyx.db.notification import get_notification_by_id
from onyx.db.notification import get_notifications
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.build.utils import ensure_build_mode_intro_notification
from onyx.server.features.release_notes.utils import (
ensure_release_notes_fresh_and_notify,
@@ -64,10 +65,10 @@ def dismiss_notification_endpoint(
try:
notification = get_notification_by_id(notification_id, user, db_session)
except PermissionError:
raise HTTPException(
status_code=403, detail="Not authorized to dismiss this notification"
raise OnyxError(
OnyxErrorCode.UNAUTHORIZED, "Not authorized to dismiss this notification"
)
except ValueError:
raise HTTPException(status_code=404, detail="Notification not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Notification not found")
dismiss_notification(notification, db_session)

View File

@@ -2,7 +2,6 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.oauth_token_manager import OAuthTokenManager
@@ -20,6 +19,8 @@ from onyx.db.oauth_config import get_oauth_configs
from onyx.db.oauth_config import get_tools_by_oauth_config
from onyx.db.oauth_config import update_oauth_config
from onyx.db.oauth_config import upsert_user_oauth_token
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.federated_connectors.oauth_utils import generate_oauth_state
from onyx.federated_connectors.oauth_utils import verify_oauth_state
from onyx.server.features.oauth_config.models import OAuthCallbackResponse
@@ -79,7 +80,7 @@ def create_oauth_config_endpoint(
)
return _oauth_config_to_snapshot(oauth_config, db_session)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
@admin_router.get("")
@@ -101,8 +102,8 @@ def get_oauth_config_endpoint(
"""Retrieve a single OAuth configuration (admin only)."""
oauth_config = get_oauth_config(oauth_config_id, db_session)
if not oauth_config:
raise HTTPException(
status_code=404, detail=f"OAuth config with id {oauth_config_id} not found"
raise OnyxError(
OnyxErrorCode.NOT_FOUND, f"OAuth config with id {oauth_config_id} not found"
)
return _oauth_config_to_snapshot(oauth_config, db_session)
@@ -131,7 +132,7 @@ def update_oauth_config_endpoint(
)
return _oauth_config_to_snapshot(updated_config, db_session)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))
@admin_router.delete("/{oauth_config_id}")
@@ -145,7 +146,7 @@ def delete_oauth_config_endpoint(
delete_oauth_config(oauth_config_id, db_session)
return {"message": "OAuth configuration deleted successfully"}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))
"""User endpoints for OAuth flow"""
@@ -165,9 +166,9 @@ def initiate_oauth_flow(
# Get OAuth config
oauth_config = get_oauth_config(request.oauth_config_id, db_session)
if not oauth_config:
raise HTTPException(
status_code=404,
detail=f"OAuth config with id {request.oauth_config_id} not found",
raise OnyxError(
OnyxErrorCode.NOT_FOUND,
f"OAuth config with id {request.oauth_config_id} not found",
)
# Generate state parameter and store in Redis
@@ -206,8 +207,8 @@ def handle_oauth_callback(
# Verify the user_id matches
if str(user.id) != session.user_id:
raise HTTPException(
status_code=403, detail="User mismatch in OAuth callback"
raise OnyxError(
OnyxErrorCode.UNAUTHORIZED, "User mismatch in OAuth callback"
)
# Extract oauth_config_id from session (stored during initiate)
@@ -216,9 +217,9 @@ def handle_oauth_callback(
# Get OAuth config
oauth_config = get_oauth_config(oauth_config_id, db_session)
if not oauth_config:
raise HTTPException(
status_code=404,
detail=f"OAuth config with id {oauth_config_id} not found",
raise OnyxError(
OnyxErrorCode.NOT_FOUND,
f"OAuth config with id {oauth_config_id} not found",
)
# Exchange code for token
@@ -262,4 +263,4 @@ def revoke_oauth_token(
delete_user_oauth_token(oauth_config_id, user.id, db_session)
return {"message": "OAuth token revoked successfully"}
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi_users.exceptions import InvalidPasswordException
from sqlalchemy.orm import Session
@@ -11,6 +10,8 @@ from onyx.auth.users import User
from onyx.auth.users import UserManager
from onyx.db.engine.sql_engine import get_session
from onyx.db.users import get_user_by_email
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.password.models import ChangePasswordRequest
from onyx.server.features.password.models import UserResetRequest
from onyx.server.features.password.models import UserResetResponse
@@ -34,10 +35,10 @@ async def change_my_password(
new_password=form_data.new_password,
)
except InvalidPasswordException as e:
raise HTTPException(status_code=400, detail=str(e.reason))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e.reason))
except Exception as e:
raise HTTPException(
status_code=500, detail=f"An unexpected error occurred: {str(e)}"
raise OnyxError(
OnyxErrorCode.INTERNAL_ERROR, f"An unexpected error occurred: {str(e)}"
)
@@ -53,7 +54,7 @@ async def admin_reset_user_password(
"""
user = get_user_by_email(user_reset_request.user_email, db_session)
if not user:
raise HTTPException(status_code=404, detail="User not found")
raise OnyxError(OnyxErrorCode.USER_NOT_FOUND, "User not found")
new_password = await user_manager.reset_password_as_admin(user.id)
return UserResetResponse(
user_id=str(user.id),

View File

@@ -2,7 +2,6 @@ from uuid import UUID
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi import Query
from fastapi import UploadFile
from pydantic import BaseModel
@@ -38,6 +37,8 @@ from onyx.db.persona import update_persona_public_status
from onyx.db.persona import update_persona_shared
from onyx.db.persona import update_persona_visibility
from onyx.db.persona import update_personas_display_priority
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.file_store.file_store import get_default_file_store
from onyx.file_store.models import ChatFileType
from onyx.server.documents.models import PaginatedReturn
@@ -69,9 +70,9 @@ def _validate_user_knowledge_enabled(
if persona_upsert_request.user_file_ids or getattr(
persona_upsert_request, "user_project_ids", None
):
raise HTTPException(
status_code=400,
detail=f"User Knowledge is disabled. Cannot {action} assistant with user files or projects.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
f"User Knowledge is disabled. Cannot {action} assistant with user files or projects.",
)
@@ -88,28 +89,22 @@ def _validate_vector_db_knowledge(
return
if persona_upsert_request.document_set_ids:
raise HTTPException(
status_code=400,
detail=(
"Cannot attach document sets to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set)."
),
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Cannot attach document sets to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set).",
)
if persona_upsert_request.hierarchy_node_ids:
raise HTTPException(
status_code=400,
detail=(
"Cannot attach hierarchy nodes to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set)."
),
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Cannot attach hierarchy nodes to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set).",
)
if persona_upsert_request.document_ids:
raise HTTPException(
status_code=400,
detail=(
"Cannot attach documents to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set)."
),
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Cannot attach documents to an assistant when "
"the vector database is disabled (DISABLE_VECTOR_DB is set).",
)
@@ -165,7 +160,7 @@ def patch_user_persona_public_status(
)
except ValueError as e:
logger.exception("Failed to update persona public status")
raise HTTPException(status_code=403, detail=str(e))
raise OnyxError(OnyxErrorCode.UNAUTHORIZED, str(e))
@admin_router.patch("/{persona_id}/featured")
@@ -184,7 +179,7 @@ def patch_persona_featured_status(
)
except ValueError as e:
logger.exception("Failed to update persona featured status")
raise HTTPException(status_code=403, detail=str(e))
raise OnyxError(OnyxErrorCode.UNAUTHORIZED, str(e))
@admin_agents_router.patch("/display-priorities")
@@ -202,7 +197,7 @@ def patch_agents_display_priorities(
)
except ValueError as e:
logger.exception("Failed to update agent display priorities.")
raise HTTPException(status_code=403, detail=str(e))
raise OnyxError(OnyxErrorCode.UNAUTHORIZED, str(e))
@admin_router.get("", tags=PUBLIC_API_TAGS)
@@ -372,9 +367,9 @@ def create_label(
label_model = create_assistant_label(name=label.name, db_session=db)
return PersonaLabelResponse.from_model(label_model)
except IntegrityError:
raise HTTPException(
status_code=400,
detail=f"Label with name '{label.name}' already exists. Please choose a different name.",
raise OnyxError(
OnyxErrorCode.DUPLICATE_RESOURCE,
f"Label with name '{label.name}' already exists. Please choose a different name.",
)
@@ -428,10 +423,10 @@ def share_persona(
)
except PermissionError as e:
logger.exception("Failed to share persona")
raise HTTPException(status_code=403, detail=str(e))
raise OnyxError(OnyxErrorCode.UNAUTHORIZED, str(e))
except ValueError as e:
logger.exception("Failed to share persona")
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
@basic_router.delete("/{persona_id}", tags=PUBLIC_API_TAGS)

View File

@@ -6,7 +6,6 @@ from fastapi import BackgroundTasks
from fastapi import Depends
from fastapi import File
from fastapi import Form
from fastapi import HTTPException
from fastapi import Response
from fastapi import UploadFile
from pydantic import BaseModel
@@ -29,6 +28,8 @@ from onyx.db.models import UserProject
from onyx.db.persona import get_personas_by_ids
from onyx.db.projects import get_project_token_count
from onyx.db.projects import upload_files_to_user_files_with_indexing
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.projects.models import CategorizedFilesSnapshot
from onyx.server.features.projects.models import ChatSessionRequest
from onyx.server.features.projects.models import TokenCountResponse
@@ -115,7 +116,7 @@ def create_project(
db_session: Session = Depends(get_session),
) -> UserProjectSnapshot:
if name == "":
raise HTTPException(status_code=400, detail="Project name cannot be empty")
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, "Project name cannot be empty")
user_id = user.id
project = UserProject(name=name, user_id=user_id)
db_session.add(project)
@@ -159,9 +160,9 @@ def upload_user_files(
except Exception as e:
logger.exception(f"Error uploading files - {type(e).__name__}: {str(e)}")
raise HTTPException(
status_code=500,
detail="Failed to upload files. Please try again or contact support if the issue persists.",
raise OnyxError(
OnyxErrorCode.INTERNAL_ERROR,
"Failed to upload files. Please try again or contact support if the issue persists.",
)
@@ -178,7 +179,7 @@ def get_project(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
return UserProjectSnapshot.from_model(project)
@@ -222,7 +223,7 @@ def unlink_user_file_from_project(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
user_file = (
db_session.query(UserFile)
@@ -230,7 +231,7 @@ def unlink_user_file_from_project(
.one_or_none()
)
if user_file is None:
raise HTTPException(status_code=404, detail="File not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "File not found")
# Remove the association if it exists
if user_file in project.user_files:
@@ -268,7 +269,7 @@ def link_user_file_to_project(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
user_file = (
db_session.query(UserFile)
@@ -276,7 +277,7 @@ def link_user_file_to_project(
.one_or_none()
)
if user_file is None:
raise HTTPException(status_code=404, detail="File not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "File not found")
if user_file not in project.user_files:
user_file.needs_project_sync = True
@@ -311,7 +312,7 @@ def get_project_instructions(
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
return ProjectInstructionsResponse(instructions=project.instructions)
@@ -340,7 +341,7 @@ def upsert_project_instructions(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
project.instructions = body.instructions
db_session.commit()
@@ -397,7 +398,7 @@ def update_project(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
if body.name is not None:
project.name = body.name
@@ -422,7 +423,7 @@ def delete_project(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
# Unlink chat sessions from this project
for chat in project.chat_sessions:
@@ -455,7 +456,7 @@ def delete_user_file(
.one_or_none()
)
if user_file is None:
raise HTTPException(status_code=404, detail="File not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "File not found")
# Check associations with projects and assistants (personas)
project_names = [project.name for project in user_file.projects]
@@ -515,7 +516,7 @@ def get_user_file(
.one_or_none()
)
if user_file is None:
raise HTTPException(status_code=404, detail="File not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "File not found")
return UserFileSnapshot.from_model(user_file)
@@ -564,7 +565,7 @@ def move_chat_session(
.one_or_none()
)
if chat_session is None:
raise HTTPException(status_code=404, detail="Chat session not found")
raise OnyxError(OnyxErrorCode.SESSION_NOT_FOUND, "Chat session not found")
chat_session.project_id = project_id
db_session.commit()
return Response(status_code=204)
@@ -583,7 +584,7 @@ def remove_chat_session(
.one_or_none()
)
if chat_session is None:
raise HTTPException(status_code=404, detail="Chat session not found")
raise OnyxError(OnyxErrorCode.SESSION_NOT_FOUND, "Chat session not found")
chat_session.project_id = None
db_session.commit()
return Response(status_code=204)
@@ -606,7 +607,7 @@ def get_chat_session_project_token_count(
.one_or_none()
)
if chat_session is None:
raise HTTPException(status_code=404, detail="Chat session not found")
raise OnyxError(OnyxErrorCode.SESSION_NOT_FOUND, "Chat session not found")
total_tokens = get_project_token_count(
project_id=chat_session.project_id,
@@ -636,7 +637,7 @@ def get_chat_session_project_files(
.one_or_none()
)
if chat_session is None:
raise HTTPException(status_code=404, detail="Chat session not found")
raise OnyxError(OnyxErrorCode.SESSION_NOT_FOUND, "Chat session not found")
if chat_session.project_id is None:
return []
@@ -671,7 +672,7 @@ def get_project_total_token_count(
.one_or_none()
)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Project not found")
total_tokens = get_project_token_count(
project_id=project_id,

View File

@@ -2,7 +2,6 @@ from typing import Any
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session
@@ -19,6 +18,8 @@ from onyx.db.tools import get_tool_by_id
from onyx.db.tools import get_tools
from onyx.db.tools import get_tools_by_ids
from onyx.db.tools import update_tool
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.tool.models import CustomToolCreate
from onyx.server.features.tool.models import CustomToolUpdate
from onyx.server.features.tool.models import ToolSnapshot
@@ -40,16 +41,16 @@ def _validate_tool_definition(definition: dict[str, Any]) -> None:
try:
validate_openapi_schema(definition)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
def _validate_auth_settings(tool_data: CustomToolCreate | CustomToolUpdate) -> None:
if tool_data.passthrough_auth and tool_data.custom_headers:
for header in tool_data.custom_headers:
if header.key.lower() == "authorization":
raise HTTPException(
status_code=400,
detail="Cannot use passthrough auth with custom authorization headers",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Cannot use passthrough auth with custom authorization headers",
)
@@ -58,12 +59,12 @@ def _get_editable_custom_tool(tool_id: int, db_session: Session, user: User) ->
try:
tool = get_tool_by_id(tool_id, db_session)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))
if tool.in_code_tool_id is not None:
raise HTTPException(
status_code=400,
detail="Built-in tools cannot be modified through this endpoint.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Built-in tools cannot be modified through this endpoint.",
)
# Admins can always make changes; non-admins must own the tool.
@@ -71,9 +72,9 @@ def _get_editable_custom_tool(tool_id: int, db_session: Session, user: User) ->
return tool
if tool.user_id is None or tool.user_id != user.id:
raise HTTPException(
status_code=403,
detail="You can only modify actions that you created.",
raise OnyxError(
OnyxErrorCode.UNAUTHORIZED,
"You can only modify actions that you created.",
)
return tool
@@ -137,10 +138,10 @@ def delete_custom_tool(
try:
delete_tool__no_commit(tool_id, db_session)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))
except Exception as e:
# handles case where tool is still used by an Assistant
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
db_session.commit()
@@ -166,7 +167,7 @@ def update_tools_status(
bulk updates.
"""
if not update_data.tool_ids:
raise HTTPException(status_code=400, detail="No tool IDs provided")
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, "No tool IDs provided")
tools = get_tools_by_ids(update_data.tool_ids, db_session)
tools_by_id = {tool.id: tool for tool in tools}
@@ -183,8 +184,8 @@ def update_tools_status(
missing_tools.append(tool_id)
if missing_tools:
raise HTTPException(
status_code=404, detail=f"Tools with IDs {missing_tools} not found"
raise OnyxError(
OnyxErrorCode.NOT_FOUND, f"Tools with IDs {missing_tools} not found"
)
db_session.commit()
@@ -242,7 +243,7 @@ def get_custom_tool(
try:
tool = get_tool_by_id(tool_id, db_session)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
raise OnyxError(OnyxErrorCode.NOT_FOUND, str(e))
return ToolSnapshot.from_model(tool)

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
@@ -9,6 +8,8 @@ from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.db.web_search import fetch_active_web_content_provider
from onyx.db.web_search import fetch_active_web_search_provider
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.web_search.models import OpenUrlsToolRequest
from onyx.server.features.web_search.models import OpenUrlsToolResponse
from onyx.server.features.web_search.models import WebSearchToolRequest
@@ -61,9 +62,9 @@ def _get_active_search_provider(
) -> tuple[WebSearchProviderView, WebSearchProvider]:
provider_model = fetch_active_web_search_provider(db_session)
if provider_model is None:
raise HTTPException(
status_code=400,
detail="No web search provider configured.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"No web search provider configured.",
)
provider_view = WebSearchProviderView(
@@ -76,9 +77,9 @@ def _get_active_search_provider(
)
if provider_model.api_key is None:
raise HTTPException(
status_code=400,
detail="Web search provider requires an API key.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Web search provider requires an API key.",
)
try:
@@ -88,7 +89,7 @@ def _get_active_search_provider(
config=provider_model.config or {},
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(exc)) from exc
return provider_view, provider
@@ -110,9 +111,9 @@ def _get_active_content_provider(
if provider_model.api_key is None:
# TODO - this is not a great error, in fact, this key should not be nullable.
raise HTTPException(
status_code=400,
detail="Web content provider requires an API key.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Web content provider requires an API key.",
)
try:
@@ -125,12 +126,12 @@ def _get_active_content_provider(
config=config,
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(exc)) from exc
if provider is None:
raise HTTPException(
status_code=400,
detail="Unable to initialize the configured web content provider.",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Unable to initialize the configured web content provider.",
)
provider_view = WebContentProviderView(
@@ -154,12 +155,13 @@ def _run_web_search(
for query in request.queries:
try:
search_results = provider.search(query)
except HTTPException:
except OnyxError:
raise
except Exception as exc:
logger.exception("Web search provider failed for query '%s'", query)
raise HTTPException(
status_code=502, detail="Web search provider failed to execute query."
raise OnyxError(
OnyxErrorCode.BAD_GATEWAY,
"Web search provider failed to execute query.",
) from exc
filtered_results = filter_web_search_results_with_no_title_or_snippet(
@@ -192,12 +194,12 @@ def _open_urls(
docs = filter_web_contents_with_no_title_or_content(
list(provider.contents(urls))
)
except HTTPException:
except OnyxError:
raise
except Exception as exc:
logger.exception("Web content provider failed to fetch URLs")
raise HTTPException(
status_code=502, detail="Web content provider failed to fetch URLs."
raise OnyxError(
OnyxErrorCode.BAD_GATEWAY, "Web content provider failed to fetch URLs."
) from exc
results: list[LlmOpenUrlResult] = []