File size: 5,579 Bytes
b5df735 |
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 |
# 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")
# 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)"""
app = main()
if app:
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
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()
# Create app for HF Spaces when imported
if __name__ != "__main__":
app = get_app()
if __name__ == "__main__":
run_local() |