@1kbirds/chidori 0.1.16
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.
- package/Cargo.toml +77 -0
- package/build.rs +7 -0
- package/package.json +39 -0
- package/package_node/index.d.ts +0 -0
- package/package_node/index.js +96 -0
- package/package_python/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- package/package_python/.idea/misc.xml +4 -0
- package/package_python/.idea/modules.xml +8 -0
- package/package_python/.idea/package_python.iml +8 -0
- package/package_python/.idea/vcs.xml +6 -0
- package/package_python/__init__.py +0 -0
- package/package_python/__pycache__/__init__.cpython-310.pyc +0 -0
- package/package_python/__pycache__/test_chidori.cpython-310-pytest-7.4.0.pyc +0 -0
- package/package_python/__pycache__/test_promptgraph.cpython-310-pytest-7.4.0.pyc +0 -0
- package/package_python/chidori.pyi +30 -0
- package/package_python/py.typed +0 -0
- package/package_python/test_chidori.py +33 -0
- package/pyproject.toml +33 -0
- package/src/lib.rs +22 -0
- package/src/translations/mod.rs +8 -0
- package/src/translations/nodejs.rs +1239 -0
- package/src/translations/python.rs +964 -0
- package/src/translations/rust.rs +177 -0
- package/src/translations/wasm.rs +1 -0
- package/tests/nodejs/chidori.test.js +54 -0
- package/tests/nodejs/nodeHandle.test.js +17 -0
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
use std::collections::VecDeque;
|
|
2
|
+
use pyo3::exceptions;
|
|
3
|
+
use pyo3::prelude::*;
|
|
4
|
+
use tonic::{Response, Status};
|
|
5
|
+
use futures::executor;
|
|
6
|
+
use futures::StreamExt;
|
|
7
|
+
use pyo3::types::{PyDict, PyList, PyString};
|
|
8
|
+
use pyo3::types::{PyBool, PyFloat, PyInt};
|
|
9
|
+
use pyo3::prelude::*;
|
|
10
|
+
use std::collections::HashMap;
|
|
11
|
+
use std::time::Duration;
|
|
12
|
+
use tokio::runtime::Runtime;
|
|
13
|
+
use log::{debug, info};
|
|
14
|
+
use pyo3::exceptions::PyTypeError;
|
|
15
|
+
use tonic::transport::Channel;
|
|
16
|
+
use ::prompt_graph_exec::tonic_runtime::run_server;
|
|
17
|
+
use ::prompt_graph_core::proto2::execution_runtime_client::ExecutionRuntimeClient;
|
|
18
|
+
use ::prompt_graph_core::proto2::{File, Empty, ExecutionStatus, RequestInputProposalResponse, RequestOnlyId, RequestFileMerge, RequestAtFrame, RequestNewBranch, RequestListBranches, ListBranchesRes, QueryAtFrame, Query, QueryAtFrameResponse, SerializedValue, ChangeValueWithCounter, NodeWillExecuteOnBranch, FileAddressedChangeValueWithCounter, FilteredPollNodeWillExecuteEventsRequest, RespondPollNodeWillExecuteEvents, RequestAckNodeWillExecuteEvent, ChangeValue, Path, SerializedValueObject, SerializedValueArray, Item};
|
|
19
|
+
use ::prompt_graph_core::proto2::prompt_graph_node_loader::LoadFrom;
|
|
20
|
+
use ::prompt_graph_core::proto2::serialized_value::Val;
|
|
21
|
+
use ::prompt_graph_core::graph_definition::{create_prompt_node, create_op_map, create_code_node, create_component_node, create_vector_memory_node, create_observation_node, create_node_parameter, SourceNodeType, create_loader_node, create_custom_node};
|
|
22
|
+
use ::prompt_graph_core::utils::wasm_error::CoreError;
|
|
23
|
+
use ::prompt_graph_core::build_runtime_graph::graph_parse::{CleanedDefinitionGraph, CleanIndividualNode, construct_query_from_output_type, derive_for_individual_node};
|
|
24
|
+
|
|
25
|
+
// https://pyo3.rs/v0.19.0/
|
|
26
|
+
|
|
27
|
+
// https://chat.openai.com/c/4dfe5b29-0cd9-493f-be8f-9362d376e764
|
|
28
|
+
|
|
29
|
+
// TODO: create worker object
|
|
30
|
+
|
|
31
|
+
#[derive(Debug)]
|
|
32
|
+
pub struct CoreErrorWrapper(CoreError);
|
|
33
|
+
|
|
34
|
+
impl std::convert::From<CoreErrorWrapper> for PyErr {
|
|
35
|
+
fn from(err: CoreErrorWrapper) -> PyErr {
|
|
36
|
+
exceptions::PyOSError::new_err(err.0.to_string())
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl std::convert::From<CoreError> for PyErrWrapper {
|
|
41
|
+
fn from(err: CoreError) -> PyErrWrapper {
|
|
42
|
+
PyErrWrapper(exceptions::PyOSError::new_err(err.to_string()))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[derive(Debug)]
|
|
47
|
+
pub struct PyErrWrapper(pyo3::PyErr);
|
|
48
|
+
|
|
49
|
+
#[derive(Debug)]
|
|
50
|
+
pub struct AnyhowErrWrapper(anyhow::Error);
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
impl std::convert::From<tonic::transport::Error> for PyErrWrapper {
|
|
54
|
+
fn from(status: tonic::transport::Error) -> Self {
|
|
55
|
+
// Convert the `Status` to a `PyErr` here, then wrap it in `PyErrWrapper`.
|
|
56
|
+
PyErrWrapper(exceptions::PyOSError::new_err(status.to_string()))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
impl std::convert::From<Status> for PyErrWrapper {
|
|
61
|
+
fn from(status: Status) -> Self {
|
|
62
|
+
// Convert the `Status` to a `PyErr` here, then wrap it in `PyErrWrapper`.
|
|
63
|
+
PyErrWrapper(exceptions::PyOSError::new_err(status.message().to_string()))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
impl std::convert::From<PyErrWrapper> for PyErr {
|
|
69
|
+
fn from(err: PyErrWrapper) -> PyErr {
|
|
70
|
+
err.0
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
impl std::convert::From<AnyhowErrWrapper> for PyErr {
|
|
75
|
+
fn from(err: AnyhowErrWrapper) -> PyErr {
|
|
76
|
+
exceptions::PyOSError::new_err(err.0.to_string())
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl Into<pyo3::PyResult<()>> for PyErrWrapper {
|
|
81
|
+
fn into(self) -> pyo3::PyResult<()> {
|
|
82
|
+
Err(self.0)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pub struct PyExecutionStatus(Response<ExecutionStatus>);
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
impl IntoPy<Py<PyAny>> for PyExecutionStatus {
|
|
90
|
+
fn into_py(self, py: Python) -> Py<PyAny> {
|
|
91
|
+
let PyExecutionStatus(resp) = self;
|
|
92
|
+
let exec_status = resp.into_inner();
|
|
93
|
+
let dict = PyDict::new(py);
|
|
94
|
+
dict.set_item("id", exec_status.id).unwrap();
|
|
95
|
+
dict.set_item("monotonic_counter", exec_status.monotonic_counter).unwrap();
|
|
96
|
+
dict.set_item("branch", exec_status.branch).unwrap();
|
|
97
|
+
dict.into_py(py)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
pub struct PyListBranchesRes(Response<ListBranchesRes>);
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
impl IntoPy<Py<PyAny>> for PyListBranchesRes {
|
|
106
|
+
fn into_py(self, py: Python) -> Py<PyAny> {
|
|
107
|
+
let PyListBranchesRes(resp) = self;
|
|
108
|
+
let branches = resp.into_inner();
|
|
109
|
+
let branch_list = branches.branches.into_iter().map(|branch| {
|
|
110
|
+
let mut dict = PyDict::new(py);
|
|
111
|
+
dict.set_item("id", branch.id).unwrap();
|
|
112
|
+
dict.set_item("diverges_at_counter", branch.diverges_at_counter).unwrap();
|
|
113
|
+
dict.set_item("source_branch_ids", format!("{:?}", branch.source_branch_ids)).unwrap();
|
|
114
|
+
dict
|
|
115
|
+
}).collect::<Vec<_>>();
|
|
116
|
+
PyList::new(py, branch_list).into_py(py)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
pub struct PyQueryAtFrameResponse(Response<QueryAtFrameResponse>);
|
|
122
|
+
|
|
123
|
+
impl IntoPy<Py<PyAny>> for PyQueryAtFrameResponse {
|
|
124
|
+
fn into_py(self, py: Python) -> Py<PyAny> {
|
|
125
|
+
let PyQueryAtFrameResponse(resp) = self;
|
|
126
|
+
let res = resp.into_inner();
|
|
127
|
+
|
|
128
|
+
let mut dict = PyDict::new(py);
|
|
129
|
+
res.values.into_iter().for_each(|value| {
|
|
130
|
+
let c = value.change_value.unwrap();
|
|
131
|
+
let k = c.path.unwrap().address.join(":");
|
|
132
|
+
let v = c.value.unwrap();
|
|
133
|
+
// TODO: SerializeValue to python
|
|
134
|
+
dict.set_item(k, SerializedValueWrapper(v)).unwrap();
|
|
135
|
+
});
|
|
136
|
+
PyList::new(py, dict).into_py(py)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#[derive(Debug)]
|
|
141
|
+
pub struct SerializedValueWrapper(SerializedValue);
|
|
142
|
+
|
|
143
|
+
impl ToPyObject for SerializedValueWrapper {
|
|
144
|
+
fn to_object(&self, py: Python<'_>) -> PyObject {
|
|
145
|
+
if let None = self.0.val {
|
|
146
|
+
let x: Option<bool> = None;
|
|
147
|
+
return x.into_py(py);
|
|
148
|
+
}
|
|
149
|
+
match self.0.val.as_ref().unwrap() {
|
|
150
|
+
Val::Float(x) => { x.into_py(py) }
|
|
151
|
+
Val::Number(x) => { x.into_py(py) }
|
|
152
|
+
Val::String(x) => { x.into_py(py) }
|
|
153
|
+
Val::Boolean(x) => { x.into_py(py) }
|
|
154
|
+
Val::Array(val) => {
|
|
155
|
+
let py_list = PyList::empty(py);
|
|
156
|
+
for item in &val.values {
|
|
157
|
+
py_list.append(SerializedValueWrapper(item.clone()).to_object(py)).unwrap();
|
|
158
|
+
}
|
|
159
|
+
py_list.into_py(py)
|
|
160
|
+
}
|
|
161
|
+
Val::Object(val) => {
|
|
162
|
+
let py_dict = PyDict::new(py);
|
|
163
|
+
for (key, value) in &val.values {
|
|
164
|
+
py_dict.set_item(key, SerializedValueWrapper(value.clone()).to_object(py)).unwrap();
|
|
165
|
+
}
|
|
166
|
+
py_dict.into_py(py)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
#[derive(Debug)]
|
|
173
|
+
pub struct ChangeValueWithCounterWrapper(ChangeValueWithCounter);
|
|
174
|
+
|
|
175
|
+
impl IntoPy<Py<PyAny>> for ChangeValueWithCounterWrapper {
|
|
176
|
+
fn into_py(self, py: Python) -> Py<PyAny> {
|
|
177
|
+
let mut dict = PyDict::new(py);
|
|
178
|
+
self.0.filled_values.into_iter().for_each(|c| {
|
|
179
|
+
let k = c.path.map(|path| path.address.join(":"));
|
|
180
|
+
let v = c.value;
|
|
181
|
+
if let (Some(k), Some(v)) = (k, v) {
|
|
182
|
+
dict.set_item(k, SerializedValueWrapper(v)).unwrap();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
PyList::new(py, dict).into_py(py)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
fn add_to_dict<'p>(
|
|
190
|
+
py: Python<'p>,
|
|
191
|
+
keys: &Vec<String>,
|
|
192
|
+
value: PyObject,
|
|
193
|
+
d: &'p PyDict,
|
|
194
|
+
) -> PyResult<()> {
|
|
195
|
+
if keys.len() == 1 {
|
|
196
|
+
d.set_item(keys[0].clone(), value)?;
|
|
197
|
+
} else {
|
|
198
|
+
let key = &keys[0];
|
|
199
|
+
if d.contains(key)? {
|
|
200
|
+
let nested_dict = d.get_item(key).unwrap().downcast::<PyDict>()?;
|
|
201
|
+
add_to_dict(py, &keys[1..].to_vec(), value, &nested_dict)?;
|
|
202
|
+
} else {
|
|
203
|
+
let nested_dict = PyDict::new(py);
|
|
204
|
+
d.set_item(key, &nested_dict)?;
|
|
205
|
+
add_to_dict(py, &keys[1..].to_vec(), value, &nested_dict)?;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
Ok(())
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
fn pyany_to_serialized_value(p: &PyAny) -> SerializedValue {
|
|
213
|
+
match p.get_type().name() {
|
|
214
|
+
Ok(s) => {
|
|
215
|
+
match s {
|
|
216
|
+
"int" => {
|
|
217
|
+
let val = p.extract::<i32>().unwrap();
|
|
218
|
+
SerializedValue {
|
|
219
|
+
val: Some(Val::Number(val)),
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
"float" => {
|
|
223
|
+
let val = p.extract::<f32>().unwrap();
|
|
224
|
+
SerializedValue {
|
|
225
|
+
val: Some(Val::Float(val)),
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
"str" => {
|
|
229
|
+
let val = p.extract::<String>().unwrap();
|
|
230
|
+
SerializedValue {
|
|
231
|
+
val: Some(Val::String(val)),
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
"bool" => {
|
|
235
|
+
let val = p.extract::<bool>().unwrap();
|
|
236
|
+
SerializedValue {
|
|
237
|
+
val: Some(Val::Boolean(val)),
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
"list" => {
|
|
241
|
+
let list = p.downcast::<PyList>().unwrap();
|
|
242
|
+
let arr = list
|
|
243
|
+
.iter()
|
|
244
|
+
.map(|item| pyany_to_serialized_value(item))
|
|
245
|
+
.collect();
|
|
246
|
+
SerializedValue {
|
|
247
|
+
val: Some(Val::Array(SerializedValueArray { values: arr } )),
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
"dict" => {
|
|
251
|
+
let dict = p.downcast::<PyDict>().unwrap();
|
|
252
|
+
let mut map = HashMap::new();
|
|
253
|
+
for (key, value) in dict {
|
|
254
|
+
let key_string = key.extract::<String>().unwrap();
|
|
255
|
+
map.insert(key_string, pyany_to_serialized_value(value));
|
|
256
|
+
}
|
|
257
|
+
SerializedValue {
|
|
258
|
+
val: Some(Val::Object(SerializedValueObject { values: map })),
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
_ => SerializedValue::default(),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
Err(_) => SerializedValue::default(),
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
fn dict_to_paths<'p>(
|
|
271
|
+
py: Python<'p>,
|
|
272
|
+
d: &'p PyDict
|
|
273
|
+
) -> PyResult<Vec<(Vec<String>, SerializedValue)>> {
|
|
274
|
+
let mut paths = Vec::new();
|
|
275
|
+
let mut queue: VecDeque<(Vec<String>, &'p PyDict)> = VecDeque::new();
|
|
276
|
+
queue.push_back((Vec::new(), d));
|
|
277
|
+
|
|
278
|
+
while let Some((mut path, dict)) = queue.pop_front() {
|
|
279
|
+
for (key, val) in dict {
|
|
280
|
+
let key_str = key.extract::<String>()?;
|
|
281
|
+
path.push(key_str.clone());
|
|
282
|
+
match val.downcast::<PyDict>() {
|
|
283
|
+
Ok(sub_dict) => {
|
|
284
|
+
queue.push_back((path.clone(), sub_dict));
|
|
285
|
+
},
|
|
286
|
+
Err(_) => {
|
|
287
|
+
paths.push((path.clone(), pyany_to_serialized_value(val)));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
path.pop();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
Ok(paths)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
#[derive(Debug)]
|
|
299
|
+
pub struct NodeWillExecuteOnBranchWrapper(NodeWillExecuteOnBranch);
|
|
300
|
+
|
|
301
|
+
impl ToPyObject for NodeWillExecuteOnBranchWrapper {
|
|
302
|
+
fn to_object(&self, py: Python<'_>) -> PyObject {
|
|
303
|
+
let NodeWillExecuteOnBranch { branch, counter, node, custom_node_type_name} = &self.0;
|
|
304
|
+
let dict = PyDict::new(py);
|
|
305
|
+
dict.set_item("branch", branch).unwrap();
|
|
306
|
+
dict.set_item("counter", counter).unwrap();
|
|
307
|
+
if let Some(node) = node {
|
|
308
|
+
dict.set_item("node_name", &node.source_node).unwrap();
|
|
309
|
+
dict.set_item("type_name", &custom_node_type_name).unwrap();
|
|
310
|
+
|
|
311
|
+
let event_dict = PyDict::new(py);
|
|
312
|
+
// TODO: this needs to be fixed
|
|
313
|
+
for change in &node.change_values_used_in_execution {
|
|
314
|
+
if let Some(v) = &change.change_value {
|
|
315
|
+
match v {
|
|
316
|
+
ChangeValue { path: Some(path), value: Some(value), .. } => {
|
|
317
|
+
add_to_dict(
|
|
318
|
+
py,
|
|
319
|
+
&path.address,
|
|
320
|
+
SerializedValueWrapper(value.clone()).to_object(py),
|
|
321
|
+
&event_dict,
|
|
322
|
+
).unwrap();
|
|
323
|
+
},
|
|
324
|
+
_ => {}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
dict.set_item("event", event_dict).unwrap();
|
|
330
|
+
}
|
|
331
|
+
dict.into_py(py)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
pub struct PyRespondPollNodeWillExecuteEvents(Response<RespondPollNodeWillExecuteEvents>);
|
|
336
|
+
|
|
337
|
+
impl IntoPy<Py<PyAny>> for PyRespondPollNodeWillExecuteEvents {
|
|
338
|
+
fn into_py(self, py: Python) -> Py<PyAny> {
|
|
339
|
+
let PyRespondPollNodeWillExecuteEvents(resp) = self;
|
|
340
|
+
let res = resp.into_inner();
|
|
341
|
+
let RespondPollNodeWillExecuteEvents { node_will_execute_events } = res;
|
|
342
|
+
let x: Vec<NodeWillExecuteOnBranchWrapper> = node_will_execute_events.iter().cloned().map(NodeWillExecuteOnBranchWrapper).collect();
|
|
343
|
+
PyList::new(py, x).into_py(py)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
async fn get_client(url: String) -> Result<ExecutionRuntimeClient<tonic::transport::Channel>, PyErrWrapper> {
|
|
349
|
+
ExecutionRuntimeClient::connect(url.clone()).await.map_err(PyErrWrapper::from)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// TODO: return a handle to nodes so that we can understand and inspect them
|
|
353
|
+
// TODO: include a __repr__ method on those nodes
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
// TODO: add an api for wiring together a system ot nodes
|
|
357
|
+
// TODO: require the description of the complete system, in order to avoid mutably handling changing edges
|
|
358
|
+
// TODO: system description must be fully encapsulated in a single object and applied all at once
|
|
359
|
+
|
|
360
|
+
#[pyclass]
|
|
361
|
+
#[derive(Clone)]
|
|
362
|
+
struct NodeHandle {
|
|
363
|
+
url: String,
|
|
364
|
+
file_id: String,
|
|
365
|
+
node: Item,
|
|
366
|
+
exec_status: ExecutionStatus,
|
|
367
|
+
indiv: CleanIndividualNode
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
impl NodeHandle {
|
|
371
|
+
fn from(url: String, file_id: String, node: Item, exec_status: ExecutionStatus) -> anyhow::Result<NodeHandle> {
|
|
372
|
+
let indiv = derive_for_individual_node(&node)?;
|
|
373
|
+
Ok(NodeHandle {
|
|
374
|
+
url,
|
|
375
|
+
file_id,
|
|
376
|
+
node,
|
|
377
|
+
exec_status,
|
|
378
|
+
indiv
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#[pymethods]
|
|
384
|
+
impl NodeHandle {
|
|
385
|
+
fn get_name(&self) -> String {
|
|
386
|
+
self.node.core.as_ref().unwrap().name.clone()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/// This updates the definition of this node to query for the target NodeHandle's output. Moving forward
|
|
390
|
+
/// it will execute whenever the target node resolves.
|
|
391
|
+
#[pyo3(signature = (node_handle=None))]
|
|
392
|
+
fn run_when<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, node_handle: Option<NodeHandle>) -> PyResult<&'a PyAny> {
|
|
393
|
+
let file_id = self_.file_id.clone();
|
|
394
|
+
let url = self_.url.clone();
|
|
395
|
+
let mut node = self_.node.clone();
|
|
396
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
397
|
+
// TODO: scan the queries and don't insert if the query already exists
|
|
398
|
+
if let Some(node_handle) = node_handle {
|
|
399
|
+
let queries = &mut node.core.as_mut().unwrap().queries;
|
|
400
|
+
let q = construct_query_from_output_type(
|
|
401
|
+
&node_handle.get_name(),
|
|
402
|
+
&node_handle.get_name(),
|
|
403
|
+
&node_handle.indiv.output_path
|
|
404
|
+
).map_err(AnyhowErrWrapper)?;
|
|
405
|
+
queries.push(Query { query: Some(q)});
|
|
406
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
407
|
+
} else {
|
|
408
|
+
Err(PyErr::new::<PyTypeError, _>("node_handle must be a NodeHandle"))
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#[pyo3(signature = (branch=0, frame=0))]
|
|
414
|
+
fn query<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, branch: u64, frame: u64) -> PyResult<&'a PyAny> {
|
|
415
|
+
let file_id = self_.file_id.clone();
|
|
416
|
+
let url = self_.url.clone();
|
|
417
|
+
let name = self_.get_name();
|
|
418
|
+
|
|
419
|
+
let query = construct_query_from_output_type(&name, &name, &self_.indiv.output_path)
|
|
420
|
+
.map_err(AnyhowErrWrapper)?;
|
|
421
|
+
|
|
422
|
+
// TODO: we need this to watch for changes to the query instead
|
|
423
|
+
// TODO: or this should await until either the target counter has elapsed or the query has resulted
|
|
424
|
+
|
|
425
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
426
|
+
let mut client = get_client(url).await?;
|
|
427
|
+
Ok(PyQueryAtFrameResponse(client.run_query(QueryAtFrame {
|
|
428
|
+
id: file_id,
|
|
429
|
+
query: Some(Query {
|
|
430
|
+
query: Some(query)
|
|
431
|
+
}),
|
|
432
|
+
frame,
|
|
433
|
+
branch,
|
|
434
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
fn __str__(&self) -> PyResult<String> {
|
|
439
|
+
// TODO: best practice is that these could be used to re-construct the same object
|
|
440
|
+
let name = self.get_name();
|
|
441
|
+
Ok(format!("NodeHandle(file_id={}, node={})", self.file_id, name))
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
fn __repr__(&self) -> PyResult<String> {
|
|
445
|
+
let name = self.get_name();
|
|
446
|
+
Ok(format!("NodeHandle(file_id={}, node={})", self.file_id, name))
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
// TODO: all operations only apply to a specific branch at a time
|
|
452
|
+
// TODO: maintain an internal map of the generated change responses for node additions to the associated query necessary to get that result
|
|
453
|
+
#[pyclass]
|
|
454
|
+
struct Chidori {
|
|
455
|
+
file_id: String,
|
|
456
|
+
current_head: u64,
|
|
457
|
+
current_branch: u64,
|
|
458
|
+
url: String
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
async fn push_file_merge(url: &String, file_id: &String, node: Item) -> Result<NodeHandle, PyErr> {
|
|
464
|
+
let mut client = get_client(url.clone()).await?;
|
|
465
|
+
let exec_status = client.merge(RequestFileMerge {
|
|
466
|
+
id: file_id.clone(),
|
|
467
|
+
file: Some(File {
|
|
468
|
+
nodes: vec![node.clone()],
|
|
469
|
+
..Default::default()
|
|
470
|
+
}),
|
|
471
|
+
branch: 0,
|
|
472
|
+
}).await.map_err(PyErrWrapper::from)?.into_inner();
|
|
473
|
+
Ok(NodeHandle::from(
|
|
474
|
+
url.clone(),
|
|
475
|
+
file_id.clone(),
|
|
476
|
+
node,
|
|
477
|
+
exec_status
|
|
478
|
+
).map_err(AnyhowErrWrapper)?)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// TODO: internally all operations should have an assigned counter
|
|
482
|
+
// we can keep the actual target counter hidden from the host sdk
|
|
483
|
+
#[pymethods]
|
|
484
|
+
impl Chidori {
|
|
485
|
+
|
|
486
|
+
#[new]
|
|
487
|
+
#[pyo3(signature = (file_id=String::from("0"), url=String::from("http://127.0.0.1:9800"), api_token=None))]
|
|
488
|
+
fn new(file_id: String, url: String, api_token: Option<String>) -> Self {
|
|
489
|
+
debug!("Creating new Chidori instance with file_id={}, url={}, api_token={:?}", file_id, url, api_token);
|
|
490
|
+
Chidori {
|
|
491
|
+
file_id,
|
|
492
|
+
current_head: 0,
|
|
493
|
+
current_branch: 0,
|
|
494
|
+
url,
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
fn start_server<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, file_path: Option<String>) -> PyResult<&'a PyAny> {
|
|
499
|
+
let url_server = self_.url.clone();
|
|
500
|
+
std::thread::spawn(move || {
|
|
501
|
+
let result = run_server(url_server, file_path);
|
|
502
|
+
match result {
|
|
503
|
+
Ok(_) => { },
|
|
504
|
+
Err(e) => {
|
|
505
|
+
println!("Error running server: {}", e);
|
|
506
|
+
},
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
let url = self_.url.clone();
|
|
511
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
512
|
+
'retry: loop {
|
|
513
|
+
let client = get_client(url.clone());
|
|
514
|
+
match client.await {
|
|
515
|
+
Ok(connection) => {
|
|
516
|
+
eprintln!("Connection successfully established {:?}", &url);
|
|
517
|
+
break 'retry
|
|
518
|
+
},
|
|
519
|
+
Err(e) => {
|
|
520
|
+
eprintln!("Error connecting to server: {} with Error {}. Retrying...", &url, &e.0);
|
|
521
|
+
std::thread::sleep(std::time::Duration::from_millis(1000));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
Ok(())
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
#[pyo3(signature = (branch=0, frame=0))]
|
|
530
|
+
fn play<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, branch: u64, frame: u64) -> PyResult<&'a PyAny> {
|
|
531
|
+
let file_id = self_.file_id.clone();
|
|
532
|
+
let url = self_.url.clone();
|
|
533
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
534
|
+
let mut client = get_client(url).await?;
|
|
535
|
+
Ok(PyExecutionStatus(client.play(RequestAtFrame {
|
|
536
|
+
id: file_id,
|
|
537
|
+
frame,
|
|
538
|
+
branch,
|
|
539
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
#[pyo3(signature = (frame=0))]
|
|
545
|
+
fn pause<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, frame: u64) -> PyResult<&'a PyAny> {
|
|
546
|
+
let file_id = self_.file_id.clone();
|
|
547
|
+
let url = self_.url.clone();
|
|
548
|
+
let branch = self_.current_branch.clone();
|
|
549
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
550
|
+
let mut client = get_client(url).await?;
|
|
551
|
+
Ok(PyExecutionStatus(client.pause(RequestAtFrame {
|
|
552
|
+
id: file_id,
|
|
553
|
+
frame,
|
|
554
|
+
branch,
|
|
555
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
fn branch<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>) -> PyResult<&'a PyAny> {
|
|
561
|
+
let file_id = self_.file_id.clone();
|
|
562
|
+
let url = self_.url.clone();
|
|
563
|
+
let branch = self_.current_branch.clone();
|
|
564
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
565
|
+
let mut client = get_client(url).await?;
|
|
566
|
+
let result_branch = client.branch(RequestNewBranch {
|
|
567
|
+
id: file_id,
|
|
568
|
+
source_branch_id: branch,
|
|
569
|
+
diverges_at_counter: 0,
|
|
570
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
571
|
+
// TODO: need to somehow handle writing to the current_branch
|
|
572
|
+
Ok(PyExecutionStatus(result_branch))
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
#[pyo3(signature = (query=String::new(), branch=0, frame=0))]
|
|
577
|
+
fn query<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>, query: String, branch: u64, frame: u64) -> PyResult<&'a PyAny> {
|
|
578
|
+
let file_id = self_.file_id.clone();
|
|
579
|
+
let url = self_.url.clone();
|
|
580
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
581
|
+
let mut client = get_client(url).await?;
|
|
582
|
+
Ok(PyQueryAtFrameResponse(client.run_query(QueryAtFrame {
|
|
583
|
+
id: file_id,
|
|
584
|
+
query: Some(Query {
|
|
585
|
+
query: Some(query)
|
|
586
|
+
}),
|
|
587
|
+
frame,
|
|
588
|
+
branch,
|
|
589
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
590
|
+
})
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
fn list_branches<'a>(mut self_: PyRefMut<'_, Self>, py: Python<'a>) -> PyResult<&'a PyAny> {
|
|
594
|
+
let file_id = self_.file_id.clone();
|
|
595
|
+
let url = self_.url.clone();
|
|
596
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
597
|
+
let mut client = get_client(url).await?;
|
|
598
|
+
Ok(PyListBranchesRes(client.list_branches(RequestListBranches {
|
|
599
|
+
id: file_id,
|
|
600
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
601
|
+
})
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
fn display_graph_structure<'a>(
|
|
605
|
+
mut self_: PyRefMut<'_, Self>,
|
|
606
|
+
py: Python<'a>
|
|
607
|
+
) -> PyResult<&'a PyAny> {
|
|
608
|
+
let file_id = self_.file_id.clone();
|
|
609
|
+
let url = self_.url.clone();
|
|
610
|
+
let branch = self_.current_branch.clone();
|
|
611
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
612
|
+
let mut client = get_client(url).await?;
|
|
613
|
+
let file = client.current_file_state(RequestOnlyId {
|
|
614
|
+
id: file_id,
|
|
615
|
+
branch
|
|
616
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
617
|
+
let mut file = file.into_inner();
|
|
618
|
+
let mut g = CleanedDefinitionGraph::zero();
|
|
619
|
+
g.merge_file(&mut file).unwrap();
|
|
620
|
+
Ok(g.get_dot_graph())
|
|
621
|
+
})
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// TODO: some of these register handlers instead
|
|
625
|
+
// TODO: list registered graphs should not stream
|
|
626
|
+
// TODO: add a message that sends the current graph state
|
|
627
|
+
|
|
628
|
+
fn list_registered_graphs<'a>(
|
|
629
|
+
mut self_: PyRefMut<'_, Self>,
|
|
630
|
+
py: Python<'a>
|
|
631
|
+
) -> PyResult<&'a PyAny> {
|
|
632
|
+
let file_id = self_.file_id.clone();
|
|
633
|
+
let url = self_.url.clone();
|
|
634
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
635
|
+
let mut client = get_client(url).await?;
|
|
636
|
+
let resp = client.list_registered_graphs(Empty {
|
|
637
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
638
|
+
let mut stream = resp.into_inner();
|
|
639
|
+
while let Some(x) = stream.next().await {
|
|
640
|
+
// callback.call(py, (x,), None);
|
|
641
|
+
info!("Registered Graph = {:?}", x);
|
|
642
|
+
};
|
|
643
|
+
Ok(())
|
|
644
|
+
})
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
fn list_input_proposals<'a>(
|
|
648
|
+
mut self_: PyRefMut<'_, Self>,
|
|
649
|
+
py: Python<'a>,
|
|
650
|
+
callback: PyObject
|
|
651
|
+
) -> PyResult<&'a PyAny> {
|
|
652
|
+
let file_id = self_.file_id.clone();
|
|
653
|
+
let url = self_.url.clone();
|
|
654
|
+
let branch = self_.current_branch;
|
|
655
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
656
|
+
let mut client = get_client(url).await?;
|
|
657
|
+
let resp = client.list_input_proposals(RequestOnlyId {
|
|
658
|
+
id: file_id,
|
|
659
|
+
branch,
|
|
660
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
661
|
+
let mut stream = resp.into_inner();
|
|
662
|
+
while let Some(x) = stream.next().await {
|
|
663
|
+
// callback.call(py, (x,), None);
|
|
664
|
+
info!("InputProposals = {:?}", x);
|
|
665
|
+
};
|
|
666
|
+
Ok(())
|
|
667
|
+
})
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// fn respond_to_input_proposal(mut self_: PyRefMut<'_, Self>) -> PyResult<()> {
|
|
671
|
+
// Ok(())
|
|
672
|
+
// }
|
|
673
|
+
|
|
674
|
+
// TODO: this is a successful callback example
|
|
675
|
+
fn list_change_events<'a>(
|
|
676
|
+
mut self_: PyRefMut<'_, Self>,
|
|
677
|
+
py: Python<'a>,
|
|
678
|
+
callback: PyObject
|
|
679
|
+
) -> PyResult<&'a PyAny> {
|
|
680
|
+
let file_id = self_.file_id.clone();
|
|
681
|
+
let url = self_.url.clone();
|
|
682
|
+
let branch = self_.current_branch;
|
|
683
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
684
|
+
let mut client = get_client(url).await?;
|
|
685
|
+
let resp = client.list_change_events(RequestOnlyId {
|
|
686
|
+
id: file_id,
|
|
687
|
+
branch,
|
|
688
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
689
|
+
let mut stream = resp.into_inner();
|
|
690
|
+
while let Some(x) = stream.next().await {
|
|
691
|
+
Python::with_gil(|py| pyo3_asyncio::tokio::into_future(callback.as_ref(py).call((x.map(ChangeValueWithCounterWrapper).map_err(PyErrWrapper::from)?,), None)?))?
|
|
692
|
+
.await?;
|
|
693
|
+
};
|
|
694
|
+
Ok(())
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
// TODO: nodes that are added should return a clean definition of what their addition looks like
|
|
700
|
+
// TODO: adding a node should also display any errors
|
|
701
|
+
/// x = None
|
|
702
|
+
/// with open("/Users/coltonpierson/Downloads/files_and_dirs.zip", "rb") as zip_file:
|
|
703
|
+
/// contents = zip_file.read()
|
|
704
|
+
/// x = await p.load_zip_file("LoadZip", """ output: String """, contents)
|
|
705
|
+
/// x
|
|
706
|
+
#[pyo3(signature = (name=String::new(), output_tables=vec![], output=String::new(), bytes=vec![]))]
|
|
707
|
+
fn load_zip_file<'a>(
|
|
708
|
+
mut self_: PyRefMut<'_, Self>,
|
|
709
|
+
py: Python<'a>,
|
|
710
|
+
name: String,
|
|
711
|
+
output_tables: Vec<String>,
|
|
712
|
+
output: String,
|
|
713
|
+
bytes: Vec<u8>
|
|
714
|
+
) -> PyResult<&'a PyAny> {
|
|
715
|
+
let file_id = self_.file_id.clone();
|
|
716
|
+
let url = self_.url.clone();
|
|
717
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
718
|
+
let node = create_loader_node(
|
|
719
|
+
name,
|
|
720
|
+
vec![],
|
|
721
|
+
output,
|
|
722
|
+
LoadFrom::ZipfileBytes(bytes),
|
|
723
|
+
output_tables
|
|
724
|
+
);
|
|
725
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
726
|
+
})
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// TODO: nodes that are added should return a clean definition of what their addition looks like
|
|
730
|
+
// TODO: adding a node should also display any errors
|
|
731
|
+
#[pyo3(signature = (name=String::new(), queries=vec![None], output_tables=vec![], template=String::new(), model=String::from("GPT_3_5_TURBO")))]
|
|
732
|
+
fn prompt_node<'a>(
|
|
733
|
+
mut self_: PyRefMut<'_, Self>,
|
|
734
|
+
py: Python<'a>,
|
|
735
|
+
name: String,
|
|
736
|
+
queries: Vec<Option<String>>,
|
|
737
|
+
output_tables: Vec<String>,
|
|
738
|
+
template: String,
|
|
739
|
+
model: String
|
|
740
|
+
) -> PyResult<&'a PyAny> {
|
|
741
|
+
let file_id = self_.file_id.clone();
|
|
742
|
+
let url = self_.url.clone();
|
|
743
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
744
|
+
let node = create_prompt_node(
|
|
745
|
+
name,
|
|
746
|
+
queries,
|
|
747
|
+
template,
|
|
748
|
+
model,
|
|
749
|
+
output_tables
|
|
750
|
+
).map_err(PyErrWrapper::from)?;
|
|
751
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
752
|
+
})
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
fn poll_local_code_node_execution<'a>(
|
|
756
|
+
mut self_: PyRefMut<'_, Self>,
|
|
757
|
+
py: Python<'a>,
|
|
758
|
+
) -> PyResult<&'a PyAny> {
|
|
759
|
+
let file_id = self_.file_id.clone();
|
|
760
|
+
let url = self_.url.clone();
|
|
761
|
+
|
|
762
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
763
|
+
let mut client = get_client(url).await?;
|
|
764
|
+
let result = client.poll_node_will_execute_events(FilteredPollNodeWillExecuteEventsRequest {
|
|
765
|
+
id: file_id.clone(),
|
|
766
|
+
}).await.map_err(PyErrWrapper::from)?;
|
|
767
|
+
debug!("poll_local_code_node_execution result = {:?}", result);
|
|
768
|
+
Ok(PyRespondPollNodeWillExecuteEvents(result))
|
|
769
|
+
})
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
#[pyo3(signature = (branch=0, counter=0))]
|
|
773
|
+
fn ack_local_code_node_execution<'a>(
|
|
774
|
+
mut self_: PyRefMut<'_, Self>,
|
|
775
|
+
py: Python<'a>,
|
|
776
|
+
branch: u64,
|
|
777
|
+
counter: u64,
|
|
778
|
+
) -> PyResult<&'a PyAny> {
|
|
779
|
+
let file_id = self_.file_id.clone();
|
|
780
|
+
let url = self_.url.clone();
|
|
781
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
782
|
+
let mut client = get_client(url).await?;
|
|
783
|
+
Ok(PyExecutionStatus(client.ack_node_will_execute_event(RequestAckNodeWillExecuteEvent {
|
|
784
|
+
id: file_id.clone(),
|
|
785
|
+
branch,
|
|
786
|
+
counter,
|
|
787
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
788
|
+
})
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
#[pyo3(signature = (branch=0, counter=0, node_name=String::new(), response=None))]
|
|
792
|
+
fn respond_local_code_node_execution<'a>(
|
|
793
|
+
mut self_: PyRefMut<'_, Self>,
|
|
794
|
+
py: Python<'a>,
|
|
795
|
+
branch: u64,
|
|
796
|
+
counter: u64,
|
|
797
|
+
node_name: String,
|
|
798
|
+
response: Option<PyObject>
|
|
799
|
+
) -> PyResult<&'a PyAny> {
|
|
800
|
+
|
|
801
|
+
let file_id = self_.file_id.clone();
|
|
802
|
+
let url = self_.url.clone();
|
|
803
|
+
|
|
804
|
+
// TODO: need parent counters from the original change
|
|
805
|
+
// TODO: need source node
|
|
806
|
+
|
|
807
|
+
let response_paths = if let Some(response) = response {
|
|
808
|
+
dict_to_paths(py, response.downcast::<PyDict>(py)?)?
|
|
809
|
+
} else {
|
|
810
|
+
vec![]
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
814
|
+
let mut client = get_client(url).await?;
|
|
815
|
+
|
|
816
|
+
// TODO: need to add the output table paths to these
|
|
817
|
+
let filled_values = response_paths.into_iter().map(|path| {
|
|
818
|
+
ChangeValue {
|
|
819
|
+
path: Some(Path {
|
|
820
|
+
address: path.0,
|
|
821
|
+
}),
|
|
822
|
+
value: Some(path.1),
|
|
823
|
+
branch,
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
// TODO: this needs to look more like a real change
|
|
828
|
+
Ok(PyExecutionStatus(client.push_worker_event(FileAddressedChangeValueWithCounter {
|
|
829
|
+
branch,
|
|
830
|
+
counter,
|
|
831
|
+
node_name,
|
|
832
|
+
id: file_id.clone(),
|
|
833
|
+
change: Some(ChangeValueWithCounter {
|
|
834
|
+
filled_values: filled_values.collect(),
|
|
835
|
+
parent_monotonic_counters: vec![],
|
|
836
|
+
monotonic_counter: counter,
|
|
837
|
+
branch,
|
|
838
|
+
source_node: "".to_string(),
|
|
839
|
+
})
|
|
840
|
+
}).await.map_err(PyErrWrapper::from)?))
|
|
841
|
+
})
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// TODO: handle dispatch to this handler - should accept a callback
|
|
845
|
+
// https://github.com/PyO3/pyo3/issues/525
|
|
846
|
+
#[pyo3(signature = (name=String::new(), queries=vec![None], output_tables=vec![], output=String::from("type O {}"), node_type_name=String::new()))]
|
|
847
|
+
fn custom_node<'a>(
|
|
848
|
+
mut self_: PyRefMut<'_, Self>,
|
|
849
|
+
py: Python<'a>,
|
|
850
|
+
name: String,
|
|
851
|
+
queries: Vec<Option<String>>,
|
|
852
|
+
output_tables: Vec<String>,
|
|
853
|
+
output: String,
|
|
854
|
+
node_type_name: String,
|
|
855
|
+
) -> PyResult<&'a PyAny> {
|
|
856
|
+
let file_id = self_.file_id.clone();
|
|
857
|
+
let url = self_.url.clone();
|
|
858
|
+
let branch = self_.current_branch;
|
|
859
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
860
|
+
// Register the node with the system
|
|
861
|
+
let node = create_custom_node(
|
|
862
|
+
name,
|
|
863
|
+
queries,
|
|
864
|
+
output,
|
|
865
|
+
node_type_name,
|
|
866
|
+
output_tables
|
|
867
|
+
);
|
|
868
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
869
|
+
})
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
#[pyo3(signature = (name=String::new(), queries=vec![None], output_tables=vec![], output=String::from("type O { }"), code=String::new(), is_template=false))]
|
|
873
|
+
fn deno_code_node<'a>(
|
|
874
|
+
mut self_: PyRefMut<'_, Self>,
|
|
875
|
+
py: Python<'a>,
|
|
876
|
+
name: String,
|
|
877
|
+
queries: Vec<Option<String>>,
|
|
878
|
+
output_tables: Vec<String>,
|
|
879
|
+
output: String,
|
|
880
|
+
code: String,
|
|
881
|
+
is_template: bool
|
|
882
|
+
) -> PyResult<&'a PyAny> {
|
|
883
|
+
let file_id = self_.file_id.clone();
|
|
884
|
+
let url = self_.url.clone();
|
|
885
|
+
let branch = self_.current_branch.clone();
|
|
886
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
887
|
+
let node = create_code_node(
|
|
888
|
+
name,
|
|
889
|
+
queries,
|
|
890
|
+
output,
|
|
891
|
+
SourceNodeType::Code("DENO".to_string(), code, is_template),
|
|
892
|
+
output_tables
|
|
893
|
+
);
|
|
894
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
895
|
+
})
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
#[pyo3(signature = (name=String::new(), queries=vec![None], output_tables=vec![], output=String::from("type O { }"), template=String::new(), action="WRITE".to_string(), embedding_model="TEXT_EMBEDDING_ADA_002".to_string(), db_vendor="QDRANT".to_string(), collection_name=String::new()))]
|
|
900
|
+
fn vector_memory_node<'a>(
|
|
901
|
+
mut self_: PyRefMut<'_, Self>,
|
|
902
|
+
py: Python<'a>,
|
|
903
|
+
name: String,
|
|
904
|
+
queries: Vec<Option<String>>,
|
|
905
|
+
output_tables: Vec<String>,
|
|
906
|
+
output: String,
|
|
907
|
+
template: String,
|
|
908
|
+
action: String, // READ / WRITE
|
|
909
|
+
embedding_model: String,
|
|
910
|
+
db_vendor: String,
|
|
911
|
+
collection_name: String,
|
|
912
|
+
) -> PyResult<&'a PyAny> {
|
|
913
|
+
let file_id = self_.file_id.clone();
|
|
914
|
+
let url = self_.url.clone();
|
|
915
|
+
let branch = self_.current_branch.clone();
|
|
916
|
+
|
|
917
|
+
pyo3_asyncio::tokio::future_into_py(py, async move {
|
|
918
|
+
let node = create_vector_memory_node(
|
|
919
|
+
name,
|
|
920
|
+
queries,
|
|
921
|
+
output,
|
|
922
|
+
action,
|
|
923
|
+
embedding_model,
|
|
924
|
+
template,
|
|
925
|
+
db_vendor,
|
|
926
|
+
collection_name,
|
|
927
|
+
output_tables
|
|
928
|
+
).map_err(PyErrWrapper::from)?;
|
|
929
|
+
Ok(push_file_merge(&url, &file_id, node).await?)
|
|
930
|
+
})
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
//
|
|
935
|
+
// fn observation_node(mut self_: PyRefMut<'_, Self>, name: String, query_def: Option<String>, template: String, model: String) -> PyResult<()> {
|
|
936
|
+
// let file_id = self_.file_id.clone();
|
|
937
|
+
// let node = create_observation_node(
|
|
938
|
+
// "".to_string(),
|
|
939
|
+
// None,
|
|
940
|
+
// "".to_string(),
|
|
941
|
+
// );
|
|
942
|
+
// executor::block_on(self_.client.merge(RequestFileMerge {
|
|
943
|
+
// id: file_id,
|
|
944
|
+
// file: Some(File {
|
|
945
|
+
// nodes: vec![node],
|
|
946
|
+
// ..Default::default()
|
|
947
|
+
// }),
|
|
948
|
+
// branch: 0,
|
|
949
|
+
// }));
|
|
950
|
+
// Ok(())
|
|
951
|
+
// }
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
/// A Python module implemented in Rust. The name of this function must match
|
|
956
|
+
/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
|
|
957
|
+
/// import the module.
|
|
958
|
+
#[pymodule]
|
|
959
|
+
#[pyo3(name = "chidori")]
|
|
960
|
+
fn chidori(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
|
961
|
+
pyo3_log::init();
|
|
962
|
+
m.add_class::<Chidori>()?;
|
|
963
|
+
Ok(())
|
|
964
|
+
}
|