use crate::canonical::{select_model, CanonicalError, CanonicalRequest, Event, Model, Provenance};
use crate::config::{fill_absent, lead_with_preamble, strip_unsupported, ResolvedConfig};
use crate::registry::Registry;
use super::drive::{canonical_events, Sent};
use super::events::is_2xx;
use super::Host;
pub fn generate(
request: CanonicalRequest,
config: ResolvedConfig,
host: &Host,
) -> impl Iterator<Item = Event> {
match send_encoded(request, config, host) {
Ok(sent) => canonical_events(sent, host.clock.now()),
Err(e) => Box::new(std::iter::once(Event::Error(e)).chain(std::iter::once(Event::End))),
}
}
pub(super) fn send_encoded(
mut request: CanonicalRequest,
mut config: ResolvedConfig,
host: &Host,
) -> Result<Sent, CanonicalError> {
let cached = host.cache.get(&config.provider.name).unwrap_or_default();
let (wire_model, prov) = select_model(&cached, &config.model, &config.provider.name)?;
config.model = wire_model;
config.model_from_cache = matches!(prov, Provenance::Cached);
let registry = Registry::builtin();
let proto = registry.protocol(config.provider.protocol);
let beta: Vec<(&str, &str)> = config
.provider
.beta_headers
.iter()
.map(|(k, v)| (k.as_str(), v.as_str()))
.collect();
let ctx = config.provider_ctx(&beta);
fill_absent(&mut request, &config);
lead_with_preamble(&mut request, &config);
strip_unsupported(&mut request, &config);
let streamed = request.stream.unwrap_or(true);
let wire = proto.encode(&request, &ctx)?;
let resp = super::request::send(wire, &config, proto, &ctx, host)?;
let learned = !config.model_from_cache;
if is_2xx(resp.status) && (learned || cached.last_used.as_deref() != Some(&config.model)) {
let mut next = cached;
if learned {
next.models.push(Model {
id: config.model.clone(),
default: false,
..Default::default()
});
}
next.last_used = Some(config.model.clone());
host.cache.put(&config.provider.name, &next);
}
let hint = (resp.status == 404).then(|| model_hint(&config.model, config.model_from_cache));
Ok(Sent {
proto,
resp,
streamed,
hint,
})
}
fn model_hint(model: &str, from_cache: bool) -> String {
if from_cache {
format!(
"`{model}` was in the cache but the provider rejected it; \
the cache may be stale — re-run `bz --list-models`"
)
} else {
format!(
"`{model}` is not in the model cache; \
run `bz --list-models` to refresh or enable partial matching"
)
}
}