1use std::sync::Arc;
16
17use crate::{
18 ChatService, Harness, HarnessError, InMemoryMemoryBackend, MemoryBackend,
19 MemoryConversationStore, ModelProvider, ToolRuntime, create_default_memory_backend,
20};
21
22#[derive(Clone)]
23pub struct RuntimeBundle {
24 pub memory: Arc<dyn MemoryBackend>,
25 pub chat: ChatService,
26 pub harness: Harness,
27}
28
29pub fn in_memory_backend() -> Arc<dyn MemoryBackend> {
30 Arc::new(InMemoryMemoryBackend::new())
31}
32
33pub fn chat_service(provider: Arc<dyn ModelProvider>) -> ChatService {
34 ChatService::builder(provider).build()
35}
36
37pub fn chat_service_with_memory(
38 provider: Arc<dyn ModelProvider>,
39 memory: Arc<dyn MemoryBackend>,
40) -> ChatService {
41 let store = Arc::new(MemoryConversationStore::new(memory));
42 ChatService::builder(provider).store(store).build()
43}
44
45pub fn build_runtime(provider: Arc<dyn ModelProvider>) -> Result<RuntimeBundle, HarnessError> {
46 let memory = create_default_memory_backend()?;
47 build_runtime_with(provider, memory, None)
48}
49
50pub fn build_runtime_with_memory(
51 provider: Arc<dyn ModelProvider>,
52 memory: Arc<dyn MemoryBackend>,
53) -> Result<RuntimeBundle, HarnessError> {
54 build_runtime_with(provider, memory, None)
55}
56
57pub fn build_runtime_with_tooling(
58 provider: Arc<dyn ModelProvider>,
59 tool_runtime: Arc<dyn ToolRuntime>,
60) -> Result<RuntimeBundle, HarnessError> {
61 let memory = create_default_memory_backend()?;
62 build_runtime_with(provider, memory, Some(tool_runtime))
63}
64
65pub fn build_runtime_with(
66 provider: Arc<dyn ModelProvider>,
67 memory: Arc<dyn MemoryBackend>,
68 tool_runtime: Option<Arc<dyn ToolRuntime>>,
69) -> Result<RuntimeBundle, HarnessError> {
70 let store = Arc::new(MemoryConversationStore::new(Arc::clone(&memory)));
71
72 let mut chat_builder = ChatService::builder(Arc::clone(&provider)).store(store);
73 let mut harness_builder = Harness::builder(Arc::clone(&memory)).provider(provider);
74
75 if let Some(runtime) = tool_runtime {
76 chat_builder = chat_builder.tool_runtime(Arc::clone(&runtime));
77 harness_builder = harness_builder.tool_runtime(runtime);
78 }
79
80 let chat = chat_builder.build();
81 let harness = harness_builder.build()?;
82
83 Ok(RuntimeBundle {
84 memory,
85 chat,
86 harness,
87 })
88}
89
90#[cfg(test)]
91mod tests {
92 use std::sync::Arc;
93
94 use crate::{
95 DefaultToolRuntime, Message, ModelProvider, ModelRequest,
96 ModelResponse, OutputItem, ProviderError, ProviderFuture, ProviderId, Role, StopReason,
97 StreamEvent, TokenUsage, ToolRuntime, VecEventStream,
98 };
99
100 use super::{build_runtime_with_tooling};
101
102 #[derive(Debug)]
103 struct FakeProvider;
104
105 impl ModelProvider for FakeProvider {
106 fn id(&self) -> ProviderId {
107 ProviderId::OpenAi
108 }
109
110 fn complete<'a>(
111 &'a self,
112 request: ModelRequest,
113 ) -> ProviderFuture<'a, Result<ModelResponse, ProviderError>> {
114 Box::pin(async move {
115 request.validate()?;
116 Ok(ModelResponse {
117 provider: ProviderId::OpenAi,
118 model: request.model,
119 output: vec![OutputItem::Message(Message::new(Role::Assistant, "done"))],
120 stop_reason: StopReason::EndTurn,
121 usage: TokenUsage::default(),
122 })
123 })
124 }
125
126 fn stream<'a>(
127 &'a self,
128 request: ModelRequest,
129 ) -> ProviderFuture<'a, Result<crate::BoxedEventStream<'a>, ProviderError>> {
130 Box::pin(async move {
131 request.validate()?;
132 let response = ModelResponse {
133 provider: ProviderId::OpenAi,
134 model: request.model,
135 output: vec![OutputItem::Message(Message::new(Role::Assistant, "done"))],
136 stop_reason: StopReason::EndTurn,
137 usage: TokenUsage::default(),
138 };
139 let stream = VecEventStream::new(vec![Ok(StreamEvent::ResponseComplete(response))]);
140 Ok(Box::pin(stream) as crate::BoxedEventStream<'a>)
141 })
142 }
143 }
144
145 #[test]
146 fn build_runtime_with_tooling_builds_successfully() {
147 let provider: Arc<dyn ModelProvider> = Arc::new(FakeProvider);
148 let tool_runtime: Arc<dyn ToolRuntime> = Arc::new(DefaultToolRuntime::default());
149
150 let runtime =
151 build_runtime_with_tooling(provider, tool_runtime).expect("runtime should build");
152 let _chat = runtime.chat.clone();
153 let _harness = runtime.harness.clone();
154 }
155}