use std::sync::Arc;
use mlua::{Lua, UserData, UserDataFields, UserDataMethods, Value};
use crate::dialects::ToolDialectId;
use crate::model::ModelBinding;
pub(crate) type ModelInferHook =
Arc<dyn Fn(&Lua, &ModelBinding, &str) -> mlua::Result<String> + Send + Sync>;
#[derive(Debug, Clone)]
pub(crate) struct LuaModelHandle {
binding: ModelBinding,
}
impl LuaModelHandle {
#[must_use]
pub(crate) fn from_binding(binding: &ModelBinding) -> Self {
Self {
binding: binding.clone(),
}
}
#[must_use]
pub(crate) fn binding(&self) -> &ModelBinding {
&self.binding
}
#[must_use]
pub(crate) fn name(&self) -> &str {
self.binding.alias()
}
#[must_use]
pub(crate) fn model_id(&self) -> &str {
self.binding.id().name()
}
#[must_use]
pub(crate) fn description(&self) -> &str {
self.binding.description()
}
#[must_use]
pub(crate) fn context(&self) -> u32 {
self.binding.context().get()
}
#[must_use]
pub(crate) fn thinking(&self) -> Option<bool> {
self.binding.invocation().thinking
}
#[must_use]
pub(crate) fn temperature(&self) -> Option<f64> {
self.binding
.invocation()
.temperature
.map(crate::model::Temperature::get)
}
#[must_use]
pub(crate) fn max_tokens(&self) -> Option<u32> {
self.binding
.invocation()
.max_tokens
.map(std::num::NonZeroU32::get)
}
#[must_use]
pub(crate) fn dialect(&self) -> ToolDialectId {
self.binding.tool_dialect()
}
}
impl UserData for LuaModelHandle {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("name", |_, this| Ok(this.name().to_owned()));
fields.add_field_method_get("model_id", |_, this| Ok(this.model_id().to_owned()));
fields.add_field_method_get("description", |_, this| Ok(this.description().to_owned()));
fields.add_field_method_get("context", |_, this| Ok(this.context()));
fields.add_field_method_get("thinking", |_, this| Ok(this.thinking()));
fields.add_field_method_get("temperature", |_, this| Ok(this.temperature()));
fields.add_field_method_get("max_tokens", |_, this| Ok(this.max_tokens()));
fields.add_field_method_get("dialect", |_, this| Ok(this.dialect().to_string()));
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method(
"infer",
|lua, this, (prompt, opts): (String, Option<Value>)| {
reject_infer_options(opts.as_ref())?;
let hook = lua
.app_data_ref::<ModelInferHook>()
.ok_or_else(|| {
mlua::Error::external(
"model:infer is not available outside section execution",
)
})?
.clone();
hook(lua, this.binding(), &prompt)
},
);
}
}
pub(crate) fn reject_infer_options(opts: Option<&Value>) -> mlua::Result<()> {
match opts {
None | Some(Value::Nil) => Ok(()),
Some(_) => Err(mlua::Error::external(
"model:infer(prompt) does not accept a second argument; \
per-call inference options are not supported",
)),
}
}