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:
253
crates/gyandoot-core/src/config.rs
Normal file
253
crates/gyandoot-core/src/config.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
//! Configuration types for GyanDoot agents and platform.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Top-level agent configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentConfig {
|
||||
pub name: String,
|
||||
pub model: String,
|
||||
pub max_tokens: usize,
|
||||
pub temperature: f64,
|
||||
pub system_prompt: String,
|
||||
pub provider: ProviderConfig,
|
||||
pub platform: PlatformConfig,
|
||||
pub memory: MemoryConfig,
|
||||
pub security: SecurityConfig,
|
||||
pub kanban: KanbanConfig,
|
||||
pub skills: SkillsConfig,
|
||||
}
|
||||
|
||||
impl Default for AgentConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "gyandoot".to_string(),
|
||||
model: "gpt-4o".to_string(),
|
||||
max_tokens: 4096,
|
||||
temperature: 0.7,
|
||||
system_prompt: String::new(),
|
||||
provider: ProviderConfig::default(),
|
||||
platform: PlatformConfig::default(),
|
||||
memory: MemoryConfig::default(),
|
||||
security: SecurityConfig::default(),
|
||||
kanban: KanbanConfig::default(),
|
||||
skills: SkillsConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// LLM provider configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ProviderConfig {
|
||||
pub default: String,
|
||||
pub openai: Option<OpenAIConfig>,
|
||||
pub anthropic: Option<AnthropicConfig>,
|
||||
pub ollama: Option<OllamaConfig>,
|
||||
}
|
||||
|
||||
impl Default for ProviderConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default: "openai".to_string(),
|
||||
openai: Some(OpenAIConfig::default()),
|
||||
anthropic: None,
|
||||
ollama: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenAIConfig {
|
||||
pub api_key: String,
|
||||
pub model: String,
|
||||
pub base_url: Option<String>,
|
||||
pub max_retries: u32,
|
||||
}
|
||||
|
||||
impl Default for OpenAIConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
api_key: String::new(),
|
||||
model: "gpt-4o".to_string(),
|
||||
base_url: None,
|
||||
max_retries: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AnthropicConfig {
|
||||
pub api_key: String,
|
||||
pub model: String,
|
||||
pub max_retries: u32,
|
||||
}
|
||||
|
||||
impl Default for AnthropicConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
api_key: String::new(),
|
||||
model: "claude-3-5-sonnet-20241022".to_string(),
|
||||
max_retries: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OllamaConfig {
|
||||
pub base_url: String,
|
||||
pub model: String,
|
||||
pub max_retries: u32,
|
||||
}
|
||||
|
||||
impl Default for OllamaConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
base_url: "http://localhost:11434".to_string(),
|
||||
model: "llama3".to_string(),
|
||||
max_retries: 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Platform adapter configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PlatformConfig {
|
||||
pub default: String,
|
||||
pub telegram: Option<TelegramConfig>,
|
||||
pub discord: Option<DiscordConfig>,
|
||||
pub webhook: Option<WebhookConfig>,
|
||||
}
|
||||
|
||||
impl Default for PlatformConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default: "cli".to_string(),
|
||||
telegram: None,
|
||||
discord: None,
|
||||
webhook: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TelegramConfig {
|
||||
pub bot_token: String,
|
||||
pub allowed_users: Vec<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DiscordConfig {
|
||||
pub bot_token: String,
|
||||
pub guild_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WebhookConfig {
|
||||
pub listen_host: String,
|
||||
pub listen_port: u16,
|
||||
pub secret: Option<String>,
|
||||
}
|
||||
|
||||
/// Memory system configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MemoryConfig {
|
||||
pub backend: String,
|
||||
pub storage_path: Option<PathBuf>,
|
||||
pub max_entries: usize,
|
||||
pub auto_compress: bool,
|
||||
}
|
||||
|
||||
impl Default for MemoryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
backend: "local".to_string(),
|
||||
storage_path: None,
|
||||
max_entries: 10000,
|
||||
auto_compress: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Security configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SecurityConfig {
|
||||
pub vault_path: Option<PathBuf>,
|
||||
pub encryption_enabled: bool,
|
||||
pub threat_detection: bool,
|
||||
}
|
||||
|
||||
impl Default for SecurityConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vault_path: None,
|
||||
encryption_enabled: true,
|
||||
threat_detection: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kanban task system configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct KanbanConfig {
|
||||
pub enabled: bool,
|
||||
pub max_concurrent_tasks: usize,
|
||||
pub auto_decompose: bool,
|
||||
}
|
||||
|
||||
impl Default for KanbanConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
max_concurrent_tasks: 5,
|
||||
auto_decompose: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Skills system configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SkillsConfig {
|
||||
pub enabled: bool,
|
||||
pub skills_dir: Option<PathBuf>,
|
||||
pub auto_load: bool,
|
||||
}
|
||||
|
||||
impl Default for SkillsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
skills_dir: None,
|
||||
auto_load: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load configuration from a TOML file.
|
||||
pub fn load_config_from_file(path: &std::path::Path) -> anyhow::Result<AgentConfig> {
|
||||
let content = std::fs::read_to_string(path)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to read config file {}: {}", path.display(), e))?;
|
||||
let config: AgentConfig = toml::from_str(&content)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to parse config file {}: {}", path.display(), e))?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_default_config() {
|
||||
let config = AgentConfig::default();
|
||||
assert_eq!(config.name, "gyandoot");
|
||||
assert_eq!(config.max_tokens, 4096);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_provider_default() {
|
||||
let provider = ProviderConfig::default();
|
||||
assert_eq!(provider.default, "openai");
|
||||
assert!(provider.openai.is_some());
|
||||
}
|
||||
}
|
||||
231
crates/gyandoot-core/src/error.rs
Normal file
231
crates/gyandoot-core/src/error.rs
Normal file
@@ -0,0 +1,231 @@
|
||||
//! Unified error types for GyanDoot.
|
||||
//!
|
||||
//! Each subsystem defines its own error enum using `thiserror`,
|
||||
//! while application-level errors use `anyhow`.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Core platform errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CoreError {
|
||||
#[error("Configuration error: {0}")]
|
||||
Config(String),
|
||||
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Serialization error: {0}")]
|
||||
Serde(#[from] serde_json::Error),
|
||||
|
||||
#[error("Invalid input: {0}")]
|
||||
InvalidInput(String),
|
||||
|
||||
#[error("Not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Unsupported: {0}")]
|
||||
Unsupported(String),
|
||||
|
||||
#[error("Timeout")]
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// Agent loop errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AgentError {
|
||||
#[error("LLM provider error: {0}")]
|
||||
Provider(String),
|
||||
|
||||
#[error("Tool execution error: {0}")]
|
||||
ToolExecution(String),
|
||||
|
||||
#[error("Context compression error: {0}")]
|
||||
ContextCompression(String),
|
||||
|
||||
#[error("Agent loop terminated: {0}")]
|
||||
Terminated(String),
|
||||
|
||||
#[error("Internal error: {0}")]
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
/// LLM provider errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ProviderError {
|
||||
#[error("API error: {0}")]
|
||||
Api(String),
|
||||
|
||||
#[error("Authentication failed: {0}")]
|
||||
Auth(String),
|
||||
|
||||
#[error("Rate limited")]
|
||||
RateLimited,
|
||||
|
||||
#[error("Model unavailable: {0}")]
|
||||
ModelUnavailable(String),
|
||||
|
||||
#[error("Stream error: {0}")]
|
||||
Stream(String),
|
||||
|
||||
#[error("Request failed: {0}")]
|
||||
Request(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
/// Memory system errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MemoryError {
|
||||
#[error("Storage error: {0}")]
|
||||
Storage(String),
|
||||
|
||||
#[error("Query error: {0}")]
|
||||
Query(String),
|
||||
|
||||
#[error("Index corruption: {0}")]
|
||||
IndexCorruption(String),
|
||||
|
||||
#[error("Entry not found: {0}")]
|
||||
EntryNotFound(String),
|
||||
|
||||
#[error("Compression failed: {0}")]
|
||||
Compression(String),
|
||||
}
|
||||
|
||||
/// Platform adapter errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PlatformError {
|
||||
#[error("Connection failed: {0}")]
|
||||
Connection(String),
|
||||
|
||||
#[error("Message send failed: {0}")]
|
||||
Send(String),
|
||||
|
||||
#[error("Authentication failed: {0}")]
|
||||
Auth(String),
|
||||
|
||||
#[error("Platform unavailable: {0}")]
|
||||
Unavailable(String),
|
||||
|
||||
#[error("Network error: {0}")]
|
||||
Network(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
/// Security subsystem errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SecurityError {
|
||||
#[error("Encryption failed: {0}")]
|
||||
Encryption(String),
|
||||
|
||||
#[error("Decryption failed: {0}")]
|
||||
Decryption(String),
|
||||
|
||||
#[error("Vault locked or missing: {0}")]
|
||||
VaultLocked(String),
|
||||
|
||||
#[error("Threat detected: {0}")]
|
||||
ThreatDetected(String),
|
||||
|
||||
#[error("Credential not found: {0}")]
|
||||
CredentialNotFound(String),
|
||||
}
|
||||
|
||||
/// Kanban task system errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum KanbanError {
|
||||
#[error("Task not found: {0}")]
|
||||
TaskNotFound(String),
|
||||
|
||||
#[error("Decomposition failed: {0}")]
|
||||
Decomposition(String),
|
||||
|
||||
#[error("Dependency cycle detected")]
|
||||
DependencyCycle,
|
||||
|
||||
#[error("Swarm error: {0}")]
|
||||
Swarm(String),
|
||||
|
||||
#[error("Diagnostics failed: {0}")]
|
||||
Diagnostics(String),
|
||||
}
|
||||
|
||||
/// Sub-agent orchestration errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SubAgentError {
|
||||
#[error("Worker panicked: {0}")]
|
||||
WorkerPanic(String),
|
||||
|
||||
#[error("Failed to spawn: {0}")]
|
||||
SpawnFailed(String),
|
||||
|
||||
#[error("Communication error: {0}")]
|
||||
Communication(String),
|
||||
|
||||
#[error("Timeout waiting for sub-agent")]
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// Skills system errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SkillsError {
|
||||
#[error("Skill not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Load failed: {0}")]
|
||||
Load(String),
|
||||
|
||||
#[error("Preprocessing failed: {0}")]
|
||||
Preprocess(String),
|
||||
|
||||
#[error("Bundle error: {0}")]
|
||||
Bundle(String),
|
||||
|
||||
#[error("Validation failed: {0}")]
|
||||
Validation(String),
|
||||
}
|
||||
|
||||
/// Profile manager errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ProfileError {
|
||||
#[error("Profile not found: {0}")]
|
||||
NotFound(String),
|
||||
|
||||
#[error("Config resolution failed: {0}")]
|
||||
ConfigResolution(String),
|
||||
|
||||
#[error("Profile conflict: {0}")]
|
||||
Conflict(String),
|
||||
|
||||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// Gateway errors.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GatewayError {
|
||||
#[error("Session not found: {0}")]
|
||||
SessionNotFound(String),
|
||||
|
||||
#[error("Routing error: {0}")]
|
||||
Routing(String),
|
||||
|
||||
#[error("Stream error: {0}")]
|
||||
Stream(String),
|
||||
|
||||
#[error("Backpressure: {0}")]
|
||||
Backpressure(String),
|
||||
|
||||
#[error("Internal: {0}")]
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
/// Convenience alias for common result types.
|
||||
pub type CoreResult<T> = Result<T, CoreError>;
|
||||
pub type AgentResult<T> = Result<T, AgentError>;
|
||||
pub type ProviderResult<T> = Result<T, ProviderError>;
|
||||
pub type MemoryResult<T> = Result<T, MemoryError>;
|
||||
pub type PlatformResult<T> = Result<T, PlatformError>;
|
||||
pub type SecurityResult<T> = Result<T, SecurityError>;
|
||||
pub type KanbanResult<T> = Result<T, KanbanError>;
|
||||
pub type SubAgentResult<T> = Result<T, SubAgentError>;
|
||||
pub type SkillsResult<T> = Result<T, SkillsError>;
|
||||
pub type ProfileResult<T> = Result<T, ProfileError>;
|
||||
pub type GatewayResult<T> = Result<T, GatewayError>;
|
||||
13
crates/gyandoot-core/src/lib.rs
Normal file
13
crates/gyandoot-core/src/lib.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
//! GyanDoot Core — Shared types, configuration, and error types for the platform.
|
||||
//!
|
||||
//! This crate provides the foundational types used across all other GyanDoot
|
||||
//! crates: agent identifiers, message types, configuration structures, and
|
||||
//! a unified error hierarchy.
|
||||
|
||||
pub mod types;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
|
||||
pub use types::*;
|
||||
pub use config::*;
|
||||
pub use error::*;
|
||||
288
crates/gyandoot-core/src/types.rs
Normal file
288
crates/gyandoot-core/src/types.rs
Normal file
@@ -0,0 +1,288 @@
|
||||
//! Core type definitions for GyanDoot agents, messages, and sessions.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Unique identifier for an agent.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct AgentId(pub Uuid);
|
||||
|
||||
impl AgentId {
|
||||
pub fn new() -> Self {
|
||||
Self(Uuid::new_v4())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AgentId {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AgentId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique identifier for a session.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct SessionId(pub Uuid);
|
||||
|
||||
impl SessionId {
|
||||
pub fn new() -> Self {
|
||||
Self(Uuid::new_v4())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SessionId {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SessionId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The role of a message sender in a conversation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum MessageRole {
|
||||
System,
|
||||
User,
|
||||
Assistant,
|
||||
Tool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageRole {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
MessageRole::System => write!(f, "system"),
|
||||
MessageRole::User => write!(f, "user"),
|
||||
MessageRole::Assistant => write!(f, "assistant"),
|
||||
MessageRole::Tool => write!(f, "tool"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for MessageRole {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"system" => Ok(MessageRole::System),
|
||||
"user" => Ok(MessageRole::User),
|
||||
"assistant" => Ok(MessageRole::Assistant),
|
||||
"tool" => Ok(MessageRole::Tool),
|
||||
_ => Err(format!("Invalid message role: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single message in an agent conversation.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Message {
|
||||
pub role: MessageRole,
|
||||
pub content: String,
|
||||
pub tool_calls: Option<Vec<ToolCall>>,
|
||||
pub tool_result: Option<String>,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn new(role: MessageRole, content: impl Into<String>) -> Self {
|
||||
Self {
|
||||
role,
|
||||
content: content.into(),
|
||||
tool_calls: None,
|
||||
tool_result: None,
|
||||
timestamp: Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_tool_calls(mut self, calls: Vec<ToolCall>) -> Self {
|
||||
self.tool_calls = Some(calls);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_tool_result(mut self, result: String) -> Self {
|
||||
self.tool_result = Some(result);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A tool call invoked by the agent.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolCall {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub arguments: serde_json::Value,
|
||||
}
|
||||
|
||||
/// A tool definition that the agent can invoke.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolDefinition {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub parameters: serde_json::Value,
|
||||
}
|
||||
|
||||
/// The execution result of a tool.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ToolResult {
|
||||
pub tool_call_id: String,
|
||||
pub success: bool,
|
||||
pub output: String,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Represents a platform type the agent can connect to.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PlatformKind {
|
||||
Cli,
|
||||
Telegram,
|
||||
Discord,
|
||||
Webhook,
|
||||
}
|
||||
|
||||
impl FromStr for PlatformKind {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"cli" => Ok(PlatformKind::Cli),
|
||||
"telegram" => Ok(PlatformKind::Telegram),
|
||||
"discord" => Ok(PlatformKind::Discord),
|
||||
"webhook" => Ok(PlatformKind::Webhook),
|
||||
_ => Err(format!("Unknown platform: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Status of an agent or task.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AgentStatus {
|
||||
Idle,
|
||||
Running,
|
||||
Thinking,
|
||||
WaitingForTool,
|
||||
Error(String),
|
||||
}
|
||||
|
||||
/// The complete conversation context passed to the LLM.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConversationContext {
|
||||
pub messages: Vec<Message>,
|
||||
pub system_prompt: String,
|
||||
pub tools: Vec<ToolDefinition>,
|
||||
pub max_tokens: usize,
|
||||
}
|
||||
|
||||
/// Task prioritization levels for the Kanban system.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Priority {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Critical,
|
||||
}
|
||||
|
||||
/// A task unit in the Kanban system.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Task {
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub priority: Priority,
|
||||
pub status: TaskStatus,
|
||||
pub assigned_agent: Option<AgentId>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub subtasks: Vec<Uuid>,
|
||||
pub dependencies: Vec<Uuid>,
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum TaskStatus {
|
||||
Backlog,
|
||||
Ready,
|
||||
InProgress,
|
||||
Review,
|
||||
Blocked,
|
||||
Done,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
/// LLM provider identifiers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ProviderKind {
|
||||
OpenAI,
|
||||
Anthropic,
|
||||
Ollama,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl FromStr for ProviderKind {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"openai" => Ok(ProviderKind::OpenAI),
|
||||
"anthropic" => Ok(ProviderKind::Anthropic),
|
||||
"ollama" => Ok(ProviderKind::Ollama),
|
||||
other => Ok(ProviderKind::Custom(other.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A profile configuration key-value map.
|
||||
pub type ConfigMap = HashMap<String, serde_json::Value>;
|
||||
|
||||
/// Data directory resolver using platform-agnostic paths.
|
||||
pub fn data_dir() -> PathBuf {
|
||||
let home = dirs::home_dir().expect("Cannot determine home directory");
|
||||
home.join(".local").join("share").join("gyandoot")
|
||||
}
|
||||
|
||||
pub fn config_dir() -> PathBuf {
|
||||
let home = dirs::home_dir().expect("Cannot determine home directory");
|
||||
home.join(".config").join("gyandoot")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_agent_id() {
|
||||
let id = AgentId::new();
|
||||
assert!(!id.to_string().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_session_id() {
|
||||
let id = SessionId::new();
|
||||
assert!(!id.to_string().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_role_from_str() {
|
||||
assert_eq!("user".parse::<MessageRole>().unwrap(), MessageRole::User);
|
||||
assert_eq!("assistant".parse::<MessageRole>().unwrap(), MessageRole::Assistant);
|
||||
assert!("invalid".parse::<MessageRole>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_platform_kind() {
|
||||
assert_eq!("cli".parse::<PlatformKind>().unwrap(), PlatformKind::Cli);
|
||||
assert_eq!("discord".parse::<PlatformKind>().unwrap(), PlatformKind::Discord);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user