algocline_app/service/
run.rs1use algocline_core::QueryId;
2use algocline_engine::FeedResult;
3
4use super::resolve::{is_package_installed, make_require_code, resolve_code, QueryResponse};
5use super::transcript::write_transcript_log;
6use super::AppService;
7
8impl AppService {
9 pub async fn run(
14 &self,
15 code: Option<String>,
16 code_file: Option<String>,
17 ctx: Option<serde_json::Value>,
18 project_root: Option<String>,
19 ) -> Result<String, String> {
20 let code = resolve_code(code, code_file)?;
21 let ctx = ctx.unwrap_or(serde_json::Value::Null);
22 let extra = self.resolve_extra_lib_paths(project_root.as_deref());
23 self.start_and_tick(code, ctx, None, extra).await
24 }
25
26 pub async fn advice(
34 &self,
35 strategy: &str,
36 task: Option<String>,
37 opts: Option<serde_json::Value>,
38 project_root: Option<String>,
39 ) -> Result<String, String> {
40 if !is_package_installed(strategy) {
42 self.auto_install_bundled_packages().await?;
43 if !is_package_installed(strategy) {
44 return Err(format!(
45 "Package '{strategy}' not found after installing bundled collection. \
46 Use alc_pkg_install to install it manually."
47 ));
48 }
49 }
50
51 let code = make_require_code(strategy);
52
53 let mut ctx_map = match opts {
54 Some(serde_json::Value::Object(m)) => m,
55 _ => serde_json::Map::new(),
56 };
57 if let Some(task) = task {
58 ctx_map.insert("task".into(), serde_json::Value::String(task));
59 }
60 let ctx = serde_json::Value::Object(ctx_map);
61
62 let extra = self.resolve_extra_lib_paths(project_root.as_deref());
63 self.start_and_tick(code, ctx, Some(strategy), extra).await
64 }
65
66 pub async fn continue_batch(
68 &self,
69 session_id: &str,
70 responses: Vec<QueryResponse>,
71 ) -> Result<String, String> {
72 let mut last_result = None;
73 for qr in responses {
74 let qid = QueryId::parse(&qr.query_id);
75 let result = self
76 .registry
77 .feed_response(session_id, &qid, qr.response, qr.usage.as_ref())
78 .await
79 .map_err(|e| format!("Continue failed: {e}"))?;
80 last_result = Some(result);
81 }
82 let result = last_result.ok_or("Empty responses array")?;
83 self.maybe_log_transcript(&result, session_id);
84 let json = result.to_json(session_id).to_string();
85 self.maybe_save_eval(&result, session_id, &json);
86 Ok(json)
87 }
88
89 pub async fn continue_single(
91 &self,
92 session_id: &str,
93 response: String,
94 query_id: Option<&str>,
95 usage: Option<algocline_core::TokenUsage>,
96 ) -> Result<String, String> {
97 let query_id = match query_id {
98 Some(qid) => QueryId::parse(qid),
99 None => self
100 .registry
101 .resolve_sole_pending_id(session_id)
102 .await
103 .map_err(|e| format!("Continue failed: {e}"))?,
104 };
105
106 let result = self
107 .registry
108 .feed_response(session_id, &query_id, response, usage.as_ref())
109 .await
110 .map_err(|e| format!("Continue failed: {e}"))?;
111
112 self.maybe_log_transcript(&result, session_id);
113 let json = result.to_json(session_id).to_string();
114 self.maybe_save_eval(&result, session_id, &json);
115 Ok(json)
116 }
117
118 pub(super) fn maybe_log_transcript(&self, result: &FeedResult, session_id: &str) {
121 if let FeedResult::Finished(exec_result) = result {
122 let strategy = self
123 .session_strategies
124 .lock()
125 .ok()
126 .and_then(|mut map| map.remove(session_id));
127 write_transcript_log(
128 &self.log_config,
129 session_id,
130 &exec_result.metrics,
131 strategy.as_deref(),
132 );
133 }
134 }
135
136 pub(super) fn maybe_save_eval(&self, result: &FeedResult, session_id: &str, result_json: &str) {
137 if !matches!(result, FeedResult::Finished(_)) {
138 return;
139 }
140 let strategy = {
141 let mut map = match self.eval_sessions.lock() {
142 Ok(m) => m,
143 Err(_) => return,
144 };
145 map.remove(session_id)
146 };
147 if let Some(strategy) = strategy {
148 super::eval_store::save_eval_result(&strategy, result_json);
149 }
150 }
151
152 pub(super) async fn start_and_tick(
153 &self,
154 code: String,
155 ctx: serde_json::Value,
156 strategy: Option<&str>,
157 extra_lib_paths: Vec<std::path::PathBuf>,
158 ) -> Result<String, String> {
159 let session = self
160 .executor
161 .start_session(code, ctx, extra_lib_paths)
162 .await?;
163 let (session_id, result) = self
164 .registry
165 .start_execution(session)
166 .await
167 .map_err(|e| format!("Execution failed: {e}"))?;
168 if let Some(s) = strategy {
169 if let Ok(mut map) = self.session_strategies.lock() {
170 map.insert(session_id.clone(), s.to_string());
171 }
172 }
173 self.maybe_log_transcript(&result, &session_id);
174 Ok(result.to_json(&session_id).to_string())
175 }
176}