use aion::Engine;
use serde::Serialize;
use serde_json::Value;
use super::document::{
CONTINUE_END_FIELD, CONTINUE_MESSAGE_FIELD, CONTINUE_SIGNAL, EMBEDDED_ASSISTANT_FILENAME,
EmbeddedAssistant, OBJECTIVE_INPUT, REPO_PATH_INPUT, STATUS_QUERY,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case", tag = "state")]
pub enum AssistantResidency {
Routed,
LoadedNotRouted {
routed_hash: Option<String>,
},
NotLoaded {
routed_hash: Option<String>,
},
Unknown {
reason: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct AssistantSessionContract {
pub objective_input: &'static str,
pub repo_path_input: &'static str,
pub continue_signal: &'static str,
pub message_field: &'static str,
pub end_field: &'static str,
pub status_query: &'static str,
}
impl AssistantSessionContract {
#[must_use]
pub const fn current() -> Self {
Self {
objective_input: OBJECTIVE_INPUT,
repo_path_input: REPO_PATH_INPUT,
continue_signal: CONTINUE_SIGNAL,
message_field: CONTINUE_MESSAGE_FIELD,
end_field: CONTINUE_END_FIELD,
status_query: STATUS_QUERY,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct AssistantSignal {
pub name: String,
pub input_schema: Value,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct AssistantDescriptor {
pub workflow_type: String,
pub task_queue: String,
pub content_hash: String,
pub document_filename: &'static str,
pub document_bytes: usize,
pub input_schema: Value,
pub signals: Vec<AssistantSignal>,
pub queries: Vec<String>,
pub session: AssistantSessionContract,
pub residency: AssistantResidency,
}
#[must_use]
pub fn describe(embedded: &EmbeddedAssistant, engine: &Engine) -> AssistantDescriptor {
AssistantDescriptor {
workflow_type: embedded.workflow_type().to_owned(),
task_queue: embedded.task_queue().to_owned(),
content_hash: embedded.content_hash().to_string(),
document_filename: EMBEDDED_ASSISTANT_FILENAME,
document_bytes: embedded.source().len(),
input_schema: embedded.input_schema().clone(),
signals: embedded
.signals()
.iter()
.map(|signal| AssistantSignal {
name: signal.name.clone(),
input_schema: signal.input_schema.clone(),
})
.collect(),
queries: embedded.queries().to_vec(),
session: AssistantSessionContract::current(),
residency: residency(embedded, engine),
}
}
fn residency(embedded: &EmbeddedAssistant, engine: &Engine) -> AssistantResidency {
let versions = match engine.list_workflow_versions() {
Ok(versions) => versions,
Err(error) => {
return AssistantResidency::Unknown {
reason: format!("the engine catalog could not be read: {error}"),
};
}
};
let embedded_hash = embedded.content_hash().to_string();
let resident: Vec<_> = versions
.into_iter()
.filter(|version| version.workflow_type == embedded.workflow_type())
.collect();
let loaded = resident
.iter()
.any(|version| version.content_hash.to_string() == embedded_hash);
let routed_hash = resident
.iter()
.find(|version| version.route_active)
.map(|version| version.content_hash.to_string());
if !loaded {
return AssistantResidency::NotLoaded { routed_hash };
}
if routed_hash.as_deref() == Some(embedded_hash.as_str()) {
AssistantResidency::Routed
} else {
AssistantResidency::LoadedNotRouted { routed_hash }
}
}