Thomas G. Lopes commited on
Commit
9b3602c
·
1 Parent(s): a261aa8

functional poc

Browse files
src/routes/canvas/+page.svelte CHANGED
@@ -1,26 +1,11 @@
1
  <script lang="ts">
2
- import { Background, Controls, MiniMap, SvelteFlow, type Edge, type Node } from "@xyflow/svelte";
3
-
4
  import "@xyflow/svelte/dist/style.css";
5
  import ChatNode from "./chat-node.svelte";
6
- import { PersistedState } from "runed";
7
 
8
  const nodeTypes = { chat: ChatNode } as const;
9
 
10
- let nodes = new PersistedState<Node[]>("inf-pg-nodes", [
11
- {
12
- id: "1",
13
- position: { x: 100, y: 100 },
14
- data: { query: "", response: "", modelId: undefined },
15
- type: "chat",
16
- width: undefined,
17
- height: undefined,
18
- },
19
- ]);
20
- $inspect(nodes);
21
-
22
- let edges = $state.raw<Edge[]>([]);
23
-
24
  // Make edges non-editable
25
  const edgeOptions = {
26
  deletable: false,
@@ -29,7 +14,13 @@
29
  </script>
30
 
31
  <div style:width="100vw" style:height="100vh">
32
- <SvelteFlow bind:nodes={nodes.current} bind:edges fitView {nodeTypes} defaultEdgeOptions={edgeOptions}>
 
 
 
 
 
 
33
  <MiniMap />
34
  <Controls />
35
  <Background />
 
1
  <script lang="ts">
2
+ import { Background, Controls, MiniMap, SvelteFlow } from "@xyflow/svelte";
 
3
  import "@xyflow/svelte/dist/style.css";
4
  import ChatNode from "./chat-node.svelte";
5
+ import { edges, nodes } from "./state.js";
6
 
7
  const nodeTypes = { chat: ChatNode } as const;
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  // Make edges non-editable
10
  const edgeOptions = {
11
  deletable: false,
 
14
  </script>
15
 
16
  <div style:width="100vw" style:height="100vh">
17
+ <SvelteFlow
18
+ bind:nodes={nodes.current}
19
+ bind:edges={edges.current}
20
+ fitView
21
+ {nodeTypes}
22
+ defaultEdgeOptions={edgeOptions}
23
+ >
24
  <MiniMap />
25
  <Controls />
26
  <Background />
src/routes/canvas/chat-node.svelte CHANGED
@@ -4,36 +4,76 @@
4
  import { token } from "$lib/state/token.svelte";
5
  import type { Model } from "$lib/types.js";
6
  import { InferenceClient } from "@huggingface/inference";
7
- import { Handle, Position, useSvelteFlow, type NodeProps } from "@xyflow/svelte";
8
  import { onMount } from "svelte";
 
 
 
9
 
10
- type Props = NodeProps & { data: { query: string; response: string; modelId?: Model["id"] } };
11
  let { id, data }: Props = $props();
12
 
13
- let { updateNodeData, updateNode } = useSvelteFlow();
14
  onMount(() => {
15
  if (!data.modelId) data.modelId = models.trending[0]?.id;
16
  updateNode(id, { height: undefined });
17
  });
18
 
19
- let modelId = $state<Model["id"] | undefined>(models.trending[0]?.id);
20
-
21
  const autosized = new TextareaAutosize();
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  async function handleSubmit(e: SubmitEvent) {
24
  e.preventDefault();
25
  updateNodeData(id, { response: "" });
26
  const client = new InferenceClient(token.value);
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  const stream = client.chatCompletionStream({
29
  provider: "auto",
30
  model: data.modelId,
31
- messages: [
32
- {
33
- role: "user",
34
- content: data.query,
35
- },
36
- ],
37
  temperature: 0.5,
38
  top_p: 0.7,
39
  });
@@ -45,14 +85,13 @@
45
  }
46
  }
47
  }
48
- $inspect(data);
49
  </script>
50
 
51
  <div
52
- class="chat-node flex h-full min-h-[150px] w-full min-w-[200px] flex-col
53
- items-stretch rounded border bg-white p-8 shadow-xs"
54
  >
55
- <select class="block border" bind:value={modelId}>
56
  {#each models.all as model}
57
  <option value={model.id}>{model.id}</option>
58
  {/each}
@@ -76,6 +115,39 @@
76
  <pre class="whitespace-pre-wrap">{data.response}</pre>
77
  </div>
78
  {/if}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </div>
80
 
81
  <Handle type="target" position={Position.Top} />
 
4
  import { token } from "$lib/state/token.svelte";
5
  import type { Model } from "$lib/types.js";
6
  import { InferenceClient } from "@huggingface/inference";
7
+ import { Handle, Position, useSvelteFlow, type Edge, type Node, type NodeProps } from "@xyflow/svelte";
8
  import { onMount } from "svelte";
9
+ import { edges, nodes } from "./state.js";
10
+ import IconCross from "~icons/carbon/x";
11
+ import type { ChatCompletionInputMessage } from "@huggingface/tasks";
12
 
13
+ type Props = Omit<NodeProps, "data"> & { data: { query: string; response: string; modelId?: Model["id"] } };
14
  let { id, data }: Props = $props();
15
 
16
+ let { updateNodeData, updateNode, getNode } = useSvelteFlow();
17
  onMount(() => {
18
  if (!data.modelId) data.modelId = models.trending[0]?.id;
19
  updateNode(id, { height: undefined });
20
  });
21
 
 
 
22
  const autosized = new TextareaAutosize();
23
 
24
+ const history = $derived.by(function getNodeHistory() {
25
+ const node = nodes.current.find(n => n.id === id);
26
+ if (!node) return [];
27
+
28
+ let history: Array<Omit<Node, "data"> & { data: Props["data"] }> = [node as any];
29
+ let target = node.id;
30
+
31
+ while (true) {
32
+ const parentEdge = edges.current.find(edge => edge.target === target);
33
+ if (!parentEdge) break; // No more parents found
34
+
35
+ const parentNode = nodes.current.find(n => n.id === parentEdge.source);
36
+ if (!parentNode) {
37
+ // Optional: clean up broken edges
38
+ // edges.current = edges.current.filter(e => e.id !== parentEdge.id);
39
+ break;
40
+ }
41
+
42
+ history.unshift(parentNode as any);
43
+ target = parentNode.id; // Move up the chain
44
+ }
45
+
46
+ return history;
47
+ });
48
+ $inspect(data.query, history);
49
+
50
  async function handleSubmit(e: SubmitEvent) {
51
  e.preventDefault();
52
  updateNodeData(id, { response: "" });
53
  const client = new InferenceClient(token.value);
54
 
55
+ const messages: ChatCompletionInputMessage[] = history.flatMap(n => {
56
+ const res: ChatCompletionInputMessage[] = [];
57
+ if (n.data.query) {
58
+ res.push({
59
+ role: "user",
60
+ content: n.data.query,
61
+ });
62
+ }
63
+ if (n.data.response) {
64
+ res.push({
65
+ role: "assistant",
66
+ content: n.data.response,
67
+ });
68
+ }
69
+
70
+ return res;
71
+ });
72
+
73
  const stream = client.chatCompletionStream({
74
  provider: "auto",
75
  model: data.modelId,
76
+ messages,
 
 
 
 
 
77
  temperature: 0.5,
78
  top_p: 0.7,
79
  });
 
85
  }
86
  }
87
  }
 
88
  </script>
89
 
90
  <div
91
+ class="chat-node relative flex h-full min-h-[150px] w-full max-w-[500px] min-w-[200px]
92
+ flex-col items-stretch rounded border bg-white p-8 shadow-xs"
93
  >
94
+ <select class="block border" bind:value={() => data.modelId, modelId => updateNodeData(id, { modelId })}>
95
  {#each models.all as model}
96
  <option value={model.id}>{model.id}</option>
97
  {/each}
 
115
  <pre class="whitespace-pre-wrap">{data.response}</pre>
116
  </div>
117
  {/if}
118
+
119
+ <!-- Add node -->
120
+ <button
121
+ class="abs-x-center absolute bottom-0 z-10 translate-y-1/2 rounded bg-black px-3 py-1
122
+ text-xs text-white hover:bg-neutral-700"
123
+ onclick={() => {
124
+ const curr = getNode(id);
125
+ const newNode: Node = {
126
+ id: crypto.randomUUID(),
127
+ position: { x: curr?.position.x ?? 100, y: (curr?.position.y ?? 0) + 500 },
128
+ data: { query: "", response: "", modelId: data.modelId },
129
+ type: "chat",
130
+ width: undefined,
131
+ height: undefined,
132
+ };
133
+ nodes.current.push(newNode);
134
+ const edge: Edge = {
135
+ id: crypto.randomUUID(),
136
+ source: curr!.id,
137
+ target: newNode.id,
138
+ animated: true,
139
+ label: "",
140
+ data: {},
141
+ };
142
+ edges.current.push(edge);
143
+ }}
144
+ >
145
+ Add
146
+ </button>
147
+
148
+ <button class="absolute top-1 right-1" onclick={() => (nodes.current = nodes.current.filter(n => n.id !== id))}>
149
+ <IconCross />
150
+ </button>
151
  </div>
152
 
153
  <Handle type="target" position={Position.Top} />
src/routes/canvas/state.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Edge, Node } from "@xyflow/svelte";
2
+ import { PersistedState } from "runed";
3
+
4
+ export const nodes = new PersistedState<Node[]>("inf-pg-nodes", [
5
+ {
6
+ id: "1",
7
+ position: { x: 100, y: 100 },
8
+ data: { query: "", response: "", modelId: undefined },
9
+ type: "chat",
10
+ width: undefined,
11
+ height: undefined,
12
+ },
13
+ ]);
14
+
15
+ export const edges = new PersistedState<Edge[]>("inf-pg-edges", []);