Skip to content

Commit dbaf7e3

Browse files
authored
Merge pull request #806 from smallcloudai/cloud-chore-subs
Cloud chore subs
2 parents 7560458 + 52344af commit dbaf7e3

16 files changed

Lines changed: 1389 additions & 13 deletions

File tree

refact-agent/engine/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ build = "build.rs"
1414
[build-dependencies]
1515
shadow-rs = "1.1.0"
1616

17+
[target.'cfg(windows)'.dependencies]
18+
winreg = "0.55.0"
19+
1720
[dependencies]
1821
astral-tokio-tar = "0.5.2"
1922
axum = { version = "0.6.20", features = ["default", "http2"] }
@@ -43,6 +46,7 @@ log = "0.4.20"
4346
md5 = "0.7"
4447
notify = { version = "8.0.0", features = ["serde"] }
4548
parking_lot = { version = "0.12.1", features = ["serde"] }
49+
pnet_datalink = "0.35.0"
4650
process-wrap = { version = "8.0.2", features = ["tokio1"] }
4751
rand = "0.8.5"
4852
rayon = "1.8.0"
@@ -75,6 +79,7 @@ tokenizers = "0.21.0"
7579
tokio = { version = "1.43.0", features = ["fs", "io-std", "io-util", "macros", "rt-multi-thread", "signal", "process"] }
7680
tokio-rusqlite = "0.5.0"
7781
tokio-util = { version = "0.7.12", features = ["compat"] }
82+
tokio-tungstenite = "0.21.0"
7883
tower = { version = "0.4", features = ["full"] }
7984
tower-http = { version = "0.4.0", features = ["cors"] }
8085
tower-lsp = "0.20"

refact-agent/engine/src/background_tasks.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub async fn start_background_tasks(gcx: Arc<ARwLock<GlobalContext>>, config_dir
4747
tokio::spawn(crate::vecdb::vdb_highlev::vecdb_background_reload(gcx.clone())), // this in turn can create global_context::vec_db
4848
tokio::spawn(crate::integrations::sessions::remove_expired_sessions_background_task(gcx.clone())),
4949
tokio::spawn(crate::memories::memories_migration(gcx.clone(), config_dir.clone())),
50+
tokio::spawn(crate::cloud::threads_sub::watch_threads_subscription(gcx.clone())),
5051
]);
5152
let ast = gcx.clone().read().await.ast_service.clone();
5253
if let Some(ast_service) = ast {
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use log::error;
2+
use regex::Regex;
3+
use reqwest::Client;
4+
use serde::{Deserialize, Serialize};
5+
use serde_json::{json, Value};
6+
use std::sync::Arc;
7+
use tokio::sync::RwLock as ARwLock;
8+
9+
use crate::global_context::GlobalContext;
10+
11+
#[derive(Debug, Serialize, Deserialize)]
12+
pub struct Expert {
13+
pub owner_fuser_id: Option<String>,
14+
pub owner_shared: bool,
15+
pub located_fgroup_id: Option<String>,
16+
pub fexp_name: String,
17+
pub fexp_system_prompt: String,
18+
pub fexp_python_kernel: String,
19+
pub fexp_block_tools: String,
20+
pub fexp_allow_tools: String,
21+
}
22+
23+
impl Expert {
24+
pub fn is_tool_allowed(&self, tool_name: &str) -> bool {
25+
let mut blocked = false;
26+
if !self.fexp_block_tools.trim().is_empty() {
27+
match Regex::new(&self.fexp_block_tools) {
28+
Ok(re) => {
29+
if re.is_match(tool_name) {
30+
blocked = true;
31+
}
32+
}
33+
Err(e) => {
34+
error!(
35+
"Failed to compile fexp_block_tools regex: {}: {}",
36+
self.fexp_block_tools, e
37+
);
38+
}
39+
}
40+
}
41+
// Allow if matches allow regex, even if blocked
42+
if !self.fexp_allow_tools.trim().is_empty() {
43+
match Regex::new(&self.fexp_allow_tools) {
44+
Ok(re) => {
45+
if re.is_match(tool_name) {
46+
return true;
47+
}
48+
}
49+
Err(e) => {
50+
error!(
51+
"Failed to compile fexp_allow_tools regex: {}: {}",
52+
self.fexp_allow_tools, e
53+
);
54+
}
55+
}
56+
}
57+
58+
!blocked
59+
}
60+
}
61+
62+
pub async fn get_expert(
63+
gcx: Arc<ARwLock<GlobalContext>>,
64+
expert_name: &str
65+
) -> Result<Expert, String> {
66+
let client = Client::new();
67+
let api_key = gcx.read().await.cmdline.api_key.clone();
68+
let query = r#"
69+
query GetExpert($id: String!) {
70+
expert_get(id: $id) {
71+
owner_fuser_id
72+
owner_shared
73+
located_fgroup_id
74+
fexp_name
75+
fexp_system_prompt
76+
fexp_python_kernel
77+
fexp_block_tools
78+
fexp_allow_tools
79+
}
80+
}
81+
"#;
82+
let response = client
83+
.post(&crate::constants::GRAPHQL_URL.to_string())
84+
.header("Authorization", format!("Bearer {}", api_key))
85+
.header("Content-Type", "application/json")
86+
.json(&json!({
87+
"query": query,
88+
"variables": {
89+
"id": expert_name
90+
}
91+
}))
92+
.send()
93+
.await
94+
.map_err(|e| format!("Failed to send GraphQL request: {}", e))?;
95+
96+
if response.status().is_success() {
97+
let response_body = response
98+
.text()
99+
.await
100+
.map_err(|e| format!("Failed to read response body: {}", e))?;
101+
let response_json: Value = serde_json::from_str(&response_body)
102+
.map_err(|e| format!("Failed to parse response JSON: {}", e))?;
103+
if let Some(errors) = response_json.get("errors") {
104+
let error_msg = errors.to_string();
105+
error!("GraphQL error: {}", error_msg);
106+
return Err(format!("GraphQL error: {}", error_msg));
107+
}
108+
if let Some(data) = response_json.get("data") {
109+
if let Some(expert_value) = data.get("expert_get") {
110+
let expert: Expert = serde_json::from_value(expert_value.clone())
111+
.map_err(|e| format!("Failed to parse expert: {}", e))?;
112+
return Ok(expert);
113+
}
114+
}
115+
Err(format!(
116+
"Expert with name '{}' not found or unexpected response format: {}",
117+
expert_name, response_body
118+
))
119+
} else {
120+
let status = response.status();
121+
let error_text = response
122+
.text()
123+
.await
124+
.unwrap_or_else(|_| "Unknown error".to_string());
125+
Err(format!(
126+
"Failed to get expert with name {}: HTTP status {}, error: {}",
127+
expert_name, status, error_text
128+
))
129+
}
130+
}

0 commit comments

Comments
 (0)