style: 项目重构
1.项目改名为kilostar(千星) 2.后端部分进行大规模重构 3.node功能进行大规模重新设计
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI, WebSocket, Request
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from ray import serve
|
||||
|
||||
from .agent import agent_router
|
||||
from .auth import auth_router
|
||||
from .cluster import cluster_router
|
||||
from .platform.frontend import client_router
|
||||
from .provider import provider_router
|
||||
from .resource import resource_router
|
||||
from .workflow import workflow_router
|
||||
from kilostar.utils.error import (
|
||||
DemandError,
|
||||
ModelNotExistError,
|
||||
UserError,
|
||||
UserNotExistError,
|
||||
UserPasswordError,
|
||||
ProviderError,
|
||||
ProviderNotExistError,
|
||||
WorkflowError,
|
||||
WorkflowExit,
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.include_router(client_router) # 客户端路径
|
||||
app.include_router(auth_router) # 用户路径
|
||||
app.include_router(provider_router) # 供应商路径
|
||||
app.include_router(resource_router) # 资源路径
|
||||
app.include_router(cluster_router) # 集群信息路径
|
||||
app.include_router(agent_router) # agent路径
|
||||
app.include_router(workflow_router) # workflow路径
|
||||
|
||||
|
||||
@app.exception_handler(UserNotExistError)
|
||||
async def user_not_exist_handler(request: Request, exc: UserNotExistError):
|
||||
return JSONResponse(status_code=404, content={"message": "用户不存在"})
|
||||
|
||||
|
||||
@app.exception_handler(UserPasswordError)
|
||||
async def user_password_handler(request: Request, exc: UserPasswordError):
|
||||
return JSONResponse(status_code=401, content={"message": "密码错误"})
|
||||
|
||||
|
||||
@app.exception_handler(UserError)
|
||||
async def user_error_handler(request: Request, exc: UserError):
|
||||
return JSONResponse(status_code=400, content={"message": "用户相关错误"})
|
||||
|
||||
|
||||
@app.exception_handler(ProviderNotExistError)
|
||||
async def provider_not_exist_handler(request: Request, exc: ProviderNotExistError):
|
||||
return JSONResponse(status_code=404, content={"message": "服务提供商不存在"})
|
||||
|
||||
|
||||
@app.exception_handler(ProviderError)
|
||||
async def provider_error_handler(request: Request, exc: ProviderError):
|
||||
return JSONResponse(status_code=400, content={"message": "服务提供商错误"})
|
||||
|
||||
|
||||
@app.exception_handler(ModelNotExistError)
|
||||
async def model_not_exist_handler(request: Request, exc: ModelNotExistError):
|
||||
return JSONResponse(status_code=404, content={"message": "模型不存在"})
|
||||
|
||||
|
||||
@app.exception_handler(DemandError)
|
||||
async def demand_error_handler(request: Request, exc: DemandError):
|
||||
return JSONResponse(status_code=400, content={"message": "需求格式错误或不满足"})
|
||||
|
||||
|
||||
@app.exception_handler(WorkflowExit)
|
||||
async def workflow_exit_handler(request: Request, exc: WorkflowExit):
|
||||
return JSONResponse(status_code=400, content={"message": "工作流已退出"})
|
||||
|
||||
|
||||
@app.exception_handler(WorkflowError)
|
||||
async def workflow_error_handler(request: Request, exc: WorkflowError):
|
||||
return JSONResponse(status_code=500, content={"message": "工作流执行错误"})
|
||||
|
||||
|
||||
base_dir = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
)
|
||||
frontend_dir = os.path.join(base_dir, "frontend", "dist")
|
||||
|
||||
if os.path.exists(frontend_dir):
|
||||
app.mount(
|
||||
"/assets",
|
||||
StaticFiles(directory=os.path.join(frontend_dir, "assets")),
|
||||
name="assets",
|
||||
)
|
||||
|
||||
@app.get("/favicon.svg", include_in_schema=False)
|
||||
async def serve_favicon():
|
||||
return FileResponse(os.path.join(frontend_dir, "favicon.svg"))
|
||||
|
||||
@app.get("/icons.svg", include_in_schema=False)
|
||||
async def serve_icons():
|
||||
return FileResponse(os.path.join(frontend_dir, "icons.svg"))
|
||||
|
||||
@app.get("/{full_path:path}", include_in_schema=False)
|
||||
async def serve_frontend(full_path: str):
|
||||
# 【重要安全修复】避免拦截不存在的 API 路由。如果是调用了不存在的 /api/ 接口,直接返回 404,不返回前端页面
|
||||
if full_path.startswith("api/"):
|
||||
return JSONResponse(
|
||||
status_code=404, content={"detail": "API endpoint not found"}
|
||||
)
|
||||
|
||||
index_path = os.path.join(frontend_dir, "index.html")
|
||||
if os.path.exists(index_path):
|
||||
return FileResponse(index_path)
|
||||
return JSONResponse(
|
||||
status_code=404, content={"detail": "Frontend build not found"}
|
||||
)
|
||||
else:
|
||||
import logging
|
||||
|
||||
logging.getLogger("kilostar").warning(
|
||||
f"Frontend dist folder not found at {frontend_dir}. Skipping frontend mount."
|
||||
)
|
||||
|
||||
|
||||
@serve.deployment
|
||||
@serve.ingress(app)
|
||||
class kilostarGateway:
|
||||
gateway: Dict[str, WebSocket]
|
||||
|
||||
def __init__(self):
|
||||
self.gateway = {}
|
||||
@@ -0,0 +1,275 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from typing import Union
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.core.postgres_database.model import AgentType
|
||||
from fastapi import HTTPException
|
||||
from typing import Optional, List, Dict
|
||||
from kilostar.utils.check_user.role_check import RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
|
||||
agent_router = APIRouter(prefix="/api/v1/agent", tags=["agent"])
|
||||
|
||||
|
||||
class AgentRegister(BaseModel):
|
||||
"""AgentRegister 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 AgentRegister 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
provider_title: str
|
||||
model_id: str
|
||||
individual_name: str
|
||||
tools: Optional[List[str]] = None
|
||||
|
||||
|
||||
class AgentLocalRegister(BaseModel):
|
||||
"""AgentLocalRegister 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 AgentLocalRegister 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
path: str
|
||||
individual_name: str
|
||||
tools: Optional[List[str]] = None
|
||||
|
||||
|
||||
@agent_router.get("")
|
||||
async def get_system_nodes(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""处理针对 get system nodes 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: _ (TokenData): 参与 get system nodes 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
configs = await postgres_database.get_all_system_node_configs.remote()
|
||||
return {"system_nodes": configs}
|
||||
|
||||
|
||||
@agent_router.post("")
|
||||
async def load_agent(
|
||||
agent_register: Union[AgentRegister, AgentLocalRegister],
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""处理针对 load agent 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: agent_register (Union[AgentRegister, AgentLocalRegister]): 参与 load agent 逻辑运算或数据构建的上下文依赖对象。 _ (TokenData): 参与 load agent 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
|
||||
if isinstance(agent_register, AgentLocalRegister):
|
||||
pass
|
||||
|
||||
elif isinstance(agent_register, AgentRegister):
|
||||
try:
|
||||
# Persist configuration
|
||||
await postgres_database.upsert_system_node_config.remote(
|
||||
agent_register.individual_name,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
agent_register.tools,
|
||||
)
|
||||
|
||||
# Load agent into state machine
|
||||
match agent_register.individual_name:
|
||||
case "regulatory_node":
|
||||
node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
await node.create_agent.remote(
|
||||
global_state_machine,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
agent_register.tools,
|
||||
)
|
||||
case "consciousness_node":
|
||||
node = ray_actor_hook("consciousness_node").consciousness_node
|
||||
await node.create_agent.remote(
|
||||
global_state_machine,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
agent_register.tools,
|
||||
)
|
||||
case "control_node":
|
||||
node = ray_actor_hook("control_node").control_node
|
||||
await node.create_agent.remote(
|
||||
global_state_machine,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
agent_register.tools,
|
||||
)
|
||||
case _:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"加载节点失败: {str(e)}")
|
||||
return {"message": "创建成功"}
|
||||
|
||||
|
||||
class WorkerIndividualCreate(BaseModel):
|
||||
"""WorkerIndividualCreate 核心组件类。
|
||||
这是一个具体的 Worker 智能体实体类,代表着具备特定人设、领域技能或长文本处理能力的数字员工。它可以被控制器动态拉起,并在安全沙箱内执行复杂的工作流指令与多步骤推理任务。"""
|
||||
|
||||
agent_name: str
|
||||
agent_type: AgentType
|
||||
description: str
|
||||
provider_title: str
|
||||
model_id: str
|
||||
system_prompt: str
|
||||
output_template: dict
|
||||
bound_skill: Dict[str, List[str]]
|
||||
workspace: List[str]
|
||||
tools: Optional[List[str]] = None
|
||||
|
||||
|
||||
class WorkerIndividualUpdate(BaseModel):
|
||||
"""WorkerIndividualUpdate 核心组件类。
|
||||
这是一个具体的 Worker 智能体实体类,代表着具备特定人设、领域技能或长文本处理能力的数字员工。它可以被控制器动态拉起,并在安全沙箱内执行复杂的工作流指令与多步骤推理任务。"""
|
||||
|
||||
agent_name: Optional[str] = None
|
||||
agent_type: Optional[AgentType] = None
|
||||
description: Optional[str] = None
|
||||
provider_title: Optional[str] = None
|
||||
model_id: Optional[str] = None
|
||||
system_prompt: Optional[str] = None
|
||||
output_template: Optional[dict] = None
|
||||
bound_skill: Optional[Dict[str, List[str]]] = None
|
||||
workspace: Optional[List[str]] = None
|
||||
tools: Optional[List[str]] = None
|
||||
|
||||
|
||||
@agent_router.post("/worker")
|
||||
async def create_worker_individual(
|
||||
worker_data: WorkerIndividualCreate,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""处理针对 create worker individual 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: worker_data (WorkerIndividualCreate): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
data_dict = worker_data.model_dump()
|
||||
data_dict["owner_id"] = token_data.user_id
|
||||
worker = await postgres_database.add_worker_individual.remote(**data_dict)
|
||||
return {"message": "success", "agent_id": worker.agent_id}
|
||||
|
||||
|
||||
@agent_router.get("/worker")
|
||||
async def get_worker_individual_list(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""处理针对 get worker individual list 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
workers = await postgres_database.get_worker_individual_list.remote(
|
||||
owner_id=token_data.user_id
|
||||
)
|
||||
return {"workers": workers}
|
||||
|
||||
|
||||
@agent_router.get("/worker/{agent_id}")
|
||||
async def get_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""处理针对 get worker individual 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: agent_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 agent 实例。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if worker.owner_id != token_data.user_id:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Forbidden: You do not own this agent"
|
||||
)
|
||||
return worker
|
||||
|
||||
|
||||
@agent_router.put("/worker/{agent_id}")
|
||||
async def update_worker_individual(
|
||||
agent_id: str,
|
||||
worker_data: WorkerIndividualUpdate,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""处理针对 update worker individual 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: agent_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 agent 实例。 worker_data (WorkerIndividualUpdate): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if worker.owner_id != token_data.user_id:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Forbidden: You do not own this agent"
|
||||
)
|
||||
|
||||
update_data = worker_data.model_dump(exclude_unset=True)
|
||||
updated_worker = await postgres_database.update_worker_individual.remote(
|
||||
agent_id=agent_id, **update_data
|
||||
)
|
||||
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
try:
|
||||
await global_state_machine.remove_individual.remote(agent_id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {"message": "success", "worker": updated_worker}
|
||||
|
||||
|
||||
@agent_router.post("/worker/{agent_id}/reload")
|
||||
async def reload_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""处理针对 reload worker individual 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: agent_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 agent 实例。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if worker.owner_id != token_data.user_id:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Forbidden: You do not own this agent"
|
||||
)
|
||||
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.remove_individual.remote(agent_id)
|
||||
|
||||
return {"message": "Worker will be reloaded on next use"}
|
||||
|
||||
|
||||
@agent_router.delete("/worker/{agent_id}")
|
||||
async def delete_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""处理针对 delete worker individual 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: agent_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 agent 实例。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if worker.owner_id != token_data.user_id:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Forbidden: You do not own this agent"
|
||||
)
|
||||
await postgres_database.delete_worker_individual.remote(agent_id=agent_id)
|
||||
return {"message": "success"}
|
||||
@@ -0,0 +1,136 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import Depends
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.check_user.role_check import RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from kilostar.utils.error import UserNotExistError
|
||||
|
||||
auth_router = APIRouter(prefix="/api/v1/auth", tags=["auth"])
|
||||
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
"""UserRegister 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 UserRegister 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
user_name: str
|
||||
password: str
|
||||
|
||||
|
||||
@auth_router.post("/register")
|
||||
async def create_user(user_register: UserRegister):
|
||||
"""处理针对 create user 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: user_register (UserRegister): 参与 create user 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
hashed_password = await run_in_threadpool(
|
||||
Accessor.hash_password, user_register.password
|
||||
)
|
||||
user = await postgres_database.add_user.remote(
|
||||
user_register.user_name, hashed_password
|
||||
)
|
||||
return {"message": "success", "user_id": user.user_id}
|
||||
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
"""UserLogin 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 UserLogin 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
user_name: str
|
||||
password: str
|
||||
|
||||
|
||||
@auth_router.post("/login")
|
||||
async def login_user(user_login: UserLogin):
|
||||
"""处理针对 login user 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: user_login (UserLogin): 参与 login user 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
user = await postgres_database.login_user.remote(user_login.user_name)
|
||||
if not user:
|
||||
raise UserNotExistError()
|
||||
token = await run_in_threadpool(
|
||||
Accessor.login_hashed_password, user, user_login.password
|
||||
)
|
||||
return {"message": "success", "token": token}
|
||||
|
||||
|
||||
class ChangeAuthorityRequest(BaseModel):
|
||||
"""ChangeAuthorityRequest 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 ChangeAuthorityRequest 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
user_id: str
|
||||
new_authority: UserAuthority
|
||||
|
||||
|
||||
@auth_router.put("/authority")
|
||||
async def change_authority(
|
||||
request: ChangeAuthorityRequest,
|
||||
_: TokenData = Depends(
|
||||
RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)
|
||||
),
|
||||
):
|
||||
"""
|
||||
Update a user's authority level. Only accessible by SUPER_ADMINISTRATOR.
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
user = await postgres_database.change_user_authority.remote(
|
||||
user_id=request.user_id, new_authority=request.new_authority
|
||||
)
|
||||
return {
|
||||
"message": "success",
|
||||
"user_id": user.user_id,
|
||||
"new_authority": user.user_authority,
|
||||
}
|
||||
|
||||
|
||||
@auth_router.get("/list")
|
||||
async def get_user_list(
|
||||
_: TokenData = Depends(
|
||||
RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)
|
||||
),
|
||||
):
|
||||
"""
|
||||
Get a list of all users. Only accessible by SUPER_ADMINISTRATOR.
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
users = await postgres_database.get_all_users.remote()
|
||||
return {
|
||||
"users": [
|
||||
{"user_id": u.user_id, "user_name": u.user_name, "role": u.user_authority}
|
||||
for u in users
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@auth_router.delete("/{user_id}")
|
||||
async def delete_user(
|
||||
user_id: str,
|
||||
_: TokenData = Depends(
|
||||
RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)
|
||||
),
|
||||
):
|
||||
"""
|
||||
Delete a user. Only accessible by SUPER_ADMINISTRATOR.
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
await postgres_database.delete_user_by_id.remote(user_id=user_id)
|
||||
return {"message": "success"}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
cluster_router = APIRouter(prefix="/api/v1/cluster", tags=["cluster"])
|
||||
|
||||
# Monitor websocket API temporarily removed
|
||||
@@ -0,0 +1,17 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .frontend import client_router
|
||||
|
||||
__all__ = ["client_router"]
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from ulid import ULID
|
||||
from typing import Any, Dict
|
||||
from kilostar.core.workflow_running_engine.workflow import kilostarWorkflow
|
||||
import asyncio
|
||||
|
||||
|
||||
class kilostarEvent(BaseModel):
|
||||
"""kilostarEvent 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 kilostarEvent 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
trace_id: str = Field(
|
||||
default_factory=lambda: str(ULID()), description="事件的唯一标识符"
|
||||
)
|
||||
platform: str = Field(description="消息来源的平台")
|
||||
user_id: str = Field(description="用户id")
|
||||
user_name: str = Field(description="用户名")
|
||||
create_time: str = Field(
|
||||
default_factory=lambda: str(
|
||||
datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
),
|
||||
description="事件创建时间",
|
||||
)
|
||||
message: str = Field(description="用户发来的消息")
|
||||
attachment: Dict[str, str] | None = Field(default=None, description="附件")
|
||||
# --------------------------------------------------------------------------------------------------------------
|
||||
context: Dict[str, Any] = Field(
|
||||
default_factory=dict, description="事件上下文内容,可包含工作流模板等信息"
|
||||
)
|
||||
workflow: kilostarWorkflow | None = Field(default=None, description="工作流")
|
||||
pending_queue: asyncio.Queue[str] | None = Field(
|
||||
default=None, description="待处理队列"
|
||||
)
|
||||
receive_queue: asyncio.Queue[str] | None = Field(
|
||||
default=None, description="待接收队列"
|
||||
)
|
||||
@@ -0,0 +1,87 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.api.platform.event import kilostarEvent
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
import os
|
||||
import anyio
|
||||
from kilostar.utils.logger import get_logger
|
||||
|
||||
|
||||
logger = get_logger("frontend")
|
||||
client_router = APIRouter(prefix="/api/v1/adapter/client", tags=["client"])
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
"""Message 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 Message 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
message: str
|
||||
|
||||
|
||||
@client_router.post("")
|
||||
async def create_message(
|
||||
message: Message, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""处理针对 create message 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: message (Message): 参与 create message 逻辑运算或数据构建的上下文依赖对象。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
logger.info("收到消息,来源:客户端")
|
||||
logger.debug(f"消息内容:{message.message}")
|
||||
event = kilostarEvent(
|
||||
platform="client",
|
||||
user_id=str(token_data.user_id),
|
||||
user_name=token_data.username,
|
||||
message=message.message,
|
||||
)
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
message = await regulatory_node.working.remote(event)
|
||||
if message.startswith("任务已创建"):
|
||||
return {"message": f"{event.trace_id}\n\n{message}"}
|
||||
elif message == "未知相应类型":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="模型回复错误"
|
||||
)
|
||||
else:
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@client_router.post("/upload")
|
||||
async def upload_file(
|
||||
file: UploadFile = File(...),
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""处理针对 upload file 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: file (UploadFile): 参与 upload file 逻辑运算或数据构建的上下文依赖对象。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
try:
|
||||
upload_dir = "uploads"
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
file_path = os.path.join(upload_dir, file.filename)
|
||||
async with await anyio.open_file(file_path, "wb") as buffer:
|
||||
while chunk := await file.read(64 * 1024): # 64KB chunks
|
||||
await buffer.write(chunk)
|
||||
logger.info(f"用户 {token_data.username} 上传了文件: {file.filename}")
|
||||
return {
|
||||
"filename": file.filename,
|
||||
"message": f"File {file.filename} uploaded successfully",
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"文件上传失败: {e}")
|
||||
raise HTTPException(status_code=500, detail="文件上传失败")
|
||||
@@ -0,0 +1,85 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal
|
||||
from kilostar.utils.access import TokenData, Accessor
|
||||
from kilostar.utils.check_user.role_check import RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from typing import Dict
|
||||
from kilostar.core.global_state_machine.model_provider.base_provider import Provider
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
|
||||
provider_router = APIRouter(prefix="/api/v1/provider", tags=["provider"])
|
||||
|
||||
|
||||
class ProviderRegister(BaseModel):
|
||||
"""ProviderRegister 核心组件类。
|
||||
这是一个模型/服务提供商适配器类,屏蔽了外部不同供应商(如 OpenAI、Anthropic 等)的底层 API 差异。它负责标准化参数组装、网络请求发送、鉴权处理以及响应结构的反序列化。"""
|
||||
|
||||
provider_type: Literal["openai", "claude", "deepseek"]
|
||||
provider_title: str
|
||||
provider_url: str
|
||||
provider_apikey: str
|
||||
|
||||
|
||||
@provider_router.post("")
|
||||
async def create_provider(
|
||||
provider_register: ProviderRegister,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
) -> None:
|
||||
"""处理针对 create provider 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: provider_register (ProviderRegister): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 provider_register 实例。 token_data (TokenData): 从客户端传递过来或由上游组件生成的核心业务数据体,通常需要进一步的清洗和结构化解析。
|
||||
Returns: (None): 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.add_provider_wrap.remote(
|
||||
provider_type=provider_register.provider_type,
|
||||
provider_title=provider_register.provider_title,
|
||||
provider_url=provider_register.provider_url,
|
||||
provider_apikey=provider_register.provider_apikey,
|
||||
provider_owner=token_data.user_id,
|
||||
)
|
||||
|
||||
|
||||
@provider_router.get("/list")
|
||||
async def get_provider_list(
|
||||
_: TokenData = Depends(Accessor.get_current_user),
|
||||
) -> Dict[str, Dict[str, Provider]]:
|
||||
"""处理针对 get provider list 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: _ (TokenData): 参与 get provider list 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: (Dict[str, Dict[str, Provider]]): 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
provider_list: Dict[
|
||||
str, Provider
|
||||
] = await global_state_machine.get_provider_list.remote()
|
||||
return {"provider_list": provider_list}
|
||||
|
||||
|
||||
@provider_router.delete("/{provider_title}")
|
||||
async def delete_provider(
|
||||
provider_title: str,
|
||||
_: TokenData = Depends(
|
||||
RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)
|
||||
),
|
||||
) -> dict:
|
||||
"""处理针对 delete provider 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: provider_title (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 provider_title 实例。 _ (TokenData): 参与 delete provider 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: (dict): 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.delete_provider.remote(provider_title=provider_title)
|
||||
return {"message": "success"}
|
||||
@@ -0,0 +1,104 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pydantic import BaseModel
|
||||
import viceroy
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from fastapi import APIRouter, Depends
|
||||
from kilostar.utils.access import TokenData
|
||||
from kilostar.utils.check_user.role_check import RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
|
||||
resource_router = APIRouter(prefix="/api/v1/resource")
|
||||
|
||||
|
||||
class Skill(BaseModel):
|
||||
"""Skill 核心组件类。
|
||||
这是一个领域数据模型或功能封装类,承载了 Skill 相关的内聚属性定义与状态维护。它的存在隔离了局部的业务复杂性,并对外提供了类型安全的访问接口。"""
|
||||
|
||||
repo_url: str
|
||||
path: str | None
|
||||
|
||||
|
||||
@resource_router.post("/skill")
|
||||
async def install_skill(
|
||||
skill: Skill, _: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))
|
||||
):
|
||||
"""处理针对 install skill 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: skill (Skill): 参与 install skill 逻辑运算或数据构建的上下文依赖对象。 _ (TokenData): 参与 install skill 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
# noinspection PyUnresolvedReferences
|
||||
import os
|
||||
|
||||
skill_output_dir = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "plugin", "skill")
|
||||
)
|
||||
os.makedirs(skill_output_dir, exist_ok=True)
|
||||
await viceroy.install_skill_async(
|
||||
url=skill.repo_url, path=skill.path, output=skill_output_dir
|
||||
)
|
||||
if skill.path:
|
||||
skill_name = skill.path.split("/")[-1]
|
||||
else:
|
||||
skill_name = skill.repo_url.split("/")[-1]
|
||||
await global_state_machine.add_skill.remote(skill_name)
|
||||
return {"message": "创建成功"}
|
||||
|
||||
|
||||
@resource_router.get("/skill")
|
||||
async def get_skills(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""处理针对 get skills 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: _ (TokenData): 参与 get skills 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
skills = await global_state_machine.get_skill_list.remote()
|
||||
return {"skills": skills}
|
||||
|
||||
|
||||
@resource_router.delete("/skill/{skill_name}")
|
||||
async def delete_skill(
|
||||
skill_name: str,
|
||||
_: TokenData = Depends(
|
||||
RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)
|
||||
),
|
||||
):
|
||||
"""处理针对 delete skill 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: skill_name (str): 赋予该实体的人类可读名称或标题字符串,主要用于前端 UI 展示、日志记录或模糊检索。 _ (TokenData): 参与 delete skill 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
# Note: this only removes it from the state machine manager.
|
||||
await global_state_machine.remove_skill.remote(skill_name)
|
||||
return {"message": "success"}
|
||||
|
||||
|
||||
@resource_router.get("/tool")
|
||||
async def get_tools(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""处理针对 get tools 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: _ (TokenData): 参与 get tools 逻辑运算或数据构建的上下文依赖对象。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
tool_mapper = await global_state_machine.get_tool_mapper.remote()
|
||||
all_tool_names = set()
|
||||
for scope_tools in tool_mapper.values():
|
||||
all_tool_names.update(scope_tools.keys())
|
||||
return {"tools": list(all_tool_names)}
|
||||
@@ -0,0 +1,128 @@
|
||||
# Copyright 2026 zhaoxi826
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from fastapi import APIRouter, Request, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
import asyncio
|
||||
|
||||
workflow_router = APIRouter(prefix="/api/v1/workflow", tags=["workflow"])
|
||||
|
||||
|
||||
@workflow_router.get("/list")
|
||||
async def get_workflow_list():
|
||||
"""处理针对 get workflow list 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
events = await global_workflow_manager.list_events.remote()
|
||||
return events
|
||||
|
||||
|
||||
@workflow_router.get("/{trace_id}")
|
||||
async def get_workflow_detail(trace_id: str):
|
||||
"""处理针对 get workflow detail 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: trace_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 trace 实例。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
event = await global_workflow_manager.get_event.remote(trace_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
|
||||
workflow = event.workflow
|
||||
if not workflow:
|
||||
return {
|
||||
"event_id": trace_id,
|
||||
"workflow_title": None,
|
||||
"status": "waiting",
|
||||
"user_name": event.user_name,
|
||||
"message": event.message,
|
||||
"create_time": event.create_time,
|
||||
"steps": [],
|
||||
}
|
||||
|
||||
steps = []
|
||||
for step in workflow.work_link:
|
||||
steps.append(
|
||||
{
|
||||
"step": step.step,
|
||||
"name": step.name,
|
||||
"node": step.node,
|
||||
"action": step.action,
|
||||
"desc": step.desc,
|
||||
"status": step.status,
|
||||
"agent_id": step.agent_id,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"event_id": trace_id,
|
||||
"workflow_title": workflow.title,
|
||||
"status": workflow.status.status,
|
||||
"command": workflow.command,
|
||||
"current_step": workflow.status.step,
|
||||
"user_name": event.user_name,
|
||||
"message": event.message,
|
||||
"create_time": event.create_time,
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
@workflow_router.get("/sse/{trace_id}")
|
||||
async def get_workflow_sse(trace_id: str, request: Request):
|
||||
"""处理针对 get workflow sse 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: trace_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 trace 实例。 request (Request): FastAPI 框架注入的原生 HTTP 请求对象,包含了完整的 Header 标头、查询参数和正文流。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
|
||||
async def event_generator():
|
||||
"""执行与 event generator 相关的核心业务流转操作。
|
||||
该方法封装了具体的算法策略或状态控制逻辑,确保操作能够在事务上下文中被原子且一致地执行。"""
|
||||
try:
|
||||
while True:
|
||||
if await request.is_disconnected():
|
||||
break
|
||||
|
||||
# You might also want to send the workflow state periodically or when updated
|
||||
# Here we just wait for pending messages and send them
|
||||
message = await global_workflow_manager.get_pending.remote(trace_id)
|
||||
# Ensure the message is formatted as SSE
|
||||
yield f"data: {message}\n\n"
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
||||
|
||||
|
||||
@workflow_router.post("/reply/{trace_id}")
|
||||
async def post_workflow_reply(trace_id: str, request: Request):
|
||||
"""处理针对 post workflow reply 相关的 HTTP API 请求。
|
||||
该接口负责解析前端传入的载荷数据,调用底层核心业务逻辑进行处理,并组装标准化的 JSON 响应。
|
||||
Args: trace_id (str): 目标对象的唯一全局标识符 (UUID/ULID),用于在数据库表或缓存结构中精准匹配该 trace 实例。 request (Request): FastAPI 框架注入的原生 HTTP 请求对象,包含了完整的 Header 标头、查询参数和正文流。
|
||||
Returns: : 序列化后的标准网络响应模型(如包含业务状态码、成功标志及对应的数据载荷 Data)。"""
|
||||
data = await request.json()
|
||||
reply_msg = data.get("message", "")
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
await global_workflow_manager.put_received.remote(trace_id, reply_msg)
|
||||
return {"status": "ok"}
|
||||
Reference in New Issue
Block a user