Skip to main content

tirea_contract/runtime/
executor.rs

1use crate::event::interaction::{FrontendToolInvocation, Interaction};
2use crate::plugin::contract::AgentPlugin;
3use crate::runtime::activity::ActivityManager;
4use crate::thread::{Message, ToolCall};
5use crate::tool::contract::{Tool, ToolDescriptor, ToolResult};
6use crate::RunConfig;
7use async_trait::async_trait;
8use futures::Stream;
9use genai::chat::{ChatOptions, ChatRequest, ChatResponse, ChatStreamEvent};
10use serde_json::Value;
11use std::collections::HashMap;
12use std::pin::Pin;
13use std::sync::Arc;
14use thiserror::Error;
15use tirea_state::TrackedPatch;
16use tokio_util::sync::CancellationToken;
17
18/// Stream item type returned by LLM streaming executors.
19pub type LlmEventStream = Pin<Box<dyn Stream<Item = genai::Result<ChatStreamEvent>> + Send>>;
20
21/// Provider-neutral LLM execution contract consumed by the loop runtime.
22#[async_trait]
23pub trait LlmExecutor: Send + Sync {
24    /// Execute one non-streaming chat call.
25    async fn exec_chat_response(
26        &self,
27        model: &str,
28        chat_req: ChatRequest,
29        options: Option<&ChatOptions>,
30    ) -> genai::Result<ChatResponse>;
31
32    /// Execute one streaming chat call.
33    async fn exec_chat_stream_events(
34        &self,
35        model: &str,
36        chat_req: ChatRequest,
37        options: Option<&ChatOptions>,
38    ) -> genai::Result<LlmEventStream>;
39
40    /// Stable executor label for debug/telemetry output.
41    fn name(&self) -> &'static str {
42        "llm_executor"
43    }
44}
45
46/// Result of one tool call execution.
47#[derive(Debug, Clone)]
48pub struct ToolExecution {
49    pub call: ToolCall,
50    pub result: ToolResult,
51    pub patch: Option<TrackedPatch>,
52}
53
54/// Input envelope passed to tool execution strategies.
55pub struct ToolExecutionRequest<'a> {
56    pub tools: &'a HashMap<String, Arc<dyn Tool>>,
57    pub calls: &'a [ToolCall],
58    pub state: &'a Value,
59    pub tool_descriptors: &'a [ToolDescriptor],
60    pub plugins: &'a [Arc<dyn AgentPlugin>],
61    pub activity_manager: Option<Arc<dyn ActivityManager>>,
62    pub run_config: &'a RunConfig,
63    pub thread_id: &'a str,
64    pub thread_messages: &'a [Arc<Message>],
65    pub state_version: u64,
66    pub cancellation_token: Option<&'a CancellationToken>,
67}
68
69/// Output item produced by tool execution strategies.
70#[derive(Debug, Clone)]
71pub struct ToolExecutionResult {
72    pub execution: ToolExecution,
73    pub reminders: Vec<String>,
74    pub pending_interaction: Option<Interaction>,
75    pub pending_frontend_invocation: Option<FrontendToolInvocation>,
76    pub pending_patches: Vec<TrackedPatch>,
77}
78
79/// Error returned by custom tool executors.
80#[derive(Debug, Clone, Error)]
81pub enum ToolExecutorError {
82    #[error("tool execution cancelled")]
83    Cancelled { thread_id: String },
84    #[error("tool execution failed: {message}")]
85    Failed { message: String },
86}
87
88/// Strategy abstraction for tool execution.
89#[async_trait]
90pub trait ToolExecutor: Send + Sync {
91    async fn execute(
92        &self,
93        request: ToolExecutionRequest<'_>,
94    ) -> Result<Vec<ToolExecutionResult>, ToolExecutorError>;
95
96    /// Stable strategy label for logs/debug output.
97    fn name(&self) -> &'static str;
98
99    /// Whether apply step should enforce parallel patch conflict checks.
100    fn requires_parallel_patch_conflict_check(&self) -> bool {
101        false
102    }
103}