codex_runtime/runtime/api/
models.rs1use std::time::Duration;
2
3use serde_json::Value;
4use thiserror::Error;
5
6use crate::plugin::{BlockReason, HookPhase};
7use crate::runtime::errors::{RpcError, RuntimeError};
8
9use super::{
10 ApprovalPolicy, PromptAttachment, ReasoningEffort, SandboxPolicy, ThreadId, TurnId,
11 DEFAULT_REASONING_EFFORT,
12};
13
14#[derive(Clone, Debug, PartialEq)]
15pub struct PromptRunParams {
16 pub cwd: String,
17 pub prompt: String,
18 pub model: Option<String>,
19 pub effort: Option<ReasoningEffort>,
20 pub approval_policy: ApprovalPolicy,
21 pub sandbox_policy: SandboxPolicy,
22 pub privileged_escalation_approved: bool,
25 pub attachments: Vec<PromptAttachment>,
26 pub timeout: Duration,
27 pub output_schema: Option<Value>,
28}
29
30impl PromptRunParams {
31 pub fn new(cwd: impl Into<String>, prompt: impl Into<String>) -> Self {
34 Self {
35 cwd: cwd.into(),
36 prompt: prompt.into(),
37 model: None,
38 effort: Some(DEFAULT_REASONING_EFFORT),
39 approval_policy: ApprovalPolicy::Never,
40 sandbox_policy: SandboxPolicy::Preset(super::SandboxPreset::ReadOnly),
41 privileged_escalation_approved: false,
42 attachments: Vec::new(),
43 timeout: Duration::from_secs(120),
44 output_schema: None,
45 }
46 }
47
48 pub fn with_model(mut self, model: impl Into<String>) -> Self {
51 self.model = Some(model.into());
52 self
53 }
54
55 pub fn with_effort(mut self, effort: ReasoningEffort) -> Self {
58 self.effort = Some(effort);
59 self
60 }
61
62 pub fn with_approval_policy(mut self, approval_policy: ApprovalPolicy) -> Self {
65 self.approval_policy = approval_policy;
66 self
67 }
68
69 pub fn with_sandbox_policy(mut self, sandbox_policy: SandboxPolicy) -> Self {
72 self.sandbox_policy = sandbox_policy;
73 self
74 }
75
76 pub fn allow_privileged_escalation(mut self) -> Self {
79 self.privileged_escalation_approved = true;
80 self
81 }
82
83 pub fn with_timeout(mut self, timeout: Duration) -> Self {
86 self.timeout = timeout;
87 self
88 }
89
90 pub fn with_output_schema(mut self, output_schema: Value) -> Self {
92 self.output_schema = Some(output_schema);
93 self
94 }
95
96 pub fn with_attachment(mut self, attachment: PromptAttachment) -> Self {
99 self.attachments.push(attachment);
100 self
101 }
102
103 pub fn attach_path(self, path: impl Into<String>) -> Self {
106 self.with_attachment(PromptAttachment::AtPath {
107 path: path.into(),
108 placeholder: None,
109 })
110 }
111
112 pub fn attach_path_with_placeholder(
115 self,
116 path: impl Into<String>,
117 placeholder: impl Into<String>,
118 ) -> Self {
119 self.with_attachment(PromptAttachment::AtPath {
120 path: path.into(),
121 placeholder: Some(placeholder.into()),
122 })
123 }
124
125 pub fn attach_image_url(self, url: impl Into<String>) -> Self {
128 self.with_attachment(PromptAttachment::ImageUrl { url: url.into() })
129 }
130
131 pub fn attach_local_image(self, path: impl Into<String>) -> Self {
134 self.with_attachment(PromptAttachment::LocalImage { path: path.into() })
135 }
136
137 pub fn attach_skill(self, name: impl Into<String>, path: impl Into<String>) -> Self {
140 self.with_attachment(PromptAttachment::Skill {
141 name: name.into(),
142 path: path.into(),
143 })
144 }
145}
146
147#[derive(Clone, Debug, PartialEq, Eq)]
148pub struct PromptRunResult {
149 pub thread_id: ThreadId,
150 pub turn_id: TurnId,
151 pub assistant_text: String,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155pub enum PromptTurnTerminalState {
156 Failed,
157 CompletedWithoutAssistantText,
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub struct PromptTurnFailure {
162 pub terminal_state: PromptTurnTerminalState,
163 pub source_method: String,
164 pub code: Option<i64>,
165 pub message: String,
166}
167
168impl std::fmt::Display for PromptTurnFailure {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 let terminal = match self.terminal_state {
171 PromptTurnTerminalState::Failed => "failed",
172 PromptTurnTerminalState::CompletedWithoutAssistantText => {
173 "completed_without_assistant_text"
174 }
175 };
176 if let Some(code) = self.code {
177 write!(
178 f,
179 "terminal={terminal} source_method={} code={code} message={}",
180 self.source_method, self.message
181 )
182 } else {
183 write!(
184 f,
185 "terminal={terminal} source_method={} message={}",
186 self.source_method, self.message
187 )
188 }
189 }
190}
191
192#[derive(Clone, Debug, Error, PartialEq, Eq)]
193pub enum PromptRunError {
194 #[error("rpc error: {0}")]
195 Rpc(#[from] RpcError),
196 #[error("runtime error: {0}")]
197 Runtime(#[from] RuntimeError),
198 #[error("turn failed: {0}")]
199 TurnFailedWithContext(PromptTurnFailure),
200 #[error("turn failed")]
201 TurnFailed,
202 #[error("turn interrupted")]
203 TurnInterrupted,
204 #[error("turn timed out after {0:?}")]
205 Timeout(Duration),
206 #[error("turn completed without assistant text: {0}")]
207 TurnCompletedWithoutAssistantText(PromptTurnFailure),
208 #[error("assistant text is empty")]
209 EmptyAssistantText,
210 #[error("attachment not found: {0}")]
211 AttachmentNotFound(String),
212 #[error("blocked by hook '{hook_name}' at {phase:?}: {message}")]
214 BlockedByHook {
215 hook_name: String,
216 phase: HookPhase,
217 message: String,
218 },
219}
220
221impl PromptRunError {
222 pub(crate) fn from_block(r: BlockReason) -> Self {
225 Self::BlockedByHook {
226 hook_name: r.hook_name,
227 phase: r.phase,
228 message: r.message,
229 }
230 }
231}