Initial scaffold: GyanDoot v2.0 in Rust
Full Cargo workspace with 11 crates: - gyandoot-core: Shared types, config, error types - gyandoot-agent: Agent loop, tool execution, context compression - gyandoot-providers: LLM provider abstraction (OpenAI, Anthropic, Ollama) - gyandoot-memory: MemPalace persistent memory system - gyandoot-platform: Platform adapters (Telegram, Discord, Webhook, CLI) - gyandoot-security: Credential vault, sanitizer, threat detector - gyandoot-kanban: Task decomposition, swarm orchestration, diagnostics - gyandoot-subagents: Sub-agent spawning and orchestration - gyandoot-skills: Skills discovery, loading, preprocessing, bundles - gyandoot-profile: Multi-agent profile manager - gyandoot-gateway: Gateway sessions, routing, access control Plus: CLI entry point with clap, cross-platform installer script, KANBAN.md, .gnpl/AGENTS.md Workspace compiles clean: cargo check passes with 0 errors.
This commit is contained in:
17
crates/gyandoot-profile/Cargo.toml
Normal file
17
crates/gyandoot-profile/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "gyandoot-profile"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "GyanDoot v2 — Multi-agent profile manager"
|
||||
|
||||
[dependencies]
|
||||
gyandoot-core = { path = "../gyandoot-core" }
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
uuid.workspace = true
|
||||
dirs.workspace = true
|
||||
120
crates/gyandoot-profile/src/lib.rs
Normal file
120
crates/gyandoot-profile/src/lib.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
//! # Gyandoot Profile Manager
|
||||
//!
|
||||
//! Multi-agent profile management. Each profile is a self-contained config.yaml
|
||||
//! with its own model, provider, and platform settings.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A named agent profile with self-contained configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Profile {
|
||||
pub name: String,
|
||||
pub model: String,
|
||||
pub provider: String,
|
||||
pub base_url: Option<String>,
|
||||
pub api_key: Option<String>,
|
||||
pub temperature: Option<f64>,
|
||||
pub max_tokens: Option<u32>,
|
||||
pub gateway: Option<GatewaySettings>,
|
||||
}
|
||||
|
||||
/// Gateway settings scoped to a profile.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GatewaySettings {
|
||||
pub enabled: bool,
|
||||
pub platforms: HashMap<String, PlatformBinding>,
|
||||
}
|
||||
|
||||
/// Platform binding within a profile.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PlatformBinding {
|
||||
pub token: Option<String>,
|
||||
pub home_channel: Option<String>,
|
||||
pub allowed_users: Option<Vec<String>>,
|
||||
pub allowed_channels: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
pub fn new(name: &str, model: &str, provider: &str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
model: model.to_string(),
|
||||
provider: provider.to_string(),
|
||||
base_url: None,
|
||||
api_key: None,
|
||||
temperature: None,
|
||||
max_tokens: None,
|
||||
gateway: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves configuration from profile marker -> file -> defaults.
|
||||
pub struct ConfigResolver {
|
||||
gyandoot_home: PathBuf,
|
||||
}
|
||||
|
||||
impl ConfigResolver {
|
||||
pub fn new() -> Self {
|
||||
let home = dirs::home_dir().unwrap_or_default();
|
||||
Self {
|
||||
gyandoot_home: home.join(".gyandoot"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn profiles_dir(&self) -> PathBuf {
|
||||
self.gyandoot_home.join("profiles")
|
||||
}
|
||||
|
||||
pub fn active_profile_path(&self) -> PathBuf {
|
||||
self.gyandoot_home.join("active_profile")
|
||||
}
|
||||
|
||||
pub fn profile_config_path(&self, name: &str) -> PathBuf {
|
||||
self.profiles_dir().join(name).join("config.yaml")
|
||||
}
|
||||
|
||||
pub fn default_config_path(&self) -> PathBuf {
|
||||
self.gyandoot_home.join("config.yaml")
|
||||
}
|
||||
|
||||
pub fn resolve(&self) -> ResolvedConfig {
|
||||
ResolvedConfig {
|
||||
gyandoot_home: self.gyandoot_home.clone(),
|
||||
profile_name: "default".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ConfigResolver {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolved configuration metadata.
|
||||
pub struct ResolvedConfig {
|
||||
pub gyandoot_home: PathBuf,
|
||||
pub profile_name: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_profile_creation() {
|
||||
let p = Profile::new("dev", "claude-sonnet-4", "anthropic");
|
||||
assert_eq!(p.name, "dev");
|
||||
assert_eq!(p.model, "claude-sonnet-4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_resolver_paths() {
|
||||
let resolver = ConfigResolver::new();
|
||||
assert!(resolver.gyandoot_home.to_string_lossy().contains(".gyandoot"));
|
||||
assert!(resolver.profiles_dir().to_string_lossy().contains("profiles"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user