wip: 修改了大量漏洞

This commit is contained in:
朝夕 2026-04-17 22:07:49 +08:00
parent 3a8b1e4054
commit 3cf2411c4a
14 changed files with 98 additions and 67 deletions

View File

@ -1,9 +1,31 @@
待解决问题
---
## 问题栏
#### 🔴 核心缺陷与修复 (Bug Fixes & Stability)
- [ ] /pretor/core/individual每个template进行优化
- [ ] /pretor/worker_individual待完善复合子个体和基础子个体
#### 🛡️ 安全与合规 (Security & Auth)
#### ⚡ 性能与资源优化 (Performance & Scalability)
- [ ] 增加对应全workflow的情况追踪使得在任务运行中人机交互更加自然方便
#### 🏗️ 架构演进 (Architecture & Refactoring)
- [ ] 使用fastapi-users完善用户系统
- [x] /pretor/api的接口函数进行重构
- [ ] /dockerfile待完善
---
## 日志
#### 2026/4/12
- [ ] /pretor/tool_plugin/approval/approval.py的approval函数event改为依赖注入
- [x] /pretor/api的接口函数进行重构
- [ ] /pretor/core/individual每个template进行优化
- [ ] /pretor/worker_individual待完善复合子个体和基础子个体
- [ ] /pretor/api待完善
- [ ] /dockerfile待完善
#### 2026/4/16
- [ ] 发布v0.1.0正式版
- [ ] 增加对应全workflow的情况追踪使得在任务运行中人机交互更加自然方便
- [ ] 使用fastapi-users完善用户系统

View File

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Union
from pretor.utils.ray_hook import ray_actor_hook
from fastapi import APIRouter, Request, Depends
from pydantic import BaseModel
from pretor.utils.access import Accessor, TokenData
@ -30,22 +30,21 @@ class AgentLocalRegister(BaseModel):
@agent_router.post("")
async def load_agent(agent_register: Union[AgentRegister, AgentLocalRegister],
request: Request,
_: TokenData = Depends(Accessor.get_current_user)):
global_state_machine = request.app.state.global_state_machine
global_state_machine = ray_actor_hook("global_state_machine")
if isinstance(agent_register, AgentLocalRegister):
pass
elif isinstance(agent_register, AgentRegister):
match agent_register.individual_title:
case "supervisory_node":
node = request.app.state.supervisory_node
node = ray_actor_hook("supervisory_node")
node.create_agent.remote(global_state_machine,agent_register.provider_title,agent_register.model_id)
case "consciousness_node":
node = request.app.state.consciousness_node
node = ray_actor_hook("consciousness_node")
node.create_agent.remote(global_state_machine,agent_register.provider_title,agent_register.model_id)
case "control_node":
node = request.app.state.control_node
node = ray_actor_hook("control_node")
node.create_agent.remote(global_state_machine,agent_register.provider_title,agent_register.model_id)
case _:
pass

View File

@ -12,10 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from fastapi import APIRouter, Request
from fastapi import APIRouter
from pydantic import BaseModel
from pretor.utils.access import Accessor
from fastapi.concurrency import run_in_threadpool
from pretor.utils.ray_hook import ray_actor_hook
auth_router = APIRouter(prefix="/api/v1/auth", tags=["auth"])
@ -24,10 +25,10 @@ class UserRegister(BaseModel):
password: str
@auth_router.post("/register")
async def create_user(user_register: UserRegister, request: Request):
postgres_database = request.app.state.postgres_database
async def create_user(user_register: UserRegister):
postgres_database = ray_actor_hook("postgres_database")
hashed_password = await run_in_threadpool(Accessor.hash_password, user_register.password)
user = await postgres_database.auth_database.add_user.remote(user_register.user_name, hashed_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):
@ -35,9 +36,9 @@ class UserLogin(BaseModel):
password: str
@auth_router.post("/login")
async def login_user(user_login: UserLogin, request: Request):
postgres_database = request.app.state.postgres_database
user = await postgres_database.auth_database.login_user.remote(user_login.user_name)
async def login_user(user_login: UserLogin):
postgres_database = ray_actor_hook("postgres_database")
user = await postgres_database.login_user.remote(user_login.user_name)
if user.user_name != user_login.user_name:
pass
token = await run_in_threadpool(Accessor.login_hashed_password, user, user_login.password)

View File

@ -14,10 +14,10 @@
from fastapi import APIRouter, Request, Depends, HTTPException, status, WebSocket, WebSocketDisconnect
from pydantic import BaseModel
from pretor.utils.access import Accessor, TokenData
from pretor.api.platform.event import PretorEvent
from loguru import logger
from pretor.utils.ray_hook import ray_actor_hook
client_router = APIRouter(prefix="/api/v1/adapter/client", tags=["client"])
@ -26,7 +26,6 @@ class Message(BaseModel):
@client_router.post("")
async def create_message(message: Message,
request: Request,
token_data: TokenData = Depends(Accessor.get_current_user)):
logger.info("收到消息,来源:客户端")
logger.debug(f"消息内容:{message.message}")
@ -34,11 +33,11 @@ async def create_message(message: Message,
user_id=str(token_data.user_id),
user_name=token_data.user_name,
message=message.message)
supervisory_node = request.app.state.supervisory_node
supervisory_node = ray_actor_hook("supervisor_node")
message = await supervisory_node.working.remote(event)
if message == "任务已创建":
global_state_machine = request.app.state.global_state_machine
global_state_machine.add.remote(event)
global_state_machine = ray_actor_hook("global_state_machine")
global_state_machine.add_event.remote(event)
return {"message": event.event_id}
elif message == "未知相应类型":
raise HTTPException(

View File

@ -18,7 +18,7 @@ from typing import Literal
from pretor.utils.access import TokenData, Accessor
from typing import Dict
from pretor.core.global_state_machine.model_provider.base_provider import Provider
from pretor.utils.ray_hook import ray_actor_hook
provider_router = APIRouter(prefix="/api/v1/provider", tags=["provider"])
@ -30,9 +30,8 @@ class ProviderRegister(BaseModel):
@provider_router.post("")
async def create_provider(provider_register: ProviderRegister,
request: Request,
token_data: TokenData = Depends(Accessor.get_current_user)) -> None:
global_state_machine = request.app.state.global_state_machine
global_state_machine = ray_actor_hook("global_state_machine")
await global_state_machine.add_provider.remote(provider_type=provider_register.provider_type,
provider_title=provider_register.provider_title,
provider_url=provider_register.provider_url,
@ -41,8 +40,7 @@ async def create_provider(provider_register: ProviderRegister,
@provider_router.get("/list")
async def get_provider_list(request: Request,
_: TokenData = Depends(Accessor.get_current_user)) -> Dict[str, Provider]:
global_state_machine = request.app.state.global_state_machine
async def get_provider_list(_: TokenData = Depends(Accessor.get_current_user)) -> Dict[str, Provider]:
global_state_machine = ray_actor_hook("global_state_machine")
provider_list: Dict[str, Provider] = await global_state_machine.get_provider_list.remote()
return {"provider_list": provider_list}

View File

@ -28,6 +28,8 @@ async def create_workflow_template(workflow_template: WorkflowTemplate,
await global_state_machine.workflow_template_generate.remote(workflow_template)
return {"message": "创建成功"}
class Skill(BaseModel):
repo_url: str
path: str | None
@ -36,6 +38,7 @@ class Skill(BaseModel):
async def install_skill(skill: Skill,
_: TokenData = Depends(Accessor.get_current_user)):
global_state_machine = ray_actor_hook("global_state_machine")
# noinspection PyUnresolvedReferences
await viceroy.install_skill_async(url = skill.repo_url,
path = skill.path,
output = "./pretor/plugin/tool_plugin")

View File

@ -38,12 +38,6 @@ class PretorGateway:
self.app = FastAPI()
self.gateway = {}
self.app.state.postgres_database = postgres_database
self.app.state.global_state_machine = global_state_machine
self.app.state.supervisory_node = supervisory_node
self.app.state.consciousness_node = consciousness_node
self.app.state.control_node = control_node
self.app.include_router(client_router)
self.app.include_router(auth_router)
self.app.include_router(provider_router)

View File

@ -24,7 +24,7 @@ class AuthDatabase:
@database_exception
async def add_user(self, user_name: str, hashed_password: str) -> User:
user = User(user_name=user_name, hashed_password=hashed_password)
async with self.async_session_maker as session:
async with self.async_session_maker() as session:
session.add(user)
await session.commit()
await session.refresh(user)

View File

@ -37,12 +37,26 @@ class PostgresDatabase:
self.auth_database = AuthDatabase(self.async_session_maker)
self.provider_database = ProviderDatabase(self.async_session_maker)
async def init_db(self) -> None:
async with self.async_engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
# provider_database操作
async def get_providers(self):
return await self.provider_database.get_provider()
async def add_provider(self, **kwargs):
return await self.provider_database.add_provider(**kwargs)
async def init_db(self) -> None:
async with self.async_engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)
# auth_database操作
async def add_user(self, **kwargs):
return await self.auth_database.add_user(**kwargs)
async def change_password(self, **kwargs):
return await self.auth_database.change_password(**kwargs)
async def delete_user(self, **kwargs):
return await self.auth_database.delete_user(**kwargs)
async def login_user(self, **kwargs):
return await self.auth_database.login_user(**kwargs)

View File

@ -158,7 +158,7 @@ class GlobalStateMachine:
###以下为workflow_template_manager方法
def workflow_template_generator(self, workflow_template: WorkflowTemplate) -> None:
def workflow_template_generate(self, workflow_template: WorkflowTemplate) -> None:
self.global_workflow_template_manager.generate_workflow_template(workflow_template)
###以下为skill_manager方法

View File

@ -35,7 +35,6 @@ class WorkflowEngine:
"""工作流当前WorkflowEngine待执行的workflow"""
self._steps_by_id: Dict[int, WorkStep] = {step.step: step for step in self.workflow.work_link}
"""步骤表将当前workflow的步骤序号和步骤内容存放"""
self.consciousness_node = consciousness_node
"""意识节点"""
self.control_node = control_node
@ -238,6 +237,9 @@ class WorkflowRunningEngine:
}
self.workflow_queue = asyncio.Queue()
async def put_workflow(self, workflow: PretorWorkflow) -> None:
await self.workflow_queue.put(workflow)
async def runner(self, i: int) -> None:
"""
runner方法从self.workflow_queue中不断取出任务并执行

View File

@ -19,18 +19,20 @@ from typing import Optional
from fastapi import HTTPException, status, Request
from pydantic import BaseModel, ValidationError
from pretor.core.database.table.user import User
from passlib.context import CryptContext
from pwdlib import PasswordHash
from pwdlib.hashers.bcrypt import BcryptHasher
class TokenData(BaseModel):
user_id: str
username: Optional[str] = None
exp: Optional[int] = None
SECRET_KEY = os.getenv("SECRET_KEY")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 # 默认有效期 1 天
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24
password_hasher = PasswordHash.recommended()
class Accessor:
@ -54,19 +56,16 @@ class Accessor:
detail="无效的认证凭证",
)
@staticmethod
def _create_access_token(data: dict) -> str:
to_encode = data.copy()
expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": int(expire.timestamp())})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
@staticmethod
def _verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
return password_hasher.verify(plain_password, hashed_password)
@staticmethod
def get_current_user(request: Request) -> TokenData:
@ -92,8 +91,8 @@ class Accessor:
detail="用户名或密码错误",
)
token_payload = {
"user_id": str(user.id), # 确保是字符串格式
"username": user.username
"user_id": str(user.user_id),
"username": user.user_name
}
return Accessor._create_access_token(data=token_payload)
@ -103,4 +102,4 @@ class Accessor:
raise ValueError("密码不能为空")
if len(password) < 6:
raise ValueError("密码长度不能小于 6 位")
return pwd_context.hash(password)
return password_hasher.hash(password)

View File

@ -11,8 +11,8 @@ dependencies = [
"httpx>=0.28.1",
"jinja2>=3.1.6",
"loguru>=0.7.3",
"passlib[bcrypt]>=1.7.4",
"pretor-viceroy>=0.2.0",
"pwdlib[bcrypt]>=0.3.0",
"pydantic-ai>=1.73.0",
"pyfiglet>=1.0.4",
"python-ulid>=3.1.0",

32
uv.lock
View File

@ -2915,20 +2915,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/42/32/658973117bf0fd82a24abbfb94fe73a5e86216e49342985e10acce54775a/partial_json_parser-0.2.1.1.post7-py3-none-any.whl", hash = "sha256:145119e5eabcf80cbb13844a6b50a85c68bf99d376f8ed771e2a3c3b03e653ae", size = 10877, upload-time = "2025-11-17T07:27:40.457Z" },
]
[[package]]
name = "passlib"
version = "1.7.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844, upload-time = "2020-10-08T19:00:52.121Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554, upload-time = "2020-10-08T19:00:49.856Z" },
]
[package.optional-dependencies]
bcrypt = [
{ name = "bcrypt" },
]
[[package]]
name = "pathable"
version = "0.5.0"
@ -3025,8 +3011,8 @@ dependencies = [
{ name = "httpx" },
{ name = "jinja2" },
{ name = "loguru" },
{ name = "passlib", extra = ["bcrypt"] },
{ name = "pretor-viceroy" },
{ name = "pwdlib", extra = ["bcrypt"] },
{ name = "pydantic-ai", version = "1.75.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.14'" },
{ name = "pydantic-ai", version = "1.84.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.14'" },
{ name = "pyfiglet" },
@ -3055,8 +3041,8 @@ requires-dist = [
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "jinja2", specifier = ">=3.1.6" },
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.4" },
{ name = "pretor-viceroy", specifier = ">=0.2.0" },
{ name = "pwdlib", extras = ["bcrypt"], specifier = ">=0.3.0" },
{ name = "pydantic-ai", specifier = ">=1.73.0" },
{ name = "pyfiglet", specifier = ">=1.0.4" },
{ name = "python-ulid", specifier = ">=3.1.0" },
@ -3245,6 +3231,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
]
[[package]]
name = "pwdlib"
version = "0.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5f/41/a7c0d8a003c36ce3828ae3ed0391fe6a15aad65f082dbd6bec817ea95c0b/pwdlib-0.3.0.tar.gz", hash = "sha256:6ca30f9642a1467d4f5d0a4d18619de1c77f17dfccb42dd200b144127d3c83fc", size = 215810, upload-time = "2025-10-25T12:44:24.395Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/0c/9086a357d02a050fbb3270bf5043ac284dbfb845670e16c9389a41defc9e/pwdlib-0.3.0-py3-none-any.whl", hash = "sha256:f86c15c138858c09f3bba0a10984d4f9178158c55deaa72eac0210849b1a140d", size = 8633, upload-time = "2025-10-25T12:44:23.406Z" },
]
[package.optional-dependencies]
bcrypt = [
{ name = "bcrypt" },
]
[[package]]
name = "py-cpuinfo"
version = "9.0.0"