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