@1kbirds/chidori 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,36 +1,63 @@
1
1
  use std::cell::RefCell;
2
2
  use std::collections::{HashMap, VecDeque};
3
+ use std::future::Future;
3
4
  use std::hash::Hash;
4
5
  use std::marker::PhantomData;
6
+ use std::pin::Pin;
7
+ use std::sync::Arc;
5
8
  use anyhow::Error;
9
+ use futures::future::BoxFuture;
6
10
  use futures::StreamExt;
7
11
  use log::{debug, info};
8
12
  use once_cell::sync::OnceCell;
9
13
  use tokio::runtime::Runtime;
10
14
  use prompt_graph_core::build_runtime_graph::graph_parse::{CleanedDefinitionGraph, CleanIndividualNode, construct_query_from_output_type, derive_for_individual_node};
11
15
  use prompt_graph_core::graph_definition::{create_code_node, create_custom_node, create_prompt_node, create_vector_memory_node, SourceNodeType};
12
- use prompt_graph_core::proto2::{ChangeValue, ChangeValueWithCounter, Empty, ExecutionStatus, File, FileAddressedChangeValueWithCounter, FilteredPollNodeWillExecuteEventsRequest, Item, ListBranchesRes, Path, Query, QueryAtFrame, QueryAtFrameResponse, RequestAckNodeWillExecuteEvent, RequestAtFrame, RequestFileMerge, RequestListBranches, RequestNewBranch, RequestOnlyId, RespondPollNodeWillExecuteEvents, SerializedValue, SerializedValueArray, SerializedValueObject};
16
+ use prompt_graph_core::proto2::{ChangeValue, ChangeValueWithCounter, Empty, ExecutionStatus, File, FileAddressedChangeValueWithCounter, FilteredPollNodeWillExecuteEventsRequest, Item, ListBranchesRes, NodeWillExecute, NodeWillExecuteOnBranch, Path, Query, QueryAtFrame, QueryAtFrameResponse, RequestAckNodeWillExecuteEvent, RequestAtFrame, RequestFileMerge, RequestListBranches, RequestNewBranch, RequestOnlyId, RespondPollNodeWillExecuteEvents, SerializedValue, SerializedValueArray, SerializedValueObject};
13
17
  use prompt_graph_core::proto2::execution_runtime_client::ExecutionRuntimeClient;
14
18
  use prompt_graph_core::proto2::serialized_value::Val;
15
19
  use prompt_graph_exec::tonic_runtime::run_server;
16
20
  use neon_serde3;
17
21
  use serde::{Deserialize, Serialize};
18
22
  use tonic::Status;
23
+ use prompt_graph_core::templates::json_value_to_serialized_value;
24
+ use crate::translations::shared::json_value_to_paths;
25
+ pub use prompt_graph_core::utils::serialized_value_to_string;
19
26
 
20
27
  async fn get_client(url: String) -> Result<ExecutionRuntimeClient<tonic::transport::Channel>, tonic::transport::Error> {
21
28
  ExecutionRuntimeClient::connect(url.clone()).await
22
29
  }
23
30
 
24
- struct Chidori {
31
+ type CallbackHandler = Box<dyn Fn(NodeWillExecuteOnBranch) -> BoxFuture<'static, anyhow::Result<serde_json::Value>> + Send + Sync>;
32
+
33
+ pub struct Handler {
34
+ pub(crate) callback: CallbackHandler
35
+ }
36
+
37
+ impl Handler {
38
+ pub fn new<F>(f: F) -> Self
39
+ where
40
+ F: Fn(NodeWillExecuteOnBranch) -> BoxFuture<'static, anyhow::Result<serde_json::Value>> + Send + Sync + 'static
41
+ {
42
+ Handler {
43
+ callback: Box::new(f),
44
+ }
45
+ }
46
+ }
47
+
48
+
49
+ #[derive(Clone)]
50
+ pub struct Chidori {
25
51
  file_id: String,
26
52
  current_head: u64,
27
53
  current_branch: u64,
28
- url: String
54
+ url: String,
55
+ pub(crate) custom_node_handlers: HashMap<String, Arc<Handler>>
29
56
  }
30
57
 
31
58
  impl Chidori {
32
59
 
33
- fn new(file_id: String, url: String) -> Self {
60
+ pub fn new(file_id: String, url: String) -> Self {
34
61
  if !url.contains("://") {
35
62
  panic!("Invalid url, must include protocol");
36
63
  }
@@ -41,10 +68,11 @@ impl Chidori {
41
68
  current_head: 0,
42
69
  current_branch: 0,
43
70
  url,
71
+ custom_node_handlers: HashMap::new(),
44
72
  }
45
73
  }
46
74
 
47
- async fn start_server(&self, file_path: Option<String>) -> anyhow::Result<()> {
75
+ pub async fn start_server(&self, file_path: Option<String>) -> anyhow::Result<()> {
48
76
  let url_server = self.url.clone();
49
77
  std::thread::spawn(move || {
50
78
  let result = run_server(url_server, file_path);
@@ -73,7 +101,7 @@ impl Chidori {
73
101
  }
74
102
  }
75
103
 
76
- async fn play(&self, branch: u64, frame: u64) -> anyhow::Result<ExecutionStatus> {
104
+ pub async fn play(&self, branch: u64, frame: u64) -> anyhow::Result<ExecutionStatus> {
77
105
  let file_id = self.file_id.clone();
78
106
  let url = self.url.clone();
79
107
  let mut client = get_client(url).await?;
@@ -85,7 +113,7 @@ impl Chidori {
85
113
  Ok(result.into_inner())
86
114
  }
87
115
 
88
- async fn pause(&self, frame: u64) -> anyhow::Result<ExecutionStatus> {
116
+ pub async fn pause(&self, frame: u64) -> anyhow::Result<ExecutionStatus> {
89
117
  let file_id = self.file_id.clone();
90
118
  let url = self.url.clone();
91
119
  let branch = self.current_branch.clone();
@@ -99,7 +127,7 @@ impl Chidori {
99
127
  Ok(result.into_inner())
100
128
  }
101
129
 
102
- async fn query( &self, query: String, branch: u64, frame: u64, ) -> anyhow::Result<QueryAtFrameResponse> {
130
+ pub async fn query( &self, query: String, branch: u64, frame: u64, ) -> anyhow::Result<QueryAtFrameResponse> {
103
131
  let file_id = self.file_id.clone();
104
132
  let url = self.url.clone();
105
133
  let mut client = get_client(url).await?;
@@ -114,7 +142,19 @@ impl Chidori {
114
142
  Ok(result.into_inner())
115
143
  }
116
144
 
117
- async fn list_branches( &self) -> anyhow::Result<ListBranchesRes> {
145
+ pub async fn branch( &self, branch: u64, frame: u64, ) -> anyhow::Result<ExecutionStatus> {
146
+ let file_id = self.file_id.clone();
147
+ let url = self.url.clone();
148
+ let mut client = get_client(url).await?;
149
+ let result = client.branch(RequestNewBranch {
150
+ id: file_id,
151
+ source_branch_id: branch,
152
+ diverges_at_counter: frame
153
+ }).await?;
154
+ Ok(result.into_inner())
155
+ }
156
+
157
+ pub async fn list_branches( &self) -> anyhow::Result<ListBranchesRes> {
118
158
  let file_id = self.file_id.clone();
119
159
  let url = self.url.clone();
120
160
  let mut client = get_client(url).await?;
@@ -124,7 +164,7 @@ impl Chidori {
124
164
  Ok(result.into_inner())
125
165
  }
126
166
 
127
- async fn display_graph_structure( &self, query: String, branch: u64, frame: u64, ) -> anyhow::Result<String> {
167
+ pub async fn display_graph_structure( &self, branch: u64) -> anyhow::Result<String> {
128
168
  let file_id = self.file_id.clone();
129
169
  let url = self.url.clone();
130
170
  let mut client = get_client(url).await?;
@@ -138,7 +178,7 @@ impl Chidori {
138
178
  Ok(g.get_dot_graph())
139
179
  }
140
180
 
141
- async fn list_registered_graphs(&self) -> anyhow::Result<()> {
181
+ pub async fn list_registered_graphs(&self) -> anyhow::Result<()> {
142
182
  let file_id = self.file_id.clone();
143
183
  let url = self.url.clone();
144
184
  let mut client = get_client(url).await?;
@@ -210,17 +250,20 @@ impl Chidori {
210
250
  // // TODO: nodes that are added should return a clean definition of what their addition looks like
211
251
  // // TODO: adding a node should also display any errors
212
252
 
253
+ pub fn register_custom_node_handle(&mut self, key: String, handler: Handler) {
254
+ self.custom_node_handlers.insert(key, Arc::new(handler));
255
+ }
213
256
 
214
- async fn poll_local_code_node_execution(&self) -> anyhow::Result<RespondPollNodeWillExecuteEvents> {
257
+ pub async fn poll_local_code_node_execution(&self) -> anyhow::Result<RespondPollNodeWillExecuteEvents> {
215
258
  let file_id = self.file_id.clone();
216
259
  let url = self.url.clone();
217
260
  let mut client = get_client(url).await?;
218
261
  let req = FilteredPollNodeWillExecuteEventsRequest { id: file_id.clone() };
219
- let result = client.poll_node_will_execute_events(req).await?;
262
+ let result = client.poll_custom_node_will_execute_events(req).await?;
220
263
  Ok(result.into_inner())
221
264
  }
222
265
 
223
- async fn ack_local_code_node_execution(&self, branch: u64, counter : u64) -> anyhow::Result<ExecutionStatus> {
266
+ pub async fn ack_local_code_node_execution(&self, branch: u64, counter : u64) -> anyhow::Result<ExecutionStatus> {
224
267
  let file_id = self.file_id.clone();
225
268
  let url = self.url.clone();
226
269
  let mut client = get_client(url).await?;
@@ -232,10 +275,28 @@ impl Chidori {
232
275
  Ok(result.into_inner())
233
276
  }
234
277
 
235
- async fn respond_local_code_node_execution(&self, branch: u64, counter: u64, node_name: String, response: Vec<ChangeValue>) -> anyhow::Result<ExecutionStatus> {
278
+ pub async fn respond_local_code_node_execution<T: Serialize>(
279
+ &self,
280
+ branch: u64,
281
+ counter: u64,
282
+ node_name: String,
283
+ response: T
284
+ ) -> anyhow::Result<ExecutionStatus> {
236
285
  let file_id = self.file_id.clone();
237
286
  let url = self.url.clone();
238
287
 
288
+ let json_object = serde_json::to_value(response)?;
289
+ let response_paths = json_value_to_paths(&json_object);
290
+ let filled_values = response_paths.into_iter().map(|path| {
291
+ ChangeValue {
292
+ path: Some(Path {
293
+ address: path.0,
294
+ }),
295
+ value: Some(path.1),
296
+ branch,
297
+ }
298
+ }).collect();
299
+
239
300
  // TODO: need parent counters from the original change
240
301
  // TODO: need source node
241
302
  let mut client = get_client(url).await?;
@@ -245,63 +306,193 @@ impl Chidori {
245
306
  Ok(client.push_worker_event(FileAddressedChangeValueWithCounter {
246
307
  branch,
247
308
  counter,
248
- node_name,
309
+ node_name: node_name.clone(),
249
310
  id: file_id.clone(),
250
311
  change: Some(ChangeValueWithCounter {
251
- filled_values: response,
312
+ filled_values,
252
313
  parent_monotonic_counters: vec![],
253
314
  monotonic_counter: counter,
254
315
  branch,
255
- source_node: "".to_string(),
316
+ source_node: node_name.clone(),
256
317
  })
257
318
  }).await?.into_inner())
258
319
  }
320
+
321
+ pub async fn run_custom_node_loop(&self) -> anyhow::Result<()> {
322
+ loop {
323
+ let mut backoff = 2;
324
+ let events = self.poll_local_code_node_execution().await?;
325
+ if events.node_will_execute_events.len() <= 0 {
326
+ backoff = backoff * backoff;
327
+ tokio::time::sleep(std::time::Duration::from_millis(100 * backoff)).await;
328
+ continue;
329
+ } else {
330
+ backoff = 2;
331
+ for ev in &events.node_will_execute_events {
332
+ // ACK messages
333
+ let NodeWillExecuteOnBranch { branch, counter, node, ..} = ev;
334
+ let node_name = &node.as_ref().unwrap().source_node;
335
+ if let Some(x) = self.custom_node_handlers.get(&ev.custom_node_type_name.clone().unwrap()) {
336
+ self.ack_local_code_node_execution(*branch, *counter).await?;
337
+ let result = (x.as_ref().callback)(ev.clone()).await?;
338
+ dbg!(&result);
339
+ self.respond_local_code_node_execution(*branch, *counter, node_name.clone(), result).await?;
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }
259
345
  }
260
346
 
261
347
 
348
+ fn default_queries() -> Option<Vec<String>> {
349
+ Some(vec!["None".to_string()])
350
+ }
351
+
262
352
  #[derive(serde::Serialize, serde::Deserialize)]
263
- struct PromptNodeCreateOpts {
264
- name: String,
265
- queries: Option<Vec<String>>,
266
- output_tables: Option<Vec<String>>,
267
- template: String,
268
- model: Option<String>
353
+ pub struct PromptNodeCreateOpts {
354
+ pub name: String,
355
+ pub queries: Option<Vec<String>>,
356
+ pub output_tables: Option<Vec<String>>,
357
+ pub template: String,
358
+ pub model: Option<String>
269
359
  }
270
360
 
361
+ impl Default for PromptNodeCreateOpts {
362
+ fn default() -> Self {
363
+ PromptNodeCreateOpts {
364
+ name: "".to_string(),
365
+ queries: default_queries(),
366
+ output_tables: None,
367
+ template: "".to_string(),
368
+ model: Some("GPT_3_5_TURBO".to_string()),
369
+ }
370
+ }
371
+ }
372
+ impl PromptNodeCreateOpts {
373
+ pub fn merge(&mut self, other: PromptNodeCreateOpts) {
374
+ self.name = other.name;
375
+ self.queries = other.queries.or(self.queries.take());
376
+ self.output_tables = other.output_tables.or(self.output_tables.take());
377
+ self.template = other.template;
378
+ self.model = other.model.or(self.model.take());
379
+ }
380
+ }
381
+
382
+
271
383
 
272
384
  #[derive(serde::Serialize, serde::Deserialize)]
273
- struct CustomNodeCreateOpts {
274
- name: String,
275
- queries: Option<Vec<String>>,
276
- output_tables: Option<Vec<String>>,
277
- output: Option<String>,
278
- node_type_name: String
385
+ pub struct CustomNodeCreateOpts {
386
+ pub name: String,
387
+ pub queries: Option<Vec<String>>,
388
+ pub output_tables: Option<Vec<String>>,
389
+ pub output: Option<String>,
390
+ pub node_type_name: String
279
391
  }
280
392
 
393
+
394
+ impl Default for CustomNodeCreateOpts {
395
+ fn default() -> Self {
396
+ CustomNodeCreateOpts {
397
+ name: "".to_string(),
398
+ queries: default_queries(),
399
+ output_tables: None,
400
+ output: None,
401
+ node_type_name: "".to_string(),
402
+ }
403
+ }
404
+ }
405
+ impl CustomNodeCreateOpts {
406
+ pub fn merge(&mut self, other: CustomNodeCreateOpts) {
407
+ self.name = other.name;
408
+ self.queries = other.queries.or(self.queries.take());
409
+ self.output_tables = other.output_tables.or(self.output_tables.take());
410
+ self.output = other.output.or(self.output.take());
411
+ self.node_type_name = other.node_type_name;
412
+ }
413
+ }
414
+
415
+
416
+
281
417
  #[derive(serde::Serialize, serde::Deserialize)]
282
- struct DenoCodeNodeCreateOpts {
283
- name: String,
284
- queries: Option<Vec<String>>,
285
- output_tables: Option<Vec<String>>,
286
- output: Option<String>,
287
- code: String,
288
- is_template: Option<bool>
418
+ pub struct DenoCodeNodeCreateOpts {
419
+ pub name: String,
420
+ pub queries: Option<Vec<String>>,
421
+ pub output_tables: Option<Vec<String>>,
422
+ pub output: Option<String>,
423
+ pub code: String,
424
+ pub is_template: Option<bool>
425
+ }
426
+
427
+ impl Default for DenoCodeNodeCreateOpts {
428
+ fn default() -> Self {
429
+ DenoCodeNodeCreateOpts {
430
+ name: "".to_string(),
431
+ queries: default_queries(),
432
+ output_tables: None,
433
+ output: None,
434
+ code: "".to_string(),
435
+ is_template: None,
436
+ }
437
+ }
438
+ }
439
+
440
+ impl DenoCodeNodeCreateOpts {
441
+ pub fn merge(&mut self, other: DenoCodeNodeCreateOpts) {
442
+ self.name = other.name;
443
+ self.queries = other.queries.or(self.queries.take());
444
+ self.output_tables = other.output_tables.or(self.output_tables.take());
445
+ self.output = other.output.or(self.output.take());
446
+ self.code = other.code;
447
+ self.is_template = other.is_template.or(self.is_template.take());
448
+ }
289
449
  }
290
450
 
291
451
  #[derive(serde::Serialize, serde::Deserialize)]
292
- struct VectorMemoryNodeCreateOpts {
293
- name: String,
294
- queries: Option<Vec<String>>,
295
- output_tables: Option<Vec<String>>,
296
- output: Option<String>,
297
- template: Option<String>, // TODO: default is the contents of the query
298
- action: Option<String>, // TODO: default WRITE
299
- embedding_model: Option<String>, // TODO: default TEXT_EMBEDDING_ADA_002
300
- db_vendor: Option<String>, // TODO: default QDRANT
301
- collection_name: String,
452
+ pub struct VectorMemoryNodeCreateOpts {
453
+ pub name: String,
454
+ pub queries: Option<Vec<String>>,
455
+ pub output_tables: Option<Vec<String>>,
456
+ pub output: Option<String>,
457
+ pub template: Option<String>, // TODO: default is the contents of the query
458
+ pub action: Option<String>, // TODO: default WRITE
459
+ pub embedding_model: Option<String>, // TODO: default TEXT_EMBEDDING_ADA_002
460
+ pub db_vendor: Option<String>, // TODO: default QDRANT
461
+ pub collection_name: String,
302
462
  }
303
463
 
304
464
 
465
+ impl Default for VectorMemoryNodeCreateOpts {
466
+ fn default() -> Self {
467
+ VectorMemoryNodeCreateOpts {
468
+ name: "".to_string(),
469
+ queries: None,
470
+ output_tables: None,
471
+ output: None,
472
+ template: None,
473
+ action: None,
474
+ embedding_model: None,
475
+ db_vendor: None,
476
+ collection_name: "".to_string(),
477
+ }
478
+ }
479
+ }
480
+
481
+
482
+ impl VectorMemoryNodeCreateOpts {
483
+ pub fn merge(&mut self, other: VectorMemoryNodeCreateOpts) {
484
+ self.name = other.name;
485
+ self.queries = other.queries.or(self.queries.take());
486
+ self.output_tables = other.output_tables.or(self.output_tables.take());
487
+ self.output = other.output.or(self.output.take());
488
+ self.template = other.template.or(self.template.take());
489
+ self.action = other.action.or(self.action.take());
490
+ self.embedding_model = other.embedding_model.or(self.embedding_model.take());
491
+ self.db_vendor = other.db_vendor.or(self.db_vendor.take());
492
+ self.collection_name = other.collection_name;
493
+ }
494
+ }
495
+
305
496
  fn remap_queries(queries: Option<Vec<String>>) -> Vec<Option<String>> {
306
497
  let queries: Vec<Option<String>> = if let Some(queries) = queries {
307
498
  queries.into_iter().map(|q| {
@@ -317,59 +508,73 @@ fn remap_queries(queries: Option<Vec<String>>) -> Vec<Option<String>> {
317
508
  queries
318
509
  }
319
510
 
320
- struct GraphBuilder {
511
+ #[derive(Clone)]
512
+ pub struct GraphBuilder {
321
513
  clean_graph: CleanedDefinitionGraph,
322
514
  }
323
515
 
324
516
  impl GraphBuilder {
325
- fn prompt_node(&mut self, arg: PromptNodeCreateOpts) -> anyhow::Result<NodeHandle> {
517
+ pub fn new() -> Self {
518
+ GraphBuilder {
519
+ clean_graph: CleanedDefinitionGraph::zero()
520
+ }
521
+ }
522
+ pub fn prompt_node(&mut self, arg: PromptNodeCreateOpts) -> anyhow::Result<NodeHandle> {
523
+ let mut def = PromptNodeCreateOpts::default();
524
+ def.merge(arg);
326
525
  let node = create_prompt_node(
327
- arg.name,
328
- remap_queries(arg.queries),
329
- arg.template,
330
- arg.model.unwrap_or("GPT_3_5_TURBO".to_string()),
331
- arg.output_tables.unwrap_or(vec![]))?;
526
+ def.name.clone(),
527
+ remap_queries(def.queries),
528
+ def.template,
529
+ def.model.unwrap_or("GPT_3_5_TURBO".to_string()),
530
+ def.output_tables.unwrap_or(vec![]))?;
332
531
  self.clean_graph.merge_file(&File { nodes: vec![node.clone()], ..Default::default() })?;
333
532
  Ok(NodeHandle::from(node)?)
334
533
  }
335
534
 
336
- fn custom_node(&mut self, arg: CustomNodeCreateOpts) -> anyhow::Result<NodeHandle> {
535
+ pub fn custom_node(&mut self, arg: CustomNodeCreateOpts) -> anyhow::Result<NodeHandle> {
536
+ let mut def = CustomNodeCreateOpts::default();
537
+ def.merge(arg);
337
538
  let node = create_custom_node(
338
- arg.name,
339
- remap_queries(arg.queries.clone()),
340
- arg.output.unwrap_or("type O {}".to_string()),
341
- arg.node_type_name,
342
- arg.output_tables.unwrap_or(vec![])
539
+ def.name.clone(),
540
+ remap_queries(def.queries.clone()),
541
+ def.output.unwrap_or("type O {}".to_string()),
542
+ def.node_type_name,
543
+ def.output_tables.unwrap_or(vec![])
343
544
  );
344
545
  self.clean_graph.merge_file(&File { nodes: vec![node.clone()], ..Default::default() })?;
345
546
  Ok(NodeHandle::from(node)?)
346
547
  }
347
548
 
348
549
 
349
- fn deno_code_node(&mut self, arg: DenoCodeNodeCreateOpts) -> anyhow::Result<NodeHandle> {
550
+ pub fn deno_code_node(&mut self, arg: DenoCodeNodeCreateOpts) -> anyhow::Result<NodeHandle> {
551
+ let mut def = DenoCodeNodeCreateOpts::default();
552
+ def.merge(arg);
350
553
  let node = create_code_node(
351
- arg.name,
352
- remap_queries(arg.queries.clone()),
353
- arg.output.unwrap_or("type O {}".to_string()),
354
- SourceNodeType::Code("DENO".to_string(), arg.code, arg.is_template.unwrap_or(false)),
355
- arg.output_tables.unwrap_or(vec![])
554
+ def.name.clone(),
555
+ remap_queries(def.queries.clone()),
556
+ def.output.unwrap_or("type O {}".to_string()),
557
+ SourceNodeType::Code("DENO".to_string(), def.code, def.is_template.unwrap_or(false)),
558
+ def.output_tables.unwrap_or(vec![])
356
559
  );
357
560
  self.clean_graph.merge_file(&File { nodes: vec![node.clone()], ..Default::default() })?;
358
561
  Ok(NodeHandle::from(node)?)
359
562
  }
360
563
 
361
564
 
362
- fn vector_memory_node(&mut self, arg: VectorMemoryNodeCreateOpts) -> anyhow::Result<NodeHandle> {
565
+ pub fn vector_memory_node(&mut self, arg: VectorMemoryNodeCreateOpts) -> anyhow::Result<NodeHandle> {
566
+ let mut def = VectorMemoryNodeCreateOpts::default();
567
+ def.merge(arg);
363
568
  let node = create_vector_memory_node(
364
- arg.name,
365
- remap_queries(arg.queries.clone()),
366
- arg.output.unwrap_or("type O {}".to_string()),
367
- arg.action.unwrap_or("READ".to_string()),
368
- arg.embedding_model.unwrap_or("TEXT_EMBEDDING_ADA_002".to_string()),
369
- arg.template.unwrap_or("".to_string()),
370
- arg.db_vendor.unwrap_or("QDRANT".to_string()),
371
- arg.collection_name,
372
- arg.output_tables.unwrap_or(vec![])
569
+ def.name.clone(),
570
+ remap_queries(def.queries.clone()),
571
+ def.output.unwrap_or("type O {}".to_string()),
572
+ def.action.unwrap_or("READ".to_string()),
573
+ def.embedding_model.unwrap_or("TEXT_EMBEDDING_ADA_002".to_string()),
574
+ def.template.unwrap_or("".to_string()),
575
+ def.db_vendor.unwrap_or("QDRANT".to_string()),
576
+ def.collection_name,
577
+ def.output_tables.unwrap_or(vec![])
373
578
  )?;
374
579
  self.clean_graph.merge_file(&File { nodes: vec![node.clone()], ..Default::default() })?;
375
580
  Ok(NodeHandle::from(node)?)
@@ -426,9 +631,12 @@ impl GraphBuilder {
426
631
  // // })
427
632
  // // }
428
633
 
429
- async fn commit(&self, file_id: String, url: String, branch: u64) -> anyhow::Result<ExecutionStatus> {
634
+ pub async fn commit(&self, c: &Chidori, branch: u64) -> anyhow::Result<ExecutionStatus> {
635
+ let url = &c.url;
636
+ let file_id = &c.file_id;
430
637
  let mut client = get_client(url.clone()).await?;
431
638
  let nodes = self.clean_graph.node_by_name.clone().into_values().collect();
639
+
432
640
  Ok(client.merge(RequestFileMerge {
433
641
  id: file_id.clone(),
434
642
  file: Some(File { nodes, ..Default::default() }),
@@ -441,7 +649,7 @@ impl GraphBuilder {
441
649
  // Node handle
442
650
  #[derive(Clone)]
443
651
  pub struct NodeHandle {
444
- node: Item,
652
+ pub node: Item,
445
653
  indiv: CleanIndividualNode
446
654
  }
447
655
 
@@ -457,18 +665,29 @@ impl NodeHandle {
457
665
 
458
666
 
459
667
  impl NodeHandle {
460
- fn get_name(&self) -> String {
668
+ pub(crate) fn get_name(&self) -> String {
461
669
  self.node.core.as_ref().unwrap().name.clone()
462
670
  }
463
671
 
464
- pub fn run_when(&mut self, other_node: &NodeHandle) -> anyhow::Result<bool> {
672
+ fn get_output_type(&self) -> Vec<Vec<String>> {
673
+ self.indiv.output_path.clone()
674
+ }
675
+
676
+ pub fn run_when(&mut self, graph_builder: &mut GraphBuilder, other_node: &NodeHandle) -> anyhow::Result<bool> {
465
677
  let queries = &mut self.node.core.as_mut().unwrap().queries;
678
+
679
+ // Remove null query if it is the only one present
680
+ if queries.len() == 1 && queries[0].query.is_none() {
681
+ queries.remove(0);
682
+ }
683
+
466
684
  let q = construct_query_from_output_type(
467
685
  &other_node.get_name(),
468
686
  &other_node.get_name(),
469
- &self.indiv.output_path
687
+ &other_node.get_output_type()
470
688
  ).unwrap();
471
689
  queries.push(Query { query: Some(q)});
690
+ graph_builder.clean_graph.merge_file(&File { nodes: vec![self.node.clone()], ..Default::default() })?;
472
691
  Ok(true)
473
692
  }
474
693
 
@@ -498,6 +717,16 @@ impl NodeHandle {
498
717
 
499
718
  }
500
719
 
720
+ #[macro_export]
721
+ macro_rules! register_node_handle {
722
+ ($c:expr, $name:expr, $handler:expr) => {
723
+ $c.register_custom_node_handle($name.to_string(), Handler::new(
724
+ move |n| Box::pin(async move { ($handler)(n).await })
725
+ ));
726
+ };
727
+ }
728
+
729
+
501
730
  #[cfg(test)]
502
731
  mod tests {
503
732
  use super::*;
@@ -0,0 +1,49 @@
1
+ use serde_json::Value as JsonValue;
2
+ use std::collections::VecDeque;
3
+ use prompt_graph_core::proto2::SerializedValue;
4
+ use prompt_graph_core::templates::json_value_to_serialized_value;
5
+
6
+ pub fn json_value_to_paths(
7
+ d: &JsonValue,
8
+ ) -> Vec<(Vec<String>, SerializedValue)> {
9
+ let mut paths = Vec::new();
10
+ let mut queue: VecDeque<(Vec<String>, &JsonValue)> = VecDeque::new();
11
+ queue.push_back((Vec::new(), d));
12
+
13
+ while let Some((mut path, dict)) = queue.pop_front() {
14
+ match dict {
15
+ JsonValue::Object(map) => {
16
+ for (key, val) in map {
17
+ let key_str = key.clone();
18
+ path.push(key_str.clone());
19
+ match val {
20
+ JsonValue::Object(_) => {
21
+ queue.push_back((path.clone(), val));
22
+ },
23
+ _ => {
24
+ paths.push((path.clone(), json_value_to_serialized_value(&val)));
25
+ }
26
+ }
27
+ path.pop();
28
+ }
29
+ },
30
+ JsonValue::Array(arr) => {
31
+ for (i, val) in arr.iter().enumerate() {
32
+ path.push(i.to_string());
33
+ match val {
34
+ JsonValue::Object(_) => {
35
+ queue.push_back((path.clone(), val));
36
+ },
37
+ _ => {
38
+ paths.push((path.clone(), json_value_to_serialized_value(&val)));
39
+ }
40
+ }
41
+ path.pop();
42
+ }
43
+ },
44
+ _ => panic!("Root should be a JSON object but was {:?}", d),
45
+ }
46
+ }
47
+
48
+ paths
49
+ }