Skip to main content

alice_runtime/
memory_context.rs

1//! Turn execution helpers with memory recall/writeback and skill injection.
2
3use bob_core::types::{RequestContext, RequestToolPolicy};
4use bob_runtime::AgentResponse;
5
6use crate::context::AliceRuntimeContext;
7
8/// Execute one turn with memory-aware + skill-augmented prompt context.
9///
10/// This function is primarily used for CLI commands that need direct agent backend access.
11/// For interactive chat scenarios, use `handle_input_with_skills` which properly integrates
12/// with AgentLoop and supports slash command routing.
13///
14/// # Errors
15///
16/// Returns an error if the agent runtime fails to execute the turn.
17pub async fn run_turn_with_memory(
18    context: &AliceRuntimeContext,
19    session_id: &str,
20    input: &str,
21) -> eyre::Result<AgentResponse> {
22    // 1. Memory recall
23    let recalled = match context.memory_service().recall_for_turn(session_id, input) {
24        Ok(hits) => hits,
25        Err(error) => {
26            tracing::warn!("memory recall failed: {error}");
27            Vec::new()
28        }
29    };
30    let memory_prompt =
31        alice_core::memory::service::MemoryService::render_recall_context(&recalled);
32
33    // 2. Skill injection
34    let skills_bundle = context.skill_composer().map(|composer| {
35        crate::skill_wiring::inject_skills_context(composer, input, context.skill_token_budget())
36    });
37
38    // 3. Compose system prompt: memory + skills
39    let mut system_parts = Vec::new();
40    if let Some(ref mem) = memory_prompt {
41        system_parts.push(mem.as_str());
42    }
43    if let Some(ref bundle) = skills_bundle &&
44        !bundle.prompt.is_empty()
45    {
46        system_parts.push(&bundle.prompt);
47    }
48    let system_prompt =
49        if system_parts.is_empty() { None } else { Some(system_parts.join("\n\n")) };
50
51    // 4. Build request context with skills metadata
52    let (selected_skills, tool_policy) = if let Some(ref bundle) = skills_bundle {
53        let policy = if bundle.selected_allowed_tools.is_empty() {
54            RequestToolPolicy::default()
55        } else {
56            RequestToolPolicy {
57                allow_tools: Some(bundle.selected_allowed_tools.clone()),
58                ..RequestToolPolicy::default()
59            }
60        };
61        (bundle.selected_skill_names.clone(), policy)
62    } else {
63        (Vec::new(), RequestToolPolicy::default())
64    };
65
66    let request_context = RequestContext { system_prompt, selected_skills, tool_policy };
67
68    // 5. Execute turn via agent backend
69    let session = context.backend().create_session_with_id(session_id);
70    let response = session.chat(input, request_context).await?;
71
72    // 6. Persist memory
73    if let Err(error) = context.memory_service().persist_turn(session_id, input, &response.content)
74    {
75        tracing::warn!("memory persistence failed: {error}");
76    }
77
78    Ok(response)
79}