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

basic chatting

Browse files
src/routes/canvas/+page.svelte CHANGED
@@ -3,20 +3,33 @@
3
 
4
  import "@xyflow/svelte/dist/style.css";
5
  import ChatNode from "./chat-node.svelte";
 
6
 
7
  const nodeTypes = { chat: ChatNode } as const;
8
 
9
- let nodes = $state.raw<Node[]>([
10
- { id: "1", position: { x: 0, y: 0 }, data: { label: "1" } },
11
- { id: "2", position: { x: 0, y: 100 }, data: { label: "2" } },
12
- { id: "3", position: { x: 0, y: 200 }, data: { label: "3" }, type: "chat" },
 
 
 
 
 
13
  ]);
 
14
 
15
- let edges = $state.raw<Edge[]>([{ id: "e1-2", source: "1", target: "2", animated: true }]);
 
 
 
 
 
 
16
  </script>
17
 
18
  <div style:width="100vw" style:height="100vh">
19
- <SvelteFlow bind:nodes bind:edges fitView {nodeTypes}>
20
  <MiniMap />
21
  <Controls />
22
  <Background />
 
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,
27
+ selectable: false,
28
+ };
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 />
src/routes/canvas/+page.ts ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { PageLoad } from "./$types.js";
2
+ import type { ApiModelsResponse } from "../api/models/+server.js";
3
+
4
+ export const load: PageLoad = async ({ fetch }) => {
5
+ const [modelsRes, routerRes] = await Promise.all([
6
+ fetch("/api/models"),
7
+ fetch("https://router.huggingface.co/v1/models"),
8
+ ]);
9
+
10
+ const models: ApiModelsResponse = await modelsRes.json();
11
+ const routerData = await routerRes.json();
12
+
13
+ return {
14
+ ...models,
15
+ routerData,
16
+ };
17
+ };
src/routes/canvas/chat-node.svelte CHANGED
@@ -1,31 +1,88 @@
1
  <script lang="ts">
2
- import { NodeResizeControl, Position, useSvelteFlow, type NodeProps } from "@xyflow/svelte";
3
- import { Handle } from "@xyflow/svelte";
4
- import IconResize from "~icons/mingcute/fullscreen-2-fill";
 
 
 
 
5
 
6
- let { id, data }: NodeProps = $props();
 
7
 
8
- let { updateNodeData } = useSvelteFlow();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </script>
10
 
11
  <div
12
- class="chat-node flex h-full min-h-[100px] w-full min-w-[200px]
13
- items-start rounded border bg-white p-4"
14
  >
15
- <label for="text">Text:</label>
16
- <input
17
- class="nodrag min-w-0 shrink grow border"
18
- name="text"
19
- value={data.text}
20
- oninput={evt => {
21
- updateNodeData(id, { text: evt.currentTarget.value });
22
- }}
23
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  </div>
25
 
26
  <Handle type="target" position={Position.Top} />
27
  <Handle type="source" position={Position.Bottom} />
 
 
28
 
29
- <NodeResizeControl minWidth={200} minHeight={100} style="background: transparent; border: none;">
30
- <IconResize class="absolute right-2 bottom-2 -scale-x-100 text-blue-500" />
31
- </NodeResizeControl>
 
1
  <script lang="ts">
2
+ import { TextareaAutosize } from "$lib/spells/textarea-autosize.svelte";
3
+ import { models } from "$lib/state/models.svelte";
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
+ });
40
+
41
+ for await (const chunk of stream) {
42
+ if (chunk.choices && chunk.choices.length > 0) {
43
+ const newContent = chunk.choices[0]?.delta.content ?? "";
44
+ updateNodeData(id, { response: data.response + newContent });
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}
59
+ </select>
60
+
61
+ <form class="mt-2 flex flex-col gap-2" onsubmit={handleSubmit}>
62
+ <textarea
63
+ class="nodrag block min-w-0 shrink grow resize-none border"
64
+ placeholder="Type your message here..."
65
+ value={data.query}
66
+ oninput={evt => {
67
+ updateNodeData(id, { query: evt.currentTarget.value });
68
+ }}
69
+ {@attach autosized.attachment}
70
+ ></textarea>
71
+ <button class="self-center bg-blue-500 px-4 py-1 text-white hover:bg-blue-400"> Send </button>
72
+ </form>
73
+
74
+ {#if data.response}
75
+ <div class="mt-2 border-t border-gray-200 p-2">
76
+ <pre class="whitespace-pre-wrap">{data.response}</pre>
77
+ </div>
78
+ {/if}
79
  </div>
80
 
81
  <Handle type="target" position={Position.Top} />
82
  <Handle type="source" position={Position.Bottom} />
83
+ <Handle type="source" position={Position.Left} />
84
+ <Handle type="source" position={Position.Right} />
85
 
86
+ <!-- <NodeResizeControl minWidth={200} minHeight={150}> -->
87
+ <!-- <IconResize class="absolute right-2 bottom-2" /> -->
88
+ <!-- </NodeResizeControl> -->