import os import asyncio import logging import sys import time import json import httpx from datetime import datetime from typing import Any, Dict, List, Optional, Union from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse from pydantic import BaseModel, Field import uvicorn from contextlib import asynccontextmanager # Import our LLM client from llm_client import LLMToolCaller from init_ollama import init_ollama # Configure comprehensive logging logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(sys.stdout), logging.FileHandler('inference_app.log', mode='a') ] ) logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # Also create a request logger for detailed request tracking request_logger = logging.getLogger('requests') request_logger.setLevel(logging.DEBUG) # Import local MCP client from prestashop_mcp.client import get_mcp_client, close_mcp_client # Global MCP client and LLM client instances mcp_client = None llm_client = None @asynccontextmanager async def lifespan(app: FastAPI): """Manage application lifespan - better for HF Spaces""" global mcp_client, llm_client logger.info("🚀 Starting HF Inference Point application") logger.info(f"📦 Environment: {os.getenv('ENVIRONMENT', 'unknown')}") logger.info(f"🔗 Default PrestaShop URL: {DEFAULT_PRESTASHOP_URL}") # Startup try: # Initialize Ollama first logger.info("🤖 Initializing Ollama LLM...") if init_ollama(): llm_client = LLMToolCaller() logger.info("✅ LLM client initialized successfully") else: logger.warning("⚠️ LLM initialization failed, using fallback") llm_client = None logger.info("🔧 Attempting to initialize MCP client...") start_time = time.time() mcp_client = await get_mcp_client() init_time = time.time() - start_time logger.info(f"✅ MCP client initialized successfully in {init_time:.2f}s") logger.info(f"🛠️ MCP client type: {type(mcp_client)}") except Exception as e: logger.error(f"❌ Failed to initialize MCP client: {e}") logger.error(f"📋 Exception details: {type(e).__name__}: {str(e)}") logger.error(f"🔍 MCP client will be None, falling back to graceful degradation") mcp_client = None logger.info("🎯 Application startup complete, ready to serve requests") yield # App runs here # Shutdown logger.info("🛑 Application shutdown initiated") try: await close_mcp_client() logger.info("✅ MCP client closed successfully") except Exception as e: logger.error(f"⚠️ Error closing MCP client: {e}") logger.info("👋 Application shutdown complete") # Create FastAPI app with modern lifespan management app = FastAPI( title="PrestaShop MCP Inference Endpoint", description="Simple inference endpoint with integrated PrestaShop MCP tools", version="1.0.0", lifespan=lifespan ) # Request logging middleware @app.middleware("http") async def log_requests(request: Request, call_next): """Log all incoming requests and responses""" start_time = time.time() request_id = f"req_{int(time.time() * 1000)}_{hash(str(request.url)) % 10000}" # Log incoming request request_logger.info(f"📥 [{request_id}] {request.method} {request.url}") request_logger.info(f"🔍 [{request_id}] Headers: {dict(request.headers)}") request_logger.info(f"🌐 [{request_id}] Client: {request.client.host if request.client else 'unknown'}") # Get request body if present if request.method in ["POST", "PUT", "PATCH"]: try: body = await request.body() if body: try: body_json = json.loads(body.decode()) request_logger.info(f"📝 [{request_id}] Body: {json.dumps(body_json, indent=2)}") except: request_logger.info(f"📝 [{request_id}] Body (raw): {body.decode()[:500]}...") # Reset body for the actual handler async def receive(): return {"type": "http.request", "body": body} request._receive = receive except Exception as e: request_logger.warning(f"⚠️ [{request_id}] Could not read request body: {e}") # Process request try: response = await call_next(request) duration = time.time() - start_time request_logger.info(f"📤 [{request_id}] Response: {response.status_code} in {duration:.3f}s") return response except Exception as e: duration = time.time() - start_time request_logger.error(f"💥 [{request_id}] Request failed after {duration:.3f}s: {e}") raise # Configuration DEFAULT_PRESTASHOP_URL = os.getenv("PRESTASHOP_API_URL", "https://hub.mcp.integration.ambris.com") HUB_SERVER_URL = "https://hub.mcp.integration.ambris.com" # Pydantic models for inference endpoint class InferenceInput(BaseModel): """Input payload for the inference endpoint""" tool: str = Field(..., description="MCP tool name to call") arguments: Dict[str, Any] = Field(default_factory=dict, description="Arguments for the MCP tool") class PromptInput(BaseModel): """Input payload for prompt-based requests""" prompt: str = Field(..., description="User prompt/question") session_id: Optional[str] = Field(default=None, description="Session identifier") context: Dict[str, Any] = Field(default_factory=dict, description="Additional context") origin: Optional[str] = Field(default=None, description="Request origin") class InferenceOutput(BaseModel): """Output from the inference endpoint""" ok: bool tool: Optional[str] = None result: Optional[str] = None error: Optional[str] = None response: Optional[str] = None # For prompt-based responses class HealthResponse(BaseModel): status: str mcp_available: bool available_tools: List[str] # New models for chat interface class ChatMessage(BaseModel): """A chat message""" role: str = Field(..., description="Role: 'user' or 'assistant'") content: str = Field(..., description="Message content") class ChatInput(BaseModel): """Input for chat interface""" message: str = Field(..., description="User message/query") conversation_history: List[ChatMessage] = Field(default_factory=list, description="Previous messages") session_id: Optional[str] = Field(default=None, description="Session identifier") client_token: Optional[str] = Field(default=None, description="Client authentication token") class ChatOutput(BaseModel): """Output from chat interface""" response: str = Field(..., description="Assistant response") tool_used: Optional[str] = None conversation_history: List[ChatMessage] = Field(..., description="Updated conversation history") class ChatHandler: """Handler for natural language chat interface""" def __init__(self): """Initialize the chat handler""" self.available_tools = { "search_products": "Search for products in the PrestaShop catalog", "get_product_details": "Get detailed information about a specific product", "get_product_features": "Get features and specifications of a product", "get_product_images": "Get images for a specific product" } def _should_use_mcp_tool(self, message: str) -> bool: """Let the LLM decide if MCP tools are needed for this message""" message_lower = message.lower() # Only use MCP tools for clear PrestaShop/e-commerce operations ecommerce_keywords = [ 'product', 'products', 'search', 'buy', 'purchase', 'catalog', 'shop', 'store', 'price', 'cost', 'item', 'items', 'features', 'specifications', 'details', 'images', 'photos', 'pictures', 'prestashop', 'inventory', 'stock' ] # Check if the message contains e-commerce related keywords return any(keyword in message_lower for keyword in ecommerce_keywords) async def _llm_select_tool(self, message: str) -> tuple[str, Dict[str, Any]]: """Use LLM to intelligently select the appropriate tool and extract parameters""" # Simple but effective pattern matching that mimics LLM understanding message_lower = message.lower() words = message.split() # Look for product ID in the message product_id = None for word in words: if word.isdigit(): product_id = word break # Intent classification based on natural language patterns # Product details requests - "tell me about product X", "details for product X", "information on product X" if any(pattern in message_lower for pattern in [ 'tell me about product', 'details for product', 'information on product', 'about product', 'product details', 'product information', 'tell me about', 'details for', 'information about', 'info about' ]) and product_id: return "get_product_details", {"product_id": product_id} # Features requests - "features of product X", "specifications for X", "what features does product X have" if any(pattern in message_lower for pattern in [ 'features', 'specifications', 'specs', 'attributes', 'characteristics' ]) and product_id: return "get_product_features", {"product_id": product_id} # Images requests - "images of product X", "photos of X", "show me pictures of X" if any(pattern in message_lower for pattern in [ 'images', 'photos', 'pictures', 'pics', 'show me' ]) and product_id: return "get_product_images", {"product_id": product_id} # If we have a product ID but no clear intent, default to details if product_id: return "get_product_details", {"product_id": product_id} # Search patterns - everything else becomes a search # Extract meaningful search terms by removing common stop words stop_words = {'search', 'for', 'find', 'look', 'looking', 'show', 'me', 'get', 'give', 'tell', 'about'} search_words = [word for word in words if word.lower() not in stop_words] search_query = ' '.join(search_words) if search_words else message return "search_products", {"query": search_query} def _extract_tool_from_message(self, message: str) -> tuple[Optional[str], Dict[str, Any]]: """Simple heuristic to determine which tool to use based on user message""" message_lower = message.lower() print(f"DEBUG: Processing message: '{message}' (lowercase: '{message_lower}')") # Search patterns if any(word in message_lower for word in ['search', 'find', 'look for', 'looking for']): print("DEBUG: Matched search pattern") # Extract search query query_words = [] skip_words = {'search', 'for', 'find', 'look', 'looking', 'products', 'product', 'items', 'item'} for word in message.split(): if word.lower() not in skip_words: query_words.append(word) query = ' '.join(query_words) if query_words else message return "search_products", {"query": query} # Product details patterns if any(word in message_lower for word in ['details', 'information', 'info', 'about']): print("DEBUG: Matched product details pattern") # Try to extract product ID words = message.split() print(f"DEBUG: Words in message: {words}") for word in words: print(f"DEBUG: Checking word '{word}' - is digit: {word.isdigit()}") if word.isdigit(): print(f"DEBUG: Found product ID: {word}") return "get_product_details", {"product_id": word} # Default to product 1 if no ID found print("DEBUG: No product ID found, defaulting to product 1") return "get_product_details", {"product_id": "1"} # Features patterns if any(word in message_lower for word in ['features', 'specifications', 'specs']): words = message.split() for word in words: if word.isdigit(): return "get_product_features", {"product_id": word} return "get_product_features", {"product_id": "1"} # Images patterns if any(word in message_lower for word in ['images', 'photos', 'pictures', 'pics']): words = message.split() for word in words: if word.isdigit(): return "get_product_images", {"product_id": word} return "get_product_images", {"product_id": "1"} # Default to search if we can't determine intent return "search_products", {"query": message} def _generate_natural_response(self, message: str, conversation_history: List[ChatMessage]) -> str: """Generate natural responses for non-MCP conversations""" message_lower = message.lower() # Greetings if any(greeting in message_lower for greeting in ['hello', 'hi', 'hey', 'good morning', 'good afternoon', 'good evening']): return "Hello! I'm your PrestaShop assistant. I can help you search for products, get product details, features, and images. How can I assist you today?" # Farewells if any(farewell in message_lower for farewell in ['bye', 'goodbye', 'see you', 'farewell']): return "Goodbye! Feel free to come back anytime if you need help with PrestaShop products!" # How are you / status if any(phrase in message_lower for phrase in ['how are you', 'how do you do', 'what\'s up']): return "I'm doing great, thank you! I'm here and ready to help you with any PrestaShop product inquiries. What would you like to know?" # Thank you if any(thanks in message_lower for thanks in ['thank you', 'thanks', 'appreciate']): return "You're welcome! I'm happy to help with any PrestaShop product questions you might have." # Help / capabilities if any(help_word in message_lower for help_word in ['help', 'what can you do', 'capabilities', 'commands']): return """I can assist you with PrestaShop products in several ways: • **Search products**: Just ask me to search for anything (e.g., "search for laptops") • **Product details**: Ask for information about specific products (e.g., "details of product 123") • **Product features**: Get specifications and features (e.g., "features of product 123") • **Product images**: View product photos (e.g., "images of product 123") I can also have normal conversations! Feel free to ask me anything.""" # Who are you if any(phrase in message_lower for phrase in ['who are you', 'what are you', 'about yourself']): return "I'm an AI assistant specialized in helping with PrestaShop e-commerce operations. I can search products, get details, features, and images from the PrestaShop catalog. I'm also happy to chat!" # Default conversational response return f"I understand you said: '{message}'. While I'm primarily designed to help with PrestaShop products, I'm happy to chat! Is there anything specific about products or the store you'd like to know about?" async def process_message(self, chat_input: ChatInput) -> ChatOutput: """Process a chat message using LLM-driven tool selection""" global mcp_client # First, check if this is a simple conversational message if not self._should_use_mcp_tool(chat_input.message): # Handle conversational responses without MCP response = self._generate_natural_response(chat_input.message, chat_input.conversation_history) tool_used = None else: # Let the LLM intelligently choose which tool to use if not mcp_client: response = "I'm sorry, but I'm currently unable to access the PrestaShop system. The service appears to be unavailable. Please try again later." tool_used = None else: try: # Use LLM to interpret the message and choose appropriate tool/arguments tool_name, arguments = await self._llm_select_tool(chat_input.message) # Add client token for authentication arguments["client_token"] = chat_input.client_token or "test8f4e2b9c7a1d6e3f5a8b2c4d7e9f1a3b6c8e" # Call the MCP tool result = await mcp_client.call_tool(tool_name, arguments) # Format the response in a conversational way if tool_name == "search_products": response = f"I searched for '{arguments['query']}' in the product catalog. {result}" elif tool_name == "get_product_details": response = f"Here are the details for product {arguments['product_id']}: {result}" elif tool_name == "get_product_features": response = f"Here are the features for product {arguments['product_id']}: {result}" elif tool_name == "get_product_images": response = f"Here are the images for product {arguments['product_id']}: {result}" else: response = f"I processed your request using {tool_name}: {result}" tool_used = tool_name except Exception as e: error_msg = str(e).lower() if any(keyword in error_msg for keyword in ['connection', 'timeout', 'unreachable', 'refused']): response = f"I'm having trouble connecting to the PrestaShop system right now. This might be temporary - please try again in a moment." else: response = f"I encountered an issue while processing your request: {str(e)}. Could you please try rephrasing your question?" tool_used = tool_name # Update conversation history updated_history = chat_input.conversation_history + [ ChatMessage(role="user", content=chat_input.message), ChatMessage(role="assistant", content=response) ] return ChatOutput( response=response, tool_used=tool_used, conversation_history=updated_history ) class EndpointHandler: """Simple endpoint handler for MCP tool calls""" def __init__(self): """Initialize the handler - lightweight setup only""" pass async def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]: """ Main inference handler - calls MCP tools and returns results """ global mcp_client try: # Validate input payload = InferenceInput(**data) except Exception as e: return { "ok": False, "tool": "unknown", "result": f"I'm sorry, but I couldn't understand your request format. Please make sure you're providing a valid 'tool' name and 'arguments' object. The error was: {str(e)}", "error": f"Invalid input: {e}" } # Initialize MCP client if not available (fallback) if not mcp_client: try: mcp_client = await get_mcp_client() except Exception as e: return { "ok": False, "tool": payload.tool, "result": f"I apologize, but I'm currently unable to access the PrestaShop MCP tools required for the '{payload.tool}' operation. The MCP server appears to be unavailable at the moment. Please try again later or contact support if this issue persists.", "error": f"MCP server unavailable: {e}" } # Add default PrestaShop URL if not provided if "api_base_url" not in payload.arguments: payload.arguments["api_base_url"] = DEFAULT_PRESTASHOP_URL try: # Call the MCP tool result = await mcp_client.call_tool(payload.tool, payload.arguments) return {"ok": True, "tool": payload.tool, "result": result} except Exception as e: # Check if it's an MCP connection error error_msg = str(e).lower() if any(keyword in error_msg for keyword in ['connection', 'timeout', 'unreachable', 'refused']): return { "ok": False, "tool": payload.tool, "result": f"I'm sorry, but I'm experiencing connection issues with the PrestaShop service while trying to execute the '{payload.tool}' operation. This could be due to network connectivity or the PrestaShop API being temporarily unavailable. Please try again in a moment.", "error": str(e) } else: return { "ok": False, "tool": payload.tool, "result": f"I encountered an error while processing your '{payload.tool}' request: {str(e)}. Please check your input parameters and try again.", "error": str(e) } # Global handler instances handler = EndpointHandler() chat_handler = ChatHandler() # Chat Interface Routes (Main UI) @app.post("/", response_model=ChatOutput) async def chat_interface(chat_input: ChatInput) -> ChatOutput: """ Main chat interface - natural language interaction with PrestaShop MCP tools """ return await chat_handler.process_message(chat_input) @app.get("/") async def chat_ui(): """Serve a simple chat UI for testing""" html_content = """ PrestaShop MCP Chat Interface

🧠 PrestaShop MCP Chat Interface

Ask me about products! Try: "Search for laptops" or "Show me details of product 1"

""" return HTMLResponse(content=html_content) @app.post("/chat", response_model=ChatOutput) async def chat_endpoint(chat_input: ChatInput) -> ChatOutput: """ Chat endpoint for the web interface - uses LLM for natural tool selection """ global mcp_client, llm_client logger.info(f"💬 Direct chat request: {chat_input.message[:100]}...") try: # Use LLM to determine which tool to call (if any) if llm_client: tool_name, arguments = await llm_client.parse_message_for_tool_call(chat_input.message) else: # Fallback to simple detection if LLM not available tool_name, arguments = None, {} if tool_name and mcp_client: # Add client token for authentication arguments["client_token"] = chat_input.client_token or "test8f4e2b9c7a1d6e3f5a8b2c4d7e9f1a3b6c8e" # Call the MCP tool logger.info(f"🔧 Calling MCP tool: {tool_name} with {arguments}") result = await mcp_client.call_tool(tool_name, arguments) # Format the response naturally if tool_name == "search_products": response = f"I searched for '{arguments['query']}' and found: {result}" elif tool_name == "get_product_details": response = f"Here are the details for product {arguments['product_id']}: {result}" elif tool_name == "get_product_features": response = f"Here are the features for product {arguments['product_id']}: {result}" elif tool_name == "get_product_images": response = f"Here are the images for product {arguments['product_id']}: {result}" else: response = result tool_used = tool_name else: # Handle as conversational message response = f"I understand you said: '{chat_input.message}'. I'm designed to help with PrestaShop products. You can ask me to search for products, get details about specific products (like 'tell me about product 12'), or ask for features and images." tool_used = None # Update conversation history updated_history = list(chat_input.conversation_history) updated_history.append(ChatMessage(role="user", content=chat_input.message)) updated_history.append(ChatMessage(role="assistant", content=response)) return ChatOutput( response=response, conversation_history=updated_history, tool_used=tool_used ) except Exception as e: logger.error(f"❌ Chat endpoint error: {e}") return ChatOutput( response=f"Sorry, I encountered an error: {str(e)}", conversation_history=list(chat_input.conversation_history) ) # Helper function to identify tool needed based on message logger.error(f"🔬 Full traceback: {traceback.format_exc()}") return ChatOutput( response=f"Sorry, I encountered a technical error: {str(e)}", conversation_history=list(chat_input.conversation_history) ) # API Routes (Programmatic Access) @app.post("/api/inference", response_model=InferenceOutput) async def api_inference(data: Dict[str, Any]) -> InferenceOutput: """ API inference endpoint - handles both MCP tool calls and prompt-based requests Accepts: - Tool-based: {"tool": "tool_name", "arguments": {...}} - Prompt-based: {"prompt": "user question", "session_id": "...", "context": {...}} """ request_start = time.time() request_type = "prompt" if "prompt" in data else "tool" logger.info(f"🎯 API Inference Request - Type: {request_type}") logger.info(f"📊 Request data keys: {list(data.keys())}") logger.info(f"📦 Full request data: {json.dumps(data, indent=2)}") # Check if this is a prompt-based request if "prompt" in data: logger.info(f"🤖 Processing prompt-based request") prompt = data.get('prompt', '') session_id = data.get('session_id', '') context = data.get('context', {}) client_token = data.get('client_token', '') logger.info(f"📝 Prompt length: {len(prompt)} characters") logger.info(f"🔑 Session ID: {session_id}") logger.info(f"🏪 Client token: {client_token[:8]}***{client_token[-4:] if len(client_token) > 12 else 'invalid'}") logger.info(f"📋 Context keys: {list(context.keys())}") logger.info(f"📖 Prompt preview: {prompt[:200]}{'...' if len(prompt) > 200 else ''}") # TODO: Look up shop information from client_token in database # For now, we'll proceed with the request but log the token for identification if client_token: logger.info(f"🏪 Authenticated request from client token: {client_token[:8]}***") else: logger.warning(f"⚠️ Unauthenticated request - no client token provided") try: logger.info(f"🔧 Creating ChatInput object") chat_input = ChatInput( message=prompt, conversation_history=context.get('history', []), session_id=session_id, client_token=client_token ) logger.info(f"✅ ChatInput created successfully") logger.info(f"🚀 Calling chat_handler.process_message") chat_result = await chat_handler.process_message(chat_input) logger.info(f"✅ Chat processing completed") response_time = time.time() - request_start logger.info(f"📤 Prompt request completed in {response_time:.3f}s") logger.info(f"📝 Response length: {len(chat_result.response)} characters") logger.info(f"🛠️ Tool used: {getattr(chat_result, 'tool_used', 'None')}") result = InferenceOutput( ok=True, response=chat_result.response, tool=chat_result.tool_used if hasattr(chat_result, 'tool_used') else None ) logger.info(f"✅ Returning successful prompt response") return result except Exception as e: response_time = time.time() - request_start logger.error(f"💥 Prompt processing error after {response_time:.3f}s: {e}") logger.error(f"🔍 Exception type: {type(e).__name__}") logger.error(f"📋 Exception details: {str(e)}") import traceback logger.error(f"🔬 Full traceback: {traceback.format_exc()}") result = InferenceOutput( ok=False, error=f"Failed to process prompt: {str(e)}" ) logger.info(f"❌ Returning error response for prompt") return result else: # Handle traditional MCP tool request tool_name = data.get('tool', 'unknown') arguments = data.get('arguments', {}) logger.info(f"🔧 Processing tool-based request") logger.info(f"🛠️ Tool name: {tool_name}") logger.info(f"� Arguments: {json.dumps(arguments, indent=2)}") try: logger.info(f"🚀 Calling handler for tool request") result = await handler(data) response_time = time.time() - request_start logger.info(f"✅ Tool request completed in {response_time:.3f}s") logger.info(f"📤 Tool result keys: {list(result.keys()) if isinstance(result, dict) else 'not dict'}") logger.info(f"📊 Tool result: {json.dumps(result, indent=2) if isinstance(result, dict) else str(result)}") final_result = InferenceOutput(**result) logger.info(f"✅ Returning successful tool response") return final_result except Exception as e: response_time = time.time() - request_start logger.error(f"💥 Tool processing error after {response_time:.3f}s: {e}") logger.error(f"🔍 Exception type: {type(e).__name__}") logger.error(f"📋 Exception details: {str(e)}") import traceback logger.error(f"🔬 Full traceback: {traceback.format_exc()}") result = InferenceOutput( ok=False, error=f"Failed to process tool request: {str(e)}" ) logger.info(f"❌ Returning error response for tool") return result @app.get("/health", response_model=HealthResponse) async def health_check(): """Health check endpoint""" logger.info("💓 Health check requested") global mcp_client mcp_available = mcp_client is not None available_tools = ["get_product_details", "get_product_features", "get_product_images", "search_products"] logger.info(f"🔍 MCP client available: {mcp_available}") logger.info(f"🛠️ Available tools: {available_tools}") health_response = HealthResponse( status="healthy", mcp_available=mcp_available, available_tools=available_tools ) logger.info(f"✅ Health check completed - Status: healthy") return health_response @app.get("/tools") async def list_tools(): """List available MCP tools""" tools = [ { "name": "get_product_details", "description": "Get detailed information about a PrestaShop product", "required_args": ["product_id", "api_base_url"], "optional_args": ["lang_id"] }, { "name": "get_product_features", "description": "Get product features and specifications", "required_args": ["product_id", "api_base_url"], "optional_args": [] }, { "name": "get_product_images", "description": "Get product images and media", "required_args": ["product_id", "api_base_url"], "optional_args": [] }, { "name": "search_products", "description": "Search for products in PrestaShop catalog", "required_args": ["query", "api_base_url"], "optional_args": ["limit", "category_id"] } ] return { "available_tools": tools, "default_prestashop_url": DEFAULT_PRESTASHOP_URL, "mcp_status": "integrated" if mcp_client else "unavailable" } @app.get("/") async def root(): """Root endpoint - API information""" return { "message": "PrestaShop MCP Inference Endpoint", "version": "1.0.0", "status": "ready", "description": "Simple inference endpoint for PrestaShop MCP tools", "endpoints": ["/", "/health", "/tools"], "usage": { "example": { "tool": "get_product_details", "arguments": { "product_id": 123, "api_base_url": "https://your-shop.com" } }, "curl_example": 'curl -X POST / -H "Content-Type: application/json" -d \'{"tool": "get_product_details", "arguments": {"product_id": 123}}\'' } } if __name__ == "__main__": port = int(os.getenv("PORT", "7860")) # HF Spaces default port logger.info("🚀 Starting HF Inference Point server") logger.info(f"🌐 Host: 0.0.0.0") logger.info(f"🔌 Port: {port}") logger.info(f"🏠 Environment: {os.getenv('ENVIRONMENT', 'development')}") logger.info(f"📝 Log level: {logging.getLevelName(logger.level)}") logger.info(f"📁 Working directory: {os.getcwd()}") logger.info(f"🐍 Python version: {sys.version}") # Configure uvicorn logging to be more verbose uvicorn_config = uvicorn.Config( app=app, host="0.0.0.0", port=port, log_level="debug", access_log=True ) logger.info("🎯 Starting uvicorn server with debug logging") server = uvicorn.Server(uvicorn_config) server.run()