forked from trpc-group/trpc-agent-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnodes.py
More file actions
107 lines (81 loc) · 3.24 KB
/
Copy pathnodes.py
File metadata and controls
107 lines (81 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
"""Node functions for graph multi-turn workflow."""
from typing import Any
from typing import Dict
from trpc_agent_sdk.context import InvocationContext
from trpc_agent_sdk.dsl.graph import STATE_KEY_LAST_RESPONSE
from trpc_agent_sdk.dsl.graph import STATE_KEY_USER_INPUT
from .state import MultiTurnState
ROUTE_LLM = "llm"
ROUTE_AGENT = "agent"
def _normalize_text(text: str) -> str:
return text.strip() if text else ""
def _truncate_text(text: str, max_len: int = 80) -> str:
if len(text) <= max_len:
return text
return text[:max_len - 3] + "..."
def _log_node(node_name: str, message: str) -> None:
print(f"[node_execute:{node_name}] {message}")
async def decide_route(state: MultiTurnState, ctx: InvocationContext) -> Dict[str, Any]:
"""Select branch based on query prefix: `llm:` or `agent:`."""
query_text = _normalize_text(state.get(STATE_KEY_USER_INPUT, ""))
route = ROUTE_LLM
if not query_text:
raise ValueError("No user_input provided")
lower_text = query_text.lower()
if lower_text.startswith("agent:"):
route = ROUTE_AGENT
query_text = _normalize_text(query_text[len("agent:"):])
elif lower_text.startswith("llm:"):
route = ROUTE_LLM
query_text = _normalize_text(query_text[len("llm:"):])
turn_count = sum(1 for event in ctx.session.events if event.author == "user")
result = {
"route": route,
"query_text": query_text,
STATE_KEY_USER_INPUT: query_text,
"context_note": f"user={ctx.user_id} session={ctx.session_id} turn={turn_count}",
}
_log_node("decide", f"return={result}")
return result
def route_choice(state: MultiTurnState) -> str:
"""Route function used by conditional edges."""
return state.get("route", ROUTE_LLM)
async def format_output(state: MultiTurnState) -> Dict[str, Any]:
"""Format the final output for current turn."""
route = state.get("route", ROUTE_LLM)
if route == ROUTE_AGENT:
reply = state.get("agent_reply", "")
else:
reply = state.get("llm_reply", "")
if not reply:
reply = state.get(STATE_KEY_LAST_RESPONSE, "")
if not reply:
reply = "(No response generated)"
flow = _format_execution_flow(state.get("node_execution_history", []))
result_text = f"""
==============================
Graph Multi-Turn Result
==============================
Branch: {route}
Context: {state.get('context_note', '')}
{reply}
{flow}
""".strip()
result = {STATE_KEY_LAST_RESPONSE: result_text}
_log_node("format_output", f"return.last_response_len={len(result_text)}")
return result
def _format_execution_flow(history: list[dict[str, Any]]) -> str:
if not history:
return ""
lines = ["", "Execution Flow:"]
for idx, entry in enumerate(history, start=1):
name = entry.get("node_name", entry.get("node_id", "unknown"))
node_type = entry.get("node_type", "")
duration = entry.get("execution_time", 0.0)
lines.append(f" {idx}. {name} ({node_type}) - {duration:.3f}s")
return "\n".join(lines)