use anyhow::{Result, anyhow};
use super::super::Console;
pub(crate) struct Scripted {
answers: std::collections::VecDeque<String>,
pub said: Vec<String>,
pub secrets_asked: Vec<String>,
}
impl Scripted {
pub fn new(answers: &[&str]) -> Self {
Self {
answers: answers.iter().map(|a| (*a).to_string()).collect(),
said: Vec::new(),
secrets_asked: Vec::new(),
}
}
pub fn transcript(&self) -> String {
self.said.join("\n")
}
pub fn is_drained(&self) -> bool {
self.answers.is_empty()
}
fn next(&mut self, question: &str) -> Result<String> {
self.answers
.pop_front()
.ok_or_else(|| anyhow!("the wizard asked more than the script answered: {question}"))
}
}
impl Console for Scripted {
fn say(&mut self, line: &str) -> Result<()> {
self.said.push(line.to_string());
Ok(())
}
fn ask(&mut self, question: &str, default: Option<&str>) -> Result<String> {
self.said.push(match default {
Some(value) => format!("{question} [{value}]"),
None => question.to_string(),
});
let answer = self.next(question)?;
Ok(match (answer.trim().is_empty(), default) {
(true, Some(value)) => value.to_string(),
_ => answer,
})
}
fn ask_secret(&mut self, question: &str) -> Result<String> {
self.said.push(question.to_string());
self.secrets_asked.push(question.to_string());
self.next(question)
}
}
pub(crate) enum Catalog {
Serves(Vec<crate::llm::models::Model>),
Unavailable,
Rejected,
}
impl Catalog {
pub fn of(ids: &[&str]) -> Self {
Self::Serves(
ids.iter()
.map(|id| crate::llm::models::Model {
id: (*id).to_string(),
display_name: None,
})
.collect(),
)
}
}
impl crate::llm::models::ModelSource for Catalog {
async fn list(
&self,
_endpoint: &str,
_api_key: &str,
_protocol: open_agent::ApiProtocol,
) -> Result<Vec<crate::llm::models::Model>, crate::llm::models::ListError> {
match self {
Self::Serves(models) => Ok(models.clone()),
Self::Unavailable => Err(crate::llm::models::ListError::Unsupported),
Self::Rejected => Err(crate::llm::models::ListError::Unauthorized(401)),
}
}
}
pub(crate) struct Recording {
inner: Catalog,
pub calls: std::cell::RefCell<Vec<(String, String, open_agent::ApiProtocol)>>,
}
impl Recording {
pub fn new(inner: Catalog) -> Self {
Self {
inner,
calls: std::cell::RefCell::new(Vec::new()),
}
}
}
impl crate::llm::models::ModelSource for Recording {
async fn list(
&self,
endpoint: &str,
api_key: &str,
protocol: open_agent::ApiProtocol,
) -> Result<Vec<crate::llm::models::Model>, crate::llm::models::ListError> {
self.calls
.borrow_mut()
.push((endpoint.to_string(), api_key.to_string(), protocol));
self.inner.list(endpoint, api_key, protocol).await
}
}
pub(crate) fn number_of(key: &str) -> String {
let index = crate::cli::init::presets::PRESETS
.iter()
.position(|preset| preset.key == key)
.unwrap_or_else(|| panic!("no preset named `{key}`"));
(index + 1).to_string()
}
pub(crate) enum Quirked {
Unavailable,
Knows(crate::llm::quirks::Registry),
}
impl Quirked {
pub fn from_json(body: &str) -> Self {
Self::Knows(
crate::llm::quirks::Registry::distil(body, 0).expect("the fixture document distils"),
)
}
}
impl crate::llm::quirks::QuirksSource for Quirked {
async fn registry(
&self,
) -> Result<crate::llm::quirks::Registry, crate::llm::quirks::QuirksError> {
match self {
Self::Unavailable => Err(crate::llm::quirks::QuirksError::Transport(
"the stub is offline".to_string(),
)),
Self::Knows(registry) => Ok(registry.clone()),
}
}
}
pub(crate) static NEVER_SET: fn(&str) -> bool = |_| false;
pub(crate) static ALWAYS_SET: fn(&str) -> bool = |_| true;
pub(crate) static CODEX_READY: fn() -> Result<crate::llm::codex::CodexStatus, String> =
|| Ok(crate::llm::codex::CodexStatus::new("test-version"));
pub(crate) fn deps<'a, S, Q>(
store: &'a crate::auth::AuthStore,
source: &'a S,
quirks_source: &'a Q,
) -> super::super::Deps<'a, S, Q> {
super::super::Deps {
store,
source,
quirks_source,
env_is_set: &NEVER_SET,
codex_status: &CODEX_READY,
}
}
pub(crate) fn deps_with_env_set<'a, S, Q>(
store: &'a crate::auth::AuthStore,
source: &'a S,
quirks_source: &'a Q,
) -> super::super::Deps<'a, S, Q> {
super::super::Deps {
env_is_set: &ALWAYS_SET,
..deps(store, source, quirks_source)
}
}