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(
11 &self,
12 code: Option<String>,
13 code_file: Option<String>,
14 ctx: Option<serde_json::Value>,
15 ) -> Result<String, String> {
16 let code = resolve_code(code, code_file)?;
17 let ctx = ctx.unwrap_or(serde_json::Value::Null);
18 self.start_and_tick(code, ctx, None).await
19 }
20
21 pub async fn advice(
26 &self,
27 strategy: &str,
28 task: String,
29 opts: Option<serde_json::Value>,
30 ) -> Result<String, String> {
31 if !is_package_installed(strategy) {
33 self.auto_install_bundled_packages().await?;
34 if !is_package_installed(strategy) {
35 return Err(format!(
36 "Package '{strategy}' not found after installing bundled collection. \
37 Use alc_pkg_install to install it manually."
38 ));
39 }
40 }
41
42 let code = make_require_code(strategy);
43
44 let mut ctx_map = match opts {
45 Some(serde_json::Value::Object(m)) => m,
46 _ => serde_json::Map::new(),
47 };
48 ctx_map.insert("task".into(), serde_json::Value::String(task));
49 let ctx = serde_json::Value::Object(ctx_map);
50
51 self.start_and_tick(code, ctx, Some(strategy)).await
52 }
53
54 pub async fn continue_batch(
56 &self,
57 session_id: &str,
58 responses: Vec<QueryResponse>,
59 ) -> Result<String, String> {
60 let mut last_result = None;
61 for qr in responses {
62 let qid = QueryId::parse(&qr.query_id);
63 let result = self
64 .registry
65 .feed_response(session_id, &qid, qr.response)
66 .await
67 .map_err(|e| format!("Continue failed: {e}"))?;
68 last_result = Some(result);
69 }
70 let result = last_result.ok_or("Empty responses array")?;
71 self.maybe_log_transcript(&result, session_id);
72 let json = result.to_json(session_id).to_string();
73 self.maybe_save_eval(&result, session_id, &json);
74 Ok(json)
75 }
76
77 pub async fn continue_single(
79 &self,
80 session_id: &str,
81 response: String,
82 query_id: Option<&str>,
83 ) -> Result<String, String> {
84 let query_id = match query_id {
85 Some(qid) => QueryId::parse(qid),
86 None => QueryId::single(),
87 };
88
89 let result = self
90 .registry
91 .feed_response(session_id, &query_id, response)
92 .await
93 .map_err(|e| format!("Continue failed: {e}"))?;
94
95 self.maybe_log_transcript(&result, session_id);
96 let json = result.to_json(session_id).to_string();
97 self.maybe_save_eval(&result, session_id, &json);
98 Ok(json)
99 }
100
101 pub(super) fn maybe_log_transcript(&self, result: &FeedResult, session_id: &str) {
104 if let FeedResult::Finished(exec_result) = result {
105 let strategy = self
106 .session_strategies
107 .lock()
108 .ok()
109 .and_then(|mut map| map.remove(session_id));
110 write_transcript_log(
111 &self.log_config,
112 session_id,
113 &exec_result.metrics,
114 strategy.as_deref(),
115 );
116 }
117 }
118
119 pub(super) fn maybe_save_eval(&self, result: &FeedResult, session_id: &str, result_json: &str) {
120 if !matches!(result, FeedResult::Finished(_)) {
121 return;
122 }
123 let strategy = {
124 let mut map = match self.eval_sessions.lock() {
125 Ok(m) => m,
126 Err(_) => return,
127 };
128 map.remove(session_id)
129 };
130 if let Some(strategy) = strategy {
131 super::eval_store::save_eval_result(&strategy, result_json);
132 }
133 }
134
135 pub(super) async fn start_and_tick(
136 &self,
137 code: String,
138 ctx: serde_json::Value,
139 strategy: Option<&str>,
140 ) -> Result<String, String> {
141 let session = self.executor.start_session(code, ctx).await?;
142 let (session_id, result) = self
143 .registry
144 .start_execution(session)
145 .await
146 .map_err(|e| format!("Execution failed: {e}"))?;
147 if let Some(s) = strategy {
148 if let Ok(mut map) = self.session_strategies.lock() {
149 map.insert(session_id.clone(), s.to_string());
150 }
151 }
152 self.maybe_log_transcript(&result, &session_id);
153 Ok(result.to_json(&session_id).to_string())
154 }
155}