#![allow(clippy::future_not_send)]
pub(crate) mod anthropic;
pub mod cli;
mod context;
mod model;
mod openai;
mod prompts;
pub(crate) mod sse_parser;
use anthropic::AnthropicModel;
use context::Context;
use futures::Stream;
use model::Task;
use openai::OpenAIGPTModel;
use serde::Deserialize;
use thiserror::Error;
enum ConfigKind {
Ask(AskConfig),
Explain(ExplainConfig),
}
impl ConfigKind {
const fn model(&self) -> &ModelKind {
match self {
Self::Ask(config) => &config.model,
Self::Explain(config) => &config.model,
}
}
}
#[derive(Deserialize)]
struct AskConfig {
operating_system: String,
shell: String,
environment: Option<Vec<String>>,
programs: Option<Vec<String>>,
cwd: Option<()>,
depth: Option<u32>,
model: ModelKind,
}
#[derive(Deserialize)]
struct ExplainConfig {
operating_system: String,
shell: String,
environment: Option<Vec<String>>,
model: ModelKind,
cwd: Option<()>,
depth: Option<u32>,
}
impl Default for AskConfig {
fn default() -> Self {
Self {
operating_system: "Linux".to_string(),
shell: "Bash".to_string(),
environment: None,
programs: None,
cwd: None,
depth: None,
model: ModelKind::OpenAIGPT(OpenAIGPTModel::GPT4oMini),
}
}
}
impl Default for ExplainConfig {
fn default() -> Self {
Self {
operating_system: "Linux".to_string(),
shell: "Bash".to_string(),
environment: None,
cwd: None,
depth: None,
model: ModelKind::OpenAIGPT(OpenAIGPTModel::GPT4oMini),
}
}
}
#[derive(Deserialize, Clone)]
enum ModelKind {
OpenAIGPT(OpenAIGPTModel),
Anthropic(AnthropicModel),
}
#[derive(Debug, Error)]
pub(crate) enum ModelError {
#[error("{0}")]
Error(String),
}
impl From<Box<dyn std::error::Error + Send>> for ModelError {
fn from(e: Box<dyn std::error::Error + Send>) -> Self {
Self::Error(e.to_string())
}
}
#[allow(unused)]
async fn model_request(
model: ModelKind,
request: String,
context: Context,
task: Task,
) -> Result<String, ModelError> {
match model {
ModelKind::OpenAIGPT(model) => model
.send(request, context, task)
.await
.map_err(|err| ModelError::Error(err.to_string())),
ModelKind::Anthropic(model) => model
.send(request, context, task)
.await
.map_err(|err| ModelError::Error(err.to_string())),
}
}
async fn model_stream_request(
model: ModelKind,
request: String,
context: Context,
task: Task,
) -> Result<impl Stream<Item = Result<String, ModelError>> + Send, ModelError> {
match model {
ModelKind::OpenAIGPT(model) => model
.send_streaming(request, context, task)
.await
.map_err(|e| ModelError::Error(e.to_string())),
ModelKind::Anthropic(model) => model
.send_streaming(request, context, task)
.await
.map_err(|e| ModelError::Error(e.to_string())),
}
}
fn build_context_request(request: &str, context: Context) -> String {
String::from(context) + &format!("Here is your <task>: \n <task>{request}</task>")
}