-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathsession_fs.rs
More file actions
139 lines (124 loc) · 3.94 KB
/
session_fs.rs
File metadata and controls
139 lines (124 loc) · 3.94 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
//! Custom `SessionFsProvider` backed by an in-memory map.
//!
//! Demonstrates registering a [`SessionFsProvider`] so the CLI delegates all
//! per-session filesystem operations to your code. Useful for sandboxed
//! sessions, projecting files into virtual storage, or applying permission
//! policies before bytes are read or written.
//!
//! ```sh
//! cargo run -p github-copilot-sdk --example session_fs
//! ```
use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::session_fs::{
DirEntry, DirEntryKind, FileInfo, FsError, SessionFsConfig, SessionFsConventions,
SessionFsProvider,
};
use github_copilot_sdk::types::{MessageOptions, SessionConfig};
use github_copilot_sdk::{Client, ClientOptions};
use parking_lot::Mutex;
struct InMemoryProvider {
files: Mutex<HashMap<String, String>>,
}
impl InMemoryProvider {
fn new() -> Self {
let mut seed = HashMap::new();
seed.insert(
"/workspace/README.md".to_string(),
"# Demo project\n\nThis file lives in memory.\n".to_string(),
);
Self {
files: Mutex::new(seed),
}
}
}
#[async_trait]
impl SessionFsProvider for InMemoryProvider {
async fn read_file(&self, path: &str) -> Result<String, FsError> {
self.files
.lock()
.get(path)
.cloned()
.ok_or_else(|| FsError::NotFound(path.to_string()))
}
async fn write_file(
&self,
path: &str,
content: &str,
_mode: Option<i64>,
) -> Result<(), FsError> {
self.files
.lock()
.insert(path.to_string(), content.to_string());
Ok(())
}
async fn exists(&self, path: &str) -> Result<bool, FsError> {
Ok(self.files.lock().contains_key(path))
}
async fn stat(&self, path: &str) -> Result<FileInfo, FsError> {
let files = self.files.lock();
let content = files
.get(path)
.ok_or_else(|| FsError::NotFound(path.to_string()))?;
Ok(FileInfo::new(
true,
false,
content.len() as i64,
"2025-01-01T00:00:00Z",
"2025-01-01T00:00:00Z",
))
}
async fn readdir_with_types(&self, path: &str) -> Result<Vec<DirEntry>, FsError> {
let prefix = if path.ends_with('/') {
path.to_string()
} else {
format!("{path}/")
};
let names: Vec<DirEntry> = self
.files
.lock()
.keys()
.filter_map(|k| k.strip_prefix(&prefix))
.filter(|rest| !rest.is_empty())
.map(|rest| {
let name = rest.split('/').next().unwrap_or(rest);
DirEntry::new(name, DirEntryKind::File)
})
.collect();
Ok(names)
}
async fn rm(&self, path: &str, _recursive: bool, force: bool) -> Result<(), FsError> {
if self.files.lock().remove(path).is_none() && !force {
return Err(FsError::NotFound(path.to_string()));
}
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider: Arc<dyn SessionFsProvider> = Arc::new(InMemoryProvider::new());
let options = {
let mut opts = ClientOptions::default();
opts.session_fs = Some(SessionFsConfig::new(
"/workspace",
"/workspace/.copilot",
SessionFsConventions::Posix,
));
opts
};
let client = Client::start(options).await?;
let session = client
.create_session(
SessionConfig::default()
.with_handler(Arc::new(ApproveAllHandler))
.with_session_fs_provider(provider),
)
.await?;
let response = session
.send(MessageOptions::new("Summarize README.md."))
.await?;
println!("Assistant: {response}");
Ok(())
}