feat(tui): add usage overlay and sidebar for displaying usage statistics and tasks
feat(tui): implement status bar with connection and turn state indicators feat(tui): create workflow panel for agent status and progress visualization feat(web): introduce web frontend interface with static file serving feat(ws): add WebSocket interface for real-time communication and session management
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
//! Conversation use-case implementation.
|
||||
//!
|
||||
//! `ConversationServiceImpl` implements [`ConversationService`] from the
|
||||
//! domain layer. It is generic over `R: ConversationRepository`, delegating
|
||||
//! all persistence to that adapter.
|
||||
//!
|
||||
//! # Flow
|
||||
//!
|
||||
//! Each method computes the session directory from the session ID, then
|
||||
//! delegates the actual I/O to the injected `repo`. Error context is
|
||||
//! added at this layer to identify which session caused the failure.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tracing;
|
||||
|
||||
use zesdex_domain::cms::{Conversation, ConversationRepository, ServiceError};
|
||||
use zesdex_domain::core::ChatMessage;
|
||||
|
||||
/// Service implementation for conversation CRUD operations.
|
||||
///
|
||||
/// Generic over `R: ConversationRepository` so the persistence layer
|
||||
/// can be swapped without changing business logic.
|
||||
pub struct ConversationServiceImpl<R> {
|
||||
pub repo: R,
|
||||
/// Base directory containing session subdirectories.
|
||||
pub sessions_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl<R: ConversationRepository> ConversationServiceImpl<R> {
|
||||
/// Create a new service with the given repository and sessions directory.
|
||||
pub fn new(repo: R, sessions_dir: impl Into<PathBuf>) -> Self {
|
||||
tracing::debug!("creating ConversationServiceImpl");
|
||||
Self {
|
||||
repo,
|
||||
sessions_dir: sessions_dir.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the session directory for a given session id.
|
||||
fn session_dir(&self, session_id: &str) -> PathBuf {
|
||||
self.sessions_dir.join(session_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ConversationRepository> zesdex_domain::cms::ConversationService
|
||||
for ConversationServiceImpl<R>
|
||||
{
|
||||
fn load_conversation(&self, session_id: &str) -> Result<Conversation, ServiceError> {
|
||||
tracing::debug!("loading conversation for session {session_id}");
|
||||
let dir = self.session_dir(session_id);
|
||||
self.repo.load(&dir).map_err(ServiceError::Repository)
|
||||
}
|
||||
|
||||
fn save_conversation(&self, conv: &Conversation) -> Result<(), ServiceError> {
|
||||
tracing::debug!("saving conversation for session {}", conv.session_id);
|
||||
let dir = self.session_dir(&conv.session_id);
|
||||
self.repo.save(&dir, conv)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_message(
|
||||
&self,
|
||||
conv: &mut Conversation,
|
||||
msg: ChatMessage,
|
||||
) -> Result<(), ServiceError> {
|
||||
tracing::debug!("adding message to session {}", conv.session_id);
|
||||
conv.push(msg);
|
||||
let dir = self.session_dir(&conv.session_id);
|
||||
self.repo.save(&dir, conv)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//! Memory use-case implementation.
|
||||
//!
|
||||
//! `MemoryServiceImpl` implements [`MemoryService`] from the domain
|
||||
//! layer. It is generic over `R: MemoryRepository`, delegating all
|
||||
//! persistence to that adapter.
|
||||
//!
|
||||
//! # Flow
|
||||
//!
|
||||
//! Each method delegates to the injected `repo` with the configured
|
||||
//! `memory_dir`. Error context is added at this layer to identify which
|
||||
//! memory operation failed.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tracing;
|
||||
|
||||
use zesdex_domain::cms::{Memory, MemoryRepository, ServiceError};
|
||||
|
||||
/// Service implementation for memory CRUD operations.
|
||||
///
|
||||
/// Generic over `R: MemoryRepository` so the persistence layer can be
|
||||
/// swapped without changing business logic.
|
||||
pub struct MemoryServiceImpl<R> {
|
||||
pub repo: R,
|
||||
/// Base directory for memory storage files.
|
||||
pub memory_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl<R: MemoryRepository> MemoryServiceImpl<R> {
|
||||
/// Create a new service with the given repository and memory directory.
|
||||
pub fn new(repo: R, memory_dir: impl Into<PathBuf>) -> Self {
|
||||
tracing::debug!("creating MemoryServiceImpl");
|
||||
Self {
|
||||
repo,
|
||||
memory_dir: memory_dir.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: MemoryRepository> zesdex_domain::cms::MemoryService for MemoryServiceImpl<R> {
|
||||
fn list_memories(&self) -> Result<Vec<String>, ServiceError> {
|
||||
tracing::debug!("listing memories from {:?}", self.memory_dir);
|
||||
self.repo
|
||||
.list(&self.memory_dir)
|
||||
.map_err(ServiceError::Repository)
|
||||
}
|
||||
|
||||
fn save_memory(&self, memory: &Memory) -> Result<(), ServiceError> {
|
||||
tracing::debug!("saving memory '{}'", memory.name);
|
||||
self.repo.save(&self.memory_dir, memory)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_memory(&self, name: &str) -> Result<(), ServiceError> {
|
||||
tracing::debug!("deleting memory '{name}'");
|
||||
self.repo
|
||||
.delete(&self.memory_dir, name)
|
||||
.map_err(ServiceError::Repository)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//! CMS use-case implementations.
|
||||
//!
|
||||
//! Contains concrete service types that implement the domain's CMS
|
||||
//! service traits by coordinating injected repository dependencies.
|
||||
//!
|
||||
//! # Use Cases
|
||||
//!
|
||||
//! - [`conversation_service`] — `ConversationServiceImpl`: conversation CRUD
|
||||
//! - [`memory_service`] — `MemoryServiceImpl`: long-term memory management
|
||||
//! - [`settings_service`] — `SettingsServiceImpl`: settings & app-config management
|
||||
|
||||
pub mod conversation_service;
|
||||
pub mod memory_service;
|
||||
pub mod settings_service;
|
||||
|
||||
pub use conversation_service::ConversationServiceImpl;
|
||||
pub use memory_service::MemoryServiceImpl;
|
||||
pub use settings_service::SettingsServiceImpl;
|
||||
@@ -0,0 +1,76 @@
|
||||
//! Settings and app-config use-case implementation.
|
||||
//!
|
||||
//! `SettingsServiceImpl` implements [`SettingsService`] from the domain
|
||||
//! layer. It is generic over `S: SettingsRepository` and `C: AppConfigRepository`,
|
||||
//! delegating persistence to those adapters.
|
||||
//!
|
||||
//! # Flow
|
||||
//!
|
||||
//! Each method delegates to the appropriate injected repository with the
|
||||
//! configured `base_dir`. The `update_provider` method coordinates between
|
||||
//! both repositories: load app config → mutate provider map → save app config.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tracing;
|
||||
|
||||
use zesdex_domain::cms::{
|
||||
AppConfig, AppConfigRepository, ProviderConfig, ServiceError, Settings,
|
||||
SettingsRepository,
|
||||
};
|
||||
|
||||
/// Service implementation for settings and app-config operations.
|
||||
///
|
||||
/// Generic over `S: SettingsRepository` and `C: AppConfigRepository` so
|
||||
/// the persistence layer can be swapped without changing business logic.
|
||||
pub struct SettingsServiceImpl<S, C> {
|
||||
pub settings_repo: S,
|
||||
pub app_config_repo: C,
|
||||
pub base_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl<S: SettingsRepository, C: AppConfigRepository> SettingsServiceImpl<S, C> {
|
||||
/// Create a new service with the given repositories and base directory.
|
||||
pub fn new(
|
||||
settings_repo: S,
|
||||
app_config_repo: C,
|
||||
base_dir: impl Into<PathBuf>,
|
||||
) -> Self {
|
||||
tracing::debug!("creating SettingsServiceImpl");
|
||||
Self {
|
||||
settings_repo,
|
||||
app_config_repo,
|
||||
base_dir: base_dir.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SettingsRepository, C: AppConfigRepository>
|
||||
zesdex_domain::cms::SettingsService for SettingsServiceImpl<S, C>
|
||||
{
|
||||
fn load_settings(&self) -> Result<Settings, ServiceError> {
|
||||
tracing::debug!("loading settings");
|
||||
self.settings_repo
|
||||
.load(&self.base_dir)
|
||||
.map_err(ServiceError::Repository)
|
||||
}
|
||||
|
||||
fn save_settings(&self, settings: &Settings) -> Result<(), ServiceError> {
|
||||
tracing::debug!("saving settings");
|
||||
self.settings_repo.save(&self.base_dir, settings)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_provider(
|
||||
&self,
|
||||
name: &str,
|
||||
config: &ProviderConfig,
|
||||
) -> Result<(), ServiceError> {
|
||||
tracing::debug!("updating provider '{name}'");
|
||||
let mut app_config: AppConfig = self.app_config_repo.load(&self.base_dir)?;
|
||||
app_config
|
||||
.providers
|
||||
.insert(name.to_string(), config.clone());
|
||||
self.app_config_repo.save(&self.base_dir, &app_config)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user