feat(sagent): add sagent domain config
Add SagentConfig (gateway base url, session issuer settings, limits and subscriber tuning) with defaults, validation and a default.toml section.
ZhenYi committed
Modified config/default.toml config/default.toml +22 −0 View config/default.toml
@@ -422,3 +422,25 @@
422422presence_ttl_secs = 43200
423423hand_ttl_secs = 3600
424424recording_enabled = false
425
426[sagent]
427# 沙箱智能体(lib/sagent)。admin_key / issuer_secret 只走 env:
428# APP_SAGENT__ADMIN_KEY / APP_SAGENT__ISSUER_SECRET。
429enabled = false
430base_url = "https://agent.gitdata.ai"
431admin_key = ""
432issuer_secret = ""
433issuer = "auth.session"
434audience = "agent-gateway"
435session_ttl_secs = 600
436tenant_id = "3f9a2c1e-77d4-4b18-9c0e-64b2f5a1d001"
437max_pagination = 100
438default_pagination = 20
439max_collection_name_len = 100
440max_title_len = 255
441max_body_len = 65535
442max_message_len = 65535
443max_mentions_per_message = 20
444request_timeout_secs = 60
445subscribe_reconnect_max = 5
446reconcile_interval_secs = 60
Modified lib/config/src/lib.rs lib/config/src/lib.rs +4 −0 View lib/config/src/lib.rs
@@ -22,6 +22,7 @@
2222pub mod redis;
2323pub mod runner;
2424pub mod s3;
25pub mod sagent;
2526pub mod scoped;
2627pub mod search;
2728pub mod session;
@@ -58,6 +59,7 @@
5859use crate::redis::RedisConfig;
5960use crate::runner::RunnerConfig;
6061use crate::s3::S3Config;
62use crate::sagent::SagentConfig;
6163use crate::search::SearchConfig;
6264use crate::session::SessionConfig;
6365use crate::space::SpaceConfig;
@@ -99,6 +101,7 @@
99101 pub ai: AiConfig,
100102 pub aihost: AiHostServerConfig,
101103 pub push: PushConfig,
104 pub sagent: SagentConfig,
102105}
103106
104107impl AppConfig {
@@ -131,6 +134,7 @@
131134 self.aihost.is_valid().map_err(ConfigError::Validation)?;
132135 self.livekit.is_valid().map_err(ConfigError::Validation)?;
133136 self.push.is_valid().map_err(ConfigError::Validation)?;
137 self.sagent.validate().map_err(ConfigError::Validation)?;
134138 Ok(())
135139 }
136140}
Added lib/config/src/sagent.rs lib/config/src/sagent.rs +103 −0 View lib/config/src/sagent.rs
@@ -0,0 +1,103 @@
1use serde::Deserialize;
2
3#[derive(Clone, Debug, Deserialize)]
4#[serde(default)]
5pub struct SagentConfig {
6 pub enabled: bool,
7 pub base_url: String,
8 pub admin_key: String,
9 pub issuer_secret: String,
10 pub issuer: String,
11 pub audience: String,
12 pub session_ttl_secs: u64,
13 pub tenant_id: String,
14 pub max_pagination: u64,
15 pub default_pagination: u64,
16 pub max_collection_name_len: usize,
17 pub max_title_len: usize,
18 pub max_body_len: usize,
19 pub max_message_len: usize,
20 pub max_mentions_per_message: usize,
21 pub request_timeout_secs: u64,
22 pub subscribe_reconnect_max: u32,
23 pub reconcile_interval_secs: u64,
24}
25
26impl Default for SagentConfig {
27 fn default() -> Self {
28 Self {
29 enabled: false,
30 base_url: "https://agent.gitdata.ai".to_string(),
31 admin_key: String::new(),
32 issuer_secret: String::new(),
33 issuer: "auth.session".to_string(),
34 audience: "agent-gateway".to_string(),
35 session_ttl_secs: 600,
36 tenant_id: "3f9a2c1e-77d4-4b18-9c0e-64b2f5a1d001".to_string(),
37 max_pagination: 100,
38 default_pagination: 20,
39 max_collection_name_len: 100,
40 max_title_len: 255,
41 max_body_len: 65_535,
42 max_message_len: 65_535,
43 max_mentions_per_message: 20,
44 request_timeout_secs: 60,
45 subscribe_reconnect_max: 5,
46 reconcile_interval_secs: 60,
47 }
48 }
49}
50
51impl SagentConfig {
52 pub fn base(&self) -> String {
53 self.base_url.trim().trim_end_matches('/').to_string()
54 }
55
56 pub fn is_on(&self) -> bool {
57 self.enabled && !self.admin_key.trim().is_empty()
58 }
59
60 pub fn validate(&self) -> Result<(), String> {
61 if !self.enabled {
62 return Ok(());
63 }
64 let base = self.base_url.trim();
65 if base.is_empty() {
66 return Err("sagent.base_url must not be empty when sagent is enabled".to_string());
67 }
68 if !(base.starts_with("http://") || base.starts_with("https://")) {
69 return Err("sagent.base_url must be an http(s) URL".to_string());
70 }
71 if self.admin_key.trim().is_empty() {
72 return Err("sagent.admin_key must not be empty when sagent is enabled".to_string());
73 }
74 if self.issuer_secret.trim().is_empty() {
75 return Err("sagent.issuer_secret must not be empty when sagent is enabled".to_string());
76 }
77 if self.issuer.trim().is_empty() || self.audience.trim().is_empty() {
78 return Err("sagent issuer and audience must not be empty".to_string());
79 }
80 if is_blank(&self.tenant_id) {
81 return Err("sagent.tenant_id must not be empty when sagent is enabled".to_string());
82 }
83 if self.session_ttl_secs == 0
84 || self.request_timeout_secs == 0
85 || self.default_pagination == 0
86 || self.reconcile_interval_secs == 0
87 || self.max_collection_name_len == 0
88 || self.max_title_len == 0
89 || self.max_body_len == 0
90 || self.max_message_len == 0
91 {
92 return Err("sagent limits must be greater than zero".to_string());
93 }
94 if self.default_pagination > self.max_pagination {
95 return Err("sagent.default_pagination must not exceed max_pagination".to_string());
96 }
97 Ok(())
98 }
99}
100
101fn is_blank(value: &str) -> bool {
102 value.trim().is_empty()
103}
No newline at end of file