use std::sync::Arc;
use locode_protocol::{ContentBlock, Event, Message, Report, Role};
use locode_provider::Provider;
use locode_tools::Registry;
use tokio_util::sync::CancellationToken;
use crate::approve::{AllowAll, Approver};
use crate::config::EngineConfig;
use crate::sink::EventSink;
pub struct Session {
pub(crate) provider: Arc<dyn Provider>,
pub(crate) registry: Registry,
pub(crate) preamble: Vec<Message>,
pub(crate) config: EngineConfig,
pub(crate) sink: Box<dyn EventSink>,
pub(crate) cancel: CancellationToken,
pub(crate) history: Vec<Message>,
pub(crate) turns_run: u32,
pub(crate) approver: Arc<dyn Approver>,
pub(crate) skills_body: Option<String>,
pub(crate) input_queue: crate::InputQueue,
}
impl Session {
#[must_use]
pub fn new(
provider: Arc<dyn Provider>,
registry: Registry,
preamble: Vec<Message>,
config: EngineConfig,
sink: Box<dyn EventSink>,
) -> Self {
Self {
provider,
registry,
history: preamble.clone(),
preamble,
config,
sink,
cancel: CancellationToken::new(),
turns_run: 0,
approver: Arc::new(AllowAll),
skills_body: None,
input_queue: crate::InputQueue::new(),
}
}
#[must_use]
pub fn with_approver(mut self, approver: Arc<dyn Approver>) -> Self {
self.approver = approver;
self
}
pub fn set_model(&mut self, provider: Arc<dyn Provider>, model: &str) -> Message {
self.provider = provider;
self.config.model = model.to_string();
Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: format!(
"<system-reminder>\nThe model for this conversation is now {model}. \
Any earlier statement about which model you are is out of date.\n\
</system-reminder>"
),
}],
}
}
pub fn add_root(&mut self, root: std::path::PathBuf) {
if !self.config.instructions.extra_roots.contains(&root) {
self.config.instructions.extra_roots.push(root.clone());
}
if !self.config.skills.extra_roots.contains(&root) {
self.config.skills.extra_roots.push(root);
}
}
#[must_use]
pub fn input_queue(&self) -> crate::InputQueue {
self.input_queue.clone()
}
pub fn set_effort(&mut self, effort: Option<locode_provider::ReasoningEffort>) {
self.config.sampling_args.reasoning_effort = effort;
}
pub fn announce(&mut self, message: Message) {
self.history.push(message.clone());
self.sink.emit(Event::Message { message });
}
#[must_use]
pub fn history(&self) -> &[Message] {
&self.history
}
#[must_use]
pub fn cancel_handle(&self) -> CancellationToken {
self.cancel.clone()
}
pub async fn run(&mut self, user: Vec<ContentBlock>) -> Report {
self.drive(user).await
}
pub async fn run_text(&mut self, prompt: impl Into<String>) -> Report {
self.run(vec![ContentBlock::Text {
text: prompt.into(),
}])
.await
}
}