434 lines
14 KiB
Python
434 lines
14 KiB
Python
import nest_asyncio
|
||
nest_asyncio.apply()
|
||
import os, sys, functools
|
||
print = functools.partial(print, flush=True) # 全局 flush
|
||
sys.stdout.reconfigure(line_buffering=True) # 3.7+ 有效
|
||
import asyncio
|
||
|
||
from flask import Flask, request, jsonify
|
||
import json
|
||
from DataProcess import Add_Collaboration_Brief_FrontEnd
|
||
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
|
||
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
|
||
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
|
||
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
|
||
fill_stepTask_TaskProcess,
|
||
)
|
||
from AgentCoord.PlanEngine.branch_PlanOutline import branch_PlanOutline
|
||
from AgentCoord.PlanEngine.branch_TaskProcess import branch_TaskProcess
|
||
from AgentCoord.PlanEngine.AgentSelectModify import (
|
||
AgentSelectModify_init,
|
||
AgentSelectModify_addAspect,
|
||
)
|
||
import os
|
||
import yaml
|
||
import argparse
|
||
|
||
# initialize global variables
|
||
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
|
||
yaml_data = {}
|
||
try:
|
||
with open(yaml_file, "r", encoding="utf-8") as file:
|
||
yaml_data = yaml.safe_load(file)
|
||
except Exception:
|
||
yaml_data = {}
|
||
USE_CACHE: bool = os.getenv("USE_CACHE")
|
||
if USE_CACHE is None:
|
||
USE_CACHE = yaml_data.get("USE_CACHE", False)
|
||
else:
|
||
USE_CACHE = USE_CACHE.lower() in ["true", "1", "yes"]
|
||
AgentBoard = None
|
||
AgentProfile_Dict = {}
|
||
Request_Cache: dict[str, str] = {}
|
||
app = Flask(__name__)
|
||
|
||
|
||
from jsonschema import validate, ValidationError
|
||
|
||
AGENT_SELECTION_SCHEMA = {
|
||
"type": "object",
|
||
"properties": {
|
||
"AgentSelectionPlan": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "string",
|
||
"minLength": 1, # 不允许空字符串
|
||
"pattern": r"^\S+$" # 不允许仅空白
|
||
},
|
||
"minItems": 1 # 至少选一个
|
||
}
|
||
},
|
||
"required": ["AgentSelectionPlan"],
|
||
"additionalProperties": False
|
||
}
|
||
|
||
|
||
BASE_PLAN_SCHEMA = {
|
||
"type": "object",
|
||
"properties": {
|
||
"Plan_Outline": {
|
||
"type": "array",
|
||
"items": {
|
||
"type": "object",
|
||
"properties": {
|
||
"StepName": {"type": "string"},
|
||
"TaskContent": {"type": "string"},
|
||
"InputObject_List":{"type": "array", "items": {"type": "string"}},
|
||
"OutputObject": {"type": "string"},
|
||
},
|
||
"required": ["StepName", "TaskContent", "InputObject_List", "OutputObject"],
|
||
"additionalProperties": False,
|
||
},
|
||
}
|
||
},
|
||
"required": ["Plan_Outline"],
|
||
"additionalProperties": False,
|
||
}
|
||
|
||
|
||
def safe_join(iterable, sep=""):
|
||
"""保证 join 前全是 str,None 变空串"""
|
||
return sep.join("" if x is None else str(x) for x in iterable)
|
||
|
||
def clean_agent_board(board):
|
||
"""把 AgentBoard 洗成只含 str 的字典列表"""
|
||
if not board:
|
||
return []
|
||
return [
|
||
{"Name": (a.get("Name") or "").strip(),
|
||
"Profile": (a.get("Profile") or "").strip()}
|
||
for a in board
|
||
if a and a.get("Name")
|
||
]
|
||
|
||
def clean_plan_outline(outline):
|
||
"""清洗 Plan_Outline 里的 None"""
|
||
if not isinstance(outline, list):
|
||
return []
|
||
for step in outline:
|
||
if not isinstance(step, dict):
|
||
continue
|
||
step["InputObject_List"] = [
|
||
str(i) for i in step.get("InputObject_List", []) if i is not None
|
||
]
|
||
step["OutputObject"] = str(step.get("OutputObject") or "")
|
||
step["StepName"] = str(step.get("StepName") or "")
|
||
step["TaskContent"] = str(step.get("TaskContent") or "")
|
||
return outline
|
||
|
||
@app.route("/fill_stepTask_TaskProcess", methods=["post"])
|
||
def Handle_fill_stepTask_TaskProcess():
|
||
incoming_data = request.get_json()
|
||
# print(f"[DEBUG] fill_stepTask_TaskProcess received data: {incoming_data}", flush=True)
|
||
|
||
# 验证必需参数
|
||
General_Goal = incoming_data.get("General Goal", "").strip()
|
||
stepTask_lackTaskProcess = incoming_data.get("stepTask_lackTaskProcess")
|
||
|
||
if not General_Goal:
|
||
return jsonify({"error": "General Goal is required and cannot be empty"}), 400
|
||
if not stepTask_lackTaskProcess:
|
||
return jsonify({"error": "stepTask_lackTaskProcess is required"}), 400
|
||
|
||
requestIdentifier = str(
|
||
(
|
||
"/fill_stepTask_TaskProcess",
|
||
General_Goal,
|
||
stepTask_lackTaskProcess,
|
||
)
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
try:
|
||
filled_stepTask = fill_stepTask_TaskProcess(
|
||
General_Goal=General_Goal,
|
||
stepTask=stepTask_lackTaskProcess,
|
||
AgentProfile_Dict=AgentProfile_Dict,
|
||
)
|
||
except Exception as e:
|
||
print(f"[ERROR] fill_stepTask_TaskProcess failed: {e}", flush=True)
|
||
return jsonify({"error": str(e)}), 500
|
||
filled_stepTask = Add_Collaboration_Brief_FrontEnd(filled_stepTask)
|
||
Request_Cache[requestIdentifier] = filled_stepTask
|
||
response = jsonify(filled_stepTask)
|
||
return response
|
||
|
||
|
||
@app.route("/agentSelectModify_init", methods=["POST"])
|
||
def Handle_agentSelectModify_init():
|
||
incoming = request.get_json(silent=True) or {}
|
||
general_goal = (incoming.get("General Goal") or "").strip()
|
||
step_task = incoming.get("stepTask")
|
||
if not general_goal or not step_task:
|
||
return jsonify({"error": "Missing field"}), 400
|
||
|
||
if not AgentBoard: # 空 Board 直接返回
|
||
return jsonify({"AgentSelectionPlan": []})
|
||
|
||
req_id = str(("/agentSelectModify_init", general_goal, step_task))
|
||
if USE_CACHE and req_id in Request_Cache:
|
||
return jsonify(Request_Cache[req_id])
|
||
|
||
try:
|
||
clean_board = clean_agent_board(AgentBoard)
|
||
raw = AgentSelectModify_init(stepTask=step_task,
|
||
General_Goal=general_goal,
|
||
Agent_Board=clean_board)
|
||
if not isinstance(raw, dict):
|
||
raise ValueError("model returned non-dict")
|
||
plan = raw.get("AgentSelectionPlan") or []
|
||
cleaned = [str(x).strip() for x in plan if x is not None and str(x).strip()]
|
||
raw["AgentSelectionPlan"] = cleaned
|
||
validate(instance=raw, schema=AGENT_SELECTION_SCHEMA)
|
||
except Exception as exc:
|
||
print(f"[ERROR] AgentSelectModify_init: {exc}")
|
||
return jsonify({"error": str(exc)}), 500
|
||
|
||
if USE_CACHE:
|
||
Request_Cache[req_id] = raw
|
||
return jsonify(raw)
|
||
|
||
|
||
@app.route("/agentSelectModify_addAspect", methods=["post"])
|
||
def Handle_agentSelectModify_addAspect():
|
||
incoming_data = request.get_json()
|
||
requestIdentifier = str(
|
||
("/agentSelectModify_addAspect", incoming_data["aspectList"])
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
scoreTable = AgentSelectModify_addAspect(
|
||
aspectList=incoming_data["aspectList"], Agent_Board=AgentBoard or []
|
||
)
|
||
Request_Cache[requestIdentifier] = scoreTable
|
||
response = jsonify(scoreTable)
|
||
return response
|
||
|
||
|
||
@app.route("/fill_stepTask", methods=["post"])
|
||
def Handle_fill_stepTask():
|
||
incoming_data = request.get_json()
|
||
# print(f"[DEBUG] fill_stepTask received data: {incoming_data}", flush=True)
|
||
|
||
# 验证必需参数
|
||
General_Goal = incoming_data.get("General Goal", "").strip()
|
||
stepTask = incoming_data.get("stepTask")
|
||
|
||
if not General_Goal:
|
||
return jsonify({"error": "General Goal is required and cannot be empty"}), 400
|
||
if not stepTask:
|
||
return jsonify({"error": "stepTask is required"}), 400
|
||
|
||
requestIdentifier = str(
|
||
(
|
||
"/fill_stepTask",
|
||
General_Goal,
|
||
stepTask,
|
||
)
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
try:
|
||
filled_stepTask = fill_stepTask(
|
||
General_Goal=General_Goal,
|
||
stepTask=stepTask,
|
||
Agent_Board=AgentBoard,
|
||
AgentProfile_Dict=AgentProfile_Dict,
|
||
)
|
||
except Exception as e:
|
||
print(f"[ERROR] fill_stepTask failed: {e}", flush=True)
|
||
return jsonify({"error": str(e)}), 500
|
||
filled_stepTask = Add_Collaboration_Brief_FrontEnd(filled_stepTask)
|
||
Request_Cache[requestIdentifier] = filled_stepTask
|
||
response = jsonify(filled_stepTask)
|
||
return response
|
||
|
||
|
||
@app.route("/branch_PlanOutline", methods=["post"])
|
||
def Handle_branch_PlanOutline():
|
||
incoming_data = request.get_json()
|
||
requestIdentifier = str(
|
||
(
|
||
"/branch_PlanOutline",
|
||
incoming_data["branch_Number"],
|
||
incoming_data["Modification_Requirement"],
|
||
incoming_data["Existing_Steps"],
|
||
incoming_data["Baseline_Completion"],
|
||
incoming_data["Initial Input Object"],
|
||
incoming_data["General Goal"],
|
||
)
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
branchList = branch_PlanOutline(
|
||
branch_Number=incoming_data["branch_Number"],
|
||
Modification_Requirement=incoming_data["Modification_Requirement"],
|
||
Existing_Steps=incoming_data["Existing_Steps"],
|
||
Baseline_Completion=incoming_data["Baseline_Completion"],
|
||
InitialObject_List=incoming_data["Initial Input Object"],
|
||
General_Goal=incoming_data["General Goal"],
|
||
)
|
||
branchList = Add_Collaboration_Brief_FrontEnd(branchList)
|
||
Request_Cache[requestIdentifier] = branchList
|
||
response = jsonify(branchList)
|
||
return response
|
||
|
||
|
||
@app.route("/branch_TaskProcess", methods=["post"])
|
||
def Handle_branch_TaskProcess():
|
||
incoming_data = request.get_json()
|
||
requestIdentifier = str(
|
||
(
|
||
"/branch_TaskProcess",
|
||
incoming_data["branch_Number"],
|
||
incoming_data["Modification_Requirement"],
|
||
incoming_data["Existing_Steps"],
|
||
incoming_data["Baseline_Completion"],
|
||
incoming_data["stepTaskExisting"],
|
||
incoming_data["General Goal"],
|
||
)
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
branchList = branch_TaskProcess(
|
||
branch_Number=incoming_data["branch_Number"],
|
||
Modification_Requirement=incoming_data["Modification_Requirement"],
|
||
Existing_Steps=incoming_data["Existing_Steps"],
|
||
Baseline_Completion=incoming_data["Baseline_Completion"],
|
||
stepTaskExisting=incoming_data["stepTaskExisting"],
|
||
General_Goal=incoming_data["General Goal"],
|
||
AgentProfile_Dict=AgentProfile_Dict,
|
||
)
|
||
Request_Cache[requestIdentifier] = branchList
|
||
response = jsonify(branchList)
|
||
return response
|
||
|
||
|
||
@app.route("/generate_basePlan", methods=["POST"])
|
||
def Handle_generate_basePlan():
|
||
incoming = request.get_json(silent=True) or {}
|
||
general_goal = (incoming.get("General Goal") or "").strip()
|
||
initial_objs = incoming.get("Initial Input Object") or []
|
||
|
||
if not general_goal:
|
||
return jsonify({"error": "General Goal is required"}), 400
|
||
|
||
# 1. 空 Board 直接短路
|
||
if not AgentBoard:
|
||
print("[SKIP] AgentBoard empty")
|
||
out = Add_Collaboration_Brief_FrontEnd({"Plan_Outline": []})
|
||
return jsonify(out)
|
||
|
||
req_id = str(("/generate_basePlan", general_goal, initial_objs))
|
||
if USE_CACHE and req_id in Request_Cache:
|
||
return jsonify(Request_Cache[req_id])
|
||
|
||
try:
|
||
# 2. 洗 Board → 调模型 → 洗返回
|
||
clean_board = clean_agent_board(AgentBoard)
|
||
raw_plan = asyncio.run(
|
||
generate_basePlan(
|
||
General_Goal=general_goal,
|
||
Agent_Board=clean_board,
|
||
AgentProfile_Dict=AgentProfile_Dict,
|
||
InitialObject_List=initial_objs,
|
||
)
|
||
)
|
||
raw_plan["Plan_Outline"] = clean_plan_outline(raw_plan.get("Plan_Outline"))
|
||
validate(instance=raw_plan, schema=BASE_PLAN_SCHEMA) # 可选,二次校验
|
||
except Exception as exc:
|
||
print(f"[ERROR] generate_basePlan failed: {exc}")
|
||
return jsonify({"error": "model call failed", "detail": str(exc)}), 500
|
||
|
||
out = Add_Collaboration_Brief_FrontEnd(raw_plan)
|
||
if USE_CACHE:
|
||
Request_Cache[req_id] = out
|
||
return jsonify(out)
|
||
|
||
|
||
@app.route("/executePlan", methods=["post"])
|
||
def Handle_executePlan():
|
||
incoming_data = request.get_json()
|
||
requestIdentifier = str(
|
||
(
|
||
"/executePlan",
|
||
incoming_data["num_StepToRun"],
|
||
incoming_data["RehearsalLog"],
|
||
incoming_data["plan"],
|
||
)
|
||
)
|
||
|
||
if USE_CACHE:
|
||
if requestIdentifier in Request_Cache:
|
||
return jsonify(Request_Cache[requestIdentifier])
|
||
|
||
RehearsalLog = executePlan(
|
||
incoming_data["plan"],
|
||
incoming_data["num_StepToRun"],
|
||
incoming_data["RehearsalLog"],
|
||
AgentProfile_Dict,
|
||
)
|
||
Request_Cache[requestIdentifier] = RehearsalLog
|
||
response = jsonify(RehearsalLog)
|
||
return response
|
||
|
||
|
||
@app.route("/_saveRequestCashe", methods=["post"])
|
||
def Handle_saveRequestCashe():
|
||
with open(
|
||
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "w"
|
||
) as json_file:
|
||
json.dump(Request_Cache, json_file, indent=4)
|
||
response = jsonify(
|
||
{"code": 200, "content": "request cashe sucessfully saved"}
|
||
)
|
||
return response
|
||
|
||
|
||
@app.route("/setAgents", methods=["POST"])
|
||
def set_agents():
|
||
global AgentBoard, AgentProfile_Dict
|
||
board_in = request.json or []
|
||
# 先清洗再赋值
|
||
AgentBoard = clean_agent_board(board_in)
|
||
AgentProfile_Dict = {a["Name"]: a["Profile"] for a in AgentBoard}
|
||
return jsonify({"code": 200, "content": "AgentBoard set successfully"})
|
||
|
||
|
||
def init():
|
||
global AgentBoard, AgentProfile_Dict, Request_Cache
|
||
with open(
|
||
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "r"
|
||
) as json_file:
|
||
Request_Cache = json.load(json_file)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
parser = argparse.ArgumentParser(
|
||
description="start the backend for AgentCoord"
|
||
)
|
||
parser.add_argument(
|
||
"--port",
|
||
type=int,
|
||
default=8017,
|
||
help="set the port number, 8017 by defaul.",
|
||
)
|
||
args = parser.parse_args()
|
||
init()
|
||
app.run(host="0.0.0.0", port=args.port, debug=True)
|