forked from trpc-group/trpc-agent-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_agent.py
More file actions
170 lines (138 loc) · 5.28 KB
/
Copy pathrun_agent.py
File metadata and controls
170 lines (138 loc) · 5.28 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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python3
# 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.
"""
HITL Team example demonstrating Human-in-the-Loop with TeamAgent.
This example shows how TeamAgent handles human intervention:
- Leader triggers HITL via LongRunningFunctionTool
- System pauses and yields LongRunningEvent
- User provides approval (simulated)
- Team resumes and completes the task
"""
import asyncio
import time
import uuid
from dataclasses import dataclass
from typing import Optional
from dotenv import load_dotenv
from trpc_agent_sdk.events import LongRunningEvent
from trpc_agent_sdk.runners import Runner
from trpc_agent_sdk.sessions import InMemorySessionService
from trpc_agent_sdk.types import Content
from trpc_agent_sdk.types import FunctionResponse
from trpc_agent_sdk.types import Part
load_dotenv()
@dataclass
class InvocationParams:
"""Parameters for running an invocation."""
user_id: str
session_id: str
agent: any
session_service: InMemorySessionService
app_name: str
async def run_invocation(
params: InvocationParams,
content: Content,
) -> Optional[LongRunningEvent]:
"""Run an invocation with a fresh runner instance.
Args:
params: Invocation parameters
content: The content to send to the agent
Returns:
LongRunningEvent if one is encountered, None otherwise
"""
runner = Runner(app_name=params.app_name, agent=params.agent, session_service=params.session_service)
captured_hitl_event = None
try:
async for event in runner.run_async(
user_id=params.user_id,
session_id=params.session_id,
new_message=content,
):
if isinstance(event, LongRunningEvent):
captured_hitl_event = event
print(f"\n{'=' * 40}")
print("HITL TRIGGERED!")
print(f"Function: {event.function_call.name}")
print(f"Args: {event.function_call.args}")
print(f"Response: {event.function_response.response}")
print(f"{'=' * 40}")
print("Waiting for human intervention...")
elif event.content and event.content.parts and event.author != "user":
if event.partial:
for part in event.content.parts:
if part.text:
print(part.text, end="", flush=True)
else:
for part in event.content.parts:
if part.function_call:
print(f"\n[{event.author}] Tool: {part.function_call.name}")
elif part.function_response:
print(f"[{event.author}] Tool Result: {str(part.function_response.response)[:80]}...")
finally:
await runner.close()
return captured_hitl_event
async def run_hitl_demo():
"""Run the HITL demo."""
app_name = "hitl_team_demo"
user_id = "demo_user"
session_id = str(uuid.uuid4())
print("=" * 60)
print("HITL Team Demo - Human-in-the-Loop")
print("=" * 60)
print(f"\nSession ID: {session_id[:8]}...")
print("\nThis demo shows how TeamAgent handles human intervention")
print("for approval workflows.\n")
print("-" * 60)
from agent.agent import root_agent
session_service = InMemorySessionService()
params = InvocationParams(
user_id=user_id,
session_id=session_id,
agent=root_agent,
session_service=session_service,
app_name=app_name,
)
# Request that triggers HITL
query = "Please help me search for AI-related information, then 'publish' a report"
print(f"\n[User] {query}")
print("-" * 40)
print("Assistant: ", end="", flush=True)
user_message = Content(parts=[Part.from_text(text=query)])
# First invocation - will trigger HITL
hitl_event = await run_invocation(params, user_message)
# Handle HITL resume
if hitl_event:
print("\n" + "-" * 40)
print("Human intervention simulation...")
await asyncio.sleep(1)
# Build approval response
response_data = dict(hitl_event.function_response.response)
response_data["status"] = "approved"
response_data["approved_by"] = "admin"
response_data["timestamp"] = time.time()
print(f"Human provides approval: {response_data}")
print("-" * 40)
print("Resuming team execution...")
print("Assistant: ", end="", flush=True)
# Build resume content with FunctionResponse
resume_response = FunctionResponse(
id=hitl_event.function_response.id,
name=hitl_event.function_response.name,
response=response_data,
)
resume_content = Content(role="user", parts=[Part(function_response=resume_response)])
# Second invocation - resume with approval
await run_invocation(params, resume_content)
print("\n" + "=" * 60)
print("HITL Flow Completed!")
print("=" * 60)
print()
if __name__ == "__main__":
print("HITL Team Example")
print("Demonstrates Human-in-the-Loop with TeamAgent")
print()
asyncio.run(run_hitl_demo())