mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
fix: preserve persona default tool selection
Fix https://github.com/AstrBotDevs/AstrBot/issues/8828
This commit is contained in:
@@ -27,6 +27,7 @@ from astrbot.core.db.po import (
|
||||
UmoAlias,
|
||||
WebChatThread,
|
||||
)
|
||||
from astrbot.core.sentinels import NOT_GIVEN
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -444,11 +445,23 @@ class BaseDatabase(abc.ABC):
|
||||
persona_id: str,
|
||||
system_prompt: str | None = None,
|
||||
begin_dialogs: list[str] | None = None,
|
||||
tools: list[str] | None = None,
|
||||
skills: list[str] | None = None,
|
||||
custom_error_message: str | None = None,
|
||||
tools: list[str] | None | object = NOT_GIVEN,
|
||||
skills: list[str] | None | object = NOT_GIVEN,
|
||||
custom_error_message: str | None | object = NOT_GIVEN,
|
||||
) -> Persona | None:
|
||||
"""Update a persona's system prompt or begin dialogs."""
|
||||
"""Update a persona record.
|
||||
|
||||
Args:
|
||||
persona_id: Persona ID to update.
|
||||
system_prompt: Optional replacement system prompt.
|
||||
begin_dialogs: Optional replacement begin dialogs.
|
||||
tools: Tool names, None for all tools, or NOT_GIVEN to leave unchanged.
|
||||
skills: Skill names, None for all skills, or NOT_GIVEN to leave unchanged.
|
||||
custom_error_message: Custom fallback message, None to clear, or NOT_GIVEN to leave unchanged.
|
||||
|
||||
Returns:
|
||||
Updated persona, or None when no fields were updated.
|
||||
"""
|
||||
...
|
||||
|
||||
@abc.abstractmethod
|
||||
|
||||
@@ -45,7 +45,15 @@ async def _json_or_empty(request: Request) -> dict[str, Any]:
|
||||
|
||||
|
||||
def _model_dict(payload) -> dict[str, Any]:
|
||||
return payload.model_dump(exclude_none=True)
|
||||
"""Serialize a request model while preserving explicit null updates.
|
||||
|
||||
Args:
|
||||
payload: Pydantic request model.
|
||||
|
||||
Returns:
|
||||
Request data without fields omitted by the caller.
|
||||
"""
|
||||
return payload.model_dump(exclude_unset=True)
|
||||
|
||||
|
||||
def _raise_persona_error(exc: PersonaServiceError | ValueError) -> None:
|
||||
|
||||
@@ -40,8 +40,12 @@ class PersonaService:
|
||||
|
||||
async def create_persona(self, data: object) -> dict:
|
||||
payload = self._payload(data)
|
||||
persona_id = str(payload.get("persona_id", "")).strip()
|
||||
system_prompt = str(payload.get("system_prompt", "")).strip()
|
||||
raw_persona_id = payload.get("persona_id")
|
||||
raw_system_prompt = payload.get("system_prompt")
|
||||
persona_id = str(raw_persona_id).strip() if raw_persona_id is not None else ""
|
||||
system_prompt = (
|
||||
str(raw_system_prompt).strip() if raw_system_prompt is not None else ""
|
||||
)
|
||||
begin_dialogs = payload.get("begin_dialogs", [])
|
||||
tools = payload.get("tools")
|
||||
skills = payload.get("skills")
|
||||
|
||||
@@ -473,7 +473,7 @@ class FakePersonaManager:
|
||||
async def update_persona(self, persona_id: str, **kwargs) -> None:
|
||||
persona = self.personas[persona_id]
|
||||
for key, value in kwargs.items():
|
||||
if value is not None:
|
||||
if key in ("tools", "skills", "custom_error_message") or value is not None:
|
||||
setattr(persona, key, value)
|
||||
|
||||
async def delete_persona(self, persona_id: str) -> None:
|
||||
@@ -2476,6 +2476,29 @@ async def test_v1_safe_persona_routes_accept_slash_ids(
|
||||
assert persona_id not in persona_mgr.personas
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_v1_persona_by_id_update_preserves_explicit_null_tools_and_skills(
|
||||
asgi_client: httpx.AsyncClient,
|
||||
fake_core_lifecycle,
|
||||
):
|
||||
persona_id = "persona/foo"
|
||||
headers = _jwt_headers()
|
||||
persona = fake_core_lifecycle.persona_mgr.personas[persona_id]
|
||||
persona.tools = ["tool-a"]
|
||||
persona.skills = ["skill-a"]
|
||||
|
||||
response = await asgi_client.put(
|
||||
"/api/v1/personas/by-id",
|
||||
json={"persona_id": persona_id, "tools": None, "skills": None},
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["data"] == {"message": "人格更新成功"}
|
||||
assert persona.tools is None
|
||||
assert persona.skills is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_v1_im_routes_use_im_scope_and_running_platform(
|
||||
asgi_client: httpx.AsyncClient,
|
||||
|
||||
Reference in New Issue
Block a user