Skip to content
Closed
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
14 changes: 12 additions & 2 deletions sdks/python/openrag_sdk/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""OpenRAG SDK client."""
"""OpenRAG SDK client.

Provides async client for interacting with the OpenRAG API.
"""

from __future__ import annotations

import os
from typing import Any
Expand Down Expand Up @@ -239,5 +244,10 @@ async def close(self) -> None:
async def __aenter__(self) -> "OpenRAGClient":
return self

async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
async def __aexit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: Any | None,
) -> None:
await self.close()
21 changes: 18 additions & 3 deletions sdks/python/openrag_sdk/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""OpenRAG SDK data models."""
"""OpenRAG SDK data models.

Defines Pydantic models for API request/response serialization.
"""

from __future__ import annotations

from datetime import datetime
from typing import Any, Literal
Expand Down Expand Up @@ -79,15 +84,25 @@ class IngestResponse(BaseModel):


class IngestTaskStatus(BaseModel):
"""Status of an ingestion task."""
"""Status of an ingestion task.

Attributes:
task_id: Unique identifier for the ingestion task.
status: Current status ("pending", "running", "completed", "failed").
total_files: Total number of files to process.
processed_files: Number of files processed so far.
successful_files: Number of successfully processed files.
failed_files: Number of files that failed to process.
files: Detailed per-file status mapping.
"""

task_id: str
status: str # "pending", "running", "completed", "failed"
total_files: int = 0
processed_files: int = 0
successful_files: int = 0
failed_files: int = 0
files: dict = {} # Detailed per-file status
files: dict[str, Any] = Field(default_factory=dict) # Detailed per-file status


class DeleteDocumentResponse(BaseModel):
Expand Down
31 changes: 22 additions & 9 deletions src/config/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
"""OpenRAG configuration settings.

This module loads and manages all application configuration from environment
variables and configuration files.
"""
import asyncio
import os
from utils.env_utils import get_env_int, get_env_float
from typing import Any, Optional

import httpx
from agentd.patch import patch_openai_with_mcp
Expand All @@ -10,7 +15,9 @@
from opensearchpy._async.http_aiohttp import AIOHttpConnection

from utils.container_utils import get_container_host
from utils.env_utils import get_env_bool, get_env_float, get_env_int, get_env_str, require_env
from utils.logging_config import get_logger

# Import configuration manager
from .config_manager import config_manager

Expand All @@ -19,16 +26,22 @@

logger = get_logger(__name__)

# Environment variables
OPENSEARCH_HOST = os.getenv("OPENSEARCH_HOST", "localhost")
OPENSEARCH_PORT = get_env_int("OPENSEARCH_PORT", 9200)
OPENSEARCH_USERNAME = os.getenv("OPENSEARCH_USERNAME", "admin")
OPENSEARCH_PASSWORD = os.getenv("OPENSEARCH_PASSWORD")
LANGFLOW_URL = os.getenv("LANGFLOW_URL", "http://localhost:7860")
# =============================================================================
# OpenSearch Configuration
# =============================================================================
OPENSEARCH_HOST: str = os.getenv("OPENSEARCH_HOST", "localhost")
OPENSEARCH_PORT: int = get_env_int("OPENSEARCH_PORT", 9200)
OPENSEARCH_USERNAME: str = os.getenv("OPENSEARCH_USERNAME", "admin")
OPENSEARCH_PASSWORD: Optional[str] = os.getenv("OPENSEARCH_PASSWORD")

# =============================================================================
# Langflow Configuration
# =============================================================================
LANGFLOW_URL: str = os.getenv("LANGFLOW_URL", "http://localhost:7860")
# Optional: public URL for browser links (e.g., http://localhost:7860)
LANGFLOW_PUBLIC_URL = os.getenv("LANGFLOW_PUBLIC_URL")
LANGFLOW_PUBLIC_URL: Optional[str] = os.getenv("LANGFLOW_PUBLIC_URL")
# Backwards compatible flow ID handling with deprecation warnings
_legacy_flow_id = os.getenv("FLOW_ID")
_legacy_flow_id: Optional[str] = os.getenv("FLOW_ID")

LANGFLOW_CHAT_FLOW_ID = os.getenv("LANGFLOW_CHAT_FLOW_ID") or _legacy_flow_id
LANGFLOW_INGEST_FLOW_ID = os.getenv("LANGFLOW_INGEST_FLOW_ID")
Expand Down
22 changes: 18 additions & 4 deletions src/services/monitor_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ async def create_knowledge_filter_monitor(
# Convert knowledge filter query to monitor query format
monitor_query = self._convert_kf_query_to_monitor_query(query_data)

# TODO: OpenSearch 3.0 has a bug with document-level monitors on indexes with KNN fields
# Note: OpenSearch 3.0 has a bug with document-level monitors on indexes with KNN fields
# Error: "Cannot invoke KNNMethodConfigContext.getVectorDataType() because knnMethodConfigContext is null"
# Consider using query-level monitors instead or excluding KNN fields from doc-level monitors
# For now, this will fail on the 'documents' index due to chunk_embedding KNN field
# Workaround: Use query-level monitors for indexes with KNN fields or exclude KNN fields from doc-level monitors
# The 'documents' index contains chunk_embedding KNN field, so we add error handling below

# Create the document-level monitor
monitor_body = {
Expand Down Expand Up @@ -120,7 +120,21 @@ async def create_knowledge_filter_monitor(
return {"success": False, "error": "Failed to create monitor"}

except Exception as e:
return {"success": False, "error": f"Monitor creation failed: {str(e)}"}
error_message = str(e)
# Detect OpenSearch KNN field bug and provide helpful workaround suggestion
if "KNNMethodConfigContext" in error_message or "chunk_embedding" in error_message:
logger.warning(
"OpenSearch KNN field compatibility issue detected. "
"Consider using query-level monitors for indexes with KNN fields, "
"or exclude KNN fields from the document-level monitor query.",
error=error_message,
)
return {
"success": False,
"error": f"OpenSearch KNN compatibility issue: {error_message}. "
"Workaround: Use query-level monitors or exclude KNN fields from the query.",
}
return {"success": False, "error": f"Monitor creation failed: {error_message}"}

async def delete_monitor(
self, monitor_id: str, user_id: str, jwt_token: str
Expand Down
41 changes: 41 additions & 0 deletions src/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""OpenRAG utility modules.

This package provides common utilities used throughout OpenRAG:
- Environment variable parsing
- Logging configuration
- Document processing
- File operations
- OpenSearch helpers
- Container utilities
"""

from .env_utils import (
get_env_bool,
get_env_dict,
get_env_float,
get_env_int,
get_env_list,
get_env_str,
require_env,
safe_bool,
safe_float,
safe_int,
)
from .logging_config import configure_logging, get_logger

__all__ = [
# Environment utilities
"get_env_bool",
"get_env_dict",
"get_env_float",
"get_env_int",
"get_env_list",
"get_env_str",
"require_env",
"safe_bool",
"safe_float",
"safe_int",
# Logging utilities
"configure_logging",
"get_logger",
]
Loading
Loading