darabos commited on
Commit
a0194e7
·
1 Parent(s): 16077e8

Load old files, few fixes for LynxScribe demo.

Browse files
server/crdt.py CHANGED
@@ -32,6 +32,7 @@ class WebsocketServer(pycrdt_websocket.WebsocketServer):
32
  ws['edges'] = pycrdt.Array()
33
  if 'env' not in ws:
34
  ws['env'] = 'unset'
 
35
  room = pycrdt_websocket.YRoom(ystore=ystore, ydoc=ydoc)
36
  room.ws = ws
37
  def on_change(changes):
@@ -59,39 +60,47 @@ def clean_input(ws_pyd):
59
  for key in list(node.model_extra.keys()):
60
  delattr(node, key)
61
 
62
- def crdt_update(crdt_obj, python_obj):
63
  if isinstance(python_obj, dict):
64
  for key, value in python_obj.items():
65
- if isinstance(value, dict):
 
 
66
  if crdt_obj.get(key) is None:
67
  crdt_obj[key] = pycrdt.Map()
68
- crdt_update(crdt_obj[key], value)
69
  elif isinstance(value, list):
70
  if crdt_obj.get(key) is None:
71
  crdt_obj[key] = pycrdt.Array()
72
- crdt_update(crdt_obj[key], value)
73
  else:
74
- print('set', key, value)
75
  crdt_obj[key] = value
76
  elif isinstance(python_obj, list):
77
  for i, value in enumerate(python_obj):
78
  if isinstance(value, dict):
79
  if i >= len(crdt_obj):
80
  crdt_obj.append(pycrdt.Map())
81
- crdt_update(crdt_obj[i], value)
82
  elif isinstance(value, list):
83
  if i >= len(crdt_obj):
84
  crdt_obj.append(pycrdt.Array())
85
- crdt_update(crdt_obj[i], value)
86
  else:
87
  if i >= len(crdt_obj):
88
  crdt_obj.append(value)
89
  else:
90
- print('set', i, value)
91
  crdt_obj[i] = value
92
  else:
93
  raise ValueError('Invalid type:', python_obj)
94
 
 
 
 
 
 
 
 
 
95
  async def workspace_changed(e, ws_crdt):
96
  global last_ws_input
97
  from . import workspace
@@ -99,14 +108,14 @@ async def workspace_changed(e, ws_crdt):
99
  clean_input(ws_pyd)
100
  if ws_pyd == last_ws_input:
101
  return
102
- print('ws changed')
103
  last_ws_input = ws_pyd.model_copy(deep=True)
104
- workspace.execute(ws_pyd)
105
  for nc, np in zip(ws_crdt['nodes'], ws_pyd.nodes):
106
  if 'data' not in nc:
107
  nc['data'] = pycrdt.Map()
108
  # Display is added as an opaque Box.
109
  nc['data']['display'] = np.data.display
 
110
 
111
  @contextlib.asynccontextmanager
112
  async def lifespan(app):
 
32
  ws['edges'] = pycrdt.Array()
33
  if 'env' not in ws:
34
  ws['env'] = 'unset'
35
+ try_to_load_workspace(ws, name)
36
  room = pycrdt_websocket.YRoom(ystore=ystore, ydoc=ydoc)
37
  room.ws = ws
38
  def on_change(changes):
 
60
  for key in list(node.model_extra.keys()):
61
  delattr(node, key)
62
 
63
+ def crdt_update(crdt_obj, python_obj, boxes=set()):
64
  if isinstance(python_obj, dict):
65
  for key, value in python_obj.items():
66
+ if key in boxes:
67
+ crdt_obj[key] = value
68
+ elif isinstance(value, dict):
69
  if crdt_obj.get(key) is None:
70
  crdt_obj[key] = pycrdt.Map()
71
+ crdt_update(crdt_obj[key], value, boxes)
72
  elif isinstance(value, list):
73
  if crdt_obj.get(key) is None:
74
  crdt_obj[key] = pycrdt.Array()
75
+ crdt_update(crdt_obj[key], value, boxes)
76
  else:
 
77
  crdt_obj[key] = value
78
  elif isinstance(python_obj, list):
79
  for i, value in enumerate(python_obj):
80
  if isinstance(value, dict):
81
  if i >= len(crdt_obj):
82
  crdt_obj.append(pycrdt.Map())
83
+ crdt_update(crdt_obj[i], value, boxes)
84
  elif isinstance(value, list):
85
  if i >= len(crdt_obj):
86
  crdt_obj.append(pycrdt.Array())
87
+ crdt_update(crdt_obj[i], value, boxes)
88
  else:
89
  if i >= len(crdt_obj):
90
  crdt_obj.append(value)
91
  else:
 
92
  crdt_obj[i] = value
93
  else:
94
  raise ValueError('Invalid type:', python_obj)
95
 
96
+
97
+ def try_to_load_workspace(ws, name):
98
+ from . import workspace
99
+ json_path = f'data/{name}'
100
+ if os.path.exists(json_path):
101
+ ws_pyd = workspace.load(json_path)
102
+ crdt_update(ws, ws_pyd.model_dump(), boxes={'display'})
103
+
104
  async def workspace_changed(e, ws_crdt):
105
  global last_ws_input
106
  from . import workspace
 
108
  clean_input(ws_pyd)
109
  if ws_pyd == last_ws_input:
110
  return
 
111
  last_ws_input = ws_pyd.model_copy(deep=True)
112
+ await workspace.execute(ws_pyd)
113
  for nc, np in zip(ws_crdt['nodes'], ws_pyd.nodes):
114
  if 'data' not in nc:
115
  nc['data'] = pycrdt.Map()
116
  # Display is added as an opaque Box.
117
  nc['data']['display'] = np.data.display
118
+ nc['data']['error'] = np.data.error
119
 
120
  @contextlib.asynccontextmanager
121
  async def lifespan(app):
server/executors/one_by_one.py CHANGED
@@ -74,7 +74,12 @@ def make_cache_key(obj):
74
 
75
  EXECUTOR_OUTPUT_CACHE = {}
76
 
77
- def execute(ws, catalog, cache=None):
 
 
 
 
 
78
  nodes = {n.id: n for n in ws.nodes}
79
  contexts = {n.id: Context(node=n) for n in ws.nodes}
80
  edges = {n.id: [] for n in ws.nodes}
@@ -113,10 +118,10 @@ def execute(ws, catalog, cache=None):
113
  if cache is not None:
114
  key = make_cache_key((inputs, params))
115
  if key not in cache:
116
- cache[key] = op(*inputs, **params)
117
  result = cache[key]
118
  else:
119
- result = op(*inputs, **params)
120
  except Exception as e:
121
  traceback.print_exc()
122
  data.error = str(e)
 
74
 
75
  EXECUTOR_OUTPUT_CACHE = {}
76
 
77
+ async def await_if_needed(obj):
78
+ if inspect.isawaitable(obj):
79
+ return await obj
80
+ return obj
81
+
82
+ async def execute(ws, catalog, cache=None):
83
  nodes = {n.id: n for n in ws.nodes}
84
  contexts = {n.id: Context(node=n) for n in ws.nodes}
85
  edges = {n.id: [] for n in ws.nodes}
 
118
  if cache is not None:
119
  key = make_cache_key((inputs, params))
120
  if key not in cache:
121
+ cache[key] = await await_if_needed(op(*inputs, **params))
122
  result = cache[key]
123
  else:
124
+ result = await await_if_needed(op(*inputs, **params))
125
  except Exception as e:
126
  traceback.print_exc()
127
  data.error = str(e)
server/lynxkite_ops.py CHANGED
@@ -83,7 +83,7 @@ def disambiguate_edges(ws):
83
 
84
 
85
  @ops.register_executor('LynxKite')
86
- def execute(ws):
87
  catalog = ops.CATALOGS['LynxKite']
88
  # Nodes are responsible for interpreting/executing their child nodes.
89
  nodes = [n for n in ws.nodes if not n.parentId]
 
83
 
84
 
85
  @ops.register_executor('LynxKite')
86
+ async def execute(ws):
87
  catalog = ops.CATALOGS['LynxKite']
88
  # Nodes are responsible for interpreting/executing their child nodes.
89
  nodes = [n for n in ws.nodes if not n.parentId]
server/lynxscribe_ops.py CHANGED
@@ -121,10 +121,10 @@ def mask(*, name='', regex='', exceptions='', mask_pattern=''):
121
 
122
  @ops.input_position(chat_api="bottom")
123
  @op("Test Chat API")
124
- def test_chat_api(message, chat_api, *, show_details=False):
125
  chat_api = chat_api[0]['chat_api']
126
  request = ChatAPIRequest(session_id="b43215a0-428f-11ef-9454-0242ac120002", question=message['text'], history=[])
127
- response = asyncio.run(chat_api.answer(request))
128
  if show_details:
129
  return {**response.__dict__}
130
  else:
 
121
 
122
  @ops.input_position(chat_api="bottom")
123
  @op("Test Chat API")
124
+ async def test_chat_api(message, chat_api, *, show_details=False):
125
  chat_api = chat_api[0]['chat_api']
126
  request = ChatAPIRequest(session_id="b43215a0-428f-11ef-9454-0242ac120002", question=message['text'], history=[])
127
+ response = await chat_api.answer(request)
128
  if show_details:
129
  return {**response.__dict__}
130
  else:
server/main.py CHANGED
@@ -35,9 +35,9 @@ def save(req: SaveRequest):
35
  workspace.save(req.ws, path)
36
 
37
  @app.post("/api/save")
38
- def save_and_execute(req: SaveRequest):
39
  save(req)
40
- workspace.execute(req.ws)
41
  save(req)
42
  return req.ws
43
 
 
35
  workspace.save(req.ws, path)
36
 
37
  @app.post("/api/save")
38
+ async def save_and_execute(req: SaveRequest):
39
  save(req)
40
+ await workspace.execute(req.ws)
41
  save(req)
42
  return req.ws
43
 
server/workspace.py CHANGED
@@ -43,9 +43,9 @@ class Workspace(BaseConfig):
43
  edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
44
 
45
 
46
- def execute(ws: Workspace):
47
  if ws.env in ops.EXECUTORS:
48
- ops.EXECUTORS[ws.env](ws)
49
 
50
 
51
  def save(ws: Workspace, path: str):
 
43
  edges: list[WorkspaceEdge] = dataclasses.field(default_factory=list)
44
 
45
 
46
+ async def execute(ws: Workspace):
47
  if ws.env in ops.EXECUTORS:
48
+ await ops.EXECUTORS[ws.env](ws)
49
 
50
 
51
  def save(ws: Workspace, path: str):
web/src/NodeWithTableView.svelte CHANGED
@@ -1,12 +1,14 @@
1
  <script lang="ts">
2
- import { type NodeProps } from '@xyflow/svelte';
3
  import LynxKiteNode from './LynxKiteNode.svelte';
4
  import Table from './Table.svelte';
5
  import SvelteMarkdown from 'svelte-markdown'
6
  type $$Props = NodeProps;
7
  export let data: $$Props['data'];
 
 
8
  const open = {};
9
- $: single = data.display?.dataframes && Object.keys(data.display.dataframes).length === 1;
10
  function toMD(v) {
11
  if (typeof v === 'string') {
12
  return v;
@@ -19,8 +21,8 @@
19
  </script>
20
 
21
  <LynxKiteNode {...$$props}>
22
- {#if data.display}
23
- {#each Object.entries(data.display.dataframes || {}) as [name, df]}
24
  {#if !single}<div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>{/if}
25
  {#if single || open[name]}
26
  {#if df.data.length > 1}
@@ -35,7 +37,7 @@
35
  {/if}
36
  {/if}
37
  {/each}
38
- {#each Object.entries(data.display.others || {}) as [name, o]}
39
  <div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>
40
  {#if open[name]}
41
  <pre>{o}</pre>
 
1
  <script lang="ts">
2
+ import { useNodes, type NodeProps } from '@xyflow/svelte';
3
  import LynxKiteNode from './LynxKiteNode.svelte';
4
  import Table from './Table.svelte';
5
  import SvelteMarkdown from 'svelte-markdown'
6
  type $$Props = NodeProps;
7
  export let data: $$Props['data'];
8
+ const nodes = useNodes(); // We don't properly get updates to "data". This is a hack.
9
+ $: D = $nodes && data;
10
  const open = {};
11
+ $: single = D.display?.value?.dataframes && Object.keys(D.display.value.dataframes).length === 1;
12
  function toMD(v) {
13
  if (typeof v === 'string') {
14
  return v;
 
21
  </script>
22
 
23
  <LynxKiteNode {...$$props}>
24
+ {#if D?.display?.value}
25
+ {#each Object.entries(D.display.value.dataframes || {}) as [name, df]}
26
  {#if !single}<div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>{/if}
27
  {#if single || open[name]}
28
  {#if df.data.length > 1}
 
37
  {/if}
38
  {/if}
39
  {/each}
40
+ {#each Object.entries(D.display.value.others || {}) as [name, o]}
41
  <div class="df-head" on:click={() => open[name] = !open[name]}>{name}</div>
42
  {#if open[name]}
43
  <pre>{o}</pre>