use std::path::Path;
use std::sync::Arc;
use anyhow::Context as _;
use async_trait::async_trait;
use futures_core::stream::BoxStream;
use paigasus_helikon_core::{
CancellationToken, Model, ModelCapabilities, ModelError, ModelEvent, ModelRequest,
};
use paigasus_helikon_evals::{MockModel, ScriptFile};
use paigasus_helikon_providers_anthropic::AnthropicModel;
use paigasus_helikon_providers_openai::OpenAiModel;
use crate::sidecar::ModelDef;
pub enum CliModel {
OpenAi(OpenAiModel),
Anthropic(AnthropicModel),
Mock(Arc<MockModel>),
}
#[async_trait]
impl Model for CliModel {
async fn invoke(
&self,
request: ModelRequest,
cancel: CancellationToken,
) -> Result<BoxStream<'static, Result<ModelEvent, ModelError>>, ModelError> {
match self {
CliModel::OpenAi(m) => m.invoke(request, cancel).await,
CliModel::Anthropic(m) => m.invoke(request, cancel).await,
CliModel::Mock(m) => m.invoke(request, cancel).await,
}
}
fn capabilities(&self) -> ModelCapabilities {
match self {
CliModel::OpenAi(m) => m.capabilities(),
CliModel::Anthropic(m) => m.capabilities(),
CliModel::Mock(m) => m.capabilities(),
}
}
fn provider(&self) -> &str {
match self {
CliModel::OpenAi(m) => m.provider(),
CliModel::Anthropic(m) => m.provider(),
CliModel::Mock(m) => m.provider(),
}
}
fn model(&self) -> &str {
match self {
CliModel::OpenAi(m) => m.model(),
CliModel::Anthropic(m) => m.model(),
CliModel::Mock(m) => m.model(),
}
}
}
pub fn build_model(def: &ModelDef, base_dir: &Path) -> anyhow::Result<CliModel> {
match def {
ModelDef::Openai { id } => Ok(CliModel::OpenAi(
OpenAiModel::chat(id)
.build()
.with_context(|| format!("failed to build OpenAI model '{id}'"))?,
)),
ModelDef::Anthropic { id } => Ok(CliModel::Anthropic(
AnthropicModel::messages(id)
.build()
.with_context(|| format!("failed to build Anthropic model '{id}'"))?,
)),
ModelDef::Mock { script } => {
let path = base_dir.join(script);
let model = MockModel::from_script_file(&path)
.with_context(|| format!("failed to load mock script '{}'", path.display()))?;
Ok(CliModel::Mock(model))
}
}
}
pub fn build_model_for_case(
def: &ModelDef,
base_dir: &Path,
case_id: &str,
) -> anyhow::Result<CliModel> {
match def {
ModelDef::Mock { script } => {
let path = base_dir.join(script);
let file = ScriptFile::load(&path)
.with_context(|| format!("failed to load mock script '{}'", path.display()))?;
Ok(CliModel::Mock(MockModel::with_scripts(
file.scripts_for(case_id),
)))
}
_ => build_model(def, base_dir),
}
}