Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dataflow_agent/toolkits/multimodaltool/req_img.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ async def _post_stream_and_accumulate(

log.info(f"POST STREAM {url}")

async with httpx.AsyncClient(timeout=httpx.Timeout(timeout), http2=False) as client:
async with httpx.AsyncClient(
timeout=httpx.Timeout(timeout, connect=30.0, read=timeout),
http2=False,
) as client:
try:
full_content = []
async with client.stream("POST", url, headers=headers, json=payload) as response:
Expand Down
2 changes: 2 additions & 0 deletions fastapi_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def _configure_runtime_tempdir() -> None:
from fastapi_app.routers import image_playground
from fastapi_app.routers import mindmap
from fastapi_app.routers import paper2drawio
from fastapi_app.routers import paper2ppt_code
from fastapi_app.routers import paper2rebuttal
from fastapi_app.middleware.api_key import APIKeyMiddleware
from dataflow_agent.utils import get_project_root
Expand Down Expand Up @@ -91,6 +92,7 @@ def create_app() -> FastAPI:
app.include_router(account.router, prefix="/api/v1", tags=["account"])
# Paper2PPT
app.include_router(paper2ppt.router, prefix="/api/v1", tags=["paper2ppt"])
app.include_router(paper2ppt_code.router, prefix="/api/v1", tags=["paper2ppt"])
# Paper2Citation
app.include_router(paper2citation.router, prefix="/api/v1", tags=["paper2citation"])
# paper2video
Expand Down
12 changes: 12 additions & 0 deletions fastapi_app/middleware/api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ class RateLimitRule:
"/api/v1/paper2ppt/generate-task": RateLimitRule(limit=12, window_seconds=300, bucket="paper2ppt-generate-task"),
"/api/v1/paper2ppt/outline-refine": RateLimitRule(limit=20, window_seconds=300, bucket="paper2ppt-outline-refine"),
"/api/v1/paper2ppt/frontend/generate": RateLimitRule(limit=16, window_seconds=300, bucket="paper2ppt-frontend-generate"),
"/api/v1/paper2ppt/code/generate": RateLimitRule(limit=12, window_seconds=300, bucket="paper2ppt-code-generate"),
"/api/v1/paper2ppt/code/generate-task": RateLimitRule(limit=12, window_seconds=300, bucket="paper2ppt-code-generate-task"),
"/api/v1/paper2ppt/code/assemble-task": RateLimitRule(limit=12, window_seconds=300, bucket="paper2ppt-code-assemble-task"),
"/api/v1/paper2ppt/code/patch-slide-task": RateLimitRule(limit=20, window_seconds=300, bucket="paper2ppt-code-patch-slide"),
"/api/v1/paper2ppt/frontend/review": RateLimitRule(limit=24, window_seconds=300, bucket="paper2ppt-frontend-review"),
"/api/v1/kb/chat": RateLimitRule(limit=30, window_seconds=300, bucket="kb-chat"),
"/api/v1/kb/search": RateLimitRule(limit=40, window_seconds=300, bucket="kb-search"),
Expand Down Expand Up @@ -129,6 +133,10 @@ class RateLimitRule:
"/api/v1/paper2ppt/generate": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/generate-task": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/frontend/generate": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/code/generate": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/code/generate-task": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/code/assemble-task": WorkflowGuard("paper2ppt"),
"/api/v1/paper2ppt/code/patch-slide-task": WorkflowGuard("paper2ppt"),
"/api/v1/kb/chat": WorkflowGuard("kb_chat"),
"/api/v1/kb/search": WorkflowGuard("kb_search"),
"/api/v1/kb/generate-ppt": WorkflowGuard("kb_ppt"),
Expand Down Expand Up @@ -281,6 +289,10 @@ async def _resolve_workflow_charge_decision(
"/api/v1/paper2ppt/generate",
"/api/v1/paper2ppt/generate-task",
"/api/v1/paper2ppt/frontend/generate",
"/api/v1/paper2ppt/code/generate",
"/api/v1/paper2ppt/code/generate-task",
"/api/v1/paper2ppt/code/assemble-task",
"/api/v1/paper2ppt/code/patch-slide-task",
}:
return None

Expand Down
157 changes: 157 additions & 0 deletions fastapi_app/routers/paper2ppt_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from __future__ import annotations

from typing import Any, Dict, Optional

from fastapi import APIRouter, Depends, Form, Request

from fastapi_app.config import settings
from fastapi_app.schemas import AssembleEditablePPTRequest, EditablePPTGenerationRequest, ErrorResponse, PatchSlideRequest
from fastapi_app.services.editable_ppt_service import EditablePPTService
from fastapi_app.services.managed_api_service import resolve_model_name
from fastapi_app.services.paper2ppt_code_task_service import Paper2PPTCodeTaskService

router = APIRouter(tags=["paper2ppt"])


def get_editable_ppt_service() -> EditablePPTService:
return EditablePPTService()


def get_code_task_service() -> Paper2PPTCodeTaskService:
return Paper2PPTCodeTaskService()


@router.post(
"/paper2ppt/code/generate",
response_model=Dict[str, Any],
responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},
)
async def paper2ppt_code_generate(
request: Request,
result_path: str = Form(...),
pagecontent: str = Form(...),
chat_api_url: Optional[str] = Form(None),
api_key: Optional[str] = Form(None),
credential_scope: Optional[str] = Form(None),
email: Optional[str] = Form(None),
model: str = Form(settings.PAPER2PPT_CONTENT_MODEL),
language: str = Form("zh"),
style: str = Form(""),
include_pdf_preview: bool = Form(True),
service: EditablePPTService = Depends(get_editable_ppt_service),
):
req = EditablePPTGenerationRequest(
result_path=result_path,
pagecontent=pagecontent,
chat_api_url=chat_api_url,
api_key=api_key,
credential_scope=credential_scope,
email=email,
model=resolve_model_name(
model,
managed_default=settings.PAPER2PPT_CONTENT_MODEL,
fallback_default=settings.PAPER2PPT_DEFAULT_MODEL,
),
language=language,
style=style,
include_pdf_preview=include_pdf_preview,
)
return await service.generate_editable_ppt(req=req, request=request)


@router.post(
"/paper2ppt/code/generate-task",
response_model=Dict[str, Any],
responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},
)
async def paper2ppt_code_generate_task(
request: Request,
result_path: str = Form(...),
pagecontent: str = Form(...),
chat_api_url: Optional[str] = Form(None),
api_key: Optional[str] = Form(None),
credential_scope: Optional[str] = Form(None),
email: Optional[str] = Form(None),
model: str = Form(settings.PAPER2PPT_CONTENT_MODEL),
language: str = Form("zh"),
style: str = Form(""),
include_pdf_preview: bool = Form(False),
task_service: Paper2PPTCodeTaskService = Depends(get_code_task_service),
):
req = EditablePPTGenerationRequest(
result_path=result_path, pagecontent=pagecontent,
chat_api_url=chat_api_url, api_key=api_key,
credential_scope=credential_scope, email=email,
model=resolve_model_name(
model, managed_default=settings.PAPER2PPT_CONTENT_MODEL,
fallback_default=settings.PAPER2PPT_DEFAULT_MODEL,
),
language=language, style=style, include_pdf_preview=include_pdf_preview,
)
return await task_service.submit_generate_slides_task(req=req, request=request)


@router.post(
"/paper2ppt/code/assemble-task",
response_model=Dict[str, Any],
responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},
)
async def paper2ppt_code_assemble_task(
request: Request,
result_path: str = Form(...),
include_pdf_preview: bool = Form(True),
task_service: Paper2PPTCodeTaskService = Depends(get_code_task_service),
):
req = AssembleEditablePPTRequest(
result_path=result_path, include_pdf_preview=include_pdf_preview,
)
return await task_service.submit_assemble_task(req=req, request=request)


@router.get(
"/paper2ppt/code/tasks/{task_id}",
response_model=Dict[str, Any],
responses={404: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},
)
async def paper2ppt_code_get_task(
task_id: str,
request: Request,
task_service: Paper2PPTCodeTaskService = Depends(get_code_task_service),
):
return task_service.get_task(task_id=task_id, request=request)


@router.post(
"/paper2ppt/code/patch-slide-task",
response_model=Dict[str, Any],
responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},
)
async def paper2ppt_code_patch_slide_task(
request: Request,
result_path: str = Form(...),
slide_index: int = Form(...),
feedback: str = Form(...),
feedback_type: str = Form("auto"),
chat_api_url: Optional[str] = Form(None),
api_key: Optional[str] = Form(None),
credential_scope: Optional[str] = Form(None),
model: str = Form(settings.PAPER2PPT_CONTENT_MODEL),
image_model: str = Form(settings.PAPER2PPT_IMAGE_GEN_MODEL),
task_service: Paper2PPTCodeTaskService = Depends(get_code_task_service),
):
req = PatchSlideRequest(
result_path=result_path,
slide_index=slide_index,
feedback=feedback,
feedback_type=feedback_type,
chat_api_url=chat_api_url,
api_key=api_key,
credential_scope=credential_scope,
model=resolve_model_name(
model,
managed_default=settings.PAPER2PPT_CONTENT_MODEL,
fallback_default=settings.PAPER2PPT_DEFAULT_MODEL,
),
image_model=image_model,
)
return await task_service.submit_patch_slide_task(req=req, request=request)
55 changes: 55 additions & 0 deletions fastapi_app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,61 @@ class FrontendPPTReviewRequest(BaseModel):
layout_issues: Optional[str] = None


class EditablePPTGenerationRequest(BaseModel):
"""Generate a real editable PPTX through the backend code runtime."""
chat_api_url: Optional[str] = None
api_key: Optional[str] = None
credential_scope: Optional[str] = None
email: Optional[str] = None
model: str = settings.PAPER2PPT_CONTENT_MODEL
vlm_model: str = settings.PAPER2PPT_VLM_MODEL
image_model: str = settings.PAPER2PPT_IMAGE_GEN_MODEL
language: str = "zh"
style: str = ""
result_path: str
pagecontent: str
include_pdf_preview: bool = True


class AssembleEditablePPTRequest(BaseModel):
"""Assemble a full editable PPTX from an existing final_ir.json."""
result_path: str
include_pdf_preview: bool = True


class PatchSlideRequest(BaseModel):
"""Patch a single slide based on user feedback."""
result_path: str
slide_index: int
feedback: str
feedback_type: str = "auto"
chat_api_url: Optional[str] = None
api_key: Optional[str] = None
credential_scope: Optional[str] = None
model: str = settings.PAPER2PPT_CONTENT_MODEL
image_api_url: Optional[str] = None
image_api_key: Optional[str] = None
image_model: str = settings.PAPER2PPT_IMAGE_GEN_MODEL


class EditablePPTGenerationResponse(BaseModel):
"""Backend response for the paper2ppt/code runtime."""
success: bool = True
result_path: str = ""
ppt_pptx_path: str = ""
ppt_pdf_path: str = ""
planned_ir_path: str = ""
final_ir_path: str = ""
materials_manifest_path: str = ""
material_resolution_path: str = ""
ir_path: str = ""
render_log_path: str = ""
slides_dir: str = ""
slide_artifacts: List[Dict[str, Any]] = []
all_output_files: List[str] = []
error: str = ""


# ===================== KB Deep Research 相关 =====================

class DeepResearchRequest(BaseModel):
Expand Down
Loading