Skip to main content

roder_api/
backend.rs

1//! A complete agent runtime behind Roder's user interface.
2//!
3//! Backends own inference, tools, and conversation state. The UI only sends
4//! user actions and renders normalized events. This is distinct from an
5//! `InferenceEngine`, which participates in Roder's own tool loop.
6
7use std::path::PathBuf;
8
9use crate::inference::TokenUsage;
10use crate::policy_mode::PolicyMode;
11use async_trait::async_trait;
12
13#[derive(Debug, Clone)]
14pub struct BackendStart {
15    pub cwd: PathBuf,
16    pub model: Option<String>,
17    pub resume_thread: Option<String>,
18    pub policy_mode: PolicyMode,
19}
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum BackendEvent {
23    TurnStarted {
24        turn_id: String,
25    },
26    Text(String),
27    Usage(TokenUsage),
28    ProviderMetadata(serde_json::Value),
29    ToolStarted {
30        id: String,
31        label: String,
32    },
33    ToolOutput {
34        id: String,
35        text: String,
36    },
37    ToolFinished {
38        id: String,
39        label: String,
40        summary: String,
41    },
42    FileChanged {
43        path: String,
44        change_type: String,
45    },
46    Approval {
47        request_id: String,
48        description: String,
49    },
50    Question {
51        request_id: String,
52        questions: Vec<BackendQuestion>,
53    },
54    TurnFinished {
55        turn_id: String,
56        status: String,
57    },
58    Notice(String),
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct BackendQuestion {
63    pub id: String,
64    pub text: String,
65    pub options: Vec<String>,
66}
67
68#[derive(Debug, Clone, Default)]
69pub struct BackendThreadSnapshot {
70    pub preview: String,
71    pub cwd: String,
72    pub created_at: i64,
73    pub updated_at: i64,
74    pub turns: Vec<BackendHistoryTurn>,
75}
76
77#[derive(Debug, Clone)]
78pub struct BackendThreadSummary {
79    pub id: String,
80    pub model: String,
81    pub snapshot: BackendThreadSnapshot,
82}
83
84#[derive(Debug, Clone)]
85pub struct BackendModel {
86    pub id: String,
87    pub name: String,
88    pub is_default: bool,
89    pub default_reasoning: String,
90    pub reasoning_efforts: Vec<String>,
91}
92
93#[derive(Debug, Clone)]
94pub struct BackendHistoryTurn {
95    pub id: String,
96    pub status: String,
97    pub started_at: Option<i64>,
98    pub completed_at: Option<i64>,
99    pub items: Vec<BackendHistoryItem>,
100}
101
102#[derive(Debug, Clone)]
103pub enum BackendHistoryItem {
104    User {
105        id: String,
106        text: String,
107    },
108    Agent {
109        id: String,
110        text: String,
111    },
112    Tool {
113        id: String,
114        name: String,
115        output: Option<String>,
116        status: String,
117    },
118    Other {
119        id: String,
120        payload: serde_json::Value,
121    },
122}
123
124#[async_trait]
125pub trait AgentBackend: Send + Sync {
126    fn id(&self) -> &'static str;
127
128    async fn connect(&self, start: BackendStart) -> anyhow::Result<Box<dyn AgentBackendSession>>;
129    async fn list_threads(
130        &self,
131        cwd: PathBuf,
132        limit: Option<usize>,
133    ) -> anyhow::Result<Vec<BackendThreadSummary>>;
134    async fn list_models(&self, cwd: PathBuf) -> anyhow::Result<Vec<BackendModel>>;
135}
136
137#[async_trait]
138pub trait AgentBackendSession: Send {
139    fn thread_id(&self) -> &str;
140    fn model(&self) -> &str;
141    fn reasoning(&self) -> &str;
142    fn snapshot(&self) -> BackendThreadSnapshot;
143    async fn send(
144        &mut self,
145        text: String,
146        policy_mode: Option<PolicyMode>,
147        model: Option<String>,
148        reasoning: Option<String>,
149    ) -> anyhow::Result<String>;
150    async fn interrupt(&mut self) -> anyhow::Result<()>;
151    async fn archive(&mut self) -> anyhow::Result<()>;
152    async fn approve(&mut self, request_id: &str, approved: bool) -> anyhow::Result<()>;
153    async fn answer(
154        &mut self,
155        request_id: &str,
156        answers: Vec<(String, String)>,
157    ) -> anyhow::Result<()>;
158    async fn next_event(&mut self) -> anyhow::Result<BackendEvent>;
159}