@dxos/react-ui-canvas-compute 0.9.1-main.c7dcc2e112 → 0.10.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-canvas-compute",
3
- "version": "0.9.1-main.c7dcc2e112",
3
+ "version": "0.10.0",
4
4
  "description": "A compute graph extension for the canvas editor component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -27,68 +27,67 @@
27
27
  "dependencies": {
28
28
  "@antv/graphlib": "^2.0.4",
29
29
  "@antv/layout": "^1.2.13",
30
- "@effect/platform": "0.96.1",
30
+ "@effect/platform": "0.96.2",
31
31
  "chess.js": "^1.0.0",
32
32
  "workerize-loader": "^2.0.2",
33
- "@dxos/ai": "0.9.1-main.c7dcc2e112",
34
- "@dxos/app-toolkit": "0.9.1-main.c7dcc2e112",
35
- "@dxos/app-framework": "0.9.1-main.c7dcc2e112",
36
- "@dxos/assistant": "0.9.1-main.c7dcc2e112",
37
- "@dxos/compute": "0.9.1-main.c7dcc2e112",
38
- "@dxos/async": "0.9.1-main.c7dcc2e112",
39
- "@dxos/conductor": "0.9.1-main.c7dcc2e112",
40
- "@dxos/debug": "0.9.1-main.c7dcc2e112",
41
- "@dxos/context": "0.9.1-main.c7dcc2e112",
42
- "@dxos/effect": "0.9.1-main.c7dcc2e112",
43
- "@dxos/echo": "0.9.1-main.c7dcc2e112",
44
- "@dxos/functions": "0.9.1-main.c7dcc2e112",
45
- "@dxos/graph": "0.9.1-main.c7dcc2e112",
46
- "@dxos/edge-client": "0.9.1-main.c7dcc2e112",
47
- "@dxos/functions-runtime": "0.9.1-main.c7dcc2e112",
48
- "@dxos/invariant": "0.9.1-main.c7dcc2e112",
49
- "@dxos/keys": "0.9.1-main.c7dcc2e112",
50
- "@dxos/log": "0.9.1-main.c7dcc2e112",
51
- "@dxos/react-edge-client": "0.9.1-main.c7dcc2e112",
52
- "@dxos/react-client": "0.9.1-main.c7dcc2e112",
53
- "@dxos/react-ui-audio": "0.9.1-main.c7dcc2e112",
54
- "@dxos/react-ui-canvas": "0.9.1-main.c7dcc2e112",
55
- "@dxos/react-ui-attention": "0.9.1-main.c7dcc2e112",
56
- "@dxos/compute-runtime": "0.9.1-main.c7dcc2e112",
57
- "@dxos/react-ui-canvas-editor": "0.9.1-main.c7dcc2e112",
58
- "@dxos/react-ui-editor": "0.9.1-main.c7dcc2e112",
59
- "@dxos/react-ui-form": "0.9.1-main.c7dcc2e112",
60
- "@dxos/react-ui-mosaic": "0.9.1-main.c7dcc2e112",
61
- "@dxos/react-ui-syntax-highlighter": "0.9.1-main.c7dcc2e112",
62
- "@dxos/react-ui-stack": "0.9.1-main.c7dcc2e112",
63
- "@dxos/types": "0.9.1-main.c7dcc2e112",
64
- "@dxos/schema": "0.9.1-main.c7dcc2e112",
65
- "@dxos/util": "0.9.1-main.c7dcc2e112",
66
- "@dxos/react-ui-experimental": "0.9.1-main.c7dcc2e112"
33
+ "@dxos/ai": "0.10.0",
34
+ "@dxos/app-toolkit": "0.10.0",
35
+ "@dxos/app-framework": "0.10.0",
36
+ "@dxos/compute": "0.10.0",
37
+ "@dxos/compute-runtime": "0.10.0",
38
+ "@dxos/async": "0.10.0",
39
+ "@dxos/conductor": "0.10.0",
40
+ "@dxos/context": "0.10.0",
41
+ "@dxos/echo": "0.10.0",
42
+ "@dxos/edge-client": "0.10.0",
43
+ "@dxos/effect": "0.10.0",
44
+ "@dxos/functions": "0.10.0",
45
+ "@dxos/assistant": "0.10.0",
46
+ "@dxos/invariant": "0.10.0",
47
+ "@dxos/functions-runtime": "0.10.0",
48
+ "@dxos/graph": "0.10.0",
49
+ "@dxos/keys": "0.10.0",
50
+ "@dxos/log": "0.10.0",
51
+ "@dxos/react-edge-client": "0.10.0",
52
+ "@dxos/react-client": "0.10.0",
53
+ "@dxos/react-ui-attention": "0.10.0",
54
+ "@dxos/react-ui-audio": "0.10.0",
55
+ "@dxos/react-ui-canvas": "0.10.0",
56
+ "@dxos/react-ui-canvas-editor": "0.10.0",
57
+ "@dxos/react-ui-editor": "0.10.0",
58
+ "@dxos/react-ui-form": "0.10.0",
59
+ "@dxos/react-ui-experimental": "0.10.0",
60
+ "@dxos/react-ui-mosaic": "0.10.0",
61
+ "@dxos/react-ui-syntax-highlighter": "0.10.0",
62
+ "@dxos/schema": "0.10.0",
63
+ "@dxos/types": "0.10.0",
64
+ "@dxos/util": "0.10.0",
65
+ "@dxos/debug": "0.10.0"
67
66
  },
68
67
  "devDependencies": {
69
68
  "@effect/ai": "0.36.0",
70
69
  "@effect/experimental": "0.60.0",
71
70
  "@types/lodash.defaultsdeep": "^4.6.6",
72
- "@types/react": "~19.2.7",
71
+ "@types/react": "~19.2.17",
73
72
  "@types/react-dom": "~19.2.3",
74
- "effect": "3.21.3",
73
+ "effect": "3.21.4",
75
74
  "lodash.defaultsdeep": "^4.6.1",
76
- "react": "~19.2.3",
77
- "react-dom": "~19.2.3",
75
+ "react": "~19.2.7",
76
+ "react-dom": "~19.2.7",
78
77
  "vite": "^8.0.16",
79
- "@dxos/app-framework": "0.9.1-main.c7dcc2e112",
80
- "@dxos/assistant-toolkit": "0.9.1-main.c7dcc2e112",
81
- "@dxos/react-ui": "0.9.1-main.c7dcc2e112",
82
- "@dxos/random": "0.9.1-main.c7dcc2e112",
83
- "@dxos/storybook-utils": "0.9.1-main.c7dcc2e112",
84
- "@dxos/ui-theme": "0.9.1-main.c7dcc2e112"
78
+ "@dxos/app-framework": "0.10.0",
79
+ "@dxos/assistant-toolkit": "0.10.0",
80
+ "@dxos/random": "0.10.0",
81
+ "@dxos/react-ui": "0.10.0",
82
+ "@dxos/storybook-utils": "0.10.0",
83
+ "@dxos/ui-theme": "0.10.0"
85
84
  },
86
85
  "peerDependencies": {
87
- "effect": "3.21.3",
88
- "react": "~19.2.3",
89
- "react-dom": "~19.2.3",
90
- "@dxos/react-ui": "0.9.1-main.c7dcc2e112",
91
- "@dxos/ui-theme": "0.9.1-main.c7dcc2e112"
86
+ "effect": "3.21.4",
87
+ "react": "~19.2.7",
88
+ "react-dom": "~19.2.7",
89
+ "@dxos/react-ui": "0.10.0",
90
+ "@dxos/ui-theme": "0.10.0"
92
91
  },
93
92
  "publishConfig": {
94
93
  "access": "public"
@@ -14,7 +14,6 @@ import { capabilities } from '@dxos/assistant-toolkit/testing';
14
14
  import { Operation } from '@dxos/compute';
15
15
  import { TestDatabaseLayer } from '@dxos/compute-runtime/testing';
16
16
  import { type ComputeGraphModel, type ComputeNode, type GraphDiagnostic } from '@dxos/conductor';
17
- import { Feed } from '@dxos/echo';
18
17
  import { registryLayerNoop } from '@dxos/echo/testing';
19
18
  import { configuredCredentialsLayer } from '@dxos/functions';
20
19
  import { withClientProvider } from '@dxos/react-client/testing';
@@ -38,8 +37,8 @@ import {
38
37
  createBasicCircuit,
39
38
  createControlCircuit,
40
39
  createEmptyCircuit,
41
- createGPTRealtimeCircuit,
42
40
  createGptCircuit,
41
+ createGPTRealtimeCircuit,
43
42
  createLogicCircuit,
44
43
  createTemplateCircuit,
45
44
  createTransformCircuit,
@@ -236,12 +235,7 @@ const ServiceLayer = Layer.empty.pipe(
236
235
  ),
237
236
  ),
238
237
  Layer.provideMerge(
239
- Layer.mergeAll(
240
- AiServiceTestingPreset('direct'),
241
- TestDatabaseLayer(),
242
- configuredCredentialsLayer([]),
243
- Feed.notAvailable,
244
- ),
238
+ Layer.mergeAll(AiServiceTestingPreset('direct'), TestDatabaseLayer(), configuredCredentialsLayer([])),
245
239
  ),
246
240
  Layer.orDie,
247
241
  );
@@ -14,14 +14,14 @@ import { type Credential, type Operation, Trace } from '@dxos/compute';
14
14
  import {
15
15
  ComputeBeginEvent,
16
16
  ComputeCustomEvent,
17
- ComputeEndEvent,
18
- ComputeInputEvent,
19
- ComputeNodeContext,
20
- ComputeOutputEvent,
21
17
  type ComputeEdge,
18
+ ComputeEndEvent,
22
19
  type ComputeGraphModel,
20
+ ComputeInputEvent,
23
21
  type ComputeNode,
22
+ ComputeNodeContext,
24
23
  type ComputeNodeMeta,
24
+ ComputeOutputEvent,
25
25
  type GptInput,
26
26
  type GptOutput,
27
27
  type GraphDiagnostic,
@@ -30,7 +30,7 @@ import {
30
30
  isNotExecuted,
31
31
  } from '@dxos/conductor';
32
32
  import { Resource } from '@dxos/context';
33
- import type { Database, Feed, Registry } from '@dxos/echo';
33
+ import type { Database, Registry } from '@dxos/echo';
34
34
  import { EffectEx } from '@dxos/effect';
35
35
  import { log } from '@dxos/log';
36
36
  import { type CanvasGraphModel } from '@dxos/react-ui-canvas-editor';
@@ -93,7 +93,6 @@ export type ComputeEvent =
93
93
  export type ComputeServices =
94
94
  | AiService.AiService
95
95
  | Database.Service
96
- | Feed.FeedService
97
96
  | Credential.CredentialsService
98
97
  | Operation.Service
99
98
  | Registry.Service;
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { DEFAULT_INPUT } from '@dxos/conductor';
8
+
9
+ import { createComputeGraph } from '../hooks';
10
+ import {
11
+ createArtifactCircuit,
12
+ createAudioCircuit,
13
+ createBasicCircuit,
14
+ createControlCircuit,
15
+ createGptCircuit,
16
+ createGPTRealtimeCircuit,
17
+ createLogicCircuit,
18
+ createTemplateCircuit,
19
+ createTransformCircuit,
20
+ } from '../testing';
21
+
22
+ // Each canvas shape is mapped to a compute node and added to a single ECHO-backed `ComputeGraphModel`.
23
+ // Nodes carry nested records (e.g. a template's `inputSchema`); these must be owned independently per node,
24
+ // otherwise adding a second node that embeds a shared record reassigns ECHO ownership and throws.
25
+ const circuits: [string, () => any][] = [
26
+ ['empty/basic', createBasicCircuit],
27
+ ['transform', createTransformCircuit],
28
+ ['logic', createLogicCircuit],
29
+ ['control', createControlCircuit],
30
+ ['template', createTemplateCircuit],
31
+ ['gpt', () => createGptCircuit({ history: true })],
32
+ ['gpt (plugins)', () => createGptCircuit({ history: true, image: true, artifact: true })],
33
+ ['gpt (instructions)', () => createGptCircuit({ instructions: true })],
34
+ ['artifact', createArtifactCircuit],
35
+ ['audio', createAudioCircuit],
36
+ ['voice', createGPTRealtimeCircuit],
37
+ ];
38
+
39
+ describe('createComputeGraph', () => {
40
+ for (const [name, build] of circuits) {
41
+ test(`builds the ${name} circuit without ECHO ownership errors`, ({ expect }) => {
42
+ const model = createComputeGraph(build());
43
+ expect(model.nodes.length).toBeGreaterThanOrEqual(0);
44
+ });
45
+ }
46
+
47
+ test('wires Template + Chat into GPT and GPT into the output', ({ expect }) => {
48
+ const model = createComputeGraph(createTemplateCircuit());
49
+
50
+ const gpt = model.nodes.find((node) => node.type === 'gpt');
51
+ expect(gpt).toBeDefined();
52
+
53
+ const incoming = model.edges.filter((edge) => edge.target === gpt!.id);
54
+ const outgoing = model.edges.filter((edge) => edge.source === gpt!.id);
55
+
56
+ // Chat feeds the prompt; the template feeds the system prompt.
57
+ expect(incoming.map((edge) => edge.input).sort()).toEqual(['prompt', 'systemPrompt']);
58
+ // GPT output flows downstream (to the text node).
59
+ expect(outgoing.some((edge) => edge.output === 'text')).toBe(true);
60
+ expect(outgoing.every((edge) => edge.input === DEFAULT_INPUT)).toBe(true);
61
+ });
62
+ });
@@ -40,40 +40,42 @@ const nodeFactory: Record<NodeType | 'trigger', (shape: ComputeShape) => Compute
40
40
 
41
41
  // Extensions.
42
42
  'text-to-image': () => createNode('text-to-image'), // TODO(burdon): Rename ai-impage-tool
43
- and: () => createNode('and'),
44
- append: () => createNode('append'),
45
- audio: () => createNode('audio'),
46
- beacon: () => createNode('beacon'),
47
- chat: () => createNode('chat'),
48
- constant: (shape) =>
43
+ 'and': () => createNode('and'),
44
+ 'append': () => createNode('append'),
45
+ 'audio': () => createNode('audio'),
46
+ 'beacon': () => createNode('beacon'),
47
+ 'chat': () => createNode('chat'),
48
+ 'constant': (shape) =>
49
49
  createNode('constant', {
50
50
  value: (shape as ConstantShape).value,
51
51
  }),
52
52
  'make-queue': () => createNode('make-queue'),
53
- database: () => createNode('database'),
54
- gpt: () => createNode('gpt'),
53
+ 'database': () => createNode('database'),
54
+ 'gpt': () => createNode('gpt'),
55
55
  'gpt-realtime': () => createNode('gpt-realtime'),
56
- if: () => createNode('if'),
56
+ 'if': () => createNode('if'),
57
57
  'if-else': () => createNode('if-else'),
58
- function: () => createNode('function'),
59
- json: () => createNode('json'),
58
+ 'function': () => createNode('function'),
59
+ 'json': () => createNode('json'),
60
60
  'json-transform': () => createNode('json-transform'),
61
- not: () => createNode('not'),
62
- or: () => createNode('or'),
63
- queue: () => createNode('queue'),
64
- rng: () => createNode('rng'),
65
- reducer: () => createNode('reducer'),
66
- scope: () => createNode('scope'),
67
- surface: () => createNode('surface'),
68
- switch: () => createNode('switch'),
69
- template: (shape) => {
61
+ 'not': () => createNode('not'),
62
+ 'or': () => createNode('or'),
63
+ 'queue': () => createNode('queue'),
64
+ 'rng': () => createNode('rng'),
65
+ 'reducer': () => createNode('reducer'),
66
+ 'scope': () => createNode('scope'),
67
+ 'surface': () => createNode('surface'),
68
+ 'switch': () => createNode('switch'),
69
+ 'template': (shape) => {
70
70
  const node = createNode('template', { valueType: (shape as TemplateShape).valueType, value: shape.text });
71
- node.inputSchema = JsonSchema.toJsonSchema(getTemplateInputSchema(node));
71
+ // `toJsonSchema` reuses cached fragments (e.g. the `Schema.Any` JSON form) across calls; deep-copy so each
72
+ // ECHO node owns its own schema record rather than reassigning ownership of a shared one.
73
+ node.inputSchema = structuredClone(JsonSchema.toJsonSchema(getTemplateInputSchema(node)));
72
74
  return node;
73
75
  },
74
- text: () => createNode('text'),
75
- thread: () => createNode('thread'),
76
- trigger: () => createNode(NODE_INPUT),
76
+ 'text': () => createNode('text'),
77
+ 'thread': () => createNode('thread'),
78
+ 'trigger': () => createNode(NODE_INPUT),
77
79
  };
78
80
 
79
81
  const createNode = (type: string, props?: Partial<ComputeNode>): ComputeNode => ({
@@ -113,7 +113,8 @@ const linkTriggerToCompute = (graph: ComputeGraphModel, computeNode: ComputeNode
113
113
  const functionTrigger = triggerData.functionTrigger?.target;
114
114
  invariant(functionTrigger);
115
115
  Obj.update(functionTrigger, (functionTrigger) => {
116
- functionTrigger.function = Ref.make(graph.root);
116
+ // TODO(wittjosiah): Widen Runnable union to include ComputeGraph and remove cast.
117
+ functionTrigger.runnable = Ref.make(graph.root) as any;
117
118
  functionTrigger.inputNodeId = computeNode.id;
118
119
  });
119
120
  };
package/src/registry.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  chatShape,
14
14
  constantShape,
15
15
  databaseShape,
16
+ feedShape,
16
17
  functionShape,
17
18
  gptRealtimeShape,
18
19
  gptShape,
@@ -22,7 +23,6 @@ import {
22
23
  jsonTransformShape,
23
24
  notShape,
24
25
  orShape,
25
- feedShape,
26
26
  randomShape,
27
27
  reducerShape,
28
28
  scopeShape,
@@ -5,6 +5,7 @@
5
5
  import * as Schema from 'effect/Schema';
6
6
  import React, { useEffect, useState } from 'react';
7
7
 
8
+ import { DEFAULT_OUTPUT } from '@dxos/conductor';
8
9
  import { Icon } from '@dxos/react-ui';
9
10
  import { type ShapeComponentProps, type ShapeDef, createAnchorMap } from '@dxos/react-ui-canvas-editor';
10
11
 
@@ -26,10 +27,10 @@ export const createAudio = (props: CreateAudioProps) =>
26
27
  createShape<AudioShape>({ type: 'audio', size: { width: 64, height: 64 }, ...props });
27
28
 
28
29
  export const AudioComponent = ({ shape }: ShapeComponentProps<AudioShape>) => {
29
- const { node } = useComputeNodeState(shape);
30
+ const { runtime } = useComputeNodeState(shape);
30
31
  const [active, setActive] = useState(false);
31
32
  useEffect(() => {
32
- node.value = active;
33
+ runtime.setOutput(DEFAULT_OUTPUT, active);
33
34
  }, [active]);
34
35
 
35
36
  // https://docs.pmnd.rs/react-three-fiber/api/canvas#render-props
@@ -74,7 +74,8 @@ export const FeedItem = ({ classNames, item }: ThemedClassName<{ item: any }>) =
74
74
  };
75
75
 
76
76
  export const feedShape: ShapeDef<FeedShape> = {
77
- type: 'feed',
77
+ // Must match the shape's `type` literal ('queue') so the registry resolves it; the registry is keyed by this.
78
+ type: 'queue',
78
79
  name: 'Feed',
79
80
  icon: 'ph--queue--regular',
80
81
  component: FeedComponent,
@@ -16,6 +16,7 @@ import {
16
16
  createChat,
17
17
  createConstant,
18
18
  createDatabase,
19
+ createFeed,
19
20
  createGpt,
20
21
  createGptRealtime,
21
22
  createIf,
@@ -24,7 +25,6 @@ import {
24
25
  createJsonTransform,
25
26
  createNot,
26
27
  createOr,
27
- createFeed,
28
28
  createRandom,
29
29
  createScope,
30
30
  createSurface,