From 13040b61929c6f13dc266f82760b57f11abb86f3 Mon Sep 17 00:00:00 2001
From: Ben Follington <5009316+bfollington@users.noreply.github.com>
Date: Wed, 24 Jul 2024 12:59:21 +1000
Subject: [PATCH 01/43] Configure CORS in planning server
---
.../packages/planning-server/src/actions.ts | 143 +++++++++++++
.../packages/planning-server/src/deps.ts | 2 +
.../packages/planning-server/src/index.ts | 193 +-----------------
.../packages/planning-server/src/server.ts | 90 ++++++++
4 files changed, 237 insertions(+), 191 deletions(-)
create mode 100644 typescript/packages/planning-server/src/actions.ts
create mode 100644 typescript/packages/planning-server/src/server.ts
diff --git a/typescript/packages/planning-server/src/actions.ts b/typescript/packages/planning-server/src/actions.ts
new file mode 100644
index 000000000..5b1e770d6
--- /dev/null
+++ b/typescript/packages/planning-server/src/actions.ts
@@ -0,0 +1,143 @@
+import { CoreAssistantMessage, CoreMessage, CoreTool } from "npm:ai";
+import {
+ ConversationThread,
+ InMemoryConversationThreadManager,
+} from "./conversation.ts";
+import { ask } from "./anthropic.ts";
+
+const cache: Record = {};
+const threadManager = new InMemoryConversationThreadManager();
+
+type CreateConversationThreadResponse = {
+ type: "success";
+ threadId: string;
+ output: string;
+ assistantResponse: CoreAssistantMessage;
+ conversation: CoreMessage[];
+};
+
+type AppendToConversationThreadResponse = {
+ type: "success";
+ threadId: string;
+ output: string;
+ assistantResponse: CoreAssistantMessage;
+ conversation: CoreMessage[];
+};
+
+type ConversationThreadResponse =
+ | CreateConversationThreadResponse
+ | AppendToConversationThreadResponse;
+
+type ErrorResponse = {
+ type: "error";
+ error: string;
+};
+
+export async function handleCreateConversationThread(
+ system: string,
+ message: string,
+ activeTools: CoreTool[]
+): Promise {
+ const cacheKey = `${system}:${message}`;
+
+ if (cache[cacheKey]) {
+ console.log(
+ "Cache hit!",
+ (cacheKey.slice(0, 20) + "..." + cacheKey.slice(-20)).replaceAll("\n", "")
+ );
+ return cache[cacheKey];
+ }
+
+ const thread = threadManager.create(system, message, activeTools);
+ const result = await processConversationThread(thread);
+ if (result.type === "error") {
+ throw new Error(result.error);
+ }
+
+ if (result.assistantResponse) {
+ threadManager.update(thread.id, [result.assistantResponse]);
+ }
+
+ // cache[cacheKey] = result;
+
+ return result;
+}
+
+export async function handleAppendToConversationThread(
+ threadId: string,
+ message?: string
+): Promise {
+ const thread = threadManager.get(threadId);
+ if (!thread) {
+ throw new Error("Thread not found");
+ }
+
+ if (message) {
+ threadManager.update(threadId, [
+ {
+ role: "user",
+ content: message,
+ },
+ ]);
+ }
+
+ const result = await processConversationThread(thread);
+ if (result.type === "error") {
+ return result;
+ }
+
+ // Update the thread with the assistant's response
+ if (result.assistantResponse) {
+ threadManager.update(threadId, [result.assistantResponse]);
+ }
+
+ return result;
+}
+
+type ProcessConversationThreadResult =
+ | {
+ type: "success";
+ threadId: string;
+ output: string;
+ assistantResponse: CoreAssistantMessage;
+ conversation: CoreMessage[];
+ }
+ | { type: "error"; error: string };
+
+async function processConversationThread(
+ thread: ConversationThread
+): Promise {
+ console.log("Thread", thread);
+
+ const result = await ask(
+ thread.conversation,
+ thread.system,
+ thread.activeTools
+ );
+ if (!result) {
+ return { type: "error", error: "No response from Anthropic" };
+ }
+
+ // Find the new assistant's response (it should be the last message)
+ const assistantResponse = result[result.length - 1];
+ if (assistantResponse.role !== "assistant") {
+ return { type: "error", error: "No assistant response found" };
+ }
+
+ if (Array.isArray(assistantResponse.content)) {
+ assistantResponse.content = assistantResponse.content
+ .filter((msg) => msg.type == "text")
+ .map((msg) => msg.text)
+ .join(" ");
+ }
+
+ const output = assistantResponse.content;
+ console.log("Output=", output);
+ return {
+ type: "success",
+ threadId: thread.id,
+ output,
+ assistantResponse,
+ conversation: result,
+ };
+}
diff --git a/typescript/packages/planning-server/src/deps.ts b/typescript/packages/planning-server/src/deps.ts
index 7d28ce8d0..2cf247807 100644
--- a/typescript/packages/planning-server/src/deps.ts
+++ b/typescript/packages/planning-server/src/deps.ts
@@ -2,6 +2,8 @@
export { default as datascript } from "npm:datascript";
import { config } from "https://deno.land/x/dotenv/mod.ts";
export { serve } from "https://deno.land/std@0.140.0/http/server.ts";
+export { Application, Router } from "https://deno.land/x/oak/mod.ts";
+export { oakCors } from "https://deno.land/x/cors/mod.ts";
export * as ai from "npm:ai";
export { anthropic } from "npm:@ai-sdk/anthropic";
diff --git a/typescript/packages/planning-server/src/index.ts b/typescript/packages/planning-server/src/index.ts
index 11a9d79fc..c415f6560 100644
--- a/typescript/packages/planning-server/src/index.ts
+++ b/typescript/packages/planning-server/src/index.ts
@@ -1,192 +1,3 @@
-import { ask } from "./anthropic.ts";
-import { serve } from "./deps.ts";
-import {
- InMemoryConversationThreadManager,
- ConversationThread,
-} from "./conversation.ts";
-import { CoreMessage, CoreTool } from "npm:ai";
-import { CoreAssistantMessage } from "npm:ai";
+import { start } from "./server.ts";
-const threadManager = new InMemoryConversationThreadManager();
-
-type CreateConversationThreadRequest = {
- action: "create";
- message: string;
- system: string;
- activeTools: CoreTool[];
-};
-
-type AppendToConversationThreadRequest = {
- action: "append";
- threadId: string;
- message?: string;
-};
-
-type ConversationThreadRequest =
- | CreateConversationThreadRequest
- | AppendToConversationThreadRequest;
-
-const handler = async (request: Request): Promise => {
- if (request.method === "POST") {
- try {
- const body: ConversationThreadRequest = await request.json();
- const { action } = body;
-
- switch (action) {
- case "create": {
- const { message, system, activeTools } = body;
- return handleCreateConversationThread(system, message, activeTools);
- }
- case "append": {
- const { threadId, message } = body;
- return handleAppendToConversationThread(threadId, message);
- }
- default:
- return new Response(JSON.stringify({ error: "Invalid action" }), {
- status: 400,
- headers: { "Content-Type": "application/json" },
- });
- }
- } catch (error) {
- return new Response(JSON.stringify({ error: error.message }), {
- status: 400,
- headers: { "Content-Type": "application/json" },
- });
- }
- } else {
- return new Response("Please send a POST request", { status: 405 });
- }
-};
-
-const cache: Record = {};
-
-async function handleCreateConversationThread(
- system: string,
- message: string,
- activeTools: CoreTool[]
-): Promise {
- const cacheKey = `${system}:${message}`;
-
- if (cache[cacheKey]) {
- console.log(
- "Cache hit!",
- (cacheKey.slice(0, 20) + "..." + cacheKey.slice(-20)).replaceAll("\n", "")
- );
- return new Response(JSON.stringify(cache[cacheKey]), {
- headers: { "Content-Type": "application/json" },
- });
- }
-
- const thread = threadManager.create(system, message, activeTools);
- const result = await processConversationThread(thread);
- if (result.type === "error") {
- return new Response(JSON.stringify(result), {
- status: 400,
- headers: { "Content-Type": "application/json" },
- });
- }
-
- if (result.assistantResponse) {
- threadManager.update(thread.id, [result.assistantResponse]);
- }
-
- // cache[cacheKey] = result;
-
- return new Response(JSON.stringify(result), {
- headers: { "Content-Type": "application/json" },
- });
-}
-
-async function handleAppendToConversationThread(
- threadId: string,
- message?: string
-): Promise {
- const thread = threadManager.get(threadId);
- if (!thread) {
- return new Response(JSON.stringify({ error: "Thread not found" }), {
- status: 404,
- headers: { "Content-Type": "application/json" },
- });
- }
-
- if (message) {
- threadManager.update(threadId, [
- {
- role: "user",
- content: message,
- },
- ]);
- }
-
- const result = await processConversationThread(thread);
- if (result.type === "error") {
- return new Response(JSON.stringify(result), {
- status: 400,
- headers: { "Content-Type": "application/json" },
- });
- }
-
- // Update the thread with the assistant's response
- if (result.assistantResponse) {
- threadManager.update(threadId, [result.assistantResponse]);
- }
-
- // Remove the assistantResponse from the result before sending it to the client
- const { assistantResponse, ...responseToClient } = result;
-
- return new Response(JSON.stringify(responseToClient), {
- headers: { "Content-Type": "application/json" },
- });
-}
-
-type ProcessConversationThreadResult =
- | {
- type: "success";
- threadId: string;
- output: string;
- assistantResponse: CoreAssistantMessage;
- conversation: CoreMessage[];
- }
- | { type: "error"; error: string };
-
-async function processConversationThread(
- thread: ConversationThread
-): Promise {
- console.log("Thread", thread);
-
- const result = await ask(
- thread.conversation,
- thread.system,
- thread.activeTools
- );
- if (!result) {
- return { type: "error", error: "No response from Anthropic" };
- }
-
- // Find the new assistant's response (it should be the last message)
- const assistantResponse = result[result.length - 1];
- if (assistantResponse.role !== "assistant") {
- return { type: "error", error: "No assistant response found" };
- }
-
- if (Array.isArray(assistantResponse.content)) {
- assistantResponse.content = assistantResponse.content
- .filter((msg) => msg.type == "text")
- .map((msg) => msg.text)
- .join(" ");
- }
-
- const output = assistantResponse.content;
- console.log("Output=", output);
- return {
- type: "success",
- threadId: thread.id,
- output,
- assistantResponse,
- conversation: result,
- };
-}
-
-const port = Deno.env.get("PORT") || "8000";
-console.log(`HTTP webserver running. Access it at: http://localhost:${port}/`);
-await serve(handler, { port: parseInt(port) });
+start();
diff --git a/typescript/packages/planning-server/src/server.ts b/typescript/packages/planning-server/src/server.ts
new file mode 100644
index 000000000..29712b544
--- /dev/null
+++ b/typescript/packages/planning-server/src/server.ts
@@ -0,0 +1,90 @@
+import { CoreTool } from "npm:ai";
+import { Application, Router } from "./deps.ts";
+import { oakCors } from "./deps.ts";
+import {
+ handleAppendToConversationThread,
+ handleCreateConversationThread,
+} from "./actions.ts";
+
+type CreateConversationThreadRequest = {
+ action: "create";
+ message: string;
+ system: string;
+ activeTools: CoreTool[];
+};
+
+type AppendToConversationThreadRequest = {
+ action: "append";
+ threadId: string;
+ message?: string;
+};
+
+type ConversationThreadRequest =
+ | CreateConversationThreadRequest
+ | AppendToConversationThreadRequest;
+
+export async function start() {
+ const app = new Application();
+
+ // Enabling CORS for port 5173 on localhost using oakCors
+ // make sure to initialize oakCors before the routers
+ app.use(
+ oakCors({
+ origin: "http://localhost:8080",
+ optionsSuccessStatus: 200,
+ methods: "POST, OPTIONS",
+ })
+ );
+
+ const router = new Router();
+
+ router.post("/", async (context) => {
+ const request = context.request;
+ if (request.method === "POST") {
+ try {
+ const body: ConversationThreadRequest = await request.body.json();
+ const { action } = body;
+
+ switch (action) {
+ case "create": {
+ const { message, system, activeTools } = body;
+ const result = await handleCreateConversationThread(
+ system,
+ message,
+ activeTools
+ );
+ context.response.status = 200;
+ context.response.body = result;
+ break;
+ }
+ case "append": {
+ const { threadId, message } = body;
+ const result = await handleAppendToConversationThread(
+ threadId,
+ message
+ );
+ context.response.status = 200;
+ context.response.body = result;
+ break;
+ }
+ default:
+ context.response.status = 400;
+ context.response.body = { error: "Invalid action" };
+ }
+ } catch (error) {
+ context.response.status = 400;
+ context.response.body = { error: error.message };
+ }
+ } else {
+ context.response.status = 405;
+ context.response.body = { error: "Method not allowed" };
+ }
+ });
+
+ app.use(router.routes());
+ app.use(router.allowedMethods());
+
+ const port = Number(Deno.env.get("PORT") || 8000);
+ console.log(`Listening on port ${port}`);
+ await app.listen({ port });
+}
From 8316bc0b17f825d9fe3f5789af8010cd93602e49 Mon Sep 17 00:00:00 2001
From: Ben Follington <5009316+bfollington@users.noreply.github.com>
Date: Wed, 24 Jul 2024 12:59:37 +1000
Subject: [PATCH 02/43] Create gemcraft leptos project
---
Cargo.lock | 710 ++++++++++++++++++++++++++++++-
Cargo.toml | 2 +-
rust/gemcraft-leptos/Cargo.toml | 11 +
rust/gemcraft-leptos/Trunk.toml | 5 +
rust/gemcraft-leptos/index.html | 5 +
rust/gemcraft-leptos/src/main.rs | 131 ++++++
6 files changed, 852 insertions(+), 12 deletions(-)
create mode 100644 rust/gemcraft-leptos/Cargo.toml
create mode 100644 rust/gemcraft-leptos/Trunk.toml
create mode 100644 rust/gemcraft-leptos/index.html
create mode 100644 rust/gemcraft-leptos/src/main.rs
diff --git a/Cargo.lock b/Cargo.lock
index 7cd6b7a7a..fdb51bfee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -164,6 +164,17 @@ dependencies = [
"syn 2.0.61",
]
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
[[package]]
name = "async-trait"
version = "0.1.80"
@@ -181,6 +192,36 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+[[package]]
+name = "attribute-derive"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f1ee502851995027b06f99f5ffbeffa1406b38d0b318a1ebfa469332c6cbafd"
+dependencies = [
+ "attribute-derive-macro",
+ "derive-where",
+ "manyhow",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
+[[package]]
+name = "attribute-derive-macro"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3601467f634cfe36c4780ca9c75dea9a5b34529c1f2810676a337e7e0997f954"
+dependencies = [
+ "collection_literals",
+ "interpolator",
+ "manyhow",
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
+ "quote-use",
+ "syn 2.0.61",
+]
+
[[package]]
name = "auto_impl"
version = "1.2.0"
@@ -209,7 +250,7 @@ dependencies = [
"axum-macros",
"bytes",
"futures-util",
- "http",
+ "http 1.1.0",
"http-body",
"http-body-util",
"hyper",
@@ -243,7 +284,7 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
- "http",
+ "http 1.1.0",
"http-body",
"http-body-util",
"mime",
@@ -385,6 +426,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+[[package]]
+name = "camino"
+version = "1.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
+
[[package]]
name = "cap-fs-ext"
version = "3.1.0"
@@ -479,6 +526,33 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "ciborium"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
+dependencies = [
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
+dependencies = [
+ "ciborium-io",
+ "half",
+]
+
[[package]]
name = "clap"
version = "4.5.7"
@@ -525,18 +599,77 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
+[[package]]
+name = "collection_literals"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
+
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+[[package]]
+name = "config"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
+dependencies = [
+ "convert_case",
+ "lazy_static",
+ "nom",
+ "pathdiff",
+ "serde",
+ "toml",
+]
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "const_format"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
+dependencies = [
+ "const_format_proc_macros",
+]
+
+[[package]]
+name = "const_format_proc_macros"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -740,6 +873,12 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -918,6 +1057,17 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "derive-where"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
@@ -1006,6 +1156,12 @@ dependencies = [
"text_lines",
]
+[[package]]
+name = "drain_filter_polyfill"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408"
+
[[package]]
name = "either"
version = "1.12.0"
@@ -1242,6 +1398,15 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "gemcraft-leptos"
+version = "0.1.0"
+dependencies = [
+ "console_error_panic_hook",
+ "leptos",
+ "wasm-bindgen-futures",
+]
+
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -1259,8 +1424,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
@@ -1274,6 +1441,40 @@ dependencies = [
"stable_deref_trait",
]
+[[package]]
+name = "gloo-net"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "http 0.2.12",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
[[package]]
name = "h2"
version = "0.4.5"
@@ -1285,7 +1486,7 @@ dependencies = [
"fnv",
"futures-core",
"futures-sink",
- "http",
+ "http 1.1.0",
"indexmap",
"slab",
"tokio",
@@ -1293,6 +1494,16 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
[[package]]
name = "hashbrown"
version = "0.13.2"
@@ -1353,6 +1564,26 @@ dependencies = [
"triomphe",
]
+[[package]]
+name = "html-escape"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
+dependencies = [
+ "utf8-width",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
[[package]]
name = "http"
version = "1.1.0"
@@ -1371,7 +1602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
- "http",
+ "http 1.1.0",
]
[[package]]
@@ -1382,7 +1613,7 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
dependencies = [
"bytes",
"futures-core",
- "http",
+ "http 1.1.0",
"http-body",
"pin-project-lite",
]
@@ -1409,7 +1640,7 @@ dependencies = [
"futures-channel",
"futures-util",
"h2",
- "http",
+ "http 1.1.0",
"http-body",
"httparse",
"httpdate",
@@ -1427,7 +1658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
dependencies = [
"futures-util",
- "http",
+ "http 1.1.0",
"hyper",
"hyper-util",
"rustls",
@@ -1446,7 +1677,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
- "http",
+ "http 1.1.0",
"http-body",
"hyper",
"pin-project-lite",
@@ -1536,6 +1767,18 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "interpolator"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8"
+
+[[package]]
+name = "inventory"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
+
[[package]]
name = "io-extras"
version = "0.18.2"
@@ -1658,6 +1901,153 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
+[[package]]
+name = "leptos"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5fae88b21265cbb847c891d7cf660e284a1282da15459691992be9358e906fb"
+dependencies = [
+ "cfg-if",
+ "leptos_config",
+ "leptos_dom",
+ "leptos_macro",
+ "leptos_reactive",
+ "leptos_server",
+ "server_fn",
+ "tracing",
+ "typed-builder",
+ "typed-builder-macro",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_config"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec33b6f994829469ba7c62bfd5fb572a639071d0de715a41c2aa0df86301a4fa"
+dependencies = [
+ "config",
+ "regex",
+ "serde",
+ "thiserror",
+ "typed-builder",
+]
+
+[[package]]
+name = "leptos_dom"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "867d2afc153cc0f1d6f775872d5dfc409385f4d8544831ee45f720d88f363e6b"
+dependencies = [
+ "async-recursion",
+ "cfg-if",
+ "drain_filter_polyfill",
+ "futures",
+ "getrandom",
+ "html-escape",
+ "indexmap",
+ "itertools",
+ "js-sys",
+ "leptos_reactive",
+ "once_cell",
+ "pad-adapter",
+ "paste",
+ "rustc-hash",
+ "serde",
+ "serde_json",
+ "server_fn",
+ "smallvec",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_hot_reload"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec5ce56051f2eff2c4736b7a2056177e67be19597b767ff72fbab20917a7422d"
+dependencies = [
+ "anyhow",
+ "camino",
+ "indexmap",
+ "parking_lot 0.12.3",
+ "proc-macro2",
+ "quote",
+ "rstml",
+ "serde",
+ "syn 2.0.61",
+ "walkdir",
+]
+
+[[package]]
+name = "leptos_macro"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "019016cc0193831660a7794aa046e4c01617d97ccb2f403a5c10b21744391a71"
+dependencies = [
+ "attribute-derive",
+ "cfg-if",
+ "convert_case",
+ "html-escape",
+ "itertools",
+ "leptos_hot_reload",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "rstml",
+ "server_fn_macro",
+ "syn 2.0.61",
+ "tracing",
+ "uuid",
+]
+
+[[package]]
+name = "leptos_reactive"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "076064e3c84e3aa12d4bad283e82ba5968675f5f9714d04a5c38f169cd4f26b5"
+dependencies = [
+ "base64 0.22.1",
+ "cfg-if",
+ "futures",
+ "indexmap",
+ "js-sys",
+ "oco_ref",
+ "paste",
+ "pin-project",
+ "rustc-hash",
+ "self_cell",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "slotmap",
+ "thiserror",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "leptos_server"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "603064a2d7ac46dba4b3ed5397a475076f9738918dd605670869dfe877d5966c"
+dependencies = [
+ "inventory",
+ "lazy_static",
+ "leptos_macro",
+ "leptos_reactive",
+ "serde",
+ "server_fn",
+ "thiserror",
+ "tracing",
+]
+
[[package]]
name = "libc"
version = "0.2.155"
@@ -1711,6 +2101,29 @@ dependencies = [
"libc",
]
+[[package]]
+name = "manyhow"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91ea592d76c0b6471965708ccff7e6a5d277f676b90ab31f4d3f3fc77fade64"
+dependencies = [
+ "manyhow-macros",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
+[[package]]
+name = "manyhow-macros"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64621e2c08f2576e4194ea8be11daf24ac01249a4f53cd8befcbb7077120ead"
+dependencies = [
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "matchers"
version = "0.1.0"
@@ -1772,6 +2185,12 @@ dependencies = [
"unicase",
]
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
[[package]]
name = "miniz_oxide"
version = "0.7.3"
@@ -1807,7 +2226,7 @@ dependencies = [
"bytes",
"encoding_rs",
"futures-util",
- "http",
+ "http 1.1.0",
"httparse",
"memchr",
"mime",
@@ -1821,6 +2240,16 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -1891,6 +2320,16 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "oco_ref"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c51ebcefb2f0b9a5e0bea115532c8ae4215d1b01eff176d0f4ba4192895c2708"
+dependencies = [
+ "serde",
+ "thiserror",
+]
+
[[package]]
name = "once_cell"
version = "1.19.0"
@@ -1909,6 +2348,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+[[package]]
+name = "pad-adapter"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56d80efc4b6721e8be2a10a5df21a30fa0b470f1539e53d8b4e6e75faf938b63"
+
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -2082,6 +2527,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+[[package]]
+name = "prettyplease"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.61",
+]
+
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -2106,6 +2561,17 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "proc-macro-utils"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "smallvec",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.82"
@@ -2115,6 +2581,19 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+ "version_check",
+ "yansi",
+]
+
[[package]]
name = "psm"
version = "0.1.21"
@@ -2133,6 +2612,29 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "quote-use"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48e96ac59974192a2fa6ee55a41211cf1385c5b2a8636a4c3068b3b3dd599ece"
+dependencies = [
+ "quote",
+ "quote-use-macros",
+]
+
+[[package]]
+name = "quote-use-macros"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c57308e9dde4d7be9af804f6deeaa9951e1de1d5ffce6142eb964750109f7e"
+dependencies = [
+ "derive-where",
+ "proc-macro-utils",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
[[package]]
name = "radium"
version = "0.7.0"
@@ -2309,7 +2811,7 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
- "http",
+ "http 1.1.0",
"http-body",
"http-body-util",
"hyper",
@@ -2356,6 +2858,20 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rstml"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe542870b8f59dd45ad11d382e5339c9a1047cde059be136a7016095bbdefa77"
+dependencies = [
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn 2.0.61",
+ "syn_derive",
+ "thiserror",
+]
+
[[package]]
name = "rust-embed"
version = "8.4.0"
@@ -2506,6 +3022,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "self_cell"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
+
[[package]]
name = "semver"
version = "0.9.0"
@@ -2527,6 +3049,15 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+[[package]]
+name = "send_wrapper"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
+dependencies = [
+ "futures-core",
+]
+
[[package]]
name = "serde"
version = "1.0.201"
@@ -2536,6 +3067,17 @@ dependencies = [
"serde_derive",
]
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
[[package]]
name = "serde_derive"
version = "1.0.201"
@@ -2569,6 +3111,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_qs"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
+dependencies = [
+ "percent-encoding",
+ "serde",
+ "thiserror",
+]
+
[[package]]
name = "serde_spanned"
version = "0.6.6"
@@ -2590,6 +3143,59 @@ dependencies = [
"serde",
]
+[[package]]
+name = "server_fn"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06e6e5467a2cd93ce1accfdfd8b859404f0b3b2041131ffd774fabf666b8219"
+dependencies = [
+ "bytes",
+ "ciborium",
+ "const_format",
+ "dashmap",
+ "futures",
+ "gloo-net",
+ "http 1.1.0",
+ "js-sys",
+ "once_cell",
+ "send_wrapper",
+ "serde",
+ "serde_json",
+ "serde_qs",
+ "server_fn_macro_default",
+ "thiserror",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "server_fn_macro"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09c216bb1c1ac890151397643c663c875a1836adf0b269be4e389cb1b48c173c"
+dependencies = [
+ "const_format",
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "server_fn_macro_default"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00783df297ec85ea605779f2fef9cbec98981dffe2e01e1a9845c102ee1f1ae6"
+dependencies = [
+ "server_fn_macro",
+ "syn 2.0.61",
+]
+
[[package]]
name = "sha-1"
version = "0.10.0"
@@ -2669,6 +3275,16 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "serde",
+ "version_check",
+]
+
[[package]]
name = "smallvec"
version = "1.13.2"
@@ -3238,6 +3854,18 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "syn_derive"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
[[package]]
name = "sync_wrapper"
version = "0.1.2"
@@ -3485,7 +4113,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
dependencies = [
"bitflags 2.5.0",
"bytes",
- "http",
+ "http 1.1.0",
"http-body",
"http-body-util",
"pin-project-lite",
@@ -3614,6 +4242,26 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
+[[package]]
+name = "typed-builder"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add"
+dependencies = [
+ "typed-builder-macro",
+]
+
+[[package]]
+name = "typed-builder-macro"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.61",
+]
+
[[package]]
name = "typenum"
version = "1.17.0"
@@ -3662,6 +4310,12 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
[[package]]
name = "unicode-width"
version = "0.1.12"
@@ -3746,6 +4400,12 @@ dependencies = [
"wit-bindgen",
]
+[[package]]
+name = "utf8-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
+
[[package]]
name = "utf8parse"
version = "0.2.2"
@@ -3799,6 +4459,9 @@ name = "uuid"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
+dependencies = [
+ "getrandom",
+]
[[package]]
name = "valuable"
@@ -3982,6 +4645,19 @@ dependencies = [
"wasmparser 0.208.1",
]
+[[package]]
+name = "wasm-streams"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "wasmparser"
version = "0.202.0"
@@ -4865,6 +5541,12 @@ dependencies = [
"tap",
]
+[[package]]
+name = "xxhash-rust"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb"
+
[[package]]
name = "yaml-rust2"
version = "0.8.1"
@@ -4876,6 +5558,12 @@ dependencies = [
"hashlink",
]
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
[[package]]
name = "zerocopy"
version = "0.7.34"
diff --git a/Cargo.toml b/Cargo.toml
index f7a0160b6..abbfe5318 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ members = [
"rust/usuba-compat",
"rust/usuba-bundle",
"rust/verification-svc"
-]
+, "rust/gemcraft-leptos"]
# See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352
resolver = "2"
diff --git a/rust/gemcraft-leptos/Cargo.toml b/rust/gemcraft-leptos/Cargo.toml
new file mode 100644
index 000000000..6b596d4c1
--- /dev/null
+++ b/rust/gemcraft-leptos/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "gemcraft-leptos"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+console_error_panic_hook = "0.1.7"
+leptos = { version = "0.6.12", features = ["csr", "nightly"] }
+wasm-bindgen-futures = "0.4.42"
diff --git a/rust/gemcraft-leptos/Trunk.toml b/rust/gemcraft-leptos/Trunk.toml
new file mode 100644
index 000000000..65bbd0e2f
--- /dev/null
+++ b/rust/gemcraft-leptos/Trunk.toml
@@ -0,0 +1,5 @@
+[serve]
+# The address to serve on
+address = "127.0.0.1"
+# The port to serve on
+port = 8080
diff --git a/rust/gemcraft-leptos/index.html b/rust/gemcraft-leptos/index.html
new file mode 100644
index 000000000..a4acdc47a
--- /dev/null
+++ b/rust/gemcraft-leptos/index.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/rust/gemcraft-leptos/src/main.rs b/rust/gemcraft-leptos/src/main.rs
new file mode 100644
index 000000000..72ebc5adc
--- /dev/null
+++ b/rust/gemcraft-leptos/src/main.rs
@@ -0,0 +1,131 @@
+use leptos::*;
+use logging::log;
+use wasm_bindgen::{JsCast, JsValue};
+use wasm_bindgen_futures::JsFuture;
+use web_sys::{Request, RequestInit, Response};
+
+fn main() {
+ console_error_panic_hook::set_once();
+
+ mount_to_body(|| view! { })
+}
+
+#[component]
+fn App() -> impl IntoView {
+ let (count, set_count) = create_signal(0);
+
+ view! {
+
+ "Click me: "
+ // on stable, this is move || count.get();
+ {move || count()}
+
+
+ }
+}
+
+#[component]
+pub fn FormWithPreview() -> impl IntoView {
+ let (image_url, set_image_url) = create_signal(String::new());
+ let (description, set_description) = create_signal(String::new());
+ let (json_data, set_json_data) = create_signal(String::from("{}"));
+
+ let generate_preview = create_action(move |_| {
+ async move {
+ // LLM URL:
+ // window.location.protocol + "//" + window.location.host + "/api/v0/llm";
+ let win = window();
+
+ let mut opts = RequestInit::new();
+ opts.method("POST");
+ // set body as JSON string
+ opts.body(Some(&JsValue::from_str(
+ r#"{"action": "create", "message": "hello"}"#,
+ )));
+ opts.co
+
+ let request = Request::new_with_str_and_init("http://localhost:8000", &opts).unwrap();
+
+ let resp_value = JsFuture::from(win.fetch_with_request(&request))
+ .await
+ .expect("failed to fetch");
+ let resp: Response = resp_value.dyn_into().unwrap();
+
+ let json = JsFuture::from(resp.json().unwrap())
+ .await
+ .expect("failed to get response text");
+ }
+ });
+
+ let on_submit = move |ev: web_sys::SubmitEvent| {
+ ev.prevent_default();
+ // Handle form submission here
+ log!("Form submitted");
+ log!("Description: {}", description.get());
+ log!("JSON Data: {}", json_data.get());
+ };
+
+ view! {
+
+ }
+}
+
+fn event_target_value(ev: &web_sys::Event) -> String {
+ let target: web_sys::EventTarget = ev.target().unwrap();
+ let target: web_sys::HtmlTextAreaElement = target.dyn_into().unwrap();
+ target.value()
+}
From 77c8468a7bc53520be444d140886992f8f94496d Mon Sep 17 00:00:00 2001
From: Ben Follington <5009316+bfollington@users.noreply.github.com>
Date: Wed, 24 Jul 2024 13:10:02 +1000
Subject: [PATCH 03/43] Deserialize response to struct
---
Cargo.lock | 4 ++++
rust/gemcraft-leptos/Cargo.toml | 4 ++++
rust/gemcraft-leptos/src/main.rs | 19 ++++++++++++++++++-
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/Cargo.lock b/Cargo.lock
index fdb51bfee..768c0b293 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1404,6 +1404,10 @@ version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"leptos",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "wasm-bindgen",
"wasm-bindgen-futures",
]
diff --git a/rust/gemcraft-leptos/Cargo.toml b/rust/gemcraft-leptos/Cargo.toml
index 6b596d4c1..02ff86fb9 100644
--- a/rust/gemcraft-leptos/Cargo.toml
+++ b/rust/gemcraft-leptos/Cargo.toml
@@ -8,4 +8,8 @@ edition = "2021"
[dependencies]
console_error_panic_hook = "0.1.7"
leptos = { version = "0.6.12", features = ["csr", "nightly"] }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+serde-wasm-bindgen = "0.6"
+wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4.42"
diff --git a/rust/gemcraft-leptos/src/main.rs b/rust/gemcraft-leptos/src/main.rs
index 72ebc5adc..cb7e2db3d 100644
--- a/rust/gemcraft-leptos/src/main.rs
+++ b/rust/gemcraft-leptos/src/main.rs
@@ -1,5 +1,7 @@
use leptos::*;
use logging::log;
+use serde::{Deserialize, Serialize};
+use serde_wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, Response};
@@ -29,6 +31,15 @@ fn App() -> impl IntoView {
}
}
+#[derive(Deserialize, Serialize, Debug)]
+struct LlmResponse {
+ #[serde(rename = "type")]
+ r#type: String,
+ #[serde(rename = "threadId")]
+ thread_id: String,
+ output: String,
+}
+
#[component]
pub fn FormWithPreview() -> impl IntoView {
let (image_url, set_image_url) = create_signal(String::new());
@@ -47,7 +58,6 @@ pub fn FormWithPreview() -> impl IntoView {
opts.body(Some(&JsValue::from_str(
r#"{"action": "create", "message": "hello"}"#,
)));
- opts.co
let request = Request::new_with_str_and_init("http://localhost:8000", &opts).unwrap();
@@ -59,6 +69,13 @@ pub fn FormWithPreview() -> impl IntoView {
let json = JsFuture::from(resp.json().unwrap())
.await
.expect("failed to get response text");
+
+ let llm_response: LlmResponse =
+ serde_wasm_bindgen::from_value(json).map_err(|_| "Failed to deserialize JSON")?;
+
+ log!("Response: {:?}", llm_response);
+
+ Ok::(llm_response)
}
});
From 1ef91642ccf4cbda4033be7a692466df6ba4f2ab Mon Sep 17 00:00:00 2001
From: Ben Follington <5009316+bfollington@users.noreply.github.com>
Date: Thu, 25 Jul 2024 11:37:36 +1000
Subject: [PATCH 04/43] Checkpoint: CRUD for the gem data + classification with
central state
---
Cargo.lock | 2 +
rust/gemcraft-leptos/Cargo.toml | 2 +
rust/gemcraft-leptos/index.html | 54 ++++-
rust/gemcraft-leptos/src/extract.rs | 9 +
rust/gemcraft-leptos/src/main.rs | 304 ++++++++++++++++++++--------
5 files changed, 291 insertions(+), 80 deletions(-)
create mode 100644 rust/gemcraft-leptos/src/extract.rs
diff --git a/Cargo.lock b/Cargo.lock
index 768c0b293..e3274e3c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1404,9 +1404,11 @@ version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"leptos",
+ "regex",
"serde",
"serde-wasm-bindgen",
"serde_json",
+ "uuid",
"wasm-bindgen",
"wasm-bindgen-futures",
]
diff --git a/rust/gemcraft-leptos/Cargo.toml b/rust/gemcraft-leptos/Cargo.toml
index 02ff86fb9..5f18b1636 100644
--- a/rust/gemcraft-leptos/Cargo.toml
+++ b/rust/gemcraft-leptos/Cargo.toml
@@ -13,3 +13,5 @@ serde_json = "1.0"
serde-wasm-bindgen = "0.6"
wasm-bindgen = "0.2.92"
wasm-bindgen-futures = "0.4.42"
+regex = "1.5.4"
+uuid = { version = "1.3", features = ["v4"] }
diff --git a/rust/gemcraft-leptos/index.html b/rust/gemcraft-leptos/index.html
index a4acdc47a..98de606d2 100644
--- a/rust/gemcraft-leptos/index.html
+++ b/rust/gemcraft-leptos/index.html
@@ -1,5 +1,57 @@
-
+
+
+
diff --git a/rust/gemcraft-leptos/src/extract.rs b/rust/gemcraft-leptos/src/extract.rs
new file mode 100644
index 000000000..6e7498468
--- /dev/null
+++ b/rust/gemcraft-leptos/src/extract.rs
@@ -0,0 +1,9 @@
+use regex::Regex;
+
+pub fn extract_code_blocks_from_markdown(markdown: &str, block_type: &str) -> Vec {
+ let pattern = format!(r"```{}\s*([\s\S]*?)\s*```", regex::escape(block_type));
+ let re = Regex::new(&pattern).unwrap();
+ re.captures_iter(markdown)
+ .map(|cap| cap[1].to_string())
+ .collect()
+}
\ No newline at end of file
diff --git a/rust/gemcraft-leptos/src/main.rs b/rust/gemcraft-leptos/src/main.rs
index cb7e2db3d..946eaaa4d 100644
--- a/rust/gemcraft-leptos/src/main.rs
+++ b/rust/gemcraft-leptos/src/main.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+use uuid::Uuid;
use leptos::*;
use logging::log;
use serde::{Deserialize, Serialize};
@@ -6,31 +8,71 @@ use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, Response};
+mod extract;
+use extract::extract_code_blocks_from_markdown;
+
+const LLM_URL: &str = "http://localhost:8000";
+
fn main() {
console_error_panic_hook::set_once();
- mount_to_body(|| view! { })
+ mount_to_body(|| view! { })
}
+// create signal to store hashmap of data gems
+
#[component]
fn App() -> impl IntoView {
- let (count, set_count) = create_signal(0);
+ let gems = create_rw_signal(HashMap::::new());
+ let (selection, set_selection) = create_signal(Vec::::new());
+
+ let on_toggle_selection = move |id| {
+ set_selection.update(|current|
+ if current.contains(&id) {
+ current.retain(|x| x != &id);
+ } else {
+ current.push(id.clone());
+ })
+ };
+
+ let on_classify = move |(id, classification, description, json_data)| {
+ gems.update(|gems| {
+ gems.insert(id, DataGem {
+ classification: Some(classification),
+ description: description,
+ json_data: json_data,
+ });
+ });
+ };
view! {
-
- "Click me: "
- // on stable, this is move || count.get();
- {move || count()}
-
-
+
+ {move || selection.get().join(", ")}
+ {move || gems()
+ .into_iter()
+ .map(|(id, gem)| view! { })
+ .collect_view()}
+
+
+
}
}
-
#[derive(Deserialize, Serialize, Debug)]
struct LlmResponse {
#[serde(rename = "type")]
@@ -40,73 +82,172 @@ struct LlmResponse {
output: String,
}
+#[derive(Deserialize, Serialize, Debug)]
+struct CreateThreadRequest {
+ action: String,
+ system: String,
+ message: String,
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+pub struct ClassificationData {
+ title: String,
+ #[serde(rename = "contentType")]
+ content_type: String,
+ emoji: String,
+ sensitivity: String,
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+pub struct DataGem {
+ classification: Option,
+ description: String,
+ json_data: String,
+}
+
#[component]
-pub fn FormWithPreview() -> impl IntoView {
- let (image_url, set_image_url) = create_signal(String::new());
- let (description, set_description) = create_signal(String::new());
- let (json_data, set_json_data) = create_signal(String::from("{}"));
+pub fn DataGemPreview(classification: ClassificationData) -> impl IntoView {
+ view! {
+
+
{classification.emoji.clone()}
+
+
{classification.title.clone()}
+ {classification.content_type.clone()} "("{classification.sensitivity.clone()}")"
+
+
+ }
+}
- let generate_preview = create_action(move |_| {
- async move {
- // LLM URL:
- // window.location.protocol + "//" + window.location.host + "/api/v0/llm";
- let win = window();
+pub async fn classify_data(json: String, description: String) -> Result {
+ let win = window();
+ let mut opts = RequestInit::new();
+ opts.method("POST");
+
+ let msg = format!(
+ "Classify the following data:\n\n{}\n\nContext:\n\n{}",
+ json,
+ description
+ );
+
+ // set body as JSON string
+ let body = CreateThreadRequest {
+ action: String::from("create"),
+ system: String::from("Examine the provided raw data and context and return a brief title, it's confidentiality/sensitivity (public, shared, personal, secret), the data type and emoji to describe the data. Respond with a JSON object containing the title and emoji, e.g. {\"title\": \"Personal Budget\", \"contentType\": \"Spreadsheet\", \"emoji\": \"💸\", \"sensitivity\": \"personal/financial\"} wrapped in a code block."),
+ message: msg,
+ };
+ let body = serde_json::to_string(&body).unwrap();
+ opts.body(Some(&JsValue::from_str(&body)));
+
+ let request = Request::new_with_str_and_init(LLM_URL, &opts).unwrap();
+
+ let resp_value = JsFuture::from(win.fetch_with_request(&request))
+ .await
+ .expect("failed to fetch");
+ let resp: Response = resp_value.dyn_into().unwrap();
- let mut opts = RequestInit::new();
+ let json = JsFuture::from(resp.json().unwrap())
+ .await
+ .expect("failed to get response text");
+
+ let llm_response: LlmResponse =
+ serde_wasm_bindgen::from_value(json).map_err(|_| "Failed to deserialize JSON")?;
+
+ log!("Response: {:?}", llm_response);
+
+ let classification_data =
+ extract_code_blocks_from_markdown(&llm_response.output, "json");
+ let classification_data: ClassificationData =
+ serde_json::from_str(&classification_data[0]).unwrap();
+
+ Ok(classification_data)
+}
+
+pub async fn combine_data(json: String, description: String) -> Result {
+ let win = window();
+ let mut opts = RequestInit::new();
opts.method("POST");
- // set body as JSON string
- opts.body(Some(&JsValue::from_str(
- r#"{"action": "create", "message": "hello"}"#,
- )));
+
+ let msg = format!(
+ "Classify the following data:\n\n{}\n\nContext:\n\n{}",
+ json,
+ description
+ );
- let request = Request::new_with_str_and_init("http://localhost:8000", &opts).unwrap();
+ // set body as JSON string
+ let body = CreateThreadRequest {
+ action: String::from("create"),
+ system: String::from("Examine the provided raw data and context and return a brief title, it's confidentiality/sensitivity (public, shared, personal, secret), the data type and emoji to describe the data. Respond with a JSON object containing the title and emoji, e.g. {\"title\": \"Personal Budget\", \"contentType\": \"Spreadsheet\", \"emoji\": \"💸\", \"sensitivity\": \"personal/financial\"} wrapped in a code block."),
+ message: msg,
+ };
+ let body = serde_json::to_string(&body).unwrap();
+ opts.body(Some(&JsValue::from_str(&body)));
- let resp_value = JsFuture::from(win.fetch_with_request(&request))
- .await
- .expect("failed to fetch");
- let resp: Response = resp_value.dyn_into().unwrap();
+ let request = Request::new_with_str_and_init(LLM_URL, &opts).unwrap();
- let json = JsFuture::from(resp.json().unwrap())
- .await
- .expect("failed to get response text");
+ let resp_value = JsFuture::from(win.fetch_with_request(&request))
+ .await
+ .expect("failed to fetch");
+ let resp: Response = resp_value.dyn_into().unwrap();
+
+ let json = JsFuture::from(resp.json().unwrap())
+ .await
+ .expect("failed to get response text");
+
+ let llm_response: LlmResponse =
+ serde_wasm_bindgen::from_value(json).map_err(|_| "Failed to deserialize JSON")?;
+
+ log!("Response: {:?}", llm_response);
+
+ let classification_data =
+ extract_code_blocks_from_markdown(&llm_response.output, "json");
+ let classification_data: ClassificationData =
+ serde_json::from_str(&classification_data[0]).unwrap();
+
+ Ok(classification_data)
+}
- let llm_response: LlmResponse =
- serde_wasm_bindgen::from_value(json).map_err(|_| "Failed to deserialize JSON")?;
+#[component]
+pub fn DataGemEditor(
+ id: String,
+ gem: DataGem,
+ selected: bool,
+ #[prop(into)] on_toggle: Callback,
+ #[prop(into)] on_classify: Callback<(String, ClassificationData, String, String)>,
+) -> impl IntoView {
+ let id = store_value(id);
+ let (description, set_description) = create_signal(gem.description.clone());
+ let (json_data, set_json_data) = create_signal(gem.json_data.clone());
- log!("Response: {:?}", llm_response);
+ let classify_data = create_action(move |_| {
+ async move {
+ let json = move || json_data.get();
+ let description = move || description.get();
- Ok::(llm_response)
+ let data = classify_data(json(), description()).await;
+ match data {
+ Ok(data) => {
+ on_classify((id.get_value().clone(), data.clone(), description(), json_data()));
+ },
+ Err(e) => {
+ log!("Error: {:?}", e);
+ }
+ }
}
});
- let on_submit = move |ev: web_sys::SubmitEvent| {
- ev.prevent_default();
- // Handle form submission here
- log!("Form submitted");
- log!("Description: {}", description.get());
- log!("JSON Data: {}", json_data.get());
- };
-
view! {
-