refactor: remove legacy assistant compatibility

This commit is contained in:
Xin Wang
2026-08-18 10:07:18 +08:00
parent abfcd4cd19
commit d003ee77a5
40 changed files with 505 additions and 729 deletions
+100 -40
View File
@@ -119,6 +119,21 @@ async def noop_queue_frame(_frame):
return None
def current_tool_definition(tool_type: str, **config) -> dict:
policy = {
"allow_interruptions": True,
"execution_mode": "async" if tool_type == "client" else "immediate",
}
if tool_type == "client":
policy.update(
{
"wait_for_response": True,
"response_wait_mode": "timeout",
}
)
return {"type": tool_type, "config": {**policy, **config}}
def system_runtime_tool(kind: str, **config) -> RuntimeTool:
return RuntimeTool(
id=f"tool_{kind}",
@@ -126,7 +141,7 @@ def system_runtime_tool(kind: str, **config) -> RuntimeTool:
function_name=kind,
type="system",
description=f"{kind} system tool",
definition={"type": "system", "config": {"kind": kind, **config}},
definition=current_tool_definition("system", kind=kind, **config),
)
@@ -306,18 +321,24 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
name="开场确认",
function_name="show_message",
type="client",
definition=current_tool_definition("client"),
)
conversation_tool = RuntimeTool(
id="lookup_order",
name="查询订单",
function_name="lookup_order",
type="http",
definition=current_tool_definition("http"),
)
cfg = AssistantConfig(
type="prompt",
tools=[startup_tool, conversation_tool],
tool_bindings=[RuntimeToolBinding(tool_id=conversation_tool.id)],
startup={
"execution_mode": "sequential",
"opening_mode": "interruptible",
"entry_mode": "wait_user",
"opening_message": None,
"actions": [
{
"id": "opening_message",
@@ -359,6 +380,7 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
name=f"预检动作 {index}",
function_name=f"preflight_{index}",
type="http",
definition=current_tool_definition("http"),
)
for index in (1, 2)
]
@@ -366,6 +388,10 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
type="prompt",
tools=tools,
startup={
"execution_mode": "sequential",
"opening_mode": "interruptible",
"entry_mode": "wait_user",
"opening_message": None,
"actions": [
{
"id": tool.id,
@@ -416,6 +442,7 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
name="加载开场数据",
function_name="load_opening_data",
type="http",
definition=current_tool_definition("http"),
)
cfg = AssistantConfig(
type="prompt",
@@ -590,8 +617,10 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
type="prompt",
greeting="请先确认",
startup={
"execution_mode": "sequential",
"opening_mode": "confirmation",
"entry_mode": "wait_user",
"actions": [],
"opening_message": {
"title": "重要提示",
"message": "请确认已阅读。",
@@ -634,8 +663,11 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
type="prompt",
greeting="固定开场白",
startup={
"execution_mode": "sequential",
"opening_mode": "playback",
"entry_mode": "generate",
"actions": [],
"opening_message": None,
},
)
brain = build_brain(cfg)
@@ -675,8 +707,11 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
type="prompt",
greeting="可以打断的开场白",
startup={
"execution_mode": "sequential",
"opening_mode": "interruptible",
"entry_mode": "generate",
"actions": [],
"opening_message": None,
},
)
brain = build_brain(cfg)
@@ -712,8 +747,11 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
type="prompt",
greeting="可以打断的开场白",
startup={
"execution_mode": "sequential",
"opening_mode": "interruptible",
"entry_mode": "generate",
"actions": [],
"opening_message": None,
},
)
brain = build_brain(cfg)
@@ -749,6 +787,10 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
cfg = AssistantConfig(
type="prompt",
startup={
"execution_mode": "sequential",
"opening_mode": "confirmation",
"entry_mode": "generate",
"actions": [],
"opening_message": {
"title": "重要提示",
"message": "请确认已阅读。",
@@ -1312,6 +1354,8 @@ class PromptBrainTests(unittest.IsolatedAsyncioTestCase):
description="查询订单状态",
definition={
"config": {
"allow_interruptions": True,
"execution_mode": "immediate",
"method": "GET",
"url": "https://example.test/orders/{order_id}",
"headers": {"Authorization": "Bearer {{secret__token}}"},
@@ -1389,7 +1433,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -1511,7 +1555,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -1567,7 +1611,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_workflow_end_conversation_waits_for_generated_speech(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [{"id": "start", "type": "start", "data": {}}],
"edges": [],
@@ -1644,7 +1688,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
def test_client_tool_session_wait_disables_flow_timeout(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [{"id": "start", "type": "start", "data": {}}],
"edges": [],
@@ -1657,7 +1701,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
type="client",
definition={
"type": "client",
"config": {"timeout_seconds": 7},
"config": {
"allow_interruptions": True,
"execution_mode": "async",
"wait_for_response": True,
"response_wait_mode": "timeout",
"timeout_seconds": 7,
},
},
)
session_tool = RuntimeTool(
@@ -1668,6 +1718,9 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
definition={
"type": "client",
"config": {
"allow_interruptions": True,
"execution_mode": "async",
"wait_for_response": True,
"timeout_seconds": 7,
"response_wait_mode": "session",
},
@@ -1687,7 +1740,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_flow_transition_honors_strict_agent_entry_mode(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -1749,7 +1802,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {"globalPrompt": "面板 {{panel_open}}"},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -1812,7 +1865,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_agent_vision_tool_is_scoped_to_effective_stage(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {
"globalPrompt": "全局规则",
"defaultLlmResourceId": "llm_global",
@@ -1889,16 +1942,16 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
self.assertNotIn("fetch_user_image", custom_config["role_message"])
self.assertFalse(scopes[-1]["enabled"])
async def test_initial_message_starts_without_workflow_greeting(self):
async def test_initial_message_starts_directly(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {"globalPrompt": "全局规则"},
"nodes": [
{
"id": "start",
"type": "start",
"data": {"greeting": "欢迎使用"},
"data": {},
},
{
"id": "message",
@@ -1957,13 +2010,6 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
)
brain._manager = FakeManager()
self.assertNotIn("greeting", brain._engine.data("start"))
self.assertEqual(
await brain.greeting(
AssistantConfig(type="workflow", greeting="旧助手级开场白")
),
"",
)
await brain.on_connected()
self.assertEqual(brain._manager.current_node, "message")
@@ -1989,12 +2035,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
name="查询订单",
function_name="lookup_order",
type="http",
definition=current_tool_definition("http"),
)
cfg = prepare_dynamic_config(
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2082,12 +2129,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
name="客户端操作",
function_name="show_message",
type="client",
definition=current_tool_definition("client"),
)
brain = WorkflowBrain(
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2155,12 +2203,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
name="客户端操作",
function_name="show_message",
type="client",
definition=current_tool_definition("client"),
)
brain = WorkflowBrain(
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2209,12 +2258,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
name="客户端操作",
function_name="show_message",
type="client",
definition=current_tool_definition("client"),
)
brain = WorkflowBrain(
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2280,12 +2330,13 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
name="客户端操作",
function_name="show_message",
type="client",
definition=current_tool_definition("client"),
)
brain = WorkflowBrain(
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2340,7 +2391,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
AssistantConfig(
type="workflow",
graph={
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2441,7 +2492,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_confirmation_message_is_forwarded_to_next_agent_context(self):
graph = {
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2534,14 +2585,17 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_speech_only_message_waits_for_transport_playback(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
{
"id": "message",
"type": "message",
"data": {"speech": "正在为您准备服务。"},
"data": {
"speech": "正在为您准备服务。",
"completionPolicy": "playback",
},
},
],
"edges": [],
@@ -2581,7 +2635,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_interruptible_message_forwards_multimodal_input_once(self):
graph = {
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -2707,14 +2761,17 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_message_between_agents_resumes_after_playback(self):
graph = {
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
{
"id": "opening",
"type": "message",
"data": {"speech": "欢迎使用。"},
"data": {
"speech": "欢迎使用。",
"completionPolicy": "playback",
},
},
{
"id": "agent1",
@@ -2727,7 +2784,10 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
{
"id": "middle",
"type": "message",
"data": {"speech": "现在进入信息确认。"},
"data": {
"speech": "现在进入信息确认。",
"completionPolicy": "playback",
},
},
{
"id": "agent2",
@@ -2928,7 +2988,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
start_brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [{"id": "start", "type": "start", "data": {}}],
"edges": [],
@@ -2960,7 +3020,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
agent_brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {"globalPrompt": "全局规则"},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -3000,7 +3060,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
handoff_brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -3027,7 +3087,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_waiting_agent_does_not_reply_to_transitioning_user_turn(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {"globalPrompt": "全局规则"},
"nodes": [
{
@@ -3167,7 +3227,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_start_expression_condition_also_waits_for_user_turn(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -3206,7 +3266,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_automatic_node_can_follow_llm_condition(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {"globalPrompt": "全局规则"},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -3268,7 +3328,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_mixed_edge_conditions_follow_priority(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"specVersion": 4,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
@@ -3352,7 +3412,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
async def test_transition_and_end_are_owned_by_workflow_brain(self):
graph = {
"specVersion": 3,
"specVersion": 4,
"settings": {
"globalPrompt": "全局规则",
"defaultLlmResourceId": "llm_global",