Skip to content

Commit 87d2750

Browse files
authored
Merge pull request #5440 from Raven95676/fix/persona-loss
fix(persona): preserve conversation persona_id and unify session/conversation resolution
2 parents 6d76d55 + d80598b commit 87d2750

File tree

5 files changed

+118
-55
lines changed

5 files changed

+118
-55
lines changed

astrbot/builtin_stars/builtin_commands/commands/conversation.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,33 @@ async def convs(self, message: AstrMessageEvent, page: int = 1) -> None:
206206
_titles[conv.cid] = title
207207

208208
"""遍历分页后的对话生成列表显示"""
209+
provider_settings = cfg.get("provider_settings", {})
210+
platform_name = message.get_platform_name()
209211
for conv in conversations_paged:
210-
persona_id = conv.persona_id
211-
if not persona_id or persona_id == "[%None]":
212-
persona = await self.context.persona_manager.get_default_persona_v3(
213-
umo=message.unified_msg_origin,
214-
)
215-
persona_id = persona["name"]
212+
(
213+
persona_id,
214+
_,
215+
force_applied_persona_id,
216+
_,
217+
) = await self.context.persona_manager.resolve_selected_persona(
218+
umo=message.unified_msg_origin,
219+
conversation_persona_id=conv.persona_id,
220+
platform_name=platform_name,
221+
provider_settings=provider_settings,
222+
)
223+
if persona_id == "[%None]":
224+
persona_name = "无"
225+
elif persona_id:
226+
persona_name = persona_id
227+
else:
228+
persona_name = "无"
229+
230+
if force_applied_persona_id:
231+
persona_name = f"{persona_name} (自定义规则)"
232+
216233
title = _titles.get(conv.cid, "新对话")
217234
parts.append(
218-
f"{global_index}. {title}({conv.cid[:4]})\n 人格情景: {persona_id}\n 上次更新: {datetime.datetime.fromtimestamp(conv.updated_at).strftime('%m-%d %H:%M')}\n"
235+
f"{global_index}. {title}({conv.cid[:4]})\n 人格情景: {persona_name}\n 上次更新: {datetime.datetime.fromtimestamp(conv.updated_at).strftime('%m-%d %H:%M')}\n"
219236
)
220237
global_index += 1
221238

astrbot/builtin_stars/builtin_commands/commands/persona.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import builtins
22
from typing import TYPE_CHECKING
33

4-
from astrbot.api import sp, star
4+
from astrbot.api import star
55
from astrbot.api.event import AstrMessageEvent, MessageEventResult
66

77
if TYPE_CHECKING:
@@ -59,12 +59,7 @@ async def persona(self, message: AstrMessageEvent) -> None:
5959
default_persona = await self.context.persona_manager.get_default_persona_v3(
6060
umo=umo,
6161
)
62-
63-
force_applied_persona_id = (
64-
await sp.get_async(
65-
scope="umo", scope_id=umo, key="session_service_config", default={}
66-
)
67-
).get("persona_id")
62+
force_applied_persona_id = None
6863

6964
curr_cid_title = "无"
7065
if cid:
@@ -80,10 +75,27 @@ async def persona(self, message: AstrMessageEvent) -> None:
8075
),
8176
)
8277
return
83-
if not conv.persona_id and conv.persona_id != "[%None]":
84-
curr_persona_name = default_persona["name"]
85-
else:
86-
curr_persona_name = conv.persona_id
78+
79+
provider_settings = self.context.get_config(umo=umo).get(
80+
"provider_settings",
81+
{},
82+
)
83+
(
84+
persona_id,
85+
_,
86+
force_applied_persona_id,
87+
_,
88+
) = await self.context.persona_manager.resolve_selected_persona(
89+
umo=umo,
90+
conversation_persona_id=conv.persona_id,
91+
platform_name=message.get_platform_name(),
92+
provider_settings=provider_settings,
93+
)
94+
95+
if persona_id == "[%None]":
96+
curr_persona_name = "无"
97+
elif persona_id:
98+
curr_persona_name = persona_id
8799

88100
if force_applied_persona_id:
89101
curr_persona_name = f"{curr_persona_name} (自定义规则)"

astrbot/core/astr_main_agent.py

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import asyncio
4-
import builtins
54
import copy
65
import datetime
76
import json
@@ -10,7 +9,6 @@
109
from collections.abc import Coroutine
1110
from dataclasses import dataclass, field
1211

13-
from astrbot.api import sp
1412
from astrbot.core import logger
1513
from astrbot.core.agent.handoff import HandoffTool
1614
from astrbot.core.agent.mcp_client import MCPTool
@@ -275,47 +273,26 @@ async def _ensure_persona_and_skills(
275273
if not req.conversation:
276274
return
277275

278-
# get persona ID
279-
280-
# 1. from session service config - highest priority
281-
persona_id = (
282-
await sp.get_async(
283-
scope="umo",
284-
scope_id=event.unified_msg_origin,
285-
key="session_service_config",
286-
default={},
287-
)
288-
).get("persona_id")
289-
290-
if not persona_id:
291-
# 2. from conversation setting - second priority
292-
persona_id = req.conversation.persona_id
293-
294-
if persona_id == "[%None]":
295-
# explicitly set to no persona
296-
pass
297-
elif persona_id is None:
298-
# 3. from config default persona setting - last priority
299-
persona_id = cfg.get("default_personality")
300-
301-
persona = next(
302-
builtins.filter(
303-
lambda persona: persona["name"] == persona_id,
304-
plugin_context.persona_manager.personas_v3,
305-
),
306-
None,
276+
(
277+
persona_id,
278+
persona,
279+
_,
280+
use_webchat_special_default,
281+
) = await plugin_context.persona_manager.resolve_selected_persona(
282+
umo=event.unified_msg_origin,
283+
conversation_persona_id=req.conversation.persona_id,
284+
platform_name=event.get_platform_name(),
285+
provider_settings=cfg,
307286
)
287+
308288
if persona:
309289
# Inject persona system prompt
310290
if prompt := persona["prompt"]:
311291
req.system_prompt += f"\n# Persona Instructions\n\n{prompt}\n"
312292
if begin_dialogs := copy.deepcopy(persona.get("_begin_dialogs_processed")):
313293
req.contexts[:0] = begin_dialogs
314-
else:
315-
# special handling for webchat persona
316-
if event.get_platform_name() == "webchat" and persona_id != "[%None]":
317-
persona_id = "_chatui_default_"
318-
req.system_prompt += CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT
294+
elif use_webchat_special_default:
295+
req.system_prompt += CHATUI_SPECIAL_DEFAULT_PERSONA_PROMPT
319296

320297
# Inject skills prompt
321298
runtime = cfg.get("computer_use_runtime", "local")

astrbot/core/persona_mgr.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from astrbot import logger
2+
from astrbot.api import sp
23
from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
34
from astrbot.core.db import BaseDatabase
45
from astrbot.core.db.po import Persona, PersonaFolder, Personality
@@ -58,6 +59,60 @@ async def get_default_persona_v3(
5859
except Exception:
5960
return DEFAULT_PERSONALITY
6061

62+
async def resolve_selected_persona(
63+
self,
64+
*,
65+
umo: str | MessageSession,
66+
conversation_persona_id: str | None,
67+
platform_name: str,
68+
provider_settings: dict | None = None,
69+
) -> tuple[str | None, Personality | None, str | None, bool]:
70+
"""解析当前会话最终生效的人格。
71+
72+
Returns:
73+
tuple:
74+
- selected persona_id
75+
- selected persona object
76+
- force applied persona_id from session rule
77+
- whether use webchat special default persona
78+
"""
79+
session_service_config = (
80+
await sp.get_async(
81+
scope="umo",
82+
scope_id=str(umo),
83+
key="session_service_config",
84+
default={},
85+
)
86+
or {}
87+
)
88+
89+
force_applied_persona_id = session_service_config.get("persona_id")
90+
persona_id = force_applied_persona_id
91+
92+
if not persona_id:
93+
persona_id = conversation_persona_id
94+
if persona_id == "[%None]":
95+
pass
96+
elif persona_id is None:
97+
persona_id = (provider_settings or {}).get("default_personality")
98+
99+
persona = next(
100+
(item for item in self.personas_v3 if item["name"] == persona_id),
101+
None,
102+
)
103+
104+
use_webchat_special_default = False
105+
if not persona and platform_name == "webchat" and persona_id != "[%None]":
106+
persona_id = "_chatui_default_"
107+
use_webchat_special_default = True
108+
109+
return (
110+
persona_id,
111+
persona,
112+
force_applied_persona_id,
113+
use_webchat_special_default,
114+
)
115+
61116
async def delete_persona(self, persona_id: str) -> None:
62117
"""删除指定 persona"""
63118
if not await self.db.get_persona_by_id(persona_id):

astrbot/dashboard/routes/conversation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ async def upd_conv(self):
148148
user_id = data.get("user_id")
149149
cid = data.get("cid")
150150
title = data.get("title")
151-
persona_id = data.get("persona_id", "")
152151

153152
if not user_id or not cid:
154153
return Response().error("缺少必要参数: user_id 和 cid").__dict__
@@ -158,6 +157,9 @@ async def upd_conv(self):
158157
)
159158
if not conversation:
160159
return Response().error("对话不存在").__dict__
160+
161+
persona_id = data.get("persona_id", conversation.persona_id)
162+
161163
if title is not None or persona_id is not None:
162164
await self.conv_mgr.update_conversation(
163165
unified_msg_origin=user_id,

0 commit comments

Comments
 (0)