everruns_core/
command_host.rs1use std::collections::HashMap;
7
8use async_trait::async_trait;
9
10use crate::command::CommandResult;
11use crate::driver_registry::LlmResponseStream;
12use crate::error::{AgentLoopError, Result};
13use crate::message::{Controls, Message};
14use crate::typed_id::SessionId;
15use crate::user_facing_error::{UserFacingErrorContext, classify_runtime_error_message};
16
17#[derive(Debug, Clone)]
24pub struct CommandTurnContext {
25 pub session_id: SessionId,
27 pub messages: Vec<Message>,
29 pub system_prompt: String,
31 pub model: String,
33 pub provider_type: String,
35 pub resolved_locale: Option<String>,
37}
38
39#[derive(Debug, Clone, Default)]
41pub struct SessionCompletionRequest {
42 pub system_prompts: Vec<String>,
44 pub messages: Vec<Message>,
46 pub controls: Option<Controls>,
48 pub metadata: HashMap<String, String>,
50}
51
52#[derive(Debug, Clone)]
54pub struct SessionCompletion {
55 pub text: String,
57}
58
59pub struct SessionCompletionStream {
61 pub events: LlmResponseStream,
63 pub context: UserFacingErrorContext,
65}
66
67impl std::fmt::Debug for SessionCompletionStream {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 f.debug_struct("SessionCompletionStream")
70 .field("context", &self.context)
71 .finish()
72 }
73}
74
75#[derive(Debug)]
77pub enum SessionCompletionError {
78 InvalidRequest(AgentLoopError),
80 StreamingUnsupported,
82 Completion {
84 error: String,
86 context: UserFacingErrorContext,
88 },
89}
90
91impl SessionCompletionError {
92 pub fn into_command_result(self) -> Result<CommandResult> {
95 match self {
96 Self::InvalidRequest(error) => Err(error),
97 Self::StreamingUnsupported => Err(AgentLoopError::config(
98 "command host does not support streaming completions",
99 )),
100 Self::Completion { error, context } => {
101 let classified = classify_runtime_error_message(&error, &context);
102 Ok(CommandResult {
103 success: false,
104 message: classified.fallback_message(),
105 error_code: Some(classified.code.clone()),
106 error_fields: classified.error_fields(),
107 })
108 }
109 }
110 }
111}
112
113#[async_trait]
118pub trait CommandHost: Send + Sync {
119 async fn turn_context(&self) -> Result<CommandTurnContext>;
121
122 async fn completion(
124 &self,
125 request: SessionCompletionRequest,
126 ) -> std::result::Result<SessionCompletion, SessionCompletionError>;
127
128 async fn completion_stream(
131 &self,
132 _request: SessionCompletionRequest,
133 ) -> std::result::Result<SessionCompletionStream, SessionCompletionError> {
134 Err(SessionCompletionError::StreamingUnsupported)
135 }
136}
137
138pub struct DisabledCommandHost;
140
141#[async_trait]
142impl CommandHost for DisabledCommandHost {
143 async fn turn_context(&self) -> Result<CommandTurnContext> {
144 Err(AgentLoopError::config(
145 "command host does not provide turn-context access",
146 ))
147 }
148
149 async fn completion(
150 &self,
151 _request: SessionCompletionRequest,
152 ) -> std::result::Result<SessionCompletion, SessionCompletionError> {
153 Err(SessionCompletionError::InvalidRequest(
154 AgentLoopError::config("command host does not provide session completions"),
155 ))
156 }
157}