1use async_trait::async_trait;
4use runtime_api_contract::{
5 CreateExecutionRequest, CreateExecutionResponse, EventPage, EventPayload, ExecutionEvent,
6 ExecutionFailure, ExecutionOutcome, ExecutionState, ExecutionView, SubmitInputRequest,
7};
8use runtime_types::{CallerScope, DelegationLeaseRef, ExecutionId, OperationId, RequestAuthority};
9use thiserror::Error;
10
11#[async_trait]
12pub trait RuntimeLink: Send + Sync + 'static {
13 async fn create_execution(
14 &self,
15 authority: &RequestAuthority,
16 idempotency_key: &str,
17 request: CreateExecutionRequest,
18 ) -> Result<CreateExecutionResponse, LinkError>;
19
20 async fn execution(
21 &self,
22 authority: &RequestAuthority,
23 id: &ExecutionId,
24 ) -> Result<ExecutionView, LinkError>;
25
26 async fn events(
27 &self,
28 authority: &RequestAuthority,
29 id: &ExecutionId,
30 after: Option<u64>,
31 limit: usize,
32 ) -> Result<EventPage, LinkError>;
33
34 async fn submit_input(
35 &self,
36 authority: &RequestAuthority,
37 id: &ExecutionId,
38 request: SubmitInputRequest,
39 ) -> Result<ExecutionView, LinkError>;
40
41 async fn cancel(
42 &self,
43 authority: &RequestAuthority,
44 id: &ExecutionId,
45 ) -> Result<ExecutionView, LinkError>;
46}
47
48#[derive(Debug, Error)]
49pub enum LinkError {
50 #[error("invalid request: {0}")]
51 Invalid(String),
52 #[error("execution not found")]
53 NotFound,
54 #[error("caller is not allowed to access this execution")]
55 Forbidden,
56 #[error("execution state conflict: {0}")]
57 Conflict(String),
58 #[error("runtime is overloaded")]
59 Overloaded,
60 #[error("runtime dependency unavailable: {0}")]
61 Unavailable(String),
62 #[error("runtime internal failure: {0}")]
63 Internal(String),
64}
65
66#[derive(Debug, Clone)]
67pub struct NewJournalExecution {
68 pub idempotency_key: String,
69 pub caller: CallerScope,
70 pub request: CreateExecutionRequest,
71 pub view: ExecutionView,
72}
73
74#[derive(Debug, Clone)]
75pub struct JournalExecution {
76 pub idempotency_key: String,
77 pub caller: CallerScope,
78 pub request: CreateExecutionRequest,
79 pub view: ExecutionView,
80 pub version: u64,
81 pub pending_interaction: Option<JournalInteraction>,
82 pub input_receipts: std::collections::BTreeMap<OperationId, JournalInputReceipt>,
83 pub delegation: Option<JournalDelegation>,
84 pub claim: Option<JournalClaim>,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
88#[serde(rename_all = "camelCase", deny_unknown_fields)]
89pub struct JournalClaim {
90 pub worker_id: String,
91 pub expires_at_ms: i64,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
95#[serde(rename_all = "camelCase", deny_unknown_fields)]
96pub struct JournalDelegation {
97 pub lease_ref: DelegationLeaseRef,
98 pub expires_at_seconds: u64,
99 pub revision: u64,
100 pub cleanup_complete: bool,
101}
102
103#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
104#[serde(rename_all = "camelCase", deny_unknown_fields)]
105pub struct JournalInteraction {
106 pub request_id: String,
107 pub prompt: String,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
111#[serde(rename_all = "camelCase", deny_unknown_fields)]
112pub struct JournalInputReceipt {
113 pub request_id: String,
114 pub response_text: String,
115 pub event_sequence: u64,
116}
117
118#[derive(Debug, Clone)]
119pub struct JournalMutation {
120 pub execution: JournalExecution,
121 pub event: ExecutionEvent,
122 pub replayed: bool,
123}
124
125#[derive(Debug, Clone)]
126pub enum JournalReservation {
127 Created(JournalExecution),
128 Existing(JournalExecution),
129}
130
131#[derive(Debug, Error)]
132pub enum JournalError {
133 #[error("execution not found")]
134 NotFound,
135 #[error("journal state conflict")]
136 Conflict,
137 #[error("journal unavailable: {0}")]
138 Unavailable(String),
139}
140
141#[async_trait]
142pub trait ExecutionJournal: Send + Sync + 'static {
143 async fn reserve(
144 &self,
145 execution: NewJournalExecution,
146 ) -> Result<JournalReservation, JournalError>;
147
148 async fn get(&self, id: &ExecutionId) -> Result<JournalExecution, JournalError>;
149
150 async fn claim(
151 &self,
152 id: &ExecutionId,
153 worker_id: &str,
154 now_ms: i64,
155 expires_at_ms: i64,
156 ) -> Result<JournalExecution, JournalError>;
157
158 async fn recoverable(
159 &self,
160 now_ms: i64,
161 limit: usize,
162 ) -> Result<Vec<JournalExecution>, JournalError>;
163
164 async fn transition(
165 &self,
166 id: &ExecutionId,
167 expected: &[ExecutionState],
168 next: ExecutionState,
169 ) -> Result<JournalExecution, JournalError>;
170
171 async fn attach_delegation(
174 &self,
175 id: &ExecutionId,
176 delegation: JournalDelegation,
177 ) -> Result<JournalExecution, JournalError>;
178
179 async fn complete_delegation_cleanup(
181 &self,
182 id: &ExecutionId,
183 lease_ref: &DelegationLeaseRef,
184 ) -> Result<JournalExecution, JournalError>;
185
186 async fn finish(
187 &self,
188 id: &ExecutionId,
189 expected: &[ExecutionState],
190 next: ExecutionState,
191 outcome: Option<ExecutionOutcome>,
192 failure: Option<ExecutionFailure>,
193 ) -> Result<JournalExecution, JournalError>;
194
195 async fn transition_with_event(
199 &self,
200 id: &ExecutionId,
201 expected: &[ExecutionState],
202 next: ExecutionState,
203 outcome: Option<ExecutionOutcome>,
204 failure: Option<ExecutionFailure>,
205 payload: EventPayload,
206 ) -> Result<JournalMutation, JournalError>;
207
208 async fn append_event(
209 &self,
210 id: &ExecutionId,
211 payload: EventPayload,
212 ) -> Result<ExecutionEvent, JournalError>;
213
214 async fn begin_interaction(
216 &self,
217 id: &ExecutionId,
218 request_id: &str,
219 prompt: &str,
220 ) -> Result<JournalMutation, JournalError>;
221
222 async fn commit_interaction_input(
225 &self,
226 id: &ExecutionId,
227 operation_id: &OperationId,
228 request_id: &str,
229 response_text: &str,
230 ) -> Result<JournalMutation, JournalError>;
231
232 async fn complete_interaction(
234 &self,
235 id: &ExecutionId,
236 request_id: &str,
237 ) -> Result<JournalExecution, JournalError>;
238
239 async fn events(
240 &self,
241 id: &ExecutionId,
242 after: Option<u64>,
243 limit: usize,
244 ) -> Result<Vec<ExecutionEvent>, JournalError>;
245}