Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/app/crud/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def validate_blob_model_or_raise(session: Session, blob: ConfigBlob) -> None:
if raw_provider is None:
return

if raw_provider.endswith("-native"):
if raw_provider.endswith(NATIVE_PROVIDER_SUFFIX):
return

provider = _normalize_provider(raw_provider)
Expand Down
110 changes: 102 additions & 8 deletions backend/app/crud/rag/open_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from io import BytesIO
from typing import Iterable

import openai
from openai import OpenAI, OpenAIError
from pydantic import BaseModel

Expand Down Expand Up @@ -130,19 +131,112 @@ def update(
files.append(f_obj)

logger.info(
f"[OpenAIVectorStoreCrud.update] Uploading files to vector store | {{'vector_store_id': '{vector_store_id}', 'file_count': {len(files)}}}"
)
req = self.client.vector_stores.file_batches.upload_and_poll(
vector_store_id=vector_store_id,
files=files,
f"[OpenAIVectorStoreCrud.update] Uploading files to vector store | "
f"{{'vector_store_id': '{vector_store_id}', 'file_count': {len(files)}}}"
)

try:
req = self.client.vector_stores.file_batches.upload_and_poll(
vector_store_id=vector_store_id,
files=files,
)
except openai.RateLimitError as e:
raise InterruptedError(
f"OpenAI rate limit exceeded (code: {e.status_code}): "
f"{e.message}. Try again in 1 minute. If issue persists, "
f"contact Kaapi."
)
except openai.AuthenticationError as e:
raise InterruptedError(
f"OpenAI authentication failed (code: {e.status_code}): "
f"{e.message}. Check your OpenAI API key is valid and "
f"has not expired."
)
except openai.NotFoundError as e:
raise InterruptedError(
f"OpenAI resource not found (code: {e.status_code}): "
f"{e.message}. Verify the vector store ID exists and "
f"hasn't been deleted."
)
except openai.BadRequestError as e:
raise InterruptedError(
f"OpenAI bad request (code: {e.status_code}): {e.message}. "
f"Review the file payload and metadata; the request may "
f"be malformed."
)
except openai.UnprocessableEntityError as e:
raise InterruptedError(
f"OpenAI unprocessable entity (code: {e.status_code}): "
f"{e.message}. The uploaded files may be in an "
f"unsupported format or exceed size limits."
)
except openai.InternalServerError as e:
raise InterruptedError(
f"OpenAI server error (code: {e.status_code}): {e.message}. "
f"This is usually transient — retry in a few seconds. If "
f"issue persists, contact Kaapi."
)
except openai.APITimeoutError as e:
raise InterruptedError(
f"OpenAI request timed out: {e}. Retry the upload, or "
f"split the batch into smaller chunks."
)
except openai.OpenAIError as e:
raise InterruptedError(
f"OpenAI error: {e}. If this persists, contact Kaapi."
)

logger.info(
f"[OpenAIVectorStoreCrud.update] File upload completed | {{'vector_store_id': '{vector_store_id}', 'completed_files': {req.file_counts.completed}, 'total_files': {req.file_counts.total}}}"
f"[OpenAIVectorStoreCrud.update] File upload completed | "
f"{{'vector_store_id': '{vector_store_id}', "
f"'completed_files': {req.file_counts.completed}, "
f"'total_files': {req.file_counts.total}}}"
)

if req.file_counts.completed != req.file_counts.total:
error_msg = f"OpenAI document processing error: {req.file_counts.completed}/{req.file_counts.total} files completed"
# Enrich the error string by listing each failed file's
# `last_error.message` from OpenAI. Fall back to the
# count-only message if the follow-up list_files call
# itself fails.
failed_summary = ""
try:
page = self.client.vector_stores.file_batches.list_files(
batch_id=req.id,
vector_store_id=vector_store_id,
filter="failed",
limit=10,
)
parts = []
for f in page:
f_err = getattr(f, "last_error", None)
f_msg = (
getattr(f_err, "message", None) if f_err else None
) or "Unknown error"
parts.append(f"{f.id} ({f_msg})")
failed_summary = ", ".join(parts)
if getattr(page, "has_more", False):
failed_summary = f"{failed_summary}, ..."
if len(failed_summary) > 600:
failed_summary = failed_summary[:597] + "..."
except OpenAIError as list_err:
logger.warning(
f"[OpenAIVectorStoreCrud.update] Could not list failed "
f"files | {{'vector_store_id': '{vector_store_id}', "
f"'batch_id': '{req.id}', 'error': '{list_err}'}}"
)

error_msg = (
f"OpenAI document processing error: "
f"{req.file_counts.completed}/{req.file_counts.total} "
f"files completed"
)
if failed_summary:
error_msg = f"{error_msg}. Failed files: {failed_summary}"
logger.error(
f"[OpenAIVectorStoreCrud.update] Document processing error | {{'vector_store_id': '{vector_store_id}', 'completed_files': {req.file_counts.completed}, 'total_files': {req.file_counts.total}}}"
f"[OpenAIVectorStoreCrud.update] Document processing error | "
f"{{'vector_store_id': '{vector_store_id}', "
f"'completed_files': {req.file_counts.completed}, "
f"'total_files': {req.file_counts.total}}}"
)
raise InterruptedError(error_msg)

Expand Down
103 changes: 96 additions & 7 deletions backend/app/services/llm/providers/oai.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,110 @@ def execute(
error_message = f"Invalid or unexpected parameter in Config: {str(e)}"
return None, error_message

except openai.OpenAIError as e:
# imported here to avoid circular imports
from app.utils import handle_openai_error
except openai.RateLimitError as e:
error_message = (
f"OpenAI rate limit exceeded (code: {e.status_code}): "
f"{e.message}. Try again in 1 minute. If issue persists, "
f"contact Kaapi."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. One line explaining what to do next.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.AuthenticationError as e:
error_message = (
f"OpenAI authentication failed (code: {e.status_code}): "
f"{e.message}. Check your OpenAI API key is valid and has "
f"not expired."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

error_message = handle_openai_error(e)
except openai.NotFoundError as e:
error_message = (
f"OpenAI resource not found (code: {e.status_code}): "
f"{e.message}. Verify the model name and any referenced IDs "
f"in your config are correct."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.BadRequestError as e:
error_message = (
f"OpenAI bad request (code: {e.status_code}): {e.message}. "
f"Review your config parameters; the request shape may be "
f"invalid."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.UnprocessableEntityError as e:
error_message = (
f"OpenAI unprocessable entity (code: {e.status_code}): "
f"{e.message}. The model rejected the request payload; "
f"check input format and limits."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.InternalServerError as e:
error_message = (
f"OpenAI server error (code: {e.status_code}): {e.message}. "
f"This is usually transient — retry in a few seconds. If "
f"issue persists, contact Kaapi."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.APITimeoutError as e:
error_message = (
f"OpenAI request timed out: {e}. Retry the request, or try "
f"with a smaller payload."
)
logger.warning(
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except openai.OpenAIError as e:
error_message = f"OpenAI error: {e}. If this persists, contact Kaapi."
logger.warning(
f"[OpenAIProvider.execute] OpenAI API error: {error_message} | provider={completion_config.provider}",
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message

except Exception as e:
error_message = "Unexpected error occurred"
error_message = f"Unexpected error: {e}"
logger.error(
f"[OpenAIProvider.execute] {error_message}: {str(e)} | provider={completion_config.provider}",
f"[OpenAIProvider.execute] {error_message} | "
f"provider={completion_config.provider}",
exc_info=True,
)
return None, error_message
Empty file.
Loading
Loading