diff --git a/fastapi_server/front_apis.py b/fastapi_server/front_apis.py new file mode 100644 index 0000000..4cdb352 --- /dev/null +++ b/fastapi_server/front_apis.py @@ -0,0 +1,167 @@ +from typing import Dict, List, Optional +import os +import os.path as osp +import sys +import uuid + +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel, Field + +# Ensure we can import from project root. +sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) + +from lang_agent.config.db_config_manager import DBConfigManager +from lang_agent.front_api.build_server import GRAPH_BUILD_FNCS + +class GraphConfigUpsertRequest(BaseModel): + pipeline_id: str + prompt_set_id: Optional[str] = Field(default=None) + tool_keys: List[str] = Field(default_factory=list) + prompt_dict: Dict[str, str] = Field(default_factory=dict) + +class GraphConfigUpsertResponse(BaseModel): + pipeline_id: str + prompt_set_id: str + tool_keys: List[str] + prompt_keys: List[str] + +class PipelineCreateRequest(BaseModel): + graph_id: str = Field( + description="Graph key from GRAPH_BUILD_FNCS, e.g. routing or react" + ) + pipeline_id: str + prompt_set_id: str + tool_keys: List[str] = Field(default_factory=list) + port: int + entry_point: str = Field(default="fastapi_server/server_dashscope.py") + llm_name: str = Field(default="qwen-plus") + +class PipelineCreateResponse(BaseModel): + run_id: str + pid: int + graph_id: str + pipeline_id: str + prompt_set_id: str + url: str + port: int + + +app = FastAPI( + title="Front APIs", + description="Manage graph configs and launch graph pipelines.", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +_db = DBConfigManager() +_running_pipelines: Dict[str, Dict[str, object]] = {} + + +@app.get("/health") +async def health(): + return {"status": "healthy"} + + +@app.get("/") +async def root(): + return { + "message": "Front APIs", + "endpoints": [ + "/v1/graph-configs (POST)", + "/v1/graph-configs/{pipeline_id}/{prompt_set_id} (DELETE)", + "/v1/pipelines/graphs (GET)", + "/v1/pipelines (POST)", + ], + } + + +@app.post("/v1/graph-configs", response_model=GraphConfigUpsertResponse) +async def upsert_graph_config(body: GraphConfigUpsertRequest): + try: + resolved_prompt_set_id = _db.set_config( + pipeline_id=body.pipeline_id, + prompt_set_id=body.prompt_set_id, + tool_list=body.tool_keys, + prompt_dict=body.prompt_dict, + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + return GraphConfigUpsertResponse( + pipeline_id=body.pipeline_id, + prompt_set_id=resolved_prompt_set_id, + tool_keys=body.tool_keys, + prompt_keys=list(body.prompt_dict.keys()), + ) + + +@app.delete("/v1/graph-configs/{pipeline_id}/{prompt_set_id}") +async def delete_graph_config(pipeline_id: str, prompt_set_id: str): + try: + _db.remove_config(pipeline_id=pipeline_id, prompt_set_id=prompt_set_id) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + return { + "status": "deleted", + "pipeline_id": pipeline_id, + "prompt_set_id": prompt_set_id, + } + + +@app.get("/v1/pipelines/graphs") +async def available_graphs(): + return {"available_graphs": sorted(GRAPH_BUILD_FNCS.keys())} + + +@app.post("/v1/pipelines", response_model=PipelineCreateResponse) +async def create_pipeline(body: PipelineCreateRequest): + build_fn = GRAPH_BUILD_FNCS.get(body.graph_id) + if build_fn is None: + raise HTTPException( + status_code=400, + detail=f"Unknown graph_id '{body.graph_id}'. Valid options: {sorted(GRAPH_BUILD_FNCS.keys())}", + ) + + try: + proc, url = build_fn( + pipelin_id=body.pipeline_id, + prompt_set=body.prompt_set_id, + tool_keys=body.tool_keys, + port=str(body.port), + entry_pnt=body.entry_point, + llm_name=body.llm_name, + ) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to start pipeline: {e}") + + run_id = str(uuid.uuid4()) + _running_pipelines[run_id] = { + "proc": proc, + "graph_id": body.graph_id, + "pipeline_id": body.pipeline_id, + "prompt_set_id": body.prompt_set_id, + "url": url, + "port": body.port, + } + + return PipelineCreateResponse( + run_id=run_id, + pid=proc.pid, + graph_id=body.graph_id, + pipeline_id=body.pipeline_id, + prompt_set_id=body.prompt_set_id, + url=url, + port=body.port, + )