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: Option<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 if let Some(task) = task {
49 ctx_map.insert("task".into(), serde_json::Value::String(task));
50 }
51 let ctx = serde_json::Value::Object(ctx_map);
52
53 self.start_and_tick(code, ctx, Some(strategy)).await
54 }
55
56 pub async fn continue_batch(
58 &self,
59 session_id: &str,
60 responses: Vec<QueryResponse>,
61 ) -> Result<String, String> {
62 let mut last_result = None;
63 for qr in responses {
64 let qid = QueryId::parse(&qr.query_id);
65 let result = self
66 .registry
67 .feed_response(session_id, &qid, qr.response, qr.usage.as_ref())
68 .await
69 .map_err(|e| format!("Continue failed: {e}"))?;
70 last_result = Some(result);
71 }
72 let result = last_result.ok_or("Empty responses array")?;
73 self.maybe_log_transcript(&result, session_id);
74 let json = result.to_json(session_id).to_string();
75 self.maybe_save_eval(&result, session_id, &json);
76 Ok(json)
77 }
78
79 pub async fn continue_single(
81 &self,
82 session_id: &str,
83 response: String,
84 query_id: Option<&str>,
85 usage: Option<algocline_core::TokenUsage>,
86 ) -> Result<String, String> {
87 let query_id = match query_id {
88 Some(qid) => QueryId::parse(qid),
89 None => self
90 .registry
91 .resolve_sole_pending_id(session_id)
92 .await
93 .map_err(|e| format!("Continue failed: {e}"))?,
94 };
95
96 let result = self
97 .registry
98 .feed_response(session_id, &query_id, response, usage.as_ref())
99 .await
100 .map_err(|e| format!("Continue failed: {e}"))?;
101
102 self.maybe_log_transcript(&result, session_id);
103 let json = result.to_json(session_id).to_string();
104 self.maybe_save_eval(&result, session_id, &json);
105 Ok(json)
106 }
107
108 pub(super) fn maybe_log_transcript(&self, result: &FeedResult, session_id: &str) {
111 if let FeedResult::Finished(exec_result) = result {
112 let strategy = self
113 .session_strategies
114 .lock()
115 .ok()
116 .and_then(|mut map| map.remove(session_id));
117 write_transcript_log(
118 &self.log_config,
119 session_id,
120 &exec_result.metrics,
121 strategy.as_deref(),
122 );
123 }
124 }
125
126 pub(super) fn maybe_save_eval(&self, result: &FeedResult, session_id: &str, result_json: &str) {
127 if !matches!(result, FeedResult::Finished(_)) {
128 return;
129 }
130 let strategy = {
131 let mut map = match self.eval_sessions.lock() {
132 Ok(m) => m,
133 Err(_) => return,
134 };
135 map.remove(session_id)
136 };
137 if let Some(strategy) = strategy {
138 super::eval_store::save_eval_result(&strategy, result_json);
139 }
140 }
141
142 pub(super) async fn start_and_tick(
143 &self,
144 code: String,
145 ctx: serde_json::Value,
146 strategy: Option<&str>,
147 ) -> Result<String, String> {
148 let session = self.executor.start_session(code, ctx).await?;
149 let (session_id, result) = self
150 .registry
151 .start_execution(session)
152 .await
153 .map_err(|e| format!("Execution failed: {e}"))?;
154 if let Some(s) = strategy {
155 if let Ok(mut map) = self.session_strategies.lock() {
156 map.insert(session_id.clone(), s.to_string());
157 }
158 }
159 self.maybe_log_transcript(&result, &session_id);
160 Ok(result.to_json(&session_id).to_string())
161 }
162}