Files
KiloStar/kilostar/api/resource.py
T

92 lines
3.4 KiB
Python

# 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):
"""``POST /skill`` 入参:技能仓库地址及可选子目录路径。"""
repo_url: str
path: str | None
@resource_router.post("/skill")
async def install_skill(
skill: Skill, _: TokenData = Depends(RoleChecker(allowed_roles=UserAuthority.USER))
):
"""通过 viceroy 把 skill 仓库克隆到 ``plugin/skill``,并在状态机中登记。"""
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)),
):
"""返回当前状态机中已登记的所有 skill 名称列表。"""
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)
),
):
"""从状态机中移除 skill 注册项;不会删除磁盘上的代码文件。"""
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)),
):
"""汇总各作用域 tool_mapper,返回去重后的工具名称列表。"""
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)}