use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use smart_default::SmartDefault as Default;
use crate::Message;
use crate::prelude::*;
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize)]
pub enum Format
{
#[default]
Text,
Json,
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Options
{
#[default(Format::Text)]
pub(crate) format: Format,
#[default(false)]
pub(crate) thinking: bool,
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ChatPrompt
{
pub(crate) messages: Vec<Message>,
#[serde(default)]
pub(crate) options: Options,
pub(crate) template_context: HashMap<String, minijinja::Value>,
}
impl ChatPrompt
{
pub fn message(mut self, role: Role, content: impl Into<String>) -> Self
{
self.messages.push(Message {
role,
content: content.into(),
});
self
}
pub fn message_opt(mut self, role: Role, content: Option<String>) -> Self
{
if let Some(content) = content
{
self.messages.push(Message { role, content });
}
self
}
pub fn user(self, content: impl Into<String>) -> Self
{
self.message(Role::User, content)
}
pub fn system(self, content: impl Into<String>) -> Self
{
self.message(Role::System, content)
}
pub fn system_opt(self, content: Option<String>) -> Self
{
self.message_opt(Role::System, content)
}
pub fn assistant(self, content: impl Into<String>) -> Self
{
self.message(Role::Assistant, content)
}
pub fn assistant_opt(self, content: Option<String>) -> Self
{
self.message_opt(Role::Assistant, content)
}
pub fn format(mut self, format: impl Into<Format>) -> Self
{
self.options.format = format.into();
self
}
pub fn thiking(mut self, thinking: bool) -> Self
{
self.options.thinking = thinking;
self
}
pub fn options(mut self, options: Options) -> Self
{
self.options = options;
self
}
pub fn template_context(mut self, key: String, value: impl Into<minijinja::Value>) -> Self
{
self.template_context.insert(key, value.into());
self
}
}
#[derive(Debug)]
pub struct GenerationPrompt
{
pub(crate) user: String,
pub(crate) system: Option<String>,
pub(crate) assistant: Option<String>,
pub(crate) options: Options,
pub(crate) template_context: HashMap<String, minijinja::Value>,
#[cfg(feature = "image")]
pub(crate) image: Option<kproc_values::Image>,
}
impl GenerationPrompt
{
pub fn prompt(user: impl Into<String>) -> Self
{
Self {
user: user.into(),
system: Default::default(),
assistant: Default::default(),
options: Default::default(),
template_context: Default::default(),
#[cfg(feature = "image")]
image: None,
}
}
pub fn system(mut self, content: impl Into<String>) -> Self
{
self.system = Some(content.into());
self
}
pub fn assistant(mut self, content: impl Into<String>) -> Self
{
self.assistant = Some(content.into());
self
}
pub fn format(mut self, format: impl Into<Format>) -> Self
{
self.options.format = format.into();
self
}
pub fn thinking(mut self, thinking: bool) -> Self
{
self.options.thinking = thinking;
self
}
pub fn template_context(mut self, key: String, value: impl Into<minijinja::Value>) -> Self
{
self.template_context.insert(key, value.into());
self
}
#[cfg(feature = "image")]
pub fn image(mut self, image: impl Into<kproc_values::Image>) -> Self
{
self.image = Some(image.into());
self
}
}
impl From<GenerationPrompt> for Vec<Message>
{
fn from(value: GenerationPrompt) -> Self
{
let mut vec = Self::default();
if let Some(system) = value.system
{
vec.push(Message {
role: Role::System,
content: system,
});
}
if let Some(assistant) = value.assistant
{
vec.push(Message {
role: Role::Assistant,
content: assistant,
});
}
vec.push(Message {
role: Role::User,
content: value.user,
});
vec
}
}