chore: 迁移服务器前同步本地修改
This commit is contained in:
@@ -13,19 +13,14 @@
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI, WebSocket, Request
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from kilostar.utils.ray_compat import _STANDALONE
|
||||
from kilostar.utils.settings import get_settings
|
||||
|
||||
if not _STANDALONE:
|
||||
from ray import serve
|
||||
|
||||
from .agent import agent_router
|
||||
from .auth import auth_router
|
||||
from .system import system_router, system_api_router
|
||||
@@ -221,11 +216,3 @@ else:
|
||||
)
|
||||
|
||||
|
||||
if not _STANDALONE:
|
||||
@serve.deployment
|
||||
@serve.ingress(app)
|
||||
class KiloStarGateway:
|
||||
gateway: Dict[str, WebSocket]
|
||||
|
||||
def __init__(self):
|
||||
self.gateway = {}
|
||||
|
||||
+42
-42
@@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
from typing import Union
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from pydantic import BaseModel, field_validator
|
||||
from kilostar.utils.access import Accessor, TokenData, RoleChecker
|
||||
@@ -52,8 +52,8 @@ async def get_system_nodes(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""返回两大系统节点(regulatory/consciousness)当前的持久化配置。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
configs = await postgres_database.get_all_system_node_configs.remote()
|
||||
postgres_database = get_actor("postgres_database")
|
||||
configs = await postgres_database.get_all_system_node_configs()
|
||||
return {"system_nodes": configs}
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ async def load_agent(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""加载/重载某个系统节点的 Agent:先持久化配置,再调用对应节点 Actor 的 ``create_agent``。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
postgres_database = get_actor("postgres_database")
|
||||
accept_lang = request.headers.get("accept-language", "")
|
||||
|
||||
if isinstance(agent_register, AgentLocalRegister):
|
||||
@@ -73,7 +73,7 @@ async def load_agent(
|
||||
|
||||
elif isinstance(agent_register, AgentRegister):
|
||||
try:
|
||||
await postgres_database.upsert_system_node_config.remote(
|
||||
await postgres_database.upsert_system_node_config(
|
||||
agent_register.individual_name,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
@@ -90,14 +90,14 @@ async def load_agent(
|
||||
# Resolve persona system_prompt from DB
|
||||
persona_prompt = None
|
||||
if agent_register.persona_id:
|
||||
tpl = await postgres_database.get_template.remote(agent_register.persona_id)
|
||||
tpl = await postgres_database.get_template(agent_register.persona_id)
|
||||
if tpl:
|
||||
persona_prompt = tpl.system_prompt
|
||||
|
||||
match scope:
|
||||
case "regulatory_node":
|
||||
node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
await node.create_agent.remote(
|
||||
node = get_actor("regulatory_node")
|
||||
await node.create_agent(
|
||||
global_state_machine,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
@@ -107,8 +107,8 @@ async def load_agent(
|
||||
persona_prompt,
|
||||
)
|
||||
case "consciousness_node":
|
||||
node = ray_actor_hook("consciousness_node").consciousness_node
|
||||
await node.create_agent.remote(
|
||||
node = get_actor("consciousness_node")
|
||||
await node.create_agent(
|
||||
global_state_machine,
|
||||
agent_register.provider_title,
|
||||
agent_register.model_id,
|
||||
@@ -181,10 +181,10 @@ async def create_worker_individual(
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""创建一个 Worker Agent,``owner_id`` 自动绑定为当前登录用户。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
postgres_database = get_actor("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)
|
||||
worker = await postgres_database.add_worker_individual(**data_dict)
|
||||
return {"message": "success", "agent_id": worker.agent_id}
|
||||
|
||||
|
||||
@@ -197,11 +197,11 @@ async def get_worker_individual_list(
|
||||
plugin_owned slot 是插件登记的"占位 agent",所有用户共享同一份配置,
|
||||
在前端展示时会用徽标标记,并允许任何登录用户装配 provider/model。
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
workers = await postgres_database.get_worker_individual_list.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
workers = await postgres_database.get_worker_individual_list(
|
||||
owner_id=token_data.user_id
|
||||
) or []
|
||||
all_workers = await postgres_database.get_all_worker_individual.remote() or []
|
||||
all_workers = await postgres_database.get_all_worker_individual() or []
|
||||
seen_ids = {w.agent_id for w in workers}
|
||||
plugin_slots = [
|
||||
w for w in all_workers
|
||||
@@ -215,8 +215,8 @@ async def get_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""按 ``agent_id`` 查询 Worker Agent;非本人的 Agent 返回 403(plugin_owned slot 例外)。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
worker = await postgres_database.get_worker_individual(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if not getattr(worker, "plugin_owned", None) and worker.owner_id != token_data.user_id:
|
||||
@@ -233,8 +233,8 @@ async def update_worker_individual(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""局部更新 Worker Agent 配置;同时把状态机里的旧实例移除等待懒加载。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
worker = await postgres_database.get_worker_individual(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
# plugin_owned slot:任何登录用户都能装配 provider/model;普通 worker 仅 owner 可改
|
||||
@@ -244,21 +244,21 @@ async def update_worker_individual(
|
||||
)
|
||||
|
||||
update_data = worker_data.model_dump(exclude_unset=True)
|
||||
updated_worker = await postgres_database.update_worker_individual.remote(
|
||||
updated_worker = await postgres_database.update_worker_individual(
|
||||
agent_id=agent_id, **update_data
|
||||
)
|
||||
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
try:
|
||||
await global_state_machine.remove_individual.remote(agent_id)
|
||||
await global_state_machine.remove_individual(agent_id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# plugin_owned 时顺带触发对应插件的 reload,让新 provider/model 立刻生效
|
||||
if getattr(worker, "plugin_owned", None):
|
||||
try:
|
||||
pm = ray_actor_hook("global_plugin_manager").global_plugin_manager
|
||||
await pm.reload.remote(worker.plugin_owned)
|
||||
pm = get_actor("global_plugin_manager")
|
||||
await pm.reload(worker.plugin_owned)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -270,8 +270,8 @@ async def reload_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""强制把 Worker 从内存池中卸载,下次调用时按最新配置重新加载。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
worker = await postgres_database.get_worker_individual(agent_id=agent_id)
|
||||
if not worker:
|
||||
raise HTTPException(status_code=404, detail="Agent not found")
|
||||
if worker.owner_id != token_data.user_id:
|
||||
@@ -279,8 +279,8 @@ async def reload_worker_individual(
|
||||
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)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
await global_state_machine.remove_individual(agent_id)
|
||||
|
||||
return {"message": "Worker will be reloaded on next use"}
|
||||
|
||||
@@ -290,15 +290,15 @@ async def delete_worker_individual(
|
||||
agent_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
"""删除 Worker Agent;非本人 Agent 返回 403。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
worker = await postgres_database.get_worker_individual.remote(agent_id=agent_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
worker = await postgres_database.get_worker_individual(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)
|
||||
await postgres_database.delete_worker_individual(agent_id=agent_id)
|
||||
return {"message": "success"}
|
||||
|
||||
|
||||
@@ -318,8 +318,8 @@ class PersonaTemplateUpdate(BaseModel):
|
||||
async def list_templates(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
templates = await postgres_database.list_templates.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
templates = await postgres_database.list_templates(
|
||||
owner_id=token_data.user_id
|
||||
)
|
||||
return {"templates": templates}
|
||||
@@ -330,8 +330,8 @@ async def create_template(
|
||||
data: PersonaTemplateCreate,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
tpl = await postgres_database.add_template.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
tpl = await postgres_database.add_template(
|
||||
name=data.name,
|
||||
system_prompt=data.system_prompt,
|
||||
owner_id=token_data.user_id,
|
||||
@@ -345,13 +345,13 @@ async def update_template(
|
||||
data: PersonaTemplateUpdate,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
tpl = await postgres_database.get_template.remote(template_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
tpl = await postgres_database.get_template(template_id)
|
||||
if not tpl:
|
||||
raise HTTPException(status_code=404, detail="Template not found")
|
||||
if tpl.owner_id != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
updated = await postgres_database.update_template.remote(
|
||||
updated = await postgres_database.update_template(
|
||||
template_id, **data.model_dump(exclude_unset=True)
|
||||
)
|
||||
return {"message": "success", "template": updated}
|
||||
@@ -362,11 +362,11 @@ async def delete_template(
|
||||
template_id: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
tpl = await postgres_database.get_template.remote(template_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
tpl = await postgres_database.get_template(template_id)
|
||||
if not tpl:
|
||||
raise HTTPException(status_code=404, detail="Template not found")
|
||||
if tpl.owner_id != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
await postgres_database.delete_template.remote(template_id)
|
||||
await postgres_database.delete_template(template_id)
|
||||
return {"message": "success"}
|
||||
|
||||
+11
-11
@@ -17,7 +17,7 @@ from fastapi import Depends
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.access import Accessor, TokenData, RoleChecker
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from kilostar.utils.error import UserNotExistError
|
||||
from kilostar.utils.rate_limit import register_limiter, login_limiter
|
||||
@@ -36,7 +36,7 @@ class UserRegister(BaseModel):
|
||||
async def create_user(user_register: UserRegister, request: Request):
|
||||
"""注册新用户:异步线程池里做 argon2 哈希,再交由 PostgresDatabase Actor 落库。"""
|
||||
register_limiter.check(request)
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
postgres_database = get_actor("postgres_database")
|
||||
try:
|
||||
hashed_password = await run_in_threadpool(
|
||||
Accessor.hash_password, user_register.password
|
||||
@@ -47,7 +47,7 @@ async def create_user(user_register: UserRegister, request: Request):
|
||||
status_code=400,
|
||||
content={"code": "password_invalid", "message": str(e)},
|
||||
)
|
||||
user = await postgres_database.add_user.remote(
|
||||
user = await postgres_database.add_user(
|
||||
user_register.user_name, hashed_password
|
||||
)
|
||||
return {"message": "success", "user_id": user.user_id}
|
||||
@@ -64,8 +64,8 @@ class UserLogin(BaseModel):
|
||||
async def login_user(user_login: UserLogin, request: Request):
|
||||
"""用户登录:查询用户后在线程池中校验口令,校验成功则签发 JWT。"""
|
||||
login_limiter.check(request)
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
user = await postgres_database.login_user.remote(user_login.user_name)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
user = await postgres_database.login_user(user_login.user_name)
|
||||
if not user:
|
||||
raise UserNotExistError()
|
||||
tokens = await run_in_threadpool(
|
||||
@@ -113,8 +113,8 @@ async def change_authority(
|
||||
"""
|
||||
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(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
user = await postgres_database.change_user_authority(
|
||||
user_id=request.user_id, new_authority=request.new_authority
|
||||
)
|
||||
return {
|
||||
@@ -133,8 +133,8 @@ async def get_user_list(
|
||||
"""
|
||||
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()
|
||||
postgres_database = get_actor("postgres_database")
|
||||
users = await postgres_database.get_all_users()
|
||||
return {
|
||||
"users": [
|
||||
{"user_id": u.user_id, "user_name": u.user_name, "role": u.user_authority}
|
||||
@@ -153,6 +153,6 @@ async def delete_user(
|
||||
"""
|
||||
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)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
await postgres_database.delete_user_by_id(user_id=user_id)
|
||||
return {"message": "success"}
|
||||
|
||||
+27
-40
@@ -17,7 +17,7 @@ import asyncio
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.core.individual.regulatory_node.template import (
|
||||
MessageRequest,
|
||||
@@ -56,8 +56,8 @@ def _build_message_history(rows) -> list:
|
||||
|
||||
|
||||
async def _load_message_history(chat_id: str) -> list:
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
rows = await postgres_database.list_chat_messages.remote(chat_id=chat_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
rows = await postgres_database.list_chat_messages(chat_id=chat_id)
|
||||
return _build_message_history(rows or [])
|
||||
|
||||
|
||||
@@ -76,14 +76,14 @@ async def _ask_regulatory(
|
||||
*, user_id: str, chat_id: str, message: str, message_history: list | None = None
|
||||
) -> str | None:
|
||||
"""统一封装 chat 入口对 RegulatoryNode 的调用。"""
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
regulatory_node = get_actor("regulatory_node")
|
||||
payload = MessageRequest(
|
||||
platform="client",
|
||||
user_name=user_id,
|
||||
platform_id=chat_id,
|
||||
message=message,
|
||||
)
|
||||
resp: MessageResponse | None = await regulatory_node.working.remote(
|
||||
resp: MessageResponse | None = await regulatory_node.working(
|
||||
payload, message_history
|
||||
)
|
||||
return _extract_reply(resp)
|
||||
@@ -103,13 +103,13 @@ async def create_chat_session(
|
||||
request: CreateChatRequest,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
chat = await postgres_database.create_chat_session.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
chat = await postgres_database.create_chat_session(
|
||||
user_id=token_data.user_id, title=request.title
|
||||
)
|
||||
|
||||
# 存入用户消息
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat.chat_id, message=request.initial_message, message_owner="user"
|
||||
)
|
||||
|
||||
@@ -122,7 +122,7 @@ async def create_chat_session(
|
||||
|
||||
# 存入回复消息
|
||||
if response_msg:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat.chat_id, message=response_msg, message_owner="regulatory_node"
|
||||
)
|
||||
|
||||
@@ -133,8 +133,8 @@ async def create_chat_session(
|
||||
async def list_chat_sessions(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
sessions = await postgres_database.list_chat_sessions.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
sessions = await postgres_database.list_chat_sessions(
|
||||
user_id=token_data.user_id
|
||||
)
|
||||
return {"sessions": sessions}
|
||||
@@ -144,8 +144,8 @@ async def list_chat_sessions(
|
||||
async def get_chat_history(
|
||||
chat_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
messages = await postgres_database.list_chat_messages.remote(chat_id=chat_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
messages = await postgres_database.list_chat_messages(chat_id=chat_id)
|
||||
return {"messages": messages}
|
||||
|
||||
|
||||
@@ -155,10 +155,10 @@ async def send_chat_message(
|
||||
request: SendMessageRequest,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
postgres_database = get_actor("postgres_database")
|
||||
# 先取历史(不含当前输入),再写入用户消息,避免历史里出现重复
|
||||
message_history = await _load_message_history(chat_id)
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat_id, message=request.message, message_owner="user"
|
||||
)
|
||||
|
||||
@@ -172,7 +172,7 @@ async def send_chat_message(
|
||||
|
||||
# 存回复
|
||||
if response_msg:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat_id, message=response_msg, message_owner="regulatory_node"
|
||||
)
|
||||
|
||||
@@ -184,13 +184,13 @@ async def delete_chat_session(
|
||||
chat_id: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
session = await postgres_database.get_chat_session.remote(chat_id=chat_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
session = await postgres_database.get_chat_session(chat_id=chat_id)
|
||||
if not session:
|
||||
raise HTTPException(status_code=404, detail="Chat session not found")
|
||||
if session.user_id != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
await postgres_database.delete_chat_session.remote(chat_id=chat_id)
|
||||
await postgres_database.delete_chat_session(chat_id=chat_id)
|
||||
return {"message": "success"}
|
||||
|
||||
|
||||
@@ -201,14 +201,12 @@ async def stream_chat_message(
|
||||
request: Request,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""SSE 流式聊天端点:standalone 模式下逐 token 流式输出;distributed 模式 fallback 到整段回复。"""
|
||||
from kilostar.utils.ray_compat import _STANDALONE
|
||||
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
"""SSE 流式聊天端点:逐 token 流式输出。"""
|
||||
postgres_database = get_actor("postgres_database")
|
||||
|
||||
message_history = await _load_message_history(chat_id)
|
||||
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat_id, message=request_body.message, message_owner="user"
|
||||
)
|
||||
|
||||
@@ -219,23 +217,12 @@ async def stream_chat_message(
|
||||
message=request_body.message,
|
||||
)
|
||||
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
|
||||
if not _STANDALONE:
|
||||
async def fallback_generator():
|
||||
resp = await regulatory_node.working.remote(payload, message_history)
|
||||
full_response = resp.reply_message if resp else ""
|
||||
if full_response:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
chat_id=chat_id, message=full_response, message_owner="regulatory_node"
|
||||
)
|
||||
yield f"data: {json.dumps({'token': full_response})}\n\n"
|
||||
yield f"data: {json.dumps({'done': True, 'full_message': full_response})}\n\n"
|
||||
|
||||
return StreamingResponse(fallback_generator(), media_type="text/event-stream")
|
||||
regulatory_node = get_actor("regulatory_node")
|
||||
|
||||
token_queue = asyncio.Queue()
|
||||
stream_task = regulatory_node.stream_working.remote(payload, token_queue, message_history)
|
||||
stream_task = asyncio.create_task(
|
||||
regulatory_node.stream_working(payload, token_queue, message_history)
|
||||
)
|
||||
|
||||
async def event_generator():
|
||||
full_response = ""
|
||||
@@ -260,7 +247,7 @@ async def stream_chat_message(
|
||||
yield f"data: {json.dumps({'token': full_response})}\n\n"
|
||||
|
||||
if full_response:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
await postgres_database.add_chat_message(
|
||||
chat_id=chat_id,
|
||||
message=full_response,
|
||||
message_owner="regulatory_node",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from pydantic import BaseModel
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from kilostar.core.individual.regulatory_node.template import (
|
||||
MessageRequest,
|
||||
MessageResponse,
|
||||
@@ -42,14 +42,14 @@ async def create_message(
|
||||
"""把前端消息转交给 RegulatoryNode 处理,并把回复透传给前端。"""
|
||||
logger.info("收到消息,来源:客户端")
|
||||
logger.debug(f"消息内容:{message.message}")
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
regulatory_node = get_actor("regulatory_node")
|
||||
msg_request = MessageRequest(
|
||||
platform="client",
|
||||
user_name=token_data.username,
|
||||
platform_id=token_data.user_id,
|
||||
message=message.message,
|
||||
)
|
||||
result = await regulatory_node.working.remote(msg_request)
|
||||
result = await regulatory_node.working(msg_request)
|
||||
if isinstance(result, MessageResponse):
|
||||
return {"message": result.reply_message}
|
||||
if isinstance(result, str):
|
||||
|
||||
@@ -39,7 +39,7 @@ from kilostar.core.individual.regulatory_node.template import (
|
||||
MessageResponse,
|
||||
)
|
||||
from kilostar.utils.logger import get_logger
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
|
||||
logger = get_logger("onebot")
|
||||
|
||||
@@ -129,8 +129,8 @@ async def _dispatch_event(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
)
|
||||
|
||||
try:
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
result = await regulatory_node.working.remote(msg_request)
|
||||
regulatory_node = get_actor("regulatory_node")
|
||||
result = await regulatory_node.working(msg_request)
|
||||
except Exception as e:
|
||||
logger.exception(f"[OneBot] RegulatoryNode 调用失败: {e}")
|
||||
return None
|
||||
|
||||
+21
-23
@@ -8,7 +8,7 @@ from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from kilostar.utils.settings import get_plugin_dir
|
||||
|
||||
plugin_router = APIRouter(prefix="/api/v1/plugin", tags=["plugin"])
|
||||
@@ -25,15 +25,16 @@ async def submit_task(
|
||||
req: SubmitRequest,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
pm = ray_actor_hook("global_plugin_manager").global_plugin_manager
|
||||
plugins = await pm.list_plugins.remote()
|
||||
if req.org_name not in plugins:
|
||||
pm = get_actor("global_plugin_manager")
|
||||
plugins = await pm.list_plugins()
|
||||
# list_plugins 返回 [{"name": ..., ...}],按 name 判断插件是否已加载
|
||||
if req.org_name not in {p["name"] for p in plugins}:
|
||||
raise HTTPException(404, f"Plugin '{req.org_name}' not found")
|
||||
|
||||
org = ray_actor_hook(f"org_{req.org_name}").get(f"org_{req.org_name}")
|
||||
org = get_actor(f"org_{req.org_name}")
|
||||
ctx = req.context or {}
|
||||
ctx["user"] = token_data.username
|
||||
task_id = await org.submit.remote(req.task_description, ctx)
|
||||
task_id = await org.submit(req.task_description, ctx)
|
||||
return {"task_id": task_id}
|
||||
|
||||
|
||||
@@ -42,8 +43,8 @@ async def get_task_status(
|
||||
task_id: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
db = ray_actor_hook("postgres_database").postgres_database
|
||||
task = await db.get_org_task.remote(task_id)
|
||||
db = get_actor("postgres_database")
|
||||
task = await db.get_org_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(404, "Task not found")
|
||||
return task
|
||||
@@ -54,8 +55,8 @@ async def get_task_events(
|
||||
task_id: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
db = ray_actor_hook("postgres_database").postgres_database
|
||||
events = await db.query_org_events.remote(task_id)
|
||||
db = get_actor("postgres_database")
|
||||
events = await db.query_org_events(task_id)
|
||||
return {"events": events}
|
||||
|
||||
|
||||
@@ -64,19 +65,16 @@ async def stream_task(
|
||||
task_id: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
import asyncio
|
||||
|
||||
org_name = None
|
||||
db = ray_actor_hook("postgres_database").postgres_database
|
||||
task = await db.get_org_task.remote(task_id)
|
||||
db = get_actor("postgres_database")
|
||||
task = await db.get_org_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(404, "Task not found")
|
||||
org_name = task["org_name"]
|
||||
|
||||
org = ray_actor_hook(f"org_{org_name}").get(f"org_{org_name}")
|
||||
org = get_actor(f"org_{org_name}")
|
||||
|
||||
async def _generate():
|
||||
async for event in await org.stream.remote(task_id):
|
||||
async for event in await org.stream(task_id):
|
||||
yield f"data: {event}\n\n"
|
||||
|
||||
return StreamingResponse(_generate(), media_type="text/event-stream")
|
||||
@@ -86,8 +84,8 @@ async def stream_task(
|
||||
async def list_plugins(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
pm = ray_actor_hook("global_plugin_manager").global_plugin_manager
|
||||
plugins = await pm.list_plugins.remote()
|
||||
pm = get_actor("global_plugin_manager")
|
||||
plugins = await pm.list_plugins()
|
||||
return {"plugins": plugins}
|
||||
|
||||
|
||||
@@ -96,8 +94,8 @@ async def install_plugin(
|
||||
name: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
pm = ray_actor_hook("global_plugin_manager").global_plugin_manager
|
||||
await pm.install.remote(name)
|
||||
pm = get_actor("global_plugin_manager")
|
||||
await pm.install(name)
|
||||
return {"status": "ok", "name": name}
|
||||
|
||||
|
||||
@@ -106,8 +104,8 @@ async def reload_plugin(
|
||||
name: str,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
pm = ray_actor_hook("global_plugin_manager").global_plugin_manager
|
||||
await pm.reload.remote(name)
|
||||
pm = get_actor("global_plugin_manager")
|
||||
await pm.reload(name)
|
||||
return {"status": "ok", "name": name}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ from typing import Any, Dict, Literal, Optional
|
||||
from kilostar.utils.access import TokenData, Accessor, RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from kilostar.core.global_state_machine.model_provider.base_provider import Provider
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
|
||||
provider_router = APIRouter(prefix="/api/v1/provider", tags=["provider"])
|
||||
|
||||
@@ -39,8 +39,8 @@ async def create_provider(
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
) -> None:
|
||||
"""注册一个 Provider;owner 为当前登录用户的 ``user_id``。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.add_provider_wrap.remote(
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
await global_state_machine.add_provider_wrap(
|
||||
provider_type=provider_register.provider_type,
|
||||
provider_title=provider_register.provider_title,
|
||||
provider_url=provider_register.provider_url,
|
||||
@@ -61,10 +61,10 @@ async def get_provider_list(
|
||||
_: TokenData = Depends(Accessor.get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
"""返回当前所有已注册的 Provider,前端用以展示模型清单。apikey 脱敏。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
provider_list: Dict[
|
||||
str, Provider
|
||||
] = await global_state_machine.get_provider_list.remote()
|
||||
] = await global_state_machine.get_provider_list()
|
||||
masked = {}
|
||||
for title, p in provider_list.items():
|
||||
d = p.model_dump() if hasattr(p, "model_dump") else dict(p)
|
||||
@@ -138,6 +138,6 @@ async def delete_provider(
|
||||
),
|
||||
) -> dict:
|
||||
"""删除指定 ``provider_title`` 的 Provider;仅超管可调用。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.delete_provider.remote(provider_title=provider_title)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
await global_state_machine.delete_provider(provider_title=provider_title)
|
||||
return {"message": "success"}
|
||||
|
||||
+42
-42
@@ -15,7 +15,7 @@
|
||||
from typing import Any, Dict, List, Optional
|
||||
from pydantic import BaseModel
|
||||
import viceroy
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from kilostar.utils.access import TokenData, RoleChecker, Accessor
|
||||
@@ -50,7 +50,7 @@ async def install_skill(
|
||||
skill: Skill, _: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))
|
||||
):
|
||||
"""通过 viceroy 把 skill 仓库克隆到 ``data/plugin/skill``,并在状态机中登记。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
import os
|
||||
from kilostar.utils.settings import get_plugin_dir
|
||||
|
||||
@@ -63,7 +63,7 @@ async def install_skill(
|
||||
skill_name = skill.path.split("/")[-1]
|
||||
else:
|
||||
skill_name = skill.repo_url.split("/")[-1]
|
||||
await global_state_machine.add_skill.remote(skill_name)
|
||||
await global_state_machine.add_skill(skill_name)
|
||||
return {"message": "创建成功"}
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ async def get_skills(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""返回当前状态机中已登记的所有 skill 名称列表。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
skills = await global_state_machine.get_skill_list.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
skills = await global_state_machine.get_skill_list()
|
||||
return {"skills": skills}
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ async def delete_skill(
|
||||
),
|
||||
):
|
||||
"""从状态机中移除 skill 注册项;不会删除磁盘上的代码文件。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.remove_skill.remote(skill_name)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
await global_state_machine.remove_skill(skill_name)
|
||||
return {"message": "success"}
|
||||
|
||||
|
||||
@@ -98,12 +98,12 @@ async def add_mcp_server(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""注册一个 MCP 服务器到全局状态机。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
import uuid
|
||||
|
||||
server_id = str(uuid.uuid4())[:8]
|
||||
cfg_dict = config.model_dump(exclude_none=True)
|
||||
await global_state_machine.add_mcp_server.remote(server_id, cfg_dict)
|
||||
await global_state_machine.add_mcp_server(server_id, cfg_dict)
|
||||
return {"server_id": server_id, "message": "MCP server registered"}
|
||||
|
||||
|
||||
@@ -112,8 +112,8 @@ async def list_mcp_servers(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""返回已注册的全部 MCP 服务器配置;env 中的敏感字段脱敏。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
servers = await global_state_machine.list_mcp_servers.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
servers = await global_state_machine.list_mcp_servers()
|
||||
for s in servers:
|
||||
if "env" in s and isinstance(s["env"], dict):
|
||||
s["env"] = _mask_config(s["env"])
|
||||
@@ -126,8 +126,8 @@ async def delete_mcp_server(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""从状态机中移除一个 MCP 服务器配置。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
ok = await global_state_machine.delete_mcp_server.remote(server_id)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
ok = await global_state_machine.delete_mcp_server(server_id)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail="MCP server not found")
|
||||
return {"message": "success"}
|
||||
@@ -152,8 +152,8 @@ async def download_artifact(
|
||||
if not artifact_id.isalnum() or len(artifact_id) > 32:
|
||||
raise HTTPException(status_code=400, detail="invalid artifact id")
|
||||
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
@@ -187,8 +187,8 @@ async def list_toolset_packages(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""列出所有磁盘上的工具包(``data/toolset/<name>/`` 单元)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
packages = await global_state_machine.list_toolset_packages.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
packages = await global_state_machine.list_toolset_packages()
|
||||
return {"packages": packages}
|
||||
|
||||
|
||||
@@ -198,8 +198,8 @@ async def get_toolset_package_readme(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""返回指定工具包的 README.md 内容(markdown 文本)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
content = await global_state_machine.get_toolset_package_readme.remote(name)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
content = await global_state_machine.get_toolset_package_readme(name)
|
||||
if content is None:
|
||||
raise HTTPException(status_code=404, detail="README not found")
|
||||
return {"name": name, "content": content}
|
||||
@@ -216,9 +216,9 @@ async def get_tools(
|
||||
其中 ``mcp_servers`` 会现场尝试连接每个已注册的 MCP 服务器并列出它们暴露的
|
||||
工具名,便于前端展示;任意一台 MCP server 不可达不影响其他工具的返回。
|
||||
"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
tool_mapper = await global_state_machine.get_tool_mapper.remote()
|
||||
categories = await global_state_machine.get_tool_categories.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
tool_mapper = await global_state_machine.get_tool_mapper()
|
||||
categories = await global_state_machine.get_tool_categories()
|
||||
|
||||
all_tool_names = set()
|
||||
for scope_tools in tool_mapper.values():
|
||||
@@ -266,8 +266,8 @@ async def list_tool_configs(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""列出所有工具运行期配置;敏感字段会被脱敏。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
raw = await global_state_machine.list_tool_configs.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
raw = await global_state_machine.list_tool_configs()
|
||||
return {
|
||||
"configs": {name: _mask_config(cfg) for name, cfg in raw.items()},
|
||||
}
|
||||
@@ -279,8 +279,8 @@ async def get_tool_config(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""按工具名取出脱敏后的配置。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
raw = await global_state_machine.get_tool_config.remote(tool_name)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
raw = await global_state_machine.get_tool_config(tool_name)
|
||||
return {"tool_name": tool_name, "config": _mask_config(raw)}
|
||||
|
||||
|
||||
@@ -291,8 +291,8 @@ async def set_tool_config(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""写入/覆盖某工具的运行期配置(如 ``tavily_search`` 的 ``api_key``)。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await global_state_machine.set_tool_config.remote(tool_name, body.config)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
await global_state_machine.set_tool_config(tool_name, body.config)
|
||||
return {"message": "success"}
|
||||
|
||||
|
||||
@@ -302,8 +302,8 @@ async def delete_tool_config(
|
||||
_: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.SUPER_ADMINISTRATOR)),
|
||||
):
|
||||
"""删除某工具的运行期配置。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
ok = await global_state_machine.delete_tool_config.remote(tool_name)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
ok = await global_state_machine.delete_tool_config(tool_name)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail="Tool config not found")
|
||||
return {"message": "success"}
|
||||
@@ -343,12 +343,12 @@ async def create_custom_toolset(
|
||||
body: CustomToolsetCreate,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
import uuid
|
||||
|
||||
toolset_id = str(uuid.uuid4())[:8]
|
||||
try:
|
||||
saved = await global_state_machine.add_custom_toolset.remote(
|
||||
saved = await global_state_machine.add_custom_toolset(
|
||||
toolset_id=toolset_id,
|
||||
name=body.name,
|
||||
tools=body.tools,
|
||||
@@ -368,8 +368,8 @@ async def list_custom_toolsets(
|
||||
"""列出工具组:支持按 category 过滤。USER 只能看到自己的+系统的;ADMIN 看全部。"""
|
||||
from kilostar.utils.access import get_authority
|
||||
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
toolsets = await global_state_machine.list_custom_toolsets.remote()
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
toolsets = await global_state_machine.list_custom_toolsets()
|
||||
authority = await get_authority(token_data.user_id)
|
||||
if authority < UserAuthority.ADMINISTRATOR:
|
||||
toolsets = [
|
||||
@@ -386,8 +386,8 @@ async def get_custom_toolset(
|
||||
toolset_id: str,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
ts = await global_state_machine.get_custom_toolset.remote(toolset_id)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
ts = await global_state_machine.get_custom_toolset(toolset_id)
|
||||
if not ts:
|
||||
raise HTTPException(status_code=404, detail="Custom toolset not found")
|
||||
await _assert_toolset_owner_or_admin(ts, token_data)
|
||||
@@ -400,8 +400,8 @@ async def update_custom_toolset(
|
||||
body: CustomToolsetUpdate,
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
existing = await global_state_machine.get_custom_toolset.remote(toolset_id)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
existing = await global_state_machine.get_custom_toolset(toolset_id)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Custom toolset not found")
|
||||
if existing.get("is_system"):
|
||||
@@ -411,7 +411,7 @@ async def update_custom_toolset(
|
||||
tools = body.tools if body.tools is not None else existing["tools"]
|
||||
description = body.description if body.description is not None else existing.get("description")
|
||||
try:
|
||||
saved = await global_state_machine.add_custom_toolset.remote(
|
||||
saved = await global_state_machine.add_custom_toolset(
|
||||
toolset_id=toolset_id,
|
||||
name=name,
|
||||
tools=tools,
|
||||
@@ -429,14 +429,14 @@ async def delete_custom_toolset(
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER)),
|
||||
):
|
||||
"""删除工具组:系统预置不可删;USER 只能删自己的;ADMIN 及以上可删任意用户的。"""
|
||||
global_state_machine = ray_actor_hook("global_state_machine").global_state_machine
|
||||
existing = await global_state_machine.get_custom_toolset.remote(toolset_id)
|
||||
global_state_machine = get_actor("global_state_machine")
|
||||
existing = await global_state_machine.get_custom_toolset(toolset_id)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Custom toolset not found")
|
||||
if existing.get("is_system"):
|
||||
raise HTTPException(status_code=403, detail="系统预置工具集不可删除")
|
||||
await _assert_toolset_owner_or_admin(existing, token_data)
|
||||
ok = await global_state_machine.delete_custom_toolset.remote(toolset_id)
|
||||
ok = await global_state_machine.delete_custom_toolset(toolset_id)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail="Custom toolset not found")
|
||||
return {"message": "success"}
|
||||
|
||||
@@ -24,7 +24,7 @@ from __future__ import annotations
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
from kilostar.utils.access import Accessor, TokenData, RoleChecker
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from kilostar.utils.config_loader import (
|
||||
@@ -49,15 +49,15 @@ async def readiness():
|
||||
checks = {"postgres": False, "global_state_machine": False}
|
||||
|
||||
try:
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
await postgres_database.ping.remote()
|
||||
postgres_database = get_actor("postgres_database")
|
||||
await postgres_database.ping()
|
||||
checks["postgres"] = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
gsm = ray_actor_hook("global_state_machine").global_state_machine
|
||||
await gsm.get_skill_list.remote()
|
||||
gsm = get_actor("global_state_machine")
|
||||
await gsm.get_skill_list()
|
||||
checks["global_state_machine"] = True
|
||||
except Exception:
|
||||
pass
|
||||
@@ -95,8 +95,8 @@ async def query_system_logs(
|
||||
offset: int = 0,
|
||||
_: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
pg = ray_actor_hook("postgres_database").postgres_database
|
||||
logs = await pg.query_event_logs.remote(
|
||||
pg = get_actor("postgres_database")
|
||||
logs = await pg.query_event_logs(
|
||||
trace_id=trace_id,
|
||||
event_type=event_type,
|
||||
level=level,
|
||||
|
||||
@@ -17,7 +17,7 @@ from typing import Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor
|
||||
|
||||
task_router = APIRouter(prefix="/api/v1/task", tags=["task"])
|
||||
|
||||
@@ -30,8 +30,8 @@ async def list_tasks(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""列出当前用户的所有短任务,按时间倒序。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
tasks = await postgres_database.list_tasks_by_user.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
tasks = await postgres_database.list_tasks_by_user(
|
||||
user_id=token_data.user_id,
|
||||
status=status,
|
||||
limit=limit,
|
||||
@@ -46,8 +46,8 @@ async def get_task(
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""按 task_id 读取一条 task 详情。仅 owner 可访问。"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
task = await postgres_database.get_task.remote(task_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
task = await postgres_database.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="task not found")
|
||||
if task.get("user_id") != token_data.user_id:
|
||||
|
||||
+33
-38
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from kilostar.utils.ray_hook import ray_actor_hook
|
||||
from kilostar.utils.actor import get_actor, spawn_background
|
||||
from fastapi import APIRouter, Request, HTTPException, Depends
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
@@ -34,22 +34,23 @@ async def create_workflow(
|
||||
request: CreateWorkflowRequest,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
postgres_database = get_actor("postgres_database")
|
||||
trace_id = str(ULID())
|
||||
await postgres_database.create_workflow.remote(
|
||||
await postgres_database.create_workflow(
|
||||
trace_id=trace_id,
|
||||
user_id=token_data.user_id,
|
||||
title=request.title,
|
||||
command=request.command,
|
||||
)
|
||||
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
await global_workflow_manager.create_trace.remote(trace_id)
|
||||
global_workflow_manager = get_actor("global_workflow_manager")
|
||||
await global_workflow_manager.create_trace(trace_id)
|
||||
|
||||
consciousness_node = ray_actor_hook("consciousness_node").consciousness_node
|
||||
consciousness_node.start_workflow_design.remote(trace_id, request.command)
|
||||
consciousness_node = get_actor("consciousness_node")
|
||||
# fire-and-forget:设计过程可能跑很久,不能阻塞 HTTP 响应
|
||||
spawn_background(
|
||||
consciousness_node.start_workflow_design(trace_id, request.command)
|
||||
)
|
||||
|
||||
return {"trace_id": trace_id, "status": "creating"}
|
||||
|
||||
@@ -57,8 +58,8 @@ async def create_workflow(
|
||||
@workflow_router.get("/list")
|
||||
async def get_workflow_list(
|
||||
token_data: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
workflows = await postgres_database.list_workflows.remote(
|
||||
postgres_database = get_actor("postgres_database")
|
||||
workflows = await postgres_database.list_workflows(
|
||||
user_id=token_data.user_id
|
||||
)
|
||||
return {"workflows": workflows}
|
||||
@@ -75,23 +76,21 @@ async def get_workflow_sse(
|
||||
鉴权走标准 ``Authorization: Bearer`` 头(前端用 fetch-based SSE,
|
||||
token 不进 URL)。校验该 trace_id 属于当前用户。
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
global_workflow_manager = get_actor("global_workflow_manager")
|
||||
|
||||
async def event_generator():
|
||||
try:
|
||||
while True:
|
||||
if await request.is_disconnected():
|
||||
break
|
||||
message = await global_workflow_manager.get_pending.remote(trace_id)
|
||||
message = await global_workflow_manager.get_pending(trace_id)
|
||||
if message:
|
||||
yield f"data: {message}\n\n"
|
||||
else:
|
||||
@@ -108,8 +107,8 @@ async def post_workflow_reply(
|
||||
request: Request,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
@@ -117,10 +116,8 @@ async def post_workflow_reply(
|
||||
|
||||
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)
|
||||
global_workflow_manager = get_actor("global_workflow_manager")
|
||||
await global_workflow_manager.put_received(trace_id, reply_msg)
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@@ -128,14 +125,14 @@ async def post_workflow_reply(
|
||||
async def get_workflow_detail(
|
||||
trace_id: str, token_data: TokenData = Depends(Accessor.get_current_user)
|
||||
):
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
|
||||
context = await postgres_database.get_workflow_context.remote(trace_id)
|
||||
context = await postgres_database.get_workflow_context(trace_id)
|
||||
|
||||
work_link = (
|
||||
context.work_link if context and hasattr(context, "work_link") else []
|
||||
@@ -209,32 +206,30 @@ async def resume_workflow(
|
||||
):
|
||||
"""从 ``workflow_graph_state`` 持久化恢复一个被中断/挂起的工作流。
|
||||
|
||||
新 fire 一个 ray task,task 入口的 ``hydrate`` 检查会自动走 resume 路径
|
||||
新 fire 一个后台任务,入口的 ``hydrate`` 检查会自动走 resume 路径
|
||||
把剩余节点跑完。
|
||||
"""
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
|
||||
record = await postgres_database.get_workflow_graph_state.remote(trace_id)
|
||||
record = await postgres_database.get_workflow_graph_state(trace_id)
|
||||
if record is None:
|
||||
raise HTTPException(
|
||||
status_code=409, detail="该工作流没有可恢复的图持久化记录"
|
||||
)
|
||||
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
await global_workflow_manager.create_trace.remote(trace_id)
|
||||
global_workflow_manager = get_actor("global_workflow_manager")
|
||||
await global_workflow_manager.create_trace(trace_id)
|
||||
|
||||
from kilostar.core.work.workflow.workflow_engine import run_workflow_task
|
||||
|
||||
# resume_only=True:task 入口 hydrate 失败会 fail-fast,绝不 fall through
|
||||
# 到"全新模式空跑"。workflow_data 在 resume 路径上不会被使用,传空 dict 占位。
|
||||
run_workflow_task.remote({}, trace_id, resume_only=True)
|
||||
spawn_background(run_workflow_task({}, trace_id, resume_only=True))
|
||||
return {"trace_id": trace_id, "status": "resuming"}
|
||||
|
||||
|
||||
@@ -251,15 +246,15 @@ async def get_workflow_graph_mermaid(
|
||||
"""
|
||||
from kilostar.core.work.workflow.workflow_engine import workflow_graph
|
||||
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
wf = await postgres_database.get_workflow.remote(trace_id)
|
||||
postgres_database = get_actor("postgres_database")
|
||||
wf = await postgres_database.get_workflow(trace_id)
|
||||
if not wf:
|
||||
raise HTTPException(status_code=404, detail="Workflow not found")
|
||||
if getattr(wf, "user_id", None) != token_data.user_id:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
|
||||
visited: list[str] = []
|
||||
record = await postgres_database.get_workflow_graph_state.remote(trace_id)
|
||||
record = await postgres_database.get_workflow_graph_state(trace_id)
|
||||
if record is not None:
|
||||
history = getattr(record, "history", None) or []
|
||||
# history 里每条 NodeSnapshot.id 形如 "ClassName:hash",截前缀作为 NodeIdent
|
||||
|
||||
Reference in New Issue
Block a user