atman-runtime 1.7.0

atman flow execution runtime: evaluator, tool dispatch, provider dispatch, executor, memory stores
Documentation
use crate::error::RuntimeError;
use crate::eval::ContextMode;
use crate::eval::llm_args::LlmNodeArgs;
use crate::value::Value;

pub struct LlmContext {
    pub messages: Vec<crate::message::Message>,
    pub budget_text: String,
    pub session_messages_len: usize,
}

pub fn build_llm_context(
    args: &LlmNodeArgs,
    context_mode: ContextMode,
    _session: Option<&std::sync::Arc<crate::session::Session>>,
    session_messages_handle: Option<
        &std::sync::Arc<std::sync::Mutex<Vec<crate::message::Message>>>,
    >,
    turn_id: &crate::event::TurnId,
    events: Option<&crate::event::EventSink>,
    flow_run_id: Option<&crate::event::FlowRunId>,
) -> Result<LlmContext, Value> {
    let (final_messages, prompt_for_budget) = if let Some(msgs) = args.messages_override.clone() {
        let budget_text = msgs.last().map(|m| m.text_concat()).unwrap_or_default();
        (msgs, budget_text)
    } else if !matches!(context_mode, ContextMode::None)
        && let Some(handle) = session_messages_handle
    {
        let mut history = match context_mode {
            ContextMode::Session => handle.lock().unwrap().clone(),
            ContextMode::SessionRecent(n) => {
                let all = handle.lock().unwrap().clone();
                let start = all.len().saturating_sub(n);
                all[start..].to_vec()
            }
            ContextMode::None => Vec::new(),
        };
        let budget_text = args.prompt.clone().unwrap_or_default();
        if let Some(p) = args.prompt.clone()
            && !p.is_empty()
        {
            history.push(crate::message::Message::user_text(turn_id.clone(), p));
        }
        (history, budget_text)
    } else {
        let Some(mut prompt_text) = args.prompt.clone() else {
            return Err(Value::Err(RuntimeError::MissingArg(
                "llm node: either `prompt:` or `messages:` required".into(),
            )));
        };
        if let Some(budget) = args.context_budget {
            let (truncated, stat) = super::truncate_prompt_to_budget_tracked(prompt_text, budget);
            prompt_text = truncated;
            if let (Some(sink), Some(stat)) = (events, stat) {
                sink.emit(crate::event::Event::ContextTruncated {
                    turn_id: Some(turn_id.clone()),
                    flow_run_id: flow_run_id.cloned(),
                    original_chars: stat.original_chars as u64,
                    result_chars: stat.result_chars as u64,
                    dropped_chars: stat.dropped_chars as u64,
                    budget_tokens: stat.budget_tokens,
                });
            }
        }
        let user_msg = crate::message::Message::user_text(turn_id.clone(), prompt_text.clone());
        (vec![user_msg], prompt_text)
    };
    let session_messages_len = final_messages.len();

    Ok(LlmContext {
        messages: final_messages,
        budget_text: prompt_for_budget,
        session_messages_len,
    })
}