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()