import os import sys from dotenv import load_dotenv # --- Explicit Debugging & Env Loading --- print(f"--- [DEBUG] Current Working Directory: {os.getcwd()}", file=sys.stderr) load_result = load_dotenv() print(f"--- [DEBUG] load_dotenv() result: {load_result}", file=sys.stderr) # --- import logging from fastapi import FastAPI, HTTPException, Depends from starlette.responses import StreamingResponse from .models import IncomingRequest, ProxyResponse from .services import process_chat_request, stream_llm_api, inject_tools_into_prompt from .core.config import get_settings, Settings # --- Logging Configuration --- logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("llm_proxy.log"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # --- End of Logging Configuration --- app = FastAPI( title="LLM Tool Proxy", description="A proxy that intercepts LLM requests to inject and handle tool calls.", version="1.0.0", ) @app.on_event("startup") async def startup_event(): logger.info("Application startup complete.") current_settings = get_settings() logger.info(f"Loaded LLM API URL: {current_settings.REAL_LLM_API_URL}") @app.post("/v1/chat/completions") async def chat_completions( request: IncomingRequest, settings: Settings = Depends(get_settings) ): """ This endpoint mimics the OpenAI Chat Completions API and supports both streaming (`stream=True`) and non-streaming (`stream=False`) responses. """ if not settings.REAL_LLM_API_KEY or not settings.REAL_LLM_API_URL: logger.error("REAL_LLM_API_KEY or REAL_LLM_API_URL is not configured.") raise HTTPException(status_code=500, detail="LLM API Key or URL is not configured.") # Prepare messages, potentially with tool injection # This prepares the messages that will be sent to the LLM backend messages_to_llm = request.messages if request.tools: messages_to_llm = inject_tools_into_prompt(request.messages, request.tools) # Handle streaming request if request.stream: logger.info(f"Initiating streaming request with {len(messages_to_llm)} messages.") generator = stream_llm_api(messages_to_llm, settings) return StreamingResponse(generator, media_type="text/event-stream") # Handle non-streaming request try: logger.info(f"Initiating non-streaming request with {len(messages_to_llm)} messages.") response_message = await process_chat_request(messages_to_llm, request.tools, settings) logger.info("Successfully processed non-streaming request.") return ProxyResponse(message=response_message) except Exception as e: logger.exception("An unexpected error occurred during non-streaming request.") raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}") @app.get("/") def read_root(): return {"message": "LLM Tool Proxy is running."}