diff --git a/workflow_service/.env b/.env similarity index 58% rename from workflow_service/.env rename to .env index 4c200a2..9f82efb 100644 --- a/workflow_service/.env +++ b/.env @@ -1,10 +1,10 @@ # ComfyUI服务器地址 -COMFYUI_URL="ws://127.0.0.1:8188/ws" -COMFYUI_HTTP_URL="http://127.0.0.1:8188" +COMFYUI_URL="ws://bowongai--local-ui-4.modal.run/ws" +COMFYUI_HTTP_URL="https://bowongai--local-ui-4.modal.run" # 绝对路径!例如: /home/user/ComfyUI/input -COMFYUI_INPUT_DIR="F:\ComfyUI\input" +COMFYUI_INPUT_DIR="/db/input" # 绝对路径!例如: /home/user/ComfyUI/output -COMFYUI_OUTPUT_DIR="F:\ComfyUI\output" +COMFYUI_OUTPUT_DIR="/db/output" # AWS S3 配置 S3_BUCKET_NAME="modal-media-cache" diff --git a/modal_deploy.py b/modal_deploy.py new file mode 100644 index 0000000..5e1bc9d --- /dev/null +++ b/modal_deploy.py @@ -0,0 +1,29 @@ +import modal +from dotenv import dotenv_values +from workflow_service.main import web_app + +fastapi_image = ( + modal.Image + .debian_slim(python_version="3.11") + .pip_install_from_requirements("./workflow_service/requirements.txt") + .env(dotenv_values(".env")) + .add_local_python_source("workflow_service") +) + +app = modal.App(image=fastapi_image, name="waas-demo") +vol = modal.Volume.from_name("comfy_model", environment_name="dev", create_if_missing=True) + +with fastapi_image.imports(): + + @app.function( + cpu=(0.125, 8), + memory=(128, 4096), + scaledown_window=1200, + volumes={ + "/db": vol + } + ) + @modal.concurrent(max_inputs=100) + @modal.asgi_app() + def fastapi_webapp(): + return web_app \ No newline at end of file diff --git a/workflow_service/comfyui_client.py b/workflow_service/comfyui_client.py index 956a166..bd28a56 100644 --- a/workflow_service/comfyui_client.py +++ b/workflow_service/comfyui_client.py @@ -3,7 +3,8 @@ import json import uuid import aiohttp import random -from config import settings +from workflow_service.config import Settings +settings = Settings() async def queue_prompt(prompt: dict, client_id: str) -> str: diff --git a/workflow_service/config.py b/workflow_service/config.py index cced6f6..f632b55 100644 --- a/workflow_service/config.py +++ b/workflow_service/config.py @@ -1,7 +1,8 @@ +from dotenv import dotenv_values from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): - model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8') + model_config = SettingsConfigDict(env_file='./.env', env_file_encoding='utf-8') COMFYUI_URL: str = "ws://127.0.0.1:8188/ws" COMFYUI_HTTP_URL: str = "http://127.0.0.1:8188" COMFYUI_INPUT_DIR: str @@ -11,4 +12,4 @@ class Settings(BaseSettings): AWS_SECRET_ACCESS_KEY: str AWS_REGION_NAME: str -settings = Settings() \ No newline at end of file +# settings = Settings() \ No newline at end of file diff --git a/workflow_service/database.py b/workflow_service/database.py index fb73cde..8641c51 100644 --- a/workflow_service/database.py +++ b/workflow_service/database.py @@ -2,7 +2,7 @@ import aiosqlite import json import re -DATABASE_FILE = "workflows_service.sqlite" +DATABASE_FILE = "/db/workflows_service.sqlite" async def init_db(): async with aiosqlite.connect(DATABASE_FILE) as db: diff --git a/workflow_service/main.py b/workflow_service/main.py index 4f91165..81d8db4 100644 --- a/workflow_service/main.py +++ b/workflow_service/main.py @@ -1,14 +1,24 @@ +import aiohttp +from workflow_service import comfyui_client +from workflow_service import database +import json +import os +from workflow_service import s3_client +import uuid +from workflow_service import workflow_parser +from typing import Optional, List, Dict, Any, Set + import uvicorn from fastapi import FastAPI, Request, HTTPException, Path from fastapi.responses import JSONResponse -from typing import Optional, List, Dict, Any, Set -import json, uuid, os, shutil, aiohttp, database, workflow_parser, comfyui_client, s3_client -from config import settings -app = FastAPI(title="ComfyUI Workflow Service & Management API") +from workflow_service.config import Settings +settings = Settings() + +web_app = FastAPI(title="ComfyUI Workflow Service & Management API") -@app.on_event("startup") +@web_app.on_event("startup") async def startup_event(): await database.init_db(); os.makedirs(settings.COMFYUI_INPUT_DIR, exist_ok=True); os.makedirs( settings.COMFYUI_OUTPUT_DIR, exist_ok=True) @@ -19,17 +29,20 @@ async def startup_event(): await database.init_db(); os.makedirs(settings.COMFYU BASE_MANAGEMENT_PATH = "/api/workflow" -@app.post(BASE_MANAGEMENT_PATH, status_code=200) +@web_app.post(BASE_MANAGEMENT_PATH, status_code=200) async def publish_workflow_endpoint(request: Request): try: - data = await request.json(); name, wf_json = data.get("name"), data.get( - "workflow"); await database.save_workflow(name, json.dumps(wf_json)); return JSONResponse( + data = await request.json(); + name, wf_json = data.get("name"), data.get( + "workflow"); + await database.save_workflow(name, json.dumps(wf_json)); + return JSONResponse( content={"status": "success", "message": f"Workflow '{name}' published."}, status_code=200) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to save workflow: {e}") -@app.get(BASE_MANAGEMENT_PATH, response_model=List[dict]) +@web_app.get(BASE_MANAGEMENT_PATH, response_model=List[dict]) async def get_all_workflows_endpoint(): try: return await database.get_all_workflows() @@ -37,7 +50,7 @@ async def get_all_workflows_endpoint(): raise HTTPException(status_code=500, detail=f"Failed to get workflows: {e}") -@app.delete(f"{BASE_MANAGEMENT_PATH}/{{workflow_name:path}}") +@web_app.delete(f"{BASE_MANAGEMENT_PATH}/{{workflow_name:path}}") async def delete_workflow_endpoint(workflow_name: str = Path(..., title="...")): try: success = await database.delete_workflow(workflow_name); @@ -75,7 +88,7 @@ async def handle_file_upload(file_path: str, base_name: str) -> str: return await s3_client.upload_file_to_s3(file_path, settings.S3_BUCKET_NAME, s3_object_name) -@app.post("/api/run/{base_name}") +@web_app.post("/api/run/{base_name}") async def execute_workflow_endpoint(base_name: str, request_data_raw: Dict[str, Any], version: Optional[str] = None): cleanup_paths = [] try: @@ -102,7 +115,8 @@ async def execute_workflow_endpoint(base_name: str, request_data_raw: Dict[str, save_path = os.path.join(settings.COMFYUI_INPUT_DIR, filename) try: await download_file_from_url(session, image_url, save_path); - request_data[param_name] = filename; cleanup_paths.append(save_path) + request_data[param_name] = filename; + cleanup_paths.append(save_path) except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to download file for '{param_name}' from {image_url}. Error: {e}") @@ -187,7 +201,7 @@ async def execute_workflow_endpoint(base_name: str, request_data_raw: Dict[str, # --- Section 3: 工作流元数据/规范API (无改动) --- # ... (此部分代码与上一版完全相同) ... -@app.get("/api/spec/{base_name}") +@web_app.get("/api/spec/{base_name}") async def get_workflow_spec_endpoint(base_name: str, version: Optional[str] = None): # ... if version: @@ -204,9 +218,9 @@ async def get_workflow_spec_endpoint(base_name: str, version: Optional[str] = No raise HTTPException(status_code=500, detail=f"Failed to parse workflow specification: {e}") -@app.get("/") +@web_app.get("/") def read_root(): return {"message": "Welcome to the ComfyUI Workflow Service API!"} if __name__ == "__main__": - uvicorn.run(app, host="127.0.0.1", port=18000) + uvicorn.run(web_app, host="127.0.0.1", port=18000) diff --git a/workflow_service/s3_client.py b/workflow_service/s3_client.py index b39fbc5..bdeebc0 100644 --- a/workflow_service/s3_client.py +++ b/workflow_service/s3_client.py @@ -1,6 +1,7 @@ import boto3 -from config import settings +from workflow_service.config import Settings import asyncio +settings = Settings() s3_client = boto3.client('s3', aws_access_key_id=settings.AWS_ACCESS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, region_name=settings.AWS_REGION_NAME)