-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathmanual_tool_resume.rs
More file actions
152 lines (134 loc) · 4.98 KB
/
Copy pathmanual_tool_resume.rs
File metadata and controls
152 lines (134 loc) · 4.98 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
//! Demonstrates manually resolving permission and external tool requests across resumes.
use std::time::Duration;
use github_copilot_sdk::rpc::{
HandlePendingToolCallRequest, PermissionDecision, PermissionDecisionApproveOnce,
PermissionDecisionApproveOnceKind, PermissionDecisionRequest,
};
use github_copilot_sdk::session_events::{
AssistantMessageData, ExternalToolRequestedData, PermissionRequestedData, SessionEventType,
};
use github_copilot_sdk::subscription::RecvError;
use github_copilot_sdk::{
Client, ClientOptions, EventSubscription, ResumeSessionConfig, SessionConfig,
};
use serde_json::json;
const TOOL_NAME: &str = "manual_resume_status";
fn manual_tool() -> github_copilot_sdk::Tool {
// No handler is registered for this tool, so the SDK leaves execution pending.
github_copilot_sdk::Tool::new(TOOL_NAME)
.with_description("Looks up a status value. The SDK consumer supplies the result manually.")
.with_parameters(json!({
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Identifier to look up"
}
},
"required": ["id"]
}))
}
async fn wait_for_permission(
events: &mut EventSubscription,
) -> Result<PermissionRequestedData, RecvError> {
loop {
let event = events.recv().await?;
if event.parsed_type() == SessionEventType::PermissionRequested
&& let Some(data) = event.typed_data::<PermissionRequestedData>()
{
return Ok(data);
}
}
}
async fn wait_for_tool(
events: &mut EventSubscription,
) -> Result<ExternalToolRequestedData, RecvError> {
loop {
let event = events.recv().await?;
if event.parsed_type() == SessionEventType::ExternalToolRequested
&& let Some(data) = event.typed_data::<ExternalToolRequestedData>()
&& data.tool_name == TOOL_NAME
{
return Ok(data);
}
}
}
async fn wait_for_assistant(events: &mut EventSubscription) -> Result<String, RecvError> {
loop {
let event = events.recv().await?;
if event.parsed_type() == SessionEventType::AssistantMessage
&& let Some(data) = event.typed_data::<AssistantMessageData>()
{
return Ok(data.content);
}
}
}
async fn pause() {
println!("Simulating time passing...\n");
tokio::time::sleep(Duration::from_secs(1)).await;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let tool = manual_tool();
// 1. Create a session with a declaration-only tool, then stop after the permission prompt.
let client1 = Client::start(ClientOptions::default()).await?;
let session1 = client1
.create_session(SessionConfig::default().with_tools([tool.clone()]))
.await?;
let session_id = session1.id().clone();
// Subscribe before sending so the permission event cannot be missed.
let mut permission_events = session1.subscribe();
session1
.send("Use the manual_resume_status tool with id 'alpha', then tell me the status.")
.await?;
let permission = wait_for_permission(&mut permission_events).await?;
client1.force_stop();
pause().await;
// 2. Resume pending work and grant permission to invoke the tool.
let client2 = Client::start(ClientOptions::default()).await?;
let session2 = client2
.resume_session(
ResumeSessionConfig::new(session_id.clone())
.with_tools([tool.clone()])
.with_continue_pending_work(true),
)
.await?;
// Subscribe before approving so the external tool request cannot be missed.
let mut tool_events = session2.subscribe();
session2
.rpc()
.permissions()
.handle_pending_permission_request(PermissionDecisionRequest {
request_id: permission.request_id,
result: PermissionDecision::ApproveOnce(PermissionDecisionApproveOnce {
kind: PermissionDecisionApproveOnceKind::ApproveOnce,
}),
})
.await?;
let tool_request = wait_for_tool(&mut tool_events).await?;
client2.force_stop();
pause().await;
// 3. Resume again and manually provide the pending tool result.
let client3 = Client::start(ClientOptions::default()).await?;
let session3 = client3
.resume_session(
ResumeSessionConfig::new(session_id)
.with_tools([tool])
.with_continue_pending_work(true),
)
.await?;
let mut assistant_events = session3.subscribe();
session3
.rpc()
.tools()
.handle_pending_tool_call(HandlePendingToolCallRequest {
request_id: tool_request.request_id,
result: Some(json!("MANUAL_STATUS_READY")),
error: None,
})
.await?;
let answer = wait_for_assistant(&mut assistant_events).await?;
println!("{answer}");
client3.force_stop();
Ok(())
}