diff --git a/sample_solutions/PDFToPodcast/.env.example b/sample_solutions/PDFToPodcast/.env.example index f0666ea8..bdfda635 100644 --- a/sample_solutions/PDFToPodcast/.env.example +++ b/sample_solutions/PDFToPodcast/.env.example @@ -1,4 +1,7 @@ -# Backend Configuration +######################################################################################################### +# Backend Service Configuration +######################################################################################################### + CORS_ORIGINS=http://localhost:3000 # Service URLs (for local development) @@ -7,14 +10,102 @@ LLM_SERVICE_URL=http://llm-service:8002 TTS_SERVICE_URL=http://tts-service:8003 BACKEND_API_URL=http://localhost:8000 -# File Upload Configuration -MAX_FILE_SIZE=10485760 # 10MB in bytes - # Environment NODE_ENV=development PYTHON_ENV=development + +######################################################################################################### +# PDF Service Configuration +######################################################################################################### + +# Service Configuration +PDF_SERVICE_NAME="PDF Processing Service" +PDF_SERVICE_VERSION=1.0.0 +PDF_SERVICE_PORT=8001 + +# File Processing +MAX_FILE_SIZE=10485760 +UPLOAD_DIR=uploads + +# OCR Settings +# TESSERACT_CMD=/usr/bin/tesseract +OCR_LANGUAGE=eng +OCR_DPI=300 + +# Text Processing +ENABLE_TEXT_CLEANING=true +ENABLE_OCR_FALLBACK=true + + +######################################################################################################### +# LLM Service Configuration +######################################################################################################### + +# Inference API Configuration +# Note: model names must match one of the deployed models with Enterprise Inference. +# INFERENCE_API_ENDPOINT: URL to your inference service (without /v1 suffix). Uncomment ONE of the below. +# - For GenAI Gateway: +#INFERENCE_API_ENDPOINT=https://api.example.com +# - For APISIX Gateway (default), requires the model name in the URL path: +INFERENCE_API_ENDPOINT=https://api.example.com/DeepSeek-R1-Distill-Qwen-32B + +# INFERENCE_API_TOKEN: Authentication token/API key for the inference service +# - For GenAI Gateway: Your GenAI Gateway API key +# - For APISIX Gateway: Your APISIX authentication token +INFERENCE_API_TOKEN=your-pre-generated-token-here + +# Model Configuration +# IMPORTANT: Use the full model name i.e. model card +INFERENCE_MODEL_NAME=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B + +# Service Configuration +LLM_SERVICE_NAME="LLM Script Generation Service" +LLM_SERVICE_VERSION=1.0.0 +LLM_SERVICE_PORT=8002 + +# Model Settings +DEFAULT_TONE=conversational +DEFAULT_MAX_LENGTH=2000 + +# Generation Parameters +TEMPERATURE=0.7 +MAX_TOKENS=4000 +MAX_RETRIES=3 + # Local URL Endpoint (only needed for non-public domains) # If using a local domain like api.example.com mapped to localhost, set to the domain without https:// # Otherwise, set to: not-needed LOCAL_URL_ENDPOINT=not-needed + +# SSL Verification Settings +# Set to false only for dev with self-signed certs +VERIFY_SSL=true + + +######################################################################################################### +# TTS Service Configuration +######################################################################################################### + +# OpenAI API Configuration (Required for TTS) +OPENAI_API_KEY=your-openai-api-key-here + +# Service Configuration +TTS_SERVICE_NAME="TTS Audio Generation Service" +TTS_SERVICE_VERSION=1.0.0 +TTS_SERVICE_PORT=8003 + +# TTS Settings +TTS_MODEL=tts-1-hd +DEFAULT_HOST_VOICE=alloy +DEFAULT_GUEST_VOICE=nova + +# Audio Settings +OUTPUT_DIR=/tmp/static/audio +AUDIO_FORMAT=mp3 +AUDIO_BITRATE=192k +SILENCE_DURATION_MS=500 + +# Processing +MAX_CONCURRENT_REQUESTS=5 +MAX_SCRIPT_LENGTH=100 diff --git a/sample_solutions/PDFToPodcast/README.md b/sample_solutions/PDFToPodcast/README.md index c72d2a46..40a24199 100644 --- a/sample_solutions/PDFToPodcast/README.md +++ b/sample_solutions/PDFToPodcast/README.md @@ -104,7 +104,16 @@ This application is built using FastAPI microservices architecture with Docker c Before you begin, ensure you have the following installed: - **Docker and Docker Compose** -- **Enterprise inference endpoint access** (token-based authentication) +- **Enterprise Inference endpoint access** (token-based authentication, see below for models and configs) + +#### Deploy Required Models + +See the table below for supported models, hardware, and gateway configuration. + +| Model | Xeon w/APISIX/Keycloak | Xeon w/GenAI Gateway | Gaudi w/APISIX/Keycloak | Gaudi w/GenAI Gateway | +|---|:---:|:---:|:---:|:---:| +| **deepseek-ai/DeepSeek-R1-Distill-Qwen-32B** | ❌ | ❌ | ✅ Validated on Dell XE7740 | ✅ Validated on Dell XE7740 | +| **Qwen/Qwen3-4B-Instruct-2507** | ✅ Validated on Dell XE7740 | ✅ Validated on Dell XE7740 | ❌ | ❌ | ### Verify Docker Installation @@ -119,7 +128,7 @@ docker compose version docker ps ``` -### Required API Keys +### Required API Configuration **For LLM Service (Script Generation):** @@ -136,11 +145,6 @@ This application supports multiple inference deployment patterns: - To generate the APISIX authentication token, use the [generate-token.sh](https://github.com/opea-project/Enterprise-Inference/blob/main/core/scripts/generate-token.sh) script - The token is generated using Keycloak client credentials -Configuration requirements: -- INFERENCE_API_ENDPOINT: URL to your inference service (example: https://api.example.com) -- INFERENCE_API_TOKEN: Authentication token/API key for your chosen service -- INFERENCE_MODEL_NAME: Model name (default: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B) - **For TTS Service (Audio Generation):** OpenAI API Key for text-to-speech: @@ -149,22 +153,6 @@ OpenAI API Key for text-to-speech: - Key format starts with `sk-proj-` - Requires access to TTS APIs (tts-1-hd model) -### Local Development Configuration - -**For Local Testing Only** - -If you're testing with a local inference endpoint using a custom domain (e.g., `api.example.com` mapped to localhost in your hosts file): - -1. Edit `api/llm-service/.env` and set: - ```bash - LOCAL_URL_ENDPOINT=inference.example.com - ``` - (Use the domain name from your INFERENCE_API_ENDPOINT without `https://`) - -2. This allows Docker containers to resolve your local domain correctly. - -**Note:** For public domains or cloud-hosted endpoints, leave the default value `not-needed`. - --- ## Quick Start Deployment @@ -178,43 +166,39 @@ cd Enterprise-Inference/sample_solutions/PDFToPodcast ### Set up the Environment -Each service needs its own `.env` file. Copy the example files and edit with your credentials. - -**1. PDF Service Configuration:** - -```bash -cp api/pdf-service/.env.example api/pdf-service/.env -``` - -No changes needed. Uses default values. - -**2. TTS Service Configuration:** - -```bash -cp api/tts-service/.env.example api/tts-service/.env -``` - -Open `api/tts-service/.env` and replace `your-openai-api-key-here` with your actual OpenAI API key. - -Available TTS voices: alloy, echo, fable, onyx, nova, shimmer. Default voices are alloy (host) and nova (guest). - -**3. LLM Service Configuration:** - -```bash -cp api/llm-service/.env.example api/llm-service/.env -``` - -Open `api/llm-service/.env` and configure your inference endpoint with the values from the "Required API Keys" section above. - -**Note:** If using a local domain (e.g., `api.example.com` mapped to localhost), also edit `LOCAL_URL_ENDPOINT` and replace `not-needed` with your domain name (without `https://`). - -**4. Backend Service Configuration:** +This application requires an `.env` file in the root directory for proper configuration. Create it using [.env.example](./.env.example) with the commands below: ```bash cp .env.example .env ``` - -**Note:** If using a local domain (e.g., `api.example.com` mapped to localhost), edit `.env` and replace `not-needed` with your domain name (without `https://`). +Then modify it as needed, with special consideration to certain environment variables mentioned below. Read through the .env file for full instructions. + +1. **Backend Service Configuration** + - No changes needed. Use default values. + +2. **PDF Service Configuration** + - No changes needed. Use default values. + +3. **LLM Service Configuration** + - **INFERENCE_API_ENDPOINT**: Your actual inference service URL (replace `https://api.example.com`) + - For APISIX/Keycloak deployments, the model name must be included in the endpoint URL (e.g., `https://api.example.com/DeepSeek-R1-Distill-Qwen-32B`) + - **INFERENCE_API_TOKEN**: Your actual pre-generated authentication token + - **INFERENCE_MODEL_NAME**: Use the exact model name from your inference service + - To check available models: `curl https://api.example.com/v1/models -H "Authorization: Bearer your-token"` + - **LOCAL_URL_ENDPOINT**: Only needed if using local domain mapping (i.e. `api.example.com` mapped to localhost) for Docker containers to resolve correctly. + - Use the domain name from INFERENCE_API_ENDPOINT without `https://` + - For public domains or cloud-hosted endpoints, leave the default value `not-needed` + - **VERIFY_SSL**: Controls SSL certificate verification (default: `true`) + - Set to `false` only for development environments with self-signed certificates + - Keep as `true` for production environments + +4. **TTS Service Configuration** + - **OPENAI_API_KEY**: Replace with actual OpenAI API key + - **DEFAULT_HOST_VOICE**, **DEFAULT_GUEST_VOICE**: Voice used for the podcast + - Default voices are alloy (host) and nova (guest) + - Available TTS voices: alloy, echo, fable, onyx, nova, shimmer + +**Note**: The docker-compose.yaml file automatically loads environment variables from `.env` for all services. ### Running the Application @@ -318,12 +302,3 @@ docker compose down For detailed troubleshooting guidance and solutions to common issues, refer to: [TROUBLESHOOTING_and_ADDITIONAL_INFO.md](./TROUBLESHOOTING_and_ADDITIONAL_INFO.md) - -## Additional Info - -The following models have been validated with PDFToPodcast: - -| Model | Hardware | -|-------|----------| -| **deepseek-ai/DeepSeek-R1-Distill-Qwen-32B** | Gaudi | -| **Qwen/Qwen3-4B-Instruct-2507** | Xeon | diff --git a/sample_solutions/PDFToPodcast/api/llm-service/.env.example b/sample_solutions/PDFToPodcast/api/llm-service/.env.example deleted file mode 100644 index 15cb3c33..00000000 --- a/sample_solutions/PDFToPodcast/api/llm-service/.env.example +++ /dev/null @@ -1,40 +0,0 @@ -# Inference API Configuration -# INFERENCE_API_ENDPOINT: URL to your inference service (without /v1 suffix) -# -# **GenAI Gateway**: Provide your GenAI Gateway URL and API key -# - URL format: https://genai-gateway.example.com -# - To generate the GenAI Gateway API key, use the [generate-vault-secrets.sh](https://github.com/opea-project/Enterprise-Inference/blob/main/core/scripts/generate-vault-secrets.sh) script -# - The API key is the litellm_master_key value from the generated vault.yml file -# -# **APISIX Gateway**: Provide your APISIX Gateway URL and authentication token -# - URL format: https://apisix-gateway.example.com/DeepSeek-R1-Distill-Qwen-32B -# - Note: APISIX requires the model name in the URL path -# - To generate the APISIX authentication token, use the [generate-token.sh](https://github.com/opea-project/Enterprise-Inference/blob/main/core/scripts/generate-token.sh) script -# - The token is generated using Keycloak client credentials -# -# INFERENCE_API_TOKEN: Authentication token/API key for the inference service -INFERENCE_API_ENDPOINT=https://api.example.com -INFERENCE_API_TOKEN=your-pre-generated-token-here -INFERENCE_MODEL_NAME=deepseek-ai/DeepSeek-R1-Distill-Qwen-32B - -# Service Configuration -SERVICE_PORT=8002 - -# Model Settings -DEFAULT_TONE=conversational -DEFAULT_MAX_LENGTH=2000 - -# Generation Parameters -TEMPERATURE=0.7 -MAX_TOKENS=4000 -MAX_RETRIES=3 - -# Local URL Endpoint (only needed for non-public domains) -# If using a local domain like api.example.com mapped to localhost: -# Set this to: api.example.com (domain without https://) -# If using a public domain, set any placeholder value like: not-needed -LOCAL_URL_ENDPOINT=not-needed - -# SSL Verification Settings -# Set to false only for dev with self-signed certs -VERIFY_SSL=true diff --git a/sample_solutions/PDFToPodcast/api/llm-service/app/config.py b/sample_solutions/PDFToPodcast/api/llm-service/app/config.py index a6779e21..8420984d 100644 --- a/sample_solutions/PDFToPodcast/api/llm-service/app/config.py +++ b/sample_solutions/PDFToPodcast/api/llm-service/app/config.py @@ -5,9 +5,9 @@ class Settings(BaseSettings): """LLM Service Configuration""" # Service info - SERVICE_NAME: str = "LLM Script Generation Service" - SERVICE_VERSION: str = "1.0.0" - SERVICE_PORT: int = 8002 + LLM_SERVICE_NAME: str = "LLM Script Generation Service" + LLM_SERVICE_VERSION: str = "1.0.0" + LLM_SERVICE_PORT: int = 8002 # Inference API Configuration INFERENCE_API_ENDPOINT: Optional[str] = None diff --git a/sample_solutions/PDFToPodcast/api/llm-service/app/main.py b/sample_solutions/PDFToPodcast/api/llm-service/app/main.py index 37832312..970a7152 100644 --- a/sample_solutions/PDFToPodcast/api/llm-service/app/main.py +++ b/sample_solutions/PDFToPodcast/api/llm-service/app/main.py @@ -20,9 +20,9 @@ # Create FastAPI app app = FastAPI( - title="LLM Script Generation Service", + title=settings.LLM_SERVICE_NAME, description="Generate podcast scripts from text using AI", - version="1.0.0", + version=settings.LLM_SERVICE_VERSION, docs_url="/docs", redoc_url="/redoc" ) @@ -43,9 +43,9 @@ async def startup_event(): """Run on application startup""" logger.info("=" * 60) - logger.info("LLM Script Generation Service starting up...") + logger.info(f"{settings.LLM_SERVICE_NAME} starting up...") logger.info("=" * 60) - logger.info(f"Service running on port {settings.SERVICE_PORT}") + logger.info(f"Service running on port {settings.LLM_SERVICE_PORT}") if settings.INFERENCE_API_ENDPOINT and settings.INFERENCE_API_TOKEN: logger.info("LLM Provider: Inference API") @@ -60,14 +60,14 @@ async def startup_event(): @app.on_event("shutdown") async def shutdown_event(): """Run on application shutdown""" - logger.info("LLM Script Generation Service shutting down...") + logger.info(f"{settings.LLM_SERVICE_NAME} shutting down...") @app.get("/") async def root(): """Root endpoint""" return { - "service": "LLM Script Generation Service", - "version": "1.0.0", + "service": settings.LLM_SERVICE_NAME, + "version": settings.LLM_SERVICE_VERSION, "description": "Convert text content into engaging podcast dialogue", "config": { "llm_provider": "Inference API", @@ -86,4 +86,4 @@ async def root(): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=settings.SERVICE_PORT) + uvicorn.run(app, host="0.0.0.0", port=settings.LLM_SERVICE_PORT) diff --git a/sample_solutions/PDFToPodcast/api/pdf-service/.env.example b/sample_solutions/PDFToPodcast/api/pdf-service/.env.example deleted file mode 100644 index 02282e43..00000000 --- a/sample_solutions/PDFToPodcast/api/pdf-service/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# Service Configuration -SERVICE_NAME=PDF Processing Service -SERVICE_VERSION=1.0.0 -SERVICE_PORT=8001 - -# File Processing -MAX_FILE_SIZE=10485760 -UPLOAD_DIR=uploads - -# OCR Settings -# TESSERACT_CMD=/usr/bin/tesseract -OCR_LANGUAGE=eng -OCR_DPI=300 - -# Text Processing -ENABLE_TEXT_CLEANING=true -ENABLE_OCR_FALLBACK=true diff --git a/sample_solutions/PDFToPodcast/api/pdf-service/app/config.py b/sample_solutions/PDFToPodcast/api/pdf-service/app/config.py index dd15203d..3f348bcb 100644 --- a/sample_solutions/PDFToPodcast/api/pdf-service/app/config.py +++ b/sample_solutions/PDFToPodcast/api/pdf-service/app/config.py @@ -5,16 +5,16 @@ class Settings(BaseSettings): """PDF Service Configuration""" # Service info - SERVICE_NAME: str = "PDF Processing Service" - SERVICE_VERSION: str = "1.0.0" - SERVICE_PORT: int = 8001 + PDF_SERVICE_NAME: str = "PDF Processing Service" + PDF_SERVICE_VERSION: str = "1.0.0" + PDF_SERVICE_PORT: int = 8001 # File processing MAX_FILE_SIZE: int = 10485760 # 10MB UPLOAD_DIR: str = "uploads" # OCR settings - TESSERACT_CMD: Optional[str] = None # Path to tesseract (None = auto-detect) + TESSERACT_CMD: Optional[str] = None OCR_LANGUAGE: str = "eng" OCR_DPI: int = 300 @@ -25,5 +25,6 @@ class Settings(BaseSettings): class Config: env_file = ".env" case_sensitive = True + extra = "ignore" settings = Settings() diff --git a/sample_solutions/PDFToPodcast/api/pdf-service/app/main.py b/sample_solutions/PDFToPodcast/api/pdf-service/app/main.py index ae8fd802..67b061e4 100644 --- a/sample_solutions/PDFToPodcast/api/pdf-service/app/main.py +++ b/sample_solutions/PDFToPodcast/api/pdf-service/app/main.py @@ -4,6 +4,7 @@ import sys from app.api.routes import router +from app.config import settings # Configure logging logging.basicConfig( @@ -18,9 +19,9 @@ # Create FastAPI app app = FastAPI( - title="PDF Processing Service", + title=settings.PDF_SERVICE_NAME, description="Extract text from PDFs with OCR support for scanned documents", - version="1.0.0", + version=settings.PDF_SERVICE_VERSION, docs_url="/docs", redoc_url="/redoc" ) @@ -40,20 +41,20 @@ @app.on_event("startup") async def startup_event(): """Run on application startup""" - logger.info("PDF Processing Service starting up...") - logger.info("Service running on port 8001") + logger.info(f"{settings.PDF_SERVICE_NAME} starting up...") + logger.info(f"Service running on port {settings.PDF_SERVICE_PORT}") @app.on_event("shutdown") async def shutdown_event(): """Run on application shutdown""" - logger.info("PDF Processing Service shutting down...") + logger.info(f"{settings.PDF_SERVICE_NAME} shutting down...") @app.get("/") async def root(): """Root endpoint""" return { - "service": "PDF Processing Service", - "version": "1.0.0", + "service": settings.PDF_SERVICE_NAME, + "version": settings.PDF_SERVICE_VERSION, "description": "Extract text from PDFs with OCR support", "endpoints": { "extract": "POST /extract - Extract text from PDF", @@ -67,4 +68,4 @@ async def root(): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8001) + uvicorn.run(app, host="0.0.0.0", port=settings.PDF_SERVICE_PORT) diff --git a/sample_solutions/PDFToPodcast/api/tts-service/.env.example b/sample_solutions/PDFToPodcast/api/tts-service/.env.example deleted file mode 100644 index c5965720..00000000 --- a/sample_solutions/PDFToPodcast/api/tts-service/.env.example +++ /dev/null @@ -1,22 +0,0 @@ -# OpenAI API Configuration (Required for TTS) -OPENAI_API_KEY=your-openai-api-key-here - -# Service Configuration -SERVICE_NAME=TTS Audio Generation Service -SERVICE_VERSION=1.0.0 -SERVICE_PORT=8003 - -# TTS Settings -TTS_MODEL=tts-1-hd -DEFAULT_HOST_VOICE=alloy -DEFAULT_GUEST_VOICE=nova - -# Audio Settings -OUTPUT_DIR=static/audio -AUDIO_FORMAT=mp3 -AUDIO_BITRATE=192k -SILENCE_DURATION_MS=500 - -# Processing -MAX_CONCURRENT_REQUESTS=5 -MAX_SCRIPT_LENGTH=100 diff --git a/sample_solutions/PDFToPodcast/api/tts-service/app/config.py b/sample_solutions/PDFToPodcast/api/tts-service/app/config.py index 26b7488e..f8d7a903 100644 --- a/sample_solutions/PDFToPodcast/api/tts-service/app/config.py +++ b/sample_solutions/PDFToPodcast/api/tts-service/app/config.py @@ -5,15 +5,15 @@ class Settings(BaseSettings): """TTS Service Configuration""" # Service info - SERVICE_NAME: str = "TTS Audio Generation Service" - SERVICE_VERSION: str = "1.0.0" - SERVICE_PORT: int = 8003 + TTS_SERVICE_NAME: str = "TTS Audio Generation Service" + TTS_SERVICE_VERSION: str = "1.0.0" + TTS_SERVICE_PORT: int = 8003 # API Keys OPENAI_API_KEY: Optional[str] = None # TTS settings - TTS_MODEL: str = "tts-1-hd" # or tts-1 for faster/cheaper + TTS_MODEL: str = "tts-1-hd" DEFAULT_HOST_VOICE: str = "alloy" DEFAULT_GUEST_VOICE: str = "nova" @@ -25,10 +25,11 @@ class Settings(BaseSettings): # Processing MAX_CONCURRENT_REQUESTS: int = 5 - MAX_SCRIPT_LENGTH: int = 100 # Max dialogue turns + MAX_SCRIPT_LENGTH: int = 100 class Config: env_file = ".env" case_sensitive = True + extra = "ignore" settings = Settings() diff --git a/sample_solutions/PDFToPodcast/api/tts-service/app/main.py b/sample_solutions/PDFToPodcast/api/tts-service/app/main.py index b7b5a974..2f69a295 100644 --- a/sample_solutions/PDFToPodcast/api/tts-service/app/main.py +++ b/sample_solutions/PDFToPodcast/api/tts-service/app/main.py @@ -22,9 +22,9 @@ # Create FastAPI app app = FastAPI( - title="TTS Audio Generation Service", + title=settings.TTS_SERVICE_NAME, description="Generate podcast audio from scripts using OpenAI TTS", - version="1.0.0", + version=settings.TTS_SERVICE_VERSION, docs_url="/docs", redoc_url="/redoc" ) @@ -49,8 +49,8 @@ @app.on_event("startup") async def startup_event(): """Run on application startup""" - logger.info("TTS Audio Generation Service starting up...") - logger.info(f"Service running on port {settings.SERVICE_PORT}") + logger.info(f"{settings.TTS_SERVICE_NAME} starting up...") + logger.info(f"Service running on port {settings.TTS_SERVICE_PORT}") # Check API key if settings.OPENAI_API_KEY: @@ -65,14 +65,14 @@ async def startup_event(): @app.on_event("shutdown") async def shutdown_event(): """Run on application shutdown""" - logger.info("TTS Audio Generation Service shutting down...") + logger.info(f"{settings.TTS_SERVICE_NAME} shutting down...") @app.get("/") async def root(): """Root endpoint""" return { - "service": "TTS Audio Generation Service", - "version": "1.0.0", + "service": settings.TTS_SERVICE_NAME, + "version": settings.TTS_SERVICE_VERSION, "description": "Generate podcast audio from scripts using OpenAI TTS", "endpoints": { "generate_audio": "POST /generate-audio - Generate podcast audio", @@ -87,4 +87,4 @@ async def root(): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=settings.SERVICE_PORT) + uvicorn.run(app, host="0.0.0.0", port=settings.TTS_SERVICE_PORT) diff --git a/sample_solutions/PDFToPodcast/docker-compose.yml b/sample_solutions/PDFToPodcast/docker-compose.yml index 4969ad6f..f0c1b340 100644 --- a/sample_solutions/PDFToPodcast/docker-compose.yml +++ b/sample_solutions/PDFToPodcast/docker-compose.yml @@ -6,6 +6,8 @@ services: container_name: pdf-service ports: - "8001:8001" + env_file: + - ./.env volumes: - ./api/pdf-service:/app networks: @@ -20,7 +22,7 @@ services: ports: - "8002:8002" env_file: - - ./api/llm-service/.env + - ./.env volumes: - ./api/llm-service:/app networks: @@ -37,7 +39,7 @@ services: ports: - "8003:8003" env_file: - - ./api/tts-service/.env + - ./.env volumes: - ./api/tts-service:/app - ./api/tts-service/static:/app/static