Spaces:
Runtime error
Runtime error
Thomas G. Lopes
commited on
Commit
·
a261aa8
1
Parent(s):
1de8a7e
basic chatting
Browse files- src/routes/canvas/+page.svelte +19 -6
- src/routes/canvas/+page.ts +17 -0
- src/routes/canvas/chat-node.svelte +76 -19
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 =
|
| 10 |
-
{
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
]);
|
|
|
|
| 14 |
|
| 15 |
-
let edges = $state.raw<Edge[]>([
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 {
|
| 3 |
-
import {
|
| 4 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
|
|
|
| 7 |
|
| 8 |
-
let { updateNodeData } = useSvelteFlow();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
</script>
|
| 10 |
|
| 11 |
<div
|
| 12 |
-
class="chat-node flex h-full min-h-[
|
| 13 |
-
items-
|
| 14 |
>
|
| 15 |
-
<
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 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={
|
| 30 |
-
|
| 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> -->
|