File size: 7,084 Bytes
b5df735 7035546 b5df735 7035546 b5df735 7035546 b5df735 7035546 b5df735 1bd8ab8 7035546 b5df735 1bd8ab8 b5df735 7035546 b5df735 7035546 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# FastAPI + Gradio + FastMCP MCP server main entry point
import modal
from contextlib import asynccontextmanager
from fastapi import FastAPI
from gradio.routes import mount_gradio_app
import os
from dotenv import load_dotenv
import uvicorn
from mcp.server.fastmcp import FastMCP
# Import modules
from .tools import mcp_tools # Import the module, not get_mcp_server function
from .ui.gradio_ui import create_gradio_interface
from .config.config import is_modal_mode, is_local_mode
# Always import modal config since this module might be imported in modal context
try:
from .config.modal_config import app, image, volume, cache_dir, secrets
_modal_available = True
except ImportError:
_modal_available = False
# ==================== Application Creation Function ====================
def create_app():
"""Create and return complete Gradio + MCP application"""
print("π Starting Gradio + FastMCP server")
# Check if this is HF Spaces environment
is_hf_spaces = "SPACE_ID" in os.environ
if is_hf_spaces:
# For HF Spaces: return pure Gradio app (simpler approach)
print("π€ Creating HF Spaces compatible app")
ui_app = create_gradio_interface()
print("β
Server startup completed")
print("π¨ Gradio UI: /")
return ui_app
else:
# For other environments: full FastAPI + MCP + Gradio setup
# Create FastMCP server with new tools
mcp = FastMCP("Podcast MCP")
# Register tools using the new service architecture
@mcp.tool(description="Transcribe audio files to text using Whisper model with speaker diarization support")
async def transcribe_audio_file_tool(
audio_file_path: str,
model_size: str = "turbo",
language: str = None,
output_format: str = "srt",
enable_speaker_diarization: bool = False
):
return await mcp_tools.transcribe_audio_file(
audio_file_path, model_size, language, output_format, enable_speaker_diarization
)
@mcp.tool(description="Download Apple Podcast audio files")
async def download_apple_podcast_tool(url: str):
return await mcp_tools.download_apple_podcast(url)
@mcp.tool(description="Download XiaoYuZhou podcast audio files")
async def download_xyz_podcast_tool(url: str):
return await mcp_tools.download_xyz_podcast(url)
@mcp.tool(description="Scan directory for MP3 audio files")
async def get_mp3_files_tool(directory: str):
return await mcp_tools.get_mp3_files(directory)
@mcp.tool(description="Get basic file information")
async def get_file_info_tool(file_path: str):
return await mcp_tools.get_file_info(file_path)
@mcp.tool(description="Read text file content in segments")
async def read_text_file_segments_tool(
file_path: str,
chunk_size: int = 65536,
start_position: int = 0
):
return await mcp_tools.read_text_file_segments(file_path, chunk_size, start_position)
# Create FastAPI wrapper
fastapi_wrapper = FastAPI(
title="Modal AudioTranscriber MCP",
description="Gradio UI + FastMCP Tool + Modal Integration AudioTranscriber MCP",
version="1.0.0",
lifespan=lambda app: mcp.session_manager.run()
)
# Get FastMCP's streamable HTTP app
mcp_app = mcp.streamable_http_app()
# Mount FastMCP application to /api path
fastapi_wrapper.mount("/api", mcp_app)
# Create Gradio interface
ui_app = create_gradio_interface()
# Use Gradio's standard mounting approach
final_app = mount_gradio_app(
app=fastapi_wrapper,
blocks=ui_app,
path="/",
app_kwargs={
"docs_url": "/docs",
"redoc_url": "/redoc",
}
)
print("β
Server startup completed")
print("π¨ Gradio UI: /")
print("π§ MCP Streamable HTTP: /api/mcp")
print(f"π Server name: {mcp.name}")
return final_app
# ==================== Modal Deployment Configuration ====================
# Create a separate Modal app for the Gradio interface
if _modal_available:
gradio_mcp_app = modal.App(name="gradio-mcp-ui")
@gradio_mcp_app.function(
image=image,
cpu=2, # Adequate CPU for UI operations
memory=4096, # 4GB memory for stable UI performance
max_containers=5, # Reduced to control resource usage
min_containers=1, # Keep minimum containers for faster response
scaledown_window=600, # 20 minutes before scaling down
timeout=1800, # 30 minutes timeout to prevent preemption
volumes={cache_dir: volume},
secrets=secrets,
)
@modal.concurrent(max_inputs=100)
@modal.asgi_app()
def app_entry():
"""Modal deployment function - create and return complete Gradio + MCP application"""
return create_app()
# ==================== Main Entry Point ====================
def main():
"""Main entry point for all deployment modes"""
if is_modal_mode():
print("βοΈ Modal mode: Use 'modal deploy src.app::gradio_mcp_app'")
return None
else:
print("π Starting in local mode")
print("π‘ GPU functions will be routed to Modal endpoints")
app = create_app()
return app
def run_local():
"""Run local server with uvicorn (for direct execution)"""
# Double-check: don't run uvicorn in HF Spaces
if os.environ.get("HF_SPACES_MODE"):
print("β οΈ Skipping uvicorn.run() in HF Spaces mode")
return
app = main()
if app:
# Use port 7860 for HF Spaces compatibility, 8000 for local
port = int(os.environ.get("PORT", 8000)) # Use 8000 for local dev
uvicorn.run(
app,
host="0.0.0.0",
port=port,
reload=False
)
# ==================== Hugging Face Spaces Support ====================
# For Hugging Face Spaces, directly create the app
def get_app():
"""Get app instance for HF Spaces"""
if "DEPLOYMENT_MODE" not in os.environ:
os.environ["DEPLOYMENT_MODE"] = "local"
return main()
# HF Spaces compatibility: only create app when not in main execution
if __name__ != "__main__":
# Check if we're in HF Spaces mode and app is already created
if not os.environ.get("HF_SPACES_MODE"):
# This will be called when imported by other environments (not HF Spaces)
app = get_app()
if __name__ == "__main__":
# Check if we should run uvicorn (not in HF Spaces)
if not os.environ.get("HF_SPACES_MODE"):
run_local()
else:
print("β οΈ Skipping uvicorn in HF Spaces mode") |