diff --git a/.github/workflows/one-click-inputs-nobuild.json b/.github/workflows/one-click-inputs-nobuild.json index 610b20f563..339dc515d5 100644 --- a/.github/workflows/one-click-inputs-nobuild.json +++ b/.github/workflows/one-click-inputs-nobuild.json @@ -204,6 +204,28 @@ ], "clear": ["8", "1", "2", "docker", "$os", "$hardware", "Y"] } + }, + "ArbPostHearingAssistant": { + "inputs": { + "deploy": [ + "9", + "1", + "1", + "docker", + "$os", + "$hardware", + "$HF_TOKEN", + "$http_proxy", + "$https_proxy", + "$no_proxy", + "$LLM_model", + "N", + "N", + "y", + "Y" + ], + "clear": ["9", "1", "2", "docker", "$os", "$hardware", "Y"] + } } }, "k8s": { diff --git a/ArbPostHearingAssistant/ui/gradio/requirements.txt b/ArbPostHearingAssistant/ui/gradio/requirements.txt index 095dee2b06..eb404aec4b 100644 --- a/ArbPostHearingAssistant/ui/gradio/requirements.txt +++ b/ArbPostHearingAssistant/ui/gradio/requirements.txt @@ -1,4 +1,4 @@ gradio>5.22.0,<=5.34.0 numpy==1.26.4 Pillow==10.3.0 - +requests diff --git a/CogniwareIms/.pre-commit-config.yaml b/CogniwareIms/.pre-commit-config.yaml index e0849011e2..0d4c7f6d48 100644 --- a/CogniwareIms/.pre-commit-config.yaml +++ b/CogniwareIms/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + # Pre-commit hooks configuration for OPEA compliance # See https://pre-commit.com for more information diff --git a/CogniwareIms/README.md b/CogniwareIms/README.md index 1067563f9a..0488a3c8bd 100644 --- a/CogniwareIms/README.md +++ b/CogniwareIms/README.md @@ -91,4 +91,3 @@ Apache 2.0 - See [LICENSE](LICENSE) file for details. ## Support For issues and questions, please open an issue in the OPEA GenAIExamples repository. - diff --git a/CogniwareIms/backend/Dockerfile b/CogniwareIms/backend/Dockerfile index b981c3712a..a1455a53f8 100644 --- a/CogniwareIms/backend/Dockerfile +++ b/CogniwareIms/backend/Dockerfile @@ -1,3 +1,6 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + # Multi-stage build for production optimization FROM python:3.11-slim as builder diff --git a/CogniwareIms/backend/app/core/config.py b/CogniwareIms/backend/app/core/config.py index 68ff5f132e..a7cc7cecc0 100644 --- a/CogniwareIms/backend/app/core/config.py +++ b/CogniwareIms/backend/app/core/config.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """Application configuration Centralized settings management following 12-factor app principles.""" import os @@ -22,14 +21,10 @@ class Settings(BaseSettings): # API Configuration API_V1_PREFIX: str = "/api" - ALLOWED_ORIGINS: List[str] = os.getenv( - "ALLOWED_ORIGINS", "http://localhost:3000,http://frontend:3000" - ).split(",") + ALLOWED_ORIGINS: List[str] = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000,http://frontend:3000").split(",") # Security - JWT_SECRET_KEY: str = os.getenv( - "JWT_SECRET_KEY", "CHANGE_THIS_IN_PRODUCTION_USE_openssl_rand_hex_32" - ) + JWT_SECRET_KEY: str = os.getenv("JWT_SECRET_KEY", "CHANGE_THIS_IN_PRODUCTION_USE_openssl_rand_hex_32") JWT_ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 @@ -39,9 +34,7 @@ class Settings(BaseSettings): RATE_LIMIT_PER_MINUTE: int = int(os.getenv("RATE_LIMIT_PER_MINUTE", "60")) # Database - DATABASE_URL: str = os.getenv( - "DATABASE_URL", "postgresql://postgres:postgres@postgres:5432/opea_ims" - ) + DATABASE_URL: str = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@postgres:5432/opea_ims") DB_POOL_SIZE: int = 10 DB_MAX_OVERFLOW: int = 20 @@ -50,13 +43,9 @@ class Settings(BaseSettings): REDIS_MAX_CONNECTIONS: int = 50 # OPEA Services - OPEA_EMBEDDING_URL: str = os.getenv( - "OPEA_EMBEDDING_URL", "http://embedding-service:6000" - ) + OPEA_EMBEDDING_URL: str = os.getenv("OPEA_EMBEDDING_URL", "http://embedding-service:6000") OPEA_LLM_URL: str = os.getenv("OPEA_LLM_URL", "http://llm-service:9000") - OPEA_RETRIEVAL_URL: str = os.getenv( - "OPEA_RETRIEVAL_URL", "http://retrieval-service:7000" - ) + OPEA_RETRIEVAL_URL: str = os.getenv("OPEA_RETRIEVAL_URL", "http://retrieval-service:7000") OPEA_GATEWAY_URL: str = os.getenv("OPEA_GATEWAY_URL", "http://opea-gateway:8888") # Models diff --git a/CogniwareIms/backend/app/core/security.py b/CogniwareIms/backend/app/core/security.py index d28863eb70..7be2063458 100644 --- a/CogniwareIms/backend/app/core/security.py +++ b/CogniwareIms/backend/app/core/security.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """ Security utilities - JWT, password hashing, authentication Industry-standard security implementation @@ -53,9 +52,7 @@ def get_password_hash(password: str) -> str: return pwd_context.hash(password) @staticmethod - def create_access_token( - data: Dict[str, Any], expires_delta: Optional[timedelta] = None - ) -> str: + def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str: """Create a JWT access token. Args: @@ -141,9 +138,7 @@ def protected_route(user: Dict = Depends(get_current_user)): # Extract user info from payload email = payload.get("sub") if email is None: - raise HTTPException( - status_code=401, detail="Invalid authentication credentials" - ) + raise HTTPException(status_code=401, detail="Invalid authentication credentials") return payload @@ -160,9 +155,7 @@ def admin_route(user: Dict = Depends(require_role("Super Admin"))): def role_checker(current_user: Dict = Depends(get_current_user)): user_role = current_user.get("role") if user_role != required_role: - raise HTTPException( - status_code=403, detail=f"Access denied. Required role: {required_role}" - ) + raise HTTPException(status_code=403, detail=f"Access denied. Required role: {required_role}") return current_user return role_checker @@ -241,9 +234,7 @@ class RateLimiter: def __init__(self): self.requests = {} - def is_allowed( - self, identifier: str, max_requests: int = 60, window_seconds: int = 60 - ) -> bool: + def is_allowed(self, identifier: str, max_requests: int = 60, window_seconds: int = 60) -> bool: """Check if request is allowed under rate limit. Args: @@ -261,9 +252,7 @@ def is_allowed( # Clean old requests cutoff = now - timedelta(seconds=window_seconds) - self.requests[identifier] = [ - req_time for req_time in self.requests[identifier] if req_time > cutoff - ] + self.requests[identifier] = [req_time for req_time in self.requests[identifier] if req_time > cutoff] # Check limit if len(self.requests[identifier]) >= max_requests: diff --git a/CogniwareIms/backend/app/init_knowledge_base.py b/CogniwareIms/backend/app/init_knowledge_base.py index ac7ff511a2..6dd3c596cf 100644 --- a/CogniwareIms/backend/app/init_knowledge_base.py +++ b/CogniwareIms/backend/app/init_knowledge_base.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """Knowledge Base Initialization Script Processes all CSV files and creates initial embeddings Run this after first deployment to populate the knowledge base.""" @@ -19,9 +18,7 @@ from app.services.knowledge_manager import knowledge_manager from app.services.retrieval_service import retrieval_service -logging.basicConfig( - level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" -) +logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") logger = logging.getLogger(__name__) @@ -49,17 +46,13 @@ async def initialize_knowledge_base(): for i in range(0, len(documents), batch_size): batch = documents[i : i + batch_size] - logger.info( - f" Processing batch {i//batch_size + 1}/{(len(documents)-1)//batch_size + 1}..." - ) + logger.info(f" Processing batch {i//batch_size + 1}/{(len(documents)-1)//batch_size + 1}...") # Extract texts texts = [doc["text"] for doc in batch] # Generate embeddings in batch - embeddings = await embedding_service.embed_batch( - texts, batch_size=batch_size - ) + embeddings = await embedding_service.embed_batch(texts, batch_size=batch_size) # Index each document for doc, embedding in zip(batch, embeddings): @@ -97,11 +90,11 @@ async def initialize_knowledge_base(): logger.info("\n" + "=" * 60) logger.info("šŸŽ‰ Knowledge Base Initialization Complete!") logger.info("=" * 60) - logger.info(f"\nšŸ“Š Summary:") + logger.info("\nšŸ“Š Summary:") logger.info(f" CSV Files Processed: {len(dataframes)}") logger.info(f" Documents Indexed: {total_indexed}") logger.info(f" Vector Store Count: {doc_count}") - logger.info(f"\nāœ… System is ready for AI-powered queries!") + logger.info("\nāœ… System is ready for AI-powered queries!") return { "success": True, @@ -134,9 +127,7 @@ async def quick_test(): # Test knowledge manager stats = await knowledge_manager.get_knowledge_stats() - logger.info( - f"āœ… Knowledge manager: OK ({stats.get('total_documents', 0)} documents)" - ) + logger.info(f"āœ… Knowledge manager: OK ({stats.get('total_documents', 0)} documents)") logger.info("\nšŸŽ‰ All systems operational!") diff --git a/CogniwareIms/backend/app/main.py b/CogniwareIms/backend/app/main.py index fea2602f65..48e0984fce 100644 --- a/CogniwareIms/backend/app/main.py +++ b/CogniwareIms/backend/app/main.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """ OPEA Inventory Management System - Complete Backend API Full integration with all OPEA GenAIComps microservices @@ -160,9 +159,7 @@ async def health_check(): db_health = await dbqna_service.health_check() return { - "status": ( - "healthy" if all([embedding_health, llm_health, db_health]) else "degraded" - ), + "status": ("healthy" if all([embedding_health, llm_health, db_health]) else "degraded"), "timestamp": datetime.now().isoformat(), "services": { "api": "up", @@ -325,9 +322,7 @@ async def upload_csv_knowledge(file: UploadFile = File(...)): content = await file.read() # Process using file upload service - result = await file_upload_service.upload_and_process( - filename=file.filename, content=content - ) + result = await file_upload_service.upload_and_process(filename=file.filename, content=content) return result @@ -345,9 +340,7 @@ async def upload_knowledge_file(file: UploadFile = File(...)): content = await file.read() # Process using file upload service - result = await file_upload_service.upload_and_process( - filename=file.filename, content=content - ) + result = await file_upload_service.upload_and_process(filename=file.filename, content=content) return result @@ -658,9 +651,7 @@ async def startup_event(): # Load knowledge base stats stats = await knowledge_manager.get_knowledge_stats() - logger.info( - f" Knowledge Base: {stats.get('total_documents', 0)} documents indexed" - ) + logger.info(f" Knowledge Base: {stats.get('total_documents', 0)} documents indexed") logger.info("āœ… OPEA IMS Platform started successfully!") diff --git a/CogniwareIms/backend/app/services/csv_processor.py b/CogniwareIms/backend/app/services/csv_processor.py index ca567a8a73..c3ecfe4954 100644 --- a/CogniwareIms/backend/app/services/csv_processor.py +++ b/CogniwareIms/backend/app/services/csv_processor.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """CSV Data Processor Ingests CSV files and prepares them for OPEA knowledge base.""" import json @@ -37,9 +36,7 @@ def load_all_csv_files(self) -> Dict[str, pd.DataFrame]: return dataframes - def prepare_for_embedding( - self, dataframes: Dict[str, pd.DataFrame] - ) -> List[Dict[str, Any]]: + def prepare_for_embedding(self, dataframes: Dict[str, pd.DataFrame]) -> List[Dict[str, Any]]: """Prepare data for OPEA embedding service.""" documents = [] @@ -66,9 +63,7 @@ def prepare_for_embedding( logger.info(f"Prepared {len(documents)} documents for embedding") return documents - def extract_inventory_data( - self, dataframes: Dict[str, pd.DataFrame] - ) -> Dict[str, Any]: + def extract_inventory_data(self, dataframes: Dict[str, pd.DataFrame]) -> Dict[str, Any]: """Extract structured inventory data.""" inventory_data = { "products": [], @@ -117,9 +112,7 @@ def create_knowledge_base(self) -> Dict[str, Any]: with open(output_dir / "knowledge_base.json", "w") as f: json.dump(knowledge_base, f, indent=2, default=str) - logger.info( - f"Knowledge base created with {len(knowledge_base['documents'])} documents" - ) + logger.info(f"Knowledge base created with {len(knowledge_base['documents'])} documents") return knowledge_base diff --git a/CogniwareIms/backend/app/services/dbqna_service.py b/CogniwareIms/backend/app/services/dbqna_service.py index acda2584c4..ff1a03c6de 100644 --- a/CogniwareIms/backend/app/services/dbqna_service.py +++ b/CogniwareIms/backend/app/services/dbqna_service.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """ OPEA DBQnA Service - Database Query & Answer Converts natural language to SQL and executes against inventory database @@ -22,9 +21,7 @@ class DBQnAService: """Database Query & Answer service using OPEA LLM for SQL generation.""" def __init__(self): - self.database_url = os.getenv( - "DATABASE_URL", "postgresql://postgres:postgres@postgres:5432/opea_ims" - ) + self.database_url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@postgres:5432/opea_ims") self.engine = None self.schema_cache = None @@ -66,15 +63,9 @@ async def get_schema(self) -> Dict[str, Any]: ORDER BY ordinal_position """ ) - columns = conn.execute( - columns_query, {"table_name": table_name} - ).fetchall() - - schema["tables"][table_name] = { - "columns": [ - {"name": col, "type": dtype} for col, dtype in columns - ] - } + columns = conn.execute(columns_query, {"table_name": table_name}).fetchall() + + schema["tables"][table_name] = {"columns": [{"name": col, "type": dtype} for col, dtype in columns]} self.schema_cache = schema return schema @@ -83,9 +74,7 @@ async def get_schema(self) -> Dict[str, Any]: logger.error(f"Error getting schema: {e}") return {"tables": {}, "relationships": []} - async def natural_language_query( - self, question: str, include_explanation: bool = True - ) -> Dict[str, Any]: + async def natural_language_query(self, question: str, include_explanation: bool = True) -> Dict[str, Any]: """Convert natural language question to SQL and execute. Args: @@ -112,9 +101,7 @@ async def natural_language_query( columns = result.keys() # Convert to dict format - data = [ - {col: value for col, value in zip(columns, row)} for row in rows - ] + data = [{col: value for col, value in zip(columns, row)} for row in rows] response = { "success": True, diff --git a/CogniwareIms/backend/app/services/doc_summarization.py b/CogniwareIms/backend/app/services/doc_summarization.py index 60c17c366c..dd92775f6d 100644 --- a/CogniwareIms/backend/app/services/doc_summarization.py +++ b/CogniwareIms/backend/app/services/doc_summarization.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA DocSummarization Service Handles document summarization and analysis.""" import json @@ -69,9 +68,7 @@ async def summarize_document( "original_length": len(text.split()), "summary": summary.strip(), "summary_length": len(summary.split()), - "compression_ratio": round( - len(summary.split()) / max(len(text.split()), 1), 2 - ), + "compression_ratio": round(len(summary.split()) / max(len(text.split()), 1), 2), "type": summary_type, } @@ -79,9 +76,7 @@ async def summarize_document( logger.error(f"Summarization error: {e}") return {"success": False, "error": str(e)} - async def summarize_inventory_report( - self, report_data: Dict[str, Any] - ) -> Dict[str, Any]: + async def summarize_inventory_report(self, report_data: Dict[str, Any]) -> Dict[str, Any]: """Summarize inventory report data Specialized for inventory metrics.""" try: # Convert report data to narrative text @@ -124,9 +119,7 @@ def _format_report_for_summarization(self, report_data: Dict[str, Any]) -> str: if "stock_by_category" in report_data: parts.append("\nStock by Category:") for cat in report_data["stock_by_category"]: - parts.append( - f" - {cat['category']}: {cat['count']} units ({cat['percentage']}%)" - ) + parts.append(f" - {cat['category']}: {cat['count']} units ({cat['percentage']}%)") if "recent_activity" in report_data: parts.append("\nRecent Activity:") @@ -179,9 +172,7 @@ async def extract_key_information(self, text: str) -> Dict[str, Any]: logger.error(f"Information extraction error: {e}") return {"success": False, "error": str(e)} - async def summarize_csv_data( - self, csv_path: str, sample_size: int = 100 - ) -> Dict[str, Any]: + async def summarize_csv_data(self, csv_path: str, sample_size: int = 100) -> Dict[str, Any]: """Summarize CSV file contents.""" try: df = pd.read_csv(csv_path) @@ -207,25 +198,16 @@ async def summarize_csv_data( "success": True, "file": csv_path, "statistics": stats, - "summary": ( - summary["summary"] if summary["success"] else "Summary unavailable" - ), + "summary": (summary["summary"] if summary["success"] else "Summary unavailable"), } except Exception as e: logger.error(f"CSV summarization error: {e}") return {"success": False, "error": str(e)} - async def generate_report_narrative( - self, title: str, data_points: List[Dict[str, Any]] - ) -> str: + async def generate_report_narrative(self, title: str, data_points: List[Dict[str, Any]]) -> str: """Generate narrative report from data points.""" - data_text = "\n".join( - [ - f"- {dp.get('label', 'Item')}: {dp.get('value', 'N/A')}" - for dp in data_points - ] - ) + data_text = "\n".join([f"- {dp.get('label', 'Item')}: {dp.get('value', 'N/A')}" for dp in data_points]) prompt = f"""Generate a professional narrative report titled "{title}" based on the following data: diff --git a/CogniwareIms/backend/app/services/embedding_service.py b/CogniwareIms/backend/app/services/embedding_service.py index 1c83968005..c06d979fc3 100644 --- a/CogniwareIms/backend/app/services/embedding_service.py +++ b/CogniwareIms/backend/app/services/embedding_service.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA Embedding Service Integration Handles text vectorization and embedding generation.""" import logging @@ -60,9 +59,7 @@ async def embed_text(self, text: str) -> List[float]: logger.error(f"Error generating embedding: {e}") raise - async def embed_batch( - self, texts: List[str], batch_size: int = 32 - ) -> List[List[float]]: + async def embed_batch(self, texts: List[str], batch_size: int = 32) -> List[List[float]]: """Generate embeddings for multiple texts in batches More efficient for large datasets.""" embeddings = [] @@ -80,9 +77,7 @@ async def embed_batch( # Extract embeddings if "data" in result: - batch_embeddings = [ - item["embedding"] for item in result["data"] - ] + batch_embeddings = [item["embedding"] for item in result["data"]] else: # Fallback: generate one by one batch_embeddings = [] @@ -105,9 +100,7 @@ async def embed_batch( return embeddings - async def embed_documents( - self, documents: List[Dict[str, Any]] - ) -> List[Dict[str, Any]]: + async def embed_documents(self, documents: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """Embed a list of documents with metadata Returns documents with added embedding field.""" texts = [doc.get("text", "") for doc in documents] embeddings = await self.embed_batch(texts) @@ -137,9 +130,7 @@ def cosine_similarity(self, vec1_tuple: tuple, vec2_tuple: tuple) -> float: return float(dot_product / (norm1 * norm2)) - async def find_similar( - self, query_text: str, candidate_texts: List[str], top_k: int = 5 - ) -> List[Dict[str, Any]]: + async def find_similar(self, query_text: str, candidate_texts: List[str], top_k: int = 5) -> List[Dict[str, Any]]: """Find most similar texts to query using cosine similarity.""" # Generate query embedding query_embedding = await self.embed_text(query_text) @@ -149,12 +140,8 @@ async def find_similar( # Calculate similarities similarities = [] - for idx, (text, embedding) in enumerate( - zip(candidate_texts, candidate_embeddings) - ): - similarity = self.cosine_similarity( - tuple(query_embedding), tuple(embedding) - ) + for idx, (text, embedding) in enumerate(zip(candidate_texts, candidate_embeddings)): + similarity = self.cosine_similarity(tuple(query_embedding), tuple(embedding)) similarities.append({"text": text, "index": idx, "similarity": similarity}) # Sort by similarity and return top k diff --git a/CogniwareIms/backend/app/services/file_upload_service.py b/CogniwareIms/backend/app/services/file_upload_service.py index f75ba5e9ab..2a5c488d80 100644 --- a/CogniwareIms/backend/app/services/file_upload_service.py +++ b/CogniwareIms/backend/app/services/file_upload_service.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """File Upload Service Handles xlsx, csv, pdf, docx file uploads and processing for knowledge base Optimized for Intel Xeon processors.""" @@ -107,9 +106,7 @@ async def process_csv(self, file_path: Path) -> Dict[str, Any]: documents = [] for idx, row in df.iterrows(): # Create text representation - text_parts = [ - f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col]) - ] + text_parts = [f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col])] text = " | ".join(text_parts) documents.append( @@ -160,9 +157,7 @@ async def process_xlsx(self, file_path: Path) -> Dict[str, Any]: df = pd.read_excel(file_path, sheet_name=sheet_name) for idx, row in df.iterrows(): - text_parts = [ - f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col]) - ] + text_parts = [f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col])] text = " | ".join(text_parts) all_documents.append( @@ -369,9 +364,7 @@ async def upload_and_process(self, filename: str, content: bytes) -> Dict[str, A return result - def list_uploaded_files( - self, file_type: Optional[str] = None - ) -> List[Dict[str, Any]]: + def list_uploaded_files(self, file_type: Optional[str] = None) -> List[Dict[str, Any]]: """List all uploaded files.""" files = [] @@ -392,9 +385,7 @@ def list_uploaded_files( "filename": file_path.name, "type": file_path.suffix[1:], "size": stat.st_size, - "uploaded_at": datetime.fromtimestamp( - stat.st_ctime - ).isoformat(), + "uploaded_at": datetime.fromtimestamp(stat.st_ctime).isoformat(), "path": str(file_path), } ) @@ -403,6 +394,4 @@ def list_uploaded_files( # Global instance -file_upload_service = FileUploadService( - upload_dir=os.getenv("UPLOAD_DIR", "/app/uploads") -) +file_upload_service = FileUploadService(upload_dir=os.getenv("UPLOAD_DIR", "/app/uploads")) diff --git a/CogniwareIms/backend/app/services/graph_generator.py b/CogniwareIms/backend/app/services/graph_generator.py index 054ee7c4b8..626187b0ac 100644 --- a/CogniwareIms/backend/app/services/graph_generator.py +++ b/CogniwareIms/backend/app/services/graph_generator.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """Graph Generation Service Generates data for charts and visualizations.""" import logging @@ -14,9 +13,7 @@ class GraphGenerator: """Generate data structures for frontend charts and graphs.""" - async def generate_stock_trend( - self, product_sku: str, days: int = 30 - ) -> Dict[str, Any]: + async def generate_stock_trend(self, product_sku: str, days: int = 30) -> Dict[str, Any]: """Generate stock level trend data for time-series charts.""" # In production, this would query actual historical data # For now, generate realistic sample data @@ -178,9 +175,7 @@ async def generate_heatmap_data(self) -> Dict[str, Any]: for day_idx, day in enumerate(days): for hour in range(24): - activity = ( - random.randint(5, 95) if 8 <= hour <= 18 else random.randint(0, 30) - ) + activity = random.randint(5, 95) if 8 <= hour <= 18 else random.randint(0, 30) heatmap.append({"day": day, "hour": hour, "activity": activity}) return { @@ -189,9 +184,7 @@ async def generate_heatmap_data(self) -> Dict[str, Any]: "dimensions": {"days": days, "hours": 24}, } - async def generate_forecast( - self, product_sku: str, forecast_days: int = 30 - ) -> Dict[str, Any]: + async def generate_forecast(self, product_sku: str, forecast_days: int = 30) -> Dict[str, Any]: """Generate demand forecast using simple moving average In production, would use ML models.""" # Get historical data (simulated) historical = [] @@ -210,9 +203,7 @@ async def generate_forecast( # Generate forecast forecast = [] - avg_demand = ( - sum(h["actual"] for h in historical[-7:]) / 7 - ) # 7-day moving average + avg_demand = sum(h["actual"] for h in historical[-7:]) / 7 # 7-day moving average for i in range(forecast_days): date = datetime.now() + timedelta(days=i + 1) @@ -236,9 +227,7 @@ async def generate_forecast( "method": "moving_average", } - async def generate_comparison_chart( - self, items: List[str], metric: str = "stock_level" - ) -> Dict[str, Any]: + async def generate_comparison_chart(self, items: List[str], metric: str = "stock_level") -> Dict[str, Any]: """Generate comparison data for multiple items.""" comparison_data = [] diff --git a/CogniwareIms/backend/app/services/interactive_agent.py b/CogniwareIms/backend/app/services/interactive_agent.py index 382355ea41..a741ec7cde 100644 --- a/CogniwareIms/backend/app/services/interactive_agent.py +++ b/CogniwareIms/backend/app/services/interactive_agent.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA Interactive Agent Service Handles conversational AI interactions with context awareness.""" import json @@ -32,9 +31,7 @@ def _get_session_history(self, session_id: str) -> List[Dict[str, str]]: def _add_to_history(self, session_id: str, role: str, content: str): """Add message to conversation history.""" history = self._get_session_history(session_id) - history.append( - {"role": role, "content": content, "timestamp": datetime.now().isoformat()} - ) + history.append({"role": role, "content": content, "timestamp": datetime.now().isoformat()}) # Keep only last N messages if len(history) > self.max_history * 2: # *2 for user+assistant pairs @@ -82,9 +79,7 @@ async def chat( db_result = await dbqna_service.query_inventory(message) if db_result.get("success"): context_parts.append("Current Database Information:") - context_parts.append( - json.dumps(db_result.get("result", {}), indent=2) - ) + context_parts.append(json.dumps(db_result.get("result", {}), indent=2)) sources.append({"type": "database", "query": message}) # Step 3: Get conversation history @@ -99,9 +94,7 @@ async def chat( ) # Step 5: Generate response - response_text = await llm_service.chat_completion( - messages=messages, temperature=0.7 - ) + response_text = await llm_service.chat_completion(messages=messages, temperature=0.7) # Step 6: Update conversation history self._add_to_history(session_id, "user", message) @@ -150,18 +143,14 @@ async def _is_database_query(self, message: str) -> bool: message_lower = message.lower() return any(keyword in message_lower for keyword in db_keywords) - async def _get_rag_context( - self, query: str, top_k: int = 3 - ) -> Optional[Dict[str, Any]]: + async def _get_rag_context(self, query: str, top_k: int = 3) -> Optional[Dict[str, Any]]: """Get relevant context from knowledge base using RAG.""" try: # Generate query embedding query_embedding = await embedding_service.embed_text(query) # Search vector store - results = await retrieval_service.search( - query_embedding=query_embedding, top_k=top_k - ) + results = await retrieval_service.search(query_embedding=query_embedding, top_k=top_k) if not results: return None diff --git a/CogniwareIms/backend/app/services/knowledge_manager.py b/CogniwareIms/backend/app/services/knowledge_manager.py index 5ed81b2884..cded749bbd 100644 --- a/CogniwareIms/backend/app/services/knowledge_manager.py +++ b/CogniwareIms/backend/app/services/knowledge_manager.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """Knowledge Base Manager Handles continuous learning and knowledge base updates Allows users to add new knowledge and retrain on combined old+new data.""" @@ -107,9 +106,7 @@ async def add_knowledge_from_text( logger.error(f"Error adding knowledge: {e}") return {"success": False, "error": str(e)} - async def add_knowledge_from_csv( - self, csv_file: Path, auto_train: bool = True - ) -> Dict[str, Any]: + async def add_knowledge_from_csv(self, csv_file: Path, auto_train: bool = True) -> Dict[str, Any]: """Add knowledge from CSV file Each row becomes a document in the knowledge base.""" try: df = pd.read_csv(csv_file) @@ -117,9 +114,7 @@ async def add_knowledge_from_csv( for idx, row in df.iterrows(): # Create text representation - text_parts = [ - f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col]) - ] + text_parts = [f"{col}: {row[col]}" for col in df.columns if pd.notna(row[col])] text = " | ".join(text_parts) # Add to knowledge base @@ -284,9 +279,7 @@ async def search_knowledge( query_embedding = await embedding_service.embed_text(query) # Search vector store - results = await retrieval_service.search( - query_embedding=query_embedding, top_k=top_k, filters=filters - ) + results = await retrieval_service.search(query_embedding=query_embedding, top_k=top_k, filters=filters) return results @@ -332,9 +325,7 @@ async def retrain_all(self) -> Dict[str, Any]: ) self.save_history() - logger.info( - f"Retraining complete: {success_count}/{len(documents)} documents" - ) + logger.info(f"Retraining complete: {success_count}/{len(documents)} documents") return { "success": True, @@ -355,9 +346,7 @@ async def get_knowledge_stats(self) -> Dict[str, Any]: "total_documents": total_docs, "last_update": self.history.get("last_update"), "training_runs": len(self.history.get("training_runs", [])), - "recent_runs": self.history.get("training_runs", [])[ - -5: - ], # Last 5 runs + "recent_runs": self.history.get("training_runs", [])[-5:], # Last 5 runs "storage": {"vector_store": "Redis", "indexed": total_docs}, } except Exception as e: @@ -377,10 +366,7 @@ async def export_knowledge_base(self, output_file: Optional[Path] = None) -> Pat } if not output_file: - output_file = ( - self.knowledge_dir - / f"export_{datetime.now().strftime('%Y%m%d%H%M%S')}.json" - ) + output_file = self.knowledge_dir / f"export_{datetime.now().strftime('%Y%m%d%H%M%S')}.json" with open(output_file, "w") as f: json.dump(export_data, f, indent=2) @@ -402,10 +388,7 @@ async def import_knowledge_base(self, import_file: Path) -> Dict[str, Any]: # Add all documents result = await self.add_knowledge_batch( - documents=[ - {"text": doc["text"], "metadata": doc.get("metadata", {})} - for doc in documents - ], + documents=[{"text": doc["text"], "metadata": doc.get("metadata", {})} for doc in documents], source=f"import_{import_file.stem}", ) diff --git a/CogniwareIms/backend/app/services/llm_service.py b/CogniwareIms/backend/app/services/llm_service.py index 66ff25484c..be55f8eea8 100644 --- a/CogniwareIms/backend/app/services/llm_service.py +++ b/CogniwareIms/backend/app/services/llm_service.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA LLM Service Integration Handles text generation, chat, and intelligent responses.""" import json @@ -44,9 +43,7 @@ async def chat_completion( "max_tokens": max_tokens or self.max_tokens, } - response = await client.post( - f"{self.base_url}/v1/chat/completions", json=payload - ) + response = await client.post(f"{self.base_url}/v1/chat/completions", json=payload) response.raise_for_status() result = response.json() @@ -67,9 +64,7 @@ async def generate_text(self, prompt: str, temperature: float = 0.7) -> str: messages = [{"role": "user", "content": prompt}] return await self.chat_completion(messages, temperature) - async def query_with_context( - self, question: str, context: str, system_prompt: Optional[str] = None - ) -> str: + async def query_with_context(self, question: str, context: str, system_prompt: Optional[str] = None) -> str: """Query LLM with context (RAG pattern) Args: @@ -92,13 +87,9 @@ async def query_with_context( messages.append({"role": "user", "content": prompt}) - return await self.chat_completion( - messages, temperature=0.3 - ) # Lower temp for factual responses + return await self.chat_completion(messages, temperature=0.3) # Lower temp for factual responses - async def generate_sql_query( - self, natural_language_query: str, schema: Dict[str, Any] - ) -> str: + async def generate_sql_query(self, natural_language_query: str, schema: Dict[str, Any]) -> str: """Generate SQL query from natural language (for DBQnA) Args: @@ -191,9 +182,7 @@ async def generate_inventory_insights(self, data: Dict[str, Any]) -> str: return await self.generate_text(prompt, temperature=0.5) - async def answer_inventory_question( - self, question: str, inventory_context: List[Dict[str, Any]] - ) -> str: + async def answer_inventory_question(self, question: str, inventory_context: List[Dict[str, Any]]) -> str: """Answer inventory-related questions with context.""" # Format inventory context context_parts = [] @@ -210,9 +199,7 @@ async def answer_inventory_question( return await self.query_with_context(question, context, system_prompt) - async def stream_chat( - self, messages: List[Dict[str, str]], temperature: float = 0.7 - ) -> AsyncGenerator[str, None]: + async def stream_chat(self, messages: List[Dict[str, str]], temperature: float = 0.7) -> AsyncGenerator[str, None]: """Stream chat responses (for real-time UI updates)""" try: async with httpx.AsyncClient(timeout=self.timeout) as client: diff --git a/CogniwareIms/backend/app/services/opea_client.py b/CogniwareIms/backend/app/services/opea_client.py index 27e27f7752..24bbb36b21 100644 --- a/CogniwareIms/backend/app/services/opea_client.py +++ b/CogniwareIms/backend/app/services/opea_client.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA Microservices Client Handles communication with OPEA GenAIComps microservices.""" import logging @@ -26,37 +25,27 @@ async def generate_embedding(self, text: str) -> List[float]: """Generate embeddings using OPEA embedding service.""" try: async with httpx.AsyncClient(timeout=self.timeout) as client: - response = await client.post( - f"{self.embedding_url}/v1/embeddings", json={"text": text} - ) + response = await client.post(f"{self.embedding_url}/v1/embeddings", json={"text": text}) response.raise_for_status() result = response.json() return result.get("embedding", []) except Exception as e: logger.error(f"Embedding generation failed: {e}") - raise HTTPException( - status_code=500, detail=f"Embedding service error: {str(e)}" - ) + raise HTTPException(status_code=500, detail=f"Embedding service error: {str(e)}") - async def query_with_rag( - self, query: str, context: Optional[str] = None - ) -> Dict[str, Any]: + async def query_with_rag(self, query: str, context: Optional[str] = None) -> Dict[str, Any]: """Query using Retrieval-Augmented Generation.""" try: async with httpx.AsyncClient(timeout=self.timeout) as client: payload = {"question": query, "context": context or ""} - response = await client.post( - f"{self.llm_url}/v1/chat/completions", json=payload - ) + response = await client.post(f"{self.llm_url}/v1/chat/completions", json=payload) response.raise_for_status() return response.json() except Exception as e: logger.error(f"RAG query failed: {e}") raise HTTPException(status_code=500, detail=f"LLM service error: {str(e)}") - async def query_database( - self, question: str, database_schema: Optional[Dict] = None - ) -> Dict[str, Any]: + async def query_database(self, question: str, database_schema: Optional[Dict] = None) -> Dict[str, Any]: """Query database using OPEA DBQnA agent.""" try: async with httpx.AsyncClient(timeout=self.timeout) as client: @@ -87,9 +76,7 @@ async def summarize_document(self, text: str) -> str: """Summarize document using OPEA DocSummarization agent.""" try: async with httpx.AsyncClient(timeout=self.timeout) as client: - response = await client.post( - f"{self.llm_url}/v1/summarize", json={"text": text} - ) + response = await client.post(f"{self.llm_url}/v1/summarize", json={"text": text}) response.raise_for_status() result = response.json() return result.get("summary", "") diff --git a/CogniwareIms/backend/app/services/retrieval_service.py b/CogniwareIms/backend/app/services/retrieval_service.py index 0454da4872..3bf86cc88c 100644 --- a/CogniwareIms/backend/app/services/retrieval_service.py +++ b/CogniwareIms/backend/app/services/retrieval_service.py @@ -1,6 +1,5 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 - """OPEA Retrieval Service Integration Handles semantic search and document retrieval using Redis vector store.""" import json @@ -27,14 +26,10 @@ def __init__(self): async def get_redis_client(self): """Get or create Redis client.""" if self.redis_client is None: - self.redis_client = await redis.from_url( - self.redis_url, encoding="utf-8", decode_responses=False - ) + self.redis_client = await redis.from_url(self.redis_url, encoding="utf-8", decode_responses=False) return self.redis_client - async def index_document( - self, doc_id: str, text: str, embedding: List[float], metadata: Dict[str, Any] - ) -> bool: + async def index_document(self, doc_id: str, text: str, embedding: List[float], metadata: Dict[str, Any]) -> bool: """Index a document in the vector store.""" try: # Store in Redis @@ -88,9 +83,7 @@ async def search( return result.get("results", []) except Exception as e: - logger.warning( - f"OPEA retrieval service unavailable, using direct Redis: {e}" - ) + logger.warning(f"OPEA retrieval service unavailable, using direct Redis: {e}") # Fallback to direct Redis search return await self._redis_search(query_embedding, top_k, filters) @@ -111,9 +104,7 @@ async def _redis_search( for doc_id in doc_ids: # Get document - doc_json = await client.get( - f"doc:{doc_id.decode() if isinstance(doc_id, bytes) else doc_id}" - ) + doc_json = await client.get(f"doc:{doc_id.decode() if isinstance(doc_id, bytes) else doc_id}") if doc_json: doc = json.loads(doc_json) doc_embedding = np.array(doc.get("embedding", [])) @@ -170,9 +161,7 @@ async def delete_document(self, doc_id: str) -> bool: logger.error(f"Error deleting document {doc_id}: {e}") return False - async def update_document( - self, doc_id: str, text: str, embedding: List[float], metadata: Dict[str, Any] - ) -> bool: + async def update_document(self, doc_id: str, text: str, embedding: List[float], metadata: Dict[str, Any]) -> bool: """Update an existing document.""" # Delete old version await self.delete_document(doc_id) @@ -194,9 +183,7 @@ async def get_document(self, doc_id: str) -> Optional[Dict[str, Any]]: logger.error(f"Error retrieving document {doc_id}: {e}") return None - async def get_all_documents( - self, limit: int = 100, offset: int = 0 - ) -> List[Dict[str, Any]]: + async def get_all_documents(self, limit: int = 100, offset: int = 0) -> List[Dict[str, Any]]: """Get all indexed documents.""" try: client = await self.get_redis_client() @@ -204,9 +191,7 @@ async def get_all_documents( documents = [] for i, doc_id in enumerate(list(doc_ids)[offset : offset + limit]): - doc = await self.get_document( - doc_id.decode() if isinstance(doc_id, bytes) else doc_id - ) + doc = await self.get_document(doc_id.decode() if isinstance(doc_id, bytes) else doc_id) if doc: documents.append(doc) @@ -233,9 +218,7 @@ async def clear_all_documents(self) -> bool: doc_ids = await client.smembers("document_ids") for doc_id in doc_ids: - await self.delete_document( - doc_id.decode() if isinstance(doc_id, bytes) else doc_id - ) + await self.delete_document(doc_id.decode() if isinstance(doc_id, bytes) else doc_id) logger.warning("Cleared all documents from vector store") return True diff --git a/CogniwareIms/backend/requirements.txt b/CogniwareIms/backend/requirements.txt index 052cbb82f9..176aca10b6 100644 --- a/CogniwareIms/backend/requirements.txt +++ b/CogniwareIms/backend/requirements.txt @@ -1,50 +1,39 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: Apache-2.0 -# Web Framework -fastapi==0.115.0 -uvicorn[standard]==0.31.0 - -# Security - FIXED: Migrated from python-jose (CRITICAL CVE) to PyJWT -PyJWT>=2.9.0 # Replaced python-jose[cryptography]==3.3.0 -cryptography>=43.0.7 # FIXED: Updated from 43.0.1 (OpenSSL vulnerability) -bcrypt==4.2.0 -passlib[bcrypt]==1.7.4 # HTTP Client - FIXED: Updated aiohttp (memory leak and smuggling fixes) aiohttp>=3.11.0 # FIXED: Updated from 3.10.10 -httpx==0.27.2 -respx>=0.22.0 # Mocking library for httpx (replaces non-existent httpx-mock) - License: BSD-3-Clause - -# Form Data - FIXED: Updated python-multipart (DoS vulnerability) -python-multipart>=0.0.9 # FIXED: Updated from 0.0.12 - -# Database -psycopg2-binary==2.9.10 -sqlalchemy==2.0.35 alembic==1.13.3 +bcrypt==4.2.0 -# Redis & Caching -redis==5.2.0 +# Code Quality (dev dependencies) +black==24.10.0 +cryptography>=43.0.7 # FIXED: Updated from 43.0.1 (OpenSSL vulnerability) +email-validator==2.2.0 +# Web Framework +fastapi==0.115.0 +flake8==7.1.1 hiredis==3.0.0 - -# Data Processing - FIXED: Migrated from PyPDF2 to pypdf (infinite loop fix) -pypdf>=4.0.0 # Replaced PyPDF2==3.0.1 (License: BSD-3-Clause, verified) +httpx==0.27.2 +mypy==1.11.2 +numpy==2.1.2 openpyxl==3.1.5 -python-docx==1.1.2 pandas==2.2.3 -numpy==2.1.2 -scikit-learn==1.5.2 +passlib[bcrypt]==1.7.4 + +# Database +psycopg2-binary==2.9.10 # Validation pydantic==2.9.2 pydantic-settings==2.5.2 -email-validator==2.2.0 -# Utilities -python-dotenv==1.0.1 -python-json-logger==2.0.7 -PyYAML==6.0.2 +# Security - FIXED: Migrated from python-jose (CRITICAL CVE) to PyJWT +PyJWT>=2.9.0 # Replaced python-jose[cryptography]==3.3.0 + +# Data Processing - FIXED: Migrated from PyPDF2 to pypdf (infinite loop fix) +pypdf>=4.0.0 # Replaced PyPDF2==3.0.1 (License: BSD-3-Clause, verified) # Logging & Monitoring # (using standard library and python-json-logger) @@ -53,8 +42,19 @@ PyYAML==6.0.2 pytest==8.3.3 pytest-asyncio==0.24.0 pytest-cov==6.0.0 +python-docx==1.1.2 -# Code Quality (dev dependencies) -black==24.10.0 -flake8==7.1.1 -mypy==1.11.2 +# Utilities +python-dotenv==1.0.1 +python-json-logger==2.0.7 + +# Form Data - FIXED: Updated python-multipart (DoS vulnerability) +python-multipart>=0.0.9 # FIXED: Updated from 0.0.12 +PyYAML==6.0.2 + +# Redis & Caching +redis==5.2.0 +respx>=0.22.0 # Mocking library for httpx (replaces non-existent httpx-mock) - License: BSD-3-Clause +scikit-learn==1.5.2 +sqlalchemy==2.0.35 +uvicorn[standard]==0.31.0 diff --git a/CogniwareIms/cogniwareims.py b/CogniwareIms/cogniwareims.py index b4c810618e..46954be657 100644 --- a/CogniwareIms/cogniwareims.py +++ b/CogniwareIms/cogniwareims.py @@ -84,9 +84,7 @@ def add_remote_service(self): ) # Add services to megaservice - self.megaservice.add(embedding_service).add(retriever_service).add( - rerank_service - ).add(llm_service) + self.megaservice.add(embedding_service).add(retriever_service).add(rerank_service).add(llm_service) self.megaservice.add(dataprep_service) # Define service flow for RAG pipeline @@ -96,9 +94,7 @@ def add_remote_service(self): self.megaservice.flow_to(rerank_service, llm_service) -def align_inputs( - self, inputs: dict, cur_node: MicroService, runtime_graph: dict -) -> dict: +def align_inputs(self, inputs: dict, cur_node: MicroService, runtime_graph: dict) -> dict: """Align input format for different microservices. Args: @@ -156,9 +152,7 @@ async def cogniwareims_endpoint(request: Request): chat_request = ChatCompletionRequest(**data) # Process through megaservice pipeline - result = await cogniwareims_service.megaservice.schedule( - initial_inputs={"messages": chat_request.messages} - ) + result = await cogniwareims_service.megaservice.schedule(initial_inputs={"messages": chat_request.messages}) return result diff --git a/CogniwareIms/docker_compose/intel/cpu/xeon/set_env.sh b/CogniwareIms/docker_compose/intel/cpu/xeon/set_env.sh index b5ea6ae39f..9d896078fe 100644 --- a/CogniwareIms/docker_compose/intel/cpu/xeon/set_env.sh +++ b/CogniwareIms/docker_compose/intel/cpu/xeon/set_env.sh @@ -21,4 +21,3 @@ export https_proxy=${https_proxy:-""} export no_proxy=${no_proxy:-"localhost,127.0.0.1"} echo "Environment variables set for Cogniware IMS deployment on Intel Xeon" - diff --git a/CogniwareIms/frontend/Dockerfile b/CogniwareIms/frontend/Dockerfile index abdbb1e8b9..152cee0408 100644 --- a/CogniwareIms/frontend/Dockerfile +++ b/CogniwareIms/frontend/Dockerfile @@ -1,3 +1,6 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + # Multi-stage build for Next.js production optimization FROM node:18-alpine AS base diff --git a/CogniwareIms/frontend/app/globals.css b/CogniwareIms/frontend/app/globals.css index a0958fed19..0827df32ce 100644 --- a/CogniwareIms/frontend/app/globals.css +++ b/CogniwareIms/frontend/app/globals.css @@ -66,4 +66,3 @@ ::-webkit-scrollbar-thumb:hover { background: #555; } - diff --git a/CogniwareIms/frontend/next.config.js b/CogniwareIms/frontend/next.config.js index 71e6dbdeae..a7ea05ffef 100644 --- a/CogniwareIms/frontend/next.config.js +++ b/CogniwareIms/frontend/next.config.js @@ -45,8 +45,7 @@ const nextConfig = { // Environment variables env: { - NEXT_PUBLIC_API_URL: - process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000", + NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000", NEXT_PUBLIC_WS_URL: process.env.NEXT_PUBLIC_WS_URL || "ws://localhost:8000", }, diff --git a/CogniwareIms/kubernetes/helm/Chart.yaml b/CogniwareIms/kubernetes/helm/Chart.yaml index 0fc140151a..3c089333ba 100644 --- a/CogniwareIms/kubernetes/helm/Chart.yaml +++ b/CogniwareIms/kubernetes/helm/Chart.yaml @@ -1,3 +1,6 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + apiVersion: v2 name: cogniwareims description: OPEA Cogniware IMS - AI-powered Inventory Management System built with Cogn DREAM diff --git a/CogniwareIms/tests/test_compose_on_xeon.sh b/CogniwareIms/tests/test_compose_on_xeon.sh index a873c3c4f0..809a55e7a4 100644 --- a/CogniwareIms/tests/test_compose_on_xeon.sh +++ b/CogniwareIms/tests/test_compose_on_xeon.sh @@ -70,7 +70,7 @@ wait_for_service() { local service_name=$2 local max_attempts=${3:-30} local attempt=0 - + echo "Waiting for $service_name..." while [ $attempt -lt $max_attempts ]; do if curl -s -f "$url" > /dev/null 2>&1; then @@ -81,7 +81,7 @@ wait_for_service() { echo "Waiting for $service_name... (attempt $attempt/$max_attempts)" sleep 10 done - + echo "ERROR: $service_name failed to become ready after $((max_attempts * 10)) seconds" return 1 } @@ -97,7 +97,7 @@ function build_docker_images() { echo "ERROR: docker_image_build directory not found" exit 1 fi - + cd "$build_dir" echo "Building from: $(pwd)" echo "Verifying build context..." @@ -136,7 +136,7 @@ function start_services() { fi exit 1 fi - + echo "Changing to compose directory: $compose_dir" cd "$compose_dir" || { echo "ERROR: Failed to cd to $compose_dir" @@ -157,7 +157,7 @@ function start_services() { # Wait for services to be ready echo "Waiting for services to initialize..." sleep 90 - + # Check if containers are running echo "Checking container status..." docker compose ps @@ -181,7 +181,7 @@ function validate_services() { echo "Waiting for Redis container... (attempt $attempt/$max_attempts)" sleep 5 done - + if [ $attempt -eq $max_attempts ]; then echo "ERROR: Redis failed to become ready" docker logs redis-vector-db || true @@ -203,7 +203,7 @@ function validate_services() { echo "Waiting for PostgreSQL container... (attempt $attempt/$max_attempts)" sleep 5 done - + if [ $attempt -eq $max_attempts ]; then echo "ERROR: PostgreSQL failed to become ready" docker logs postgres-db || true @@ -246,14 +246,14 @@ function validate_backend() { "session_id": "test_session", "user_role": "Inventory Manager" }' || echo "") - + if [ -z "$response" ]; then echo "Empty response from chat endpoint (attempt $((attempt + 1))/$max_attempts)" attempt=$((attempt + 1)) sleep 5 continue fi - + if echo "$response" | grep -qi "error"; then echo "Chat test failed!" echo "Response: $response" @@ -299,7 +299,7 @@ function stop_services() { } return fi - + if [ -n "$compose_dir" ]; then cd "$compose_dir" || { echo "Warning: Failed to cd to $compose_dir, attempting cleanup from current directory" @@ -330,4 +330,3 @@ function main() { } main - diff --git a/CogniwareIms/tests/test_gmc_on_xeon.sh b/CogniwareIms/tests/test_gmc_on_xeon.sh index 1aba986b04..1bce32dc04 100644 --- a/CogniwareIms/tests/test_gmc_on_xeon.sh +++ b/CogniwareIms/tests/test_gmc_on_xeon.sh @@ -96,4 +96,3 @@ function main() { } main - diff --git a/one_click_deploy/common/update_images.sh b/one_click_deploy/common/update_images.sh index 989975d4d4..6f10510daa 100644 --- a/one_click_deploy/common/update_images.sh +++ b/one_click_deploy/common/update_images.sh @@ -64,6 +64,11 @@ get_service_list() { local device_name=$2 local os_name=$3 case "$example_name" in + "ArbPostHearingAssistant") + if [[ "$device_name" == "xeon" ]]; then + echo "arb-post-hearing-assistant arb-post-hearing-assistant-gradio-ui llm-arb-post-hearing-assistant" + fi + ;; "DocSum") if [[ "$device_name" == "gaudi" ]]; then echo "docsum docsum-gradio-ui whisper llm-docsum vllm-gaudi" @@ -163,6 +168,11 @@ declare -A AGENT_CONFIG=( [clone_vllm_fork]=true [vllm_fork_version]="${VLLM_FORK_VER}" ) +# Config for ArbPostHearingAssistant, not needs vLLM +declare -A ArbPostHearingAssistant_CONFIG=( + [clone_vllm]=false + [clone_vllm_fork]=false +) # --- Generic Build Function --- # This single function handles the build process for any example, @@ -261,6 +271,9 @@ dispatch_build() { "AgentQnA") config_name="AGENT_CONFIG" ;; + "ArbPostHearingAssistant") + config_name="ArbPostHearingAssistant_CONFIG" + ;; *) error_exit "No build configuration defined for example '${example_name}'. Please add it to the script." ;; diff --git a/one_click_deploy/core/config.py b/one_click_deploy/core/config.py index f4c4ee671c..bf42129081 100644 --- a/one_click_deploy/core/config.py +++ b/one_click_deploy/core/config.py @@ -746,6 +746,85 @@ ], }, }, + "ArbPostHearingAssistant": { + "base_dir": "ArbPostHearingAssistant", + "docker_compose": { + "paths": { + "debian": { + "xeon": "docker_compose/intel/cpu/xeon/compose_tgi.yaml", + "gaudi": "docker_compose/intel/hpu/gaudi/compose_tgi.yaml", + }, + }, + "set_env_scripts": { + "xeon": "docker_compose/intel/set_env.sh", + "gaudi": "docker_compose/intel/set_env.sh", + }, + "params_to_set_env": { + "llm_model": "LLM_MODEL_ID", + "hf_token": "HF_TOKEN", + }, + }, + "supported_os": ["debian"], + "default_os": "debian", + "supported_devices": { + "debian": ["xeon", "gaudi"], + }, + "default_device": "xeon", + "offline_support": ["docker"], + "ports": { + "docker": { + "backend": "8888", + "llm": "8008", + }, + }, + "test_connections": { + "main_service": { + "service_key": "backend", + "path": "/v1/arb-post-hearing", + "method": "POST", + "payload": { + "messages": [ + { + "role": "user", + "content": "[10:00 AM] Arbitrator Hon. Rebecca Lawson: Good morning. This hearing is now in session for Case No. ARB/2025/0917. Lets begin with appearances. [10:01 AM] Attorney Michael Grant for Mr. Jonathan Reed: Good morning Your Honor. I represent the claimant Mr. Jonathan Reed. [10:01 AM] Attorney Lisa Chen for Ms. Rachel Morgan: Good morning. I represent the respondent Ms. Rachel Morgan. [10:03 AM] Arbitrator Hon. Rebecca Lawson: Thank you. Lets proceed with Mr. Reeds opening statement. [10:04 AM] Attorney Michael Grant: Ms. Morgan failed to deliver services as per the agreement dated March 15 2023. We have submitted relevant documentation including email correspondence and payment records. The delay caused substantial financial harm to our client. [10:15 AM] Attorney Lisa Chen: We deny any breach of contract. The delays were due to regulatory issues outside our control. Furthermore Mr. Reed did not provide timely approvals which contributed to the delay. [10:30 AM] Arbitrator Hon. Rebecca Lawson: Lets turn to Clause Z of the agreement. Id like both parties to submit written briefs addressing the applicability of the force majeure clause and the timeline of approvals. [11:00 AM] Attorney Michael Grant: Understood. Well submit by the deadline. [11:01 AM] Attorney Lisa Chen: Agreed. [11:02 AM] Arbitrator Hon. Rebecca Lawson: The next hearing is scheduled for October 22 2025 at 1030 AM Eastern Time. Please ensure your witnesses are available for cross examination. [4:45 PM] Arbitrator Hon. Rebecca Lawson: This session is adjourned. Thank you everyone.", + } + ], + "type": "text", + "language": "en", + "max_new_tokens": 100, + }, + "headers": {"Content-Type": "application/json"}, + "expect_code": 200, + }, + "sub_services": [ + { + "name": "llm_check", + "service_key": "llm", + "path": "/v1/arb-post-hearing", + "method": "POST", + "payload_dynamic_llm_model": True, + "default_llm_model_id_for_test": "mistralai/Mistral-7B-Instruct-v0.2", + "payload_template": { + "role": "user", + "content": "[10:00 AM] Arbitrator Hon. Rebecca Lawson: Good morning. This hearing is now in session for Case No. ARB/2025/0917. Lets begin with appearances. [10:01 AM] Attorney Michael Grant for Mr. Jonathan Reed: Good morning Your Honor. I represent the claimant Mr. Jonathan Reed. [10:01 AM] Attorney Lisa Chen for Ms. Rachel Morgan: Good morning. I represent the respondent Ms. Rachel Morgan. [10:03 AM] Arbitrator Hon. Rebecca Lawson: Thank you. Lets proceed with Mr. Reeds opening statement. [10:04 AM] Attorney Michael Grant: Ms. Morgan failed to deliver services as per the agreement dated March 15 2023. We have submitted relevant documentation including email correspondence and payment records. The delay caused substantial financial harm to our client. [10:15 AM] Attorney Lisa Chen: We deny any breach of contract. The delays were due to regulatory issues outside our control. Furthermore Mr. Reed did not provide timely approvals which contributed to the delay. [10:30 AM] Arbitrator Hon. Rebecca Lawson: Lets turn to Clause Z of the agreement. Id like both parties to submit written briefs addressing the applicability of the force majeure clause and the timeline of approvals. [11:00 AM] Attorney Michael Grant: Understood. Well submit by the deadline. [11:01 AM] Attorney Lisa Chen: Agreed. [11:02 AM] Arbitrator Hon. Rebecca Lawson: The next hearing is scheduled for October 22 2025 at 1030 AM Eastern Time. Please ensure your witnesses are available for cross examination. [4:45 PM] Arbitrator Hon. Rebecca Lawson: This session is adjourned. Thank you everyone.", + "type": "text", + "language": "en", + }, + "headers": {"Content-Type": "application/json"}, + "expect_code": 200, + "expect_response_contains": "case_number", + } + ], + }, + "interactive_params": [ + { + "name": "llm_model", + "prompt": "LLM Model ID", + "type": str, + "help": "e.g., mistralai/Mistral-7B-Instruct-v0.2", + }, + ], + }, } # --- Deployment and Testing Configurations ---