From d2a2a3a588ef56ef45ed46a22b9e60437d2b1352 Mon Sep 17 00:00:00 2001 From: Ariel Weinberger Date: Wed, 16 Jul 2025 14:56:32 -0500 Subject: [PATCH] progress --- .env.example | 3 +- ai-service/src/handlers/chat/route.ts | 99 +++--- ai-service/src/handlers/chat/types.ts | 4 +- ai-service/src/handlers/conversation.ts | 18 +- .../src/lib/ai/custom-parts/checkpoint.ts | 6 +- .../src/lib/ai/custom-parts/thinking.ts | 20 ++ .../lib/ai/mastra/utils/runtime-context.ts | 14 +- .../lib/ai/mastra/workflows/code-workflow.ts | 75 +++-- ai-service/src/lib/ai/tools/file-tools.ts | 175 ---------- .../src/lib/ai/tools/reasoning-tools.ts | 31 -- ai-service/src/lib/constants.ts | 2 - ai-service/src/lib/git-utils.ts | 3 - .../src/lib/imagine/imagine-api-client.ts | 8 +- ai-service/src/server.ts | 8 + ai-service/src/shared-types/index.ts | 21 ++ ai-service/tsconfig.json | 4 +- package.json | 4 +- pnpm-lock.yaml | 170 +++++----- src/lib/ai-service/ai-service-api-client.ts | 7 + src/lib/components/studio/chat/chat.svelte | 154 ++------- .../studio/chat/conversation.svelte | 51 +-- .../studio/chat/message copy.svelte | 301 ++++++++++++++++++ src/lib/components/studio/chat/message.svelte | 238 +++----------- .../components/studio/chat/thinking.svelte | 89 ++++++ .../components/studio/chat/toolCalls.svelte | 196 ++++++++++++ src/lib/sdk/imagine.ts | 5 + src/lib/system.ts | 3 +- tsconfig.json | 12 +- vite.config.ts | 5 +- 29 files changed, 957 insertions(+), 769 deletions(-) create mode 100644 ai-service/src/lib/ai/custom-parts/thinking.ts delete mode 100644 ai-service/src/lib/ai/tools/file-tools.ts delete mode 100644 ai-service/src/lib/ai/tools/reasoning-tools.ts create mode 100644 ai-service/src/shared-types/index.ts create mode 100644 src/lib/ai-service/ai-service-api-client.ts create mode 100644 src/lib/components/studio/chat/message copy.svelte create mode 100644 src/lib/components/studio/chat/thinking.svelte create mode 100644 src/lib/components/studio/chat/toolCalls.svelte diff --git a/.env.example b/.env.example index 9ba4011c0..8c4ee6129 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,5 @@ PUBLIC_CONSOLE_FEATURE_FLAGS= PUBLIC_APPWRITE_MULTI_REGION=false PUBLIC_APPWRITE_ENDPOINT=http://localhost/v1 PUBLIC_STRIPE_KEY= -PUBLIC_GROWTH_ENDPOINT= \ No newline at end of file +PUBLIC_GROWTH_ENDPOINT= +PUBLIC_AI_SERVICE_BASE_URL=http://localhost:8889 \ No newline at end of file diff --git a/ai-service/src/handlers/chat/route.ts b/ai-service/src/handlers/chat/route.ts index 8a56f0b35..b81a75b70 100644 --- a/ai-service/src/handlers/chat/route.ts +++ b/ai-service/src/handlers/chat/route.ts @@ -15,21 +15,23 @@ const exec = promisify(_exec); import fs from 'fs'; import path from 'path'; import { createImagineClient } from '@/lib/imagine/create-artifact-client'; -import { IMAGINE_JWT } from '@/lib/constants'; export const handleChatRequest = async (c: Context) => { - console.log("incoming request"); const signal = c.req.raw.signal; + + const token = c.req.header('X-Imagine-Token'); + if (!token) { + return c.json({ error: 'Unauthorized' }, 401); + } + let body: ChatRequestBodyType; // Parse request body try { const json = await c.req.json(); - // console.log("json", json); body = chatRequestBodySchema.parse(json); } catch (error) { if (error instanceof z.ZodError) { - console.log("zod error", error); return c.json({ errors: error.issues }, 400); } @@ -37,51 +39,47 @@ export const handleChatRequest = async (c: Context) => { return c.json('An unknown error occurred', 500); } - const { id: conversationId, messages, trigger } = body; + const { id: conversationId, messages, trigger, artifactId, projectId } = body; - if (trigger === "submit-tool-result") { - // Skip - return c.body(null, 200); + if (trigger === 'submit-tool-result') { + // Skip + return c.body(null, 200); } - console.log("body", body); - - const artifactId = process.env.IMAGINE_ARTIFACT_ID!; // TODO: dynamically from body - const projectId = process.env.IMAGINE_PROJECT_ID!; // TODO: dynamically from body - const imagineClient = await createImagineClient({ - projectId, - token: IMAGINE_JWT, // TODO: use the token from the request + projectId, + token // TODO: use the token from the request }); const convos = await imagineClient.listConversations(artifactId); const imagineConvo = convos.conversations[0]; - const uiMessages = imagineConvo.messages.messages; - const isNewConversation = uiMessages.length === 0; const convertedMessages = convertToModelMessages(messages); const latestMessage = convertedMessages[convertedMessages.length - 1]; - + const latestMessageTextPart = latestMessage.content[0] as TextPart; const restMessages = convertedMessages.slice(0, -1); - + // If it's a new conversation, we need to clone the workspace // This is temporary and will be handled by Synapse shortly! + const isNewConversation = restMessages.length === 0; if (isNewConversation) { - const workspaceDir = path.resolve(process.cwd(), `./tmp/workspace/artifact/${artifactId}`) - console.log("workspaceDir", workspaceDir); - const exists = fs.existsSync(workspaceDir); + const workspaceDir = path.resolve(process.cwd(), `./tmp/workspace/artifact/${artifactId}`); + console.log('workspaceDir', workspaceDir); + const exists = fs.existsSync(workspaceDir); - if (exists) { - console.log("Workspace already exists, skipping clone"); - } else { - console.log("Workspace does not exist, creating directory..."); - await exec(`mkdir -p ${workspaceDir}`); - console.log("Cloning template 'base-vite-template'..."); - await exec(`pnpx degit appwrite/templates-for-frameworks/base-vite-template .`, { cwd: workspaceDir }); - console.log("Installing dependencies..."); - await exec(`bun install`, { cwd: workspaceDir }); - } + if (exists) { + console.log('Workspace already exists, skipping clone'); + } else { + console.log('Workspace does not exist, creating directory...'); + await exec(`mkdir -p ${workspaceDir}`); + console.log("Cloning template 'base-vite-template'..."); + await exec(`pnpx degit appwrite/templates-for-frameworks/base-vite-template .`, { + cwd: workspaceDir + }); + console.log('Installing dependencies...'); + await exec(`bun install`, { cwd: workspaceDir }); + } } else { - console.log("Not a new conversation, skipping workspace clone"); + console.log('Not a new conversation, skipping workspace clone'); } let didError = false; @@ -90,22 +88,28 @@ export const handleChatRequest = async (c: Context) => { originalMessages: messages, execute: async (params) => { const writer = params.writer as WriterType; - const runtimeContext = createRuntimeContext({ writer, artifactId, restMessages, isFirstMessage: isNewConversation, signal }); + const runtimeContext = createRuntimeContext({ + writer, + artifactId, + restMessages, + isFirstMessage: isNewConversation, + signal + }); const run = await mastra.getWorkflow('codeWorkflow').createRunAsync(); const result = run.stream({ inputData: { - userPrompt: latestMessageTextPart.text + userPrompt: latestMessageTextPart.text }, runtimeContext }); writer.write({ - type: 'start', + type: 'start' }); writer.write({ - type: 'start-step' + type: 'start-step' }); for await (const chunk of result.stream) { @@ -117,7 +121,7 @@ export const handleChatRequest = async (c: Context) => { }); writer.write({ - type: 'finish', + type: 'finish' }); }, onFinish: async (event) => { @@ -128,17 +132,14 @@ export const handleChatRequest = async (c: Context) => { const { messages } = event; - console.log("Saving messages to imagine"); - await imagineClient.updateConversation(artifactId, imagineConvo.$id, "Test Conversation", { messages } as any); - console.log("Messages saved to imagine"); - // await updateConversation({ - // conversation: { - // ...conversation, - // uiMessages: messages, - // }, - // artifactId, - // projectId, - // }); + console.log('Saving messages to imagine'); + await imagineClient.updateConversation( + artifactId, + imagineConvo.$id, + 'Test Conversation', + messages + ); + console.log('Messages saved to imagine'); }, onError: (error) => { didError = true; diff --git a/ai-service/src/handlers/chat/types.ts b/ai-service/src/handlers/chat/types.ts index fe2b5f099..2e3e5db3b 100644 --- a/ai-service/src/handlers/chat/types.ts +++ b/ai-service/src/handlers/chat/types.ts @@ -1,8 +1,8 @@ -import { UIMessage } from "ai"; import { z } from "zod"; +import { ImagineUIMessage } from "@/shared-types"; export const chatRequestBodySchema = z.object({ - messages: z.array(z.custom()).min(1), + messages: z.array(z.custom()).min(1), trigger: z.string(), id: z.string(), artifactId: z.string(), diff --git a/ai-service/src/handlers/conversation.ts b/ai-service/src/handlers/conversation.ts index 4819e381e..098132fd9 100644 --- a/ai-service/src/handlers/conversation.ts +++ b/ai-service/src/handlers/conversation.ts @@ -1,4 +1,3 @@ -import { IMAGINE_JWT } from "@/lib/constants"; import { createImagineClient } from "@/lib/imagine/create-artifact-client"; import { Conversation } from "@/lib/imagine/imagine-api-client"; import { Context } from "hono"; @@ -7,20 +6,24 @@ const mapConversation = (conversation: Conversation) => { return { id: conversation.$id, name: conversation.name, - messages: conversation.messages.messages, + messages: conversation.messages, }; }; export const getConversation = async (c: Context) => { - console.log("getConversation"); const { conversationId } = c.req.param(); + const token = c.req.header("X-Imagine-Token"); + + if (!token) { + return c.json({ error: "Unauthorized" }, 401); + } const projectId = process.env.IMAGINE_PROJECT_ID!; const artifactId = process.env.IMAGINE_ARTIFACT_ID!; const imagineClient = await createImagineClient({ projectId, - token: IMAGINE_JWT, // TODO: use the token from the request + token }); const conversation = await imagineClient.getConversation(artifactId, conversationId); @@ -32,10 +35,15 @@ export const getConversations = async (c: Context) => { console.log("getConversations"); const projectId = process.env.IMAGINE_PROJECT_ID!; const artifactId = process.env.IMAGINE_ARTIFACT_ID!; + const token = c.req.header("X-Imagine-Token"); + + if (!token) { + return c.json({ error: "Unauthorized" }, 401); + } const imagineClient = await createImagineClient({ projectId, - token: IMAGINE_JWT, // TODO: use the token from the request + token }); const conversations = await imagineClient.listConversations(artifactId); diff --git a/ai-service/src/lib/ai/custom-parts/checkpoint.ts b/ai-service/src/lib/ai/custom-parts/checkpoint.ts index 883e874ae..9fb9892db 100644 --- a/ai-service/src/lib/ai/custom-parts/checkpoint.ts +++ b/ai-service/src/lib/ai/custom-parts/checkpoint.ts @@ -1,13 +1,13 @@ import { z } from "zod"; -export const checkpointDataUIPartSchema = z.object({ +export const checkpointUIDataPartSchema = z.object({ commitSha: z.string(), commitMessage: z.string(), timestamp: z.string(), }); -export interface CheckpointDataUIPart { +export interface CheckpointUIDataPart { type: "checkpoint"; id: string; - data: z.infer; + data: z.infer; } diff --git a/ai-service/src/lib/ai/custom-parts/thinking.ts b/ai-service/src/lib/ai/custom-parts/thinking.ts new file mode 100644 index 000000000..4e6479e1f --- /dev/null +++ b/ai-service/src/lib/ai/custom-parts/thinking.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; + +export const thinkingUIDataPartSchema = z.union([ + z.object({ + text: z.string(), + durationMs: z.null(), + state: z.enum(["streaming"]), + }), + z.object({ + text: z.string(), + durationMs: z.number(), + state: z.enum(["done"]), + }) +]); + +export interface ThinkingUIDataPart { + type: "thinking"; + id: string; + data: z.infer; +} diff --git a/ai-service/src/lib/ai/mastra/utils/runtime-context.ts b/ai-service/src/lib/ai/mastra/utils/runtime-context.ts index 8fcc1b00c..705eb1351 100644 --- a/ai-service/src/lib/ai/mastra/utils/runtime-context.ts +++ b/ai-service/src/lib/ai/mastra/utils/runtime-context.ts @@ -1,17 +1,9 @@ import { RuntimeContext } from "@mastra/core/runtime-context"; -import { InferUITool } from "ai"; -import { fileTools } from "../tools/file-tools"; -import { UIMessage, UIMessageStreamWriter } from "ai"; -import { z } from "zod"; -import { checkpointDataUIPartSchema } from "../../custom-parts/checkpoint"; +import { UIMessageStreamWriter } from "ai"; import { createSynapseClient, SynapseHTTPClient } from "@/lib/synapse-http-client"; +import { ImagineUIMessage } from "@/shared-types"; -export type WriterType = UIMessageStreamWriter; -}, { - readFile: InferUITool; - writeFile: InferUITool; -}>>; +export type WriterType = UIMessageStreamWriter; export type RuntimeContextPayload = { writer: WriterType; skipWritingToolCalls?: boolean; diff --git a/ai-service/src/lib/ai/mastra/workflows/code-workflow.ts b/ai-service/src/lib/ai/mastra/workflows/code-workflow.ts index ecc0de4b4..767a5590e 100644 --- a/ai-service/src/lib/ai/mastra/workflows/code-workflow.ts +++ b/ai-service/src/lib/ai/mastra/workflows/code-workflow.ts @@ -81,10 +81,22 @@ const planStep = createStep({ const id = createIdGenerator({ size: 10 })(); + // writer.write({ + // type: "reasoning-start", + // id, + // }); + + const thinkingId = createIdGenerator({ size: 10 })(); + writer.write({ - type: "reasoning-start", - id, - }); + id: thinkingId, + type: "data-thinking", + data: { + text: "", + durationMs: null, + state: "streaming", + } + }) const startTime = Date.now(); @@ -161,20 +173,21 @@ ${userPrompt} const delta = partialObject.plan.slice(previousPlan.length); previousPlan = partialObject.plan; - writer.write({ - type: "reasoning-delta", - id, - delta, - }); - // writer.write({ - // type: "data-thoughts", + // type: "reasoning-delta", // id, - // data: { - // status: "streaming", - // text: partialObject.plan, - // } + // delta, // }); + + writer.write({ + type: "data-thinking", + id: thinkingId, + data: { + text: partialObject.plan ?? "", + durationMs: null, + state: "streaming", + } + }); } console.log("[planStep] postStream", latestPartialObject); @@ -185,26 +198,26 @@ ${userPrompt} const endTime = Date.now(); const timeTaken = endTime - startTime; - // writer.write({ - // type: "data-thoughts", - // id, - // data: { - // status: "done", - // timeTaken, - // text: latestPartialObject.plan, - // } - // }); - writer.write({ - type: "reasoning-end", - id, - providerMetadata: { - imagine: { - durationInMs: timeTaken, - } - } as any, + type: "data-thinking", + id: thinkingId, + data: { + text: latestPartialObject.plan, + durationMs: timeTaken, + state: "done", + } }); + // writer.write({ + // type: "reasoning-end", + // id, + // providerMetadata: { + // imagine: { + // durationInMs: timeTaken, + // } + // } as any, + // }); + const { plan, shouldInvolveUIDeveloper } = latestPartialObject; return { plan, diff --git a/ai-service/src/lib/ai/tools/file-tools.ts b/ai-service/src/lib/ai/tools/file-tools.ts deleted file mode 100644 index c4302bd4f..000000000 --- a/ai-service/src/lib/ai/tools/file-tools.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { tool } from "ai"; -import { z } from "zod"; -import { createSynapseClient } from "../../synapse-http-client"; - -const readFileTool = tool({ - name: "readFile", - description: "Read a file from the repository", - inputSchema: z.object({ - path: z.string().describe("The path to the file to read"), - }), - outputSchema: z.object({ - path: z.string().describe("The path to the file"), - content: z.string().describe("The content of the file"), - }), - execute: async ({ path }) => { - console.log(`[TOOL - readFile] Reading file at path: ${path}`); - const synapse = createSynapseClient(); - const result = await synapse.readFile({ path }); - - return { - path: result.path, - content: result.content, - }; - }, -}); - -const writeFilesTool = tool({ - name: "writeFiles", - description: "Write multiple files to the repository", - inputSchema: z.object({ - files: z.array(z.object({ - path: z.string().describe("The path to the file to write"), - content: z.string().describe("The content of the file to write"), - })), - }), - outputSchema: z.object({ - successFiles: z.array(z.object({ path: z.string().describe("The path to the file") })), - errorFiles: z.array(z.object({ path: z.string().describe("The path to the file"), error: z.string().describe("The error message") })), - }), - execute: async ({ files }) => { - const { successFiles, errorFiles } = await writeFiles(files); - - return { - successFiles, - errorFiles, - }; - }, -}); - -const writeFileTool = tool({ - name: "writeFile", - description: "Write a single file to the repository. If the file does not exist, it will be created. If the file exists, it will be overwritten. You must provide the full file contents.", - inputSchema: z.object({ - path: z.string().describe("The path to the file to write"), - content: z.string().describe("The content of the file to write"), - }), - outputSchema: z.object({ - success: z.boolean().describe("Whether the file was written successfully"), - error: z.string().optional().describe("The error message if the file was not written successfully"), - }), - execute: async ({ path, content }) => { - const { errorFiles } = await writeFiles([{ path, content }]); - - if (errorFiles.length > 0) { - return { - success: false, - error: errorFiles[0].error, - }; - } else { - return { - success: true, - }; - } - }, -}); - -async function writeFiles(files: { path: string, content: string }[]) { - const synapse = createSynapseClient(); - - const successFiles: { path: string }[] = []; - const errorFiles: { path: string, error: string }[] = []; - - for (const file of files) { - try { - await synapse.createOrUpdateFile({ filepath: file.path, content: file.content }); - successFiles.push({ path: file.path }); - } catch (error) { - errorFiles.push({ path: file.path, error: error instanceof Error ? error.message : "Unknown error" }); - } - } - - return { - successFiles, - errorFiles, - }; -} - -const listFilesInDirectoryTool = tool({ - name: "listFilesInDirectory", - description: "List all files in a directory", - inputSchema: z.object({ - path: z.string().default("/").describe("The path to the directory to list files from"), - recursive: z.boolean().default(false).describe("Whether to list files recursively (dig into subdirectories)"), - }), - outputSchema: z.object({ - files: z.array(z.object({ - path: z.string().describe("The path to the file"), - })), - }), - execute: async ({ path, recursive }) => { - console.log(`[TOOL - listFilesInDirectory] Listing files in directory at path: ${path}, recursive: ${recursive}`); - const synapse = createSynapseClient(); - const files = await synapse.listFilesInDir({ - dirPath: path || "/", - recursive: recursive || false, - withContent: false, - additionalIgnorePatterns: [], - }) - - console.log(`[TOOL - listFilesInDirectory] Found ${files.length} files in directory at path: ${path}`); - - return { - files, - }; - }, -}); - -const deleteFileTool = tool({ - name: "deleteFile", - description: "Delete a file from the repository", - inputSchema: z.object({ - path: z.string().describe("The path to the file to delete"), - }), - outputSchema: z.object({ - success: z.boolean().describe("Whether the file was deleted successfully"), - }), - execute: async ({ path }) => { - const synapse = createSynapseClient(); - await synapse.deleteFile({ filepath: path }); - - return { - success: true, - }; - }, -}); - -const moveFileTool = tool({ - name: "moveFile", - description: "Move a file from one path to another. Also useful when renaming. If the directory does not exist, it will be created.", - inputSchema: z.object({ - path: z.string().describe("The path to the file to move"), - newPath: z.string().describe("The new path to the file"), - intent: z.enum(["move", "rename"]).describe("The intent of the move operation. This will help render the appropriate UI to the user."), - }), - outputSchema: z.object({ - success: z.boolean().describe("Whether the file was moved successfully"), - }), - execute: async ({ path, newPath }) => { - const synapse = createSynapseClient(); - await synapse.updateFilePath({ filepath: path, newPath }); - - return { - success: true, - }; - }, -}); - -export const fileTools = { - readFileTool, - // writeFilesTool, - writeFileTool, - listFilesInDirectoryTool, - deleteFileTool, - moveFileTool, -}; diff --git a/ai-service/src/lib/ai/tools/reasoning-tools.ts b/ai-service/src/lib/ai/tools/reasoning-tools.ts deleted file mode 100644 index abce7c59a..000000000 --- a/ai-service/src/lib/ai/tools/reasoning-tools.ts +++ /dev/null @@ -1,31 +0,0 @@ -import z from "zod"; -import { tool } from "ai"; - -export const describeThoughtTool = tool({ - name: "describeThought", - description: "You use this tool to describe your thoughts in between steps, instead of outputting simple text.", - inputSchema: z.object({ - thought: z.string().describe("The thought you want to describe. E.g. 'I will read all files in the repository to better understand the structure of the project.'"), - }), - execute: async ({ thought }) => { - console.log("[thought] Thought recorded", thought); - return "Thought recorded"; - }, -}) - -export const respondToUser = tool({ - name: "summary", - description: "You use this tool to summarize what you did to the user after you are done.", - inputSchema: z.object({ - summary: z.string().describe("The summary of what you did to the user after you are done. This supports markdown format. Keep it concise, 3-4 sentences max. No code blocks in the markdown."), - }), - execute: async ({ summary }) => { - console.log("[summary] Summary recorded", summary); - return "Summary recorded"; - }, -}) - -export const reasoningTools = { - describeThoughtTool, - respondToUser, -} \ No newline at end of file diff --git a/ai-service/src/lib/constants.ts b/ai-service/src/lib/constants.ts index 6e3cdb0a0..a06a7ac62 100644 --- a/ai-service/src/lib/constants.ts +++ b/ai-service/src/lib/constants.ts @@ -1,4 +1,2 @@ export const OVERRIDE_BASE_DIR = `./tmp/workspace/artifact/${process.env.ARTIFACT_ID}`; export const ANTHROPIC_MAX_CACHE_CONTROL_BLOCKS = 4; // Anthropic only allows 4 cache control blocks -export const IMAGINE_JWT - = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI2ODc2ZjJlOTAwMDczNzExZjljMiIsInNlc3Npb25JZCI6IjY4NzZmMmU5NTJlZGZjODkxMWViIiwiZXhwIjoxNzUyNjM5MzAzfQ._bezYFaxf5vj_pkhEqAhR9Dy2Dzmv2iV_kVMWr-UKWE"; \ No newline at end of file diff --git a/ai-service/src/lib/git-utils.ts b/ai-service/src/lib/git-utils.ts index 4dc675094..964e4087c 100644 --- a/ai-service/src/lib/git-utils.ts +++ b/ai-service/src/lib/git-utils.ts @@ -15,9 +15,6 @@ export class GitRepositoryUtils { withContent: true, additionalIgnorePatterns: [], }); - - console.log("files", files); - return files.map((file) => ({ path: file.path, content: file.content!, diff --git a/ai-service/src/lib/imagine/imagine-api-client.ts b/ai-service/src/lib/imagine/imagine-api-client.ts index ace5bddb7..dece9a9b1 100644 --- a/ai-service/src/lib/imagine/imagine-api-client.ts +++ b/ai-service/src/lib/imagine/imagine-api-client.ts @@ -1,6 +1,6 @@ +import { ImagineUIMessage } from '@/shared-types'; import { AppwriteException } from '@appwrite.io/console'; import type { Client, Payload } from '@appwrite.io/console'; -import { UIMessage } from 'ai'; export class Imagine { client: Client; @@ -234,7 +234,7 @@ and all associated resources will be removed. artifactId: string, conversationId: string, name?: string, - messages?: UIMessage[] + messages?: ImagineUIMessage[] ): Promise { if (typeof artifactId === 'undefined') { throw new AppwriteException('Missing required parameter: "artifactId"'); @@ -569,9 +569,7 @@ export type Conversation = { /** * Messages (UI) */ - messages: { - messages: UIMessage[]; - }; + messages: ImagineUIMessage[]; }; /** * ConversationsMessage diff --git a/ai-service/src/server.ts b/ai-service/src/server.ts index 9caf24c37..6e930da4a 100644 --- a/ai-service/src/server.ts +++ b/ai-service/src/server.ts @@ -6,6 +6,7 @@ dotenv.config({ path: path.resolve(__dirname, '../.env') }); import { serve } from '@hono/node-server'; import { Hono } from 'hono'; import { cors } from 'hono/cors'; +import { HTTPException } from 'hono/http-exception'; import { fileURLToPath } from 'url'; import { handleChatRequest } from '@/handlers/chat/route'; import { getConversation, getConversations } from './handlers/conversation'; @@ -14,6 +15,13 @@ const app = new Hono(); // Middleware app.use('*', cors()); +app.onError((err, c) => { + if (err instanceof HTTPException) { + return err.getResponse(); + } + console.error(err); + return new Response('Something went wrong', { status: 500 }); +}); // Routes app.post("/api/chat", handleChatRequest); diff --git a/ai-service/src/shared-types/index.ts b/ai-service/src/shared-types/index.ts new file mode 100644 index 000000000..6161a32c6 --- /dev/null +++ b/ai-service/src/shared-types/index.ts @@ -0,0 +1,21 @@ +import { checkpointUIDataPartSchema } from '@/lib/ai/custom-parts/checkpoint'; +import { thinkingUIDataPartSchema } from '@/lib/ai/custom-parts/thinking'; +import { fileTools } from '@/lib/ai/mastra/tools/file-tools'; +import { InferUIDataParts, InferUITool, ToolUIPart, UIMessage } from 'ai'; + +type ImagineTools = { + readFile: InferUITool; + writeFile: InferUITool; + listFilesInDirectory: InferUITool; + deleteFile: InferUITool; + moveFile: InferUITool; +}; + +export type ImagineUIToolParts = ToolUIPart; + +export type ImagineUIDataParts = InferUIDataParts<{ + checkpoint: typeof checkpointUIDataPartSchema; + thinking: typeof thinkingUIDataPartSchema; +}>; + +export type ImagineUIMessage = UIMessage; diff --git a/ai-service/tsconfig.json b/ai-service/tsconfig.json index 886a6a505..8d6dc6092 100644 --- a/ai-service/tsconfig.json +++ b/ai-service/tsconfig.json @@ -6,7 +6,6 @@ "allowJs": true, "skipLibCheck": true, "strict": true, - "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", @@ -14,7 +13,8 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, - "plugins": [], + "composite": true, + "declaration": true, "paths": { "@/*": ["./src/*"] } diff --git a/package.json b/package.json index 40fc2bfed..b5ff5e41a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev": "vite dev", "build": "node build.js", "preview": "vite preview", - "prepare": "svelte-kit sync", + "prepare": "svelte-kit sync || echo ''", "clean": "rm -rf node_modules && rm -rf .svelte_kit && pnpm i --force", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", @@ -81,7 +81,7 @@ "prettier-plugin-svelte": "^3.3.3", "rehype-highlight": "^7.0.2", "sass": "^1.86.0", - "svelte": "^5.25.3", + "svelte": "^5.36.3", "svelte-check": "^4.1.5", "svelte-preprocess": "^6.0.3", "svelte-sequential-preprocessor": "^2.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64c520d3e..674b0be6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@ai-sdk/svelte': specifier: 3.0.0-beta.19 - version: 3.0.0-beta.19(svelte@5.25.3)(zod@3.25.67) + version: 3.0.0-beta.19(svelte@5.36.3)(zod@3.25.67) '@appwrite.io/console': specifier: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564 version: https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564 @@ -19,19 +19,19 @@ importers: version: 0.25.0 '@appwrite.io/pink-icons-svelte': specifier: ^2.0.0-RC.1 - version: https://try-module.cloud/module/@appwrite/%40appwrite.io%2Fpink-icons-svelte@12707b9(svelte@5.25.3) + version: https://try-module.cloud/module/@appwrite/%40appwrite.io%2Fpink-icons-svelte@12707b9(svelte@5.36.3) '@appwrite.io/pink-legacy': specifier: ^1.0.3 version: 1.0.3 '@appwrite.io/pink-svelte': specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778 - version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3) + version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.36.3) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 '@sentry/sveltekit': specifier: ^8.38.0 - version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) '@stripe/stripe-js': specifier: ^3.5.0 version: 3.5.0 @@ -79,10 +79,10 @@ importers: version: 2.0.1 svelte-confetti: specifier: ^1.4.0 - version: 1.4.0(svelte@5.25.3) + version: 1.4.0(svelte@5.36.3) svelte-exmarkdown: specifier: ^5.0.0 - version: 5.0.1(svelte@5.25.3) + version: 5.0.1(svelte@5.36.3) tippy.js: specifier: ^6.3.7 version: 6.3.7 @@ -95,22 +95,22 @@ importers: version: 9.24.0 '@melt-ui/pp': specifier: ^0.3.2 - version: 0.3.2(@melt-ui/svelte@0.86.5(svelte@5.25.3))(svelte@5.25.3) + version: 0.3.2(@melt-ui/svelte@0.86.5(svelte@5.36.3))(svelte@5.36.3) '@melt-ui/svelte': specifier: ^0.86.5 - version: 0.86.5(svelte@5.25.3) + version: 0.86.5(svelte@5.36.3) '@playwright/test': specifier: ^1.51.1 version: 1.51.1 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.8(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))) + version: 3.0.8(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))) '@sveltejs/kit': specifier: ^2.20.2 - version: 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + version: 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.3 - version: 5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + version: 5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) '@testing-library/dom': specifier: ^10.4.0 version: 10.4.0 @@ -119,7 +119,7 @@ importers: version: 6.6.3 '@testing-library/svelte': specifier: ^5.2.4 - version: 5.2.7(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))(vitest@3.0.9) + version: 5.2.7(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))(vitest@3.0.9) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) @@ -158,7 +158,7 @@ importers: version: 10.1.1(eslint@9.23.0) eslint-plugin-svelte: specifier: ^3.3.3 - version: 3.3.3(eslint@9.23.0)(svelte@5.25.3) + version: 3.3.3(eslint@9.23.0)(svelte@5.36.3) globals: specifier: ^16.0.0 version: 16.0.0 @@ -176,7 +176,7 @@ importers: version: 3.5.3 prettier-plugin-svelte: specifier: ^3.3.3 - version: 3.3.3(prettier@3.5.3)(svelte@5.25.3) + version: 3.3.3(prettier@3.5.3)(svelte@5.36.3) rehype-highlight: specifier: ^7.0.2 version: 7.0.2 @@ -184,14 +184,14 @@ importers: specifier: ^1.86.0 version: 1.86.0 svelte: - specifier: ^5.25.3 - version: 5.25.3 + specifier: ^5.36.3 + version: 5.36.3 svelte-check: specifier: ^4.1.5 - version: 4.1.5(picomatch@4.0.2)(svelte@5.25.3)(typescript@5.8.2) + version: 4.1.5(picomatch@4.0.2)(svelte@5.36.3)(typescript@5.8.2) svelte-preprocess: specifier: ^6.0.3 - version: 6.0.3(@babel/core@7.26.10)(postcss-load-config@3.1.4(postcss@8.5.3))(postcss@8.5.3)(sass@1.86.0)(svelte@5.25.3)(typescript@5.8.2) + version: 6.0.3(@babel/core@7.26.10)(postcss-load-config@3.1.4(postcss@8.5.3))(postcss@8.5.3)(sass@1.86.0)(svelte@5.36.3)(typescript@5.8.2) svelte-sequential-preprocessor: specifier: ^2.0.2 version: 2.0.2 @@ -2121,8 +2121,8 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@1.4.5: - resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==} + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3312,8 +3312,8 @@ packages: resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==} engines: {node: '>=16'} - svelte@5.25.3: - resolution: {integrity: sha512-J9rcZ/xVJonAoESqVGHHZhrNdVbrCfkdB41BP6eiwHMoFShD9it3yZXApVYMHdGfCshBsZCKsajwJeBbS/M1zg==} + svelte@5.36.3: + resolution: {integrity: sha512-0NsgzxP60BXZ0SDAJzFb4CEIuHK5Qtubx69bVSDSORvS053+IZqA+wKK1b742US4eCuKo1KJOu7sAB+vRpK9JQ==} engines: {node: '>=18'} symbol-tree@3.2.4: @@ -3701,11 +3701,11 @@ snapshots: dependencies: json-schema: 0.4.0 - '@ai-sdk/svelte@3.0.0-beta.19(svelte@5.25.3)(zod@3.25.67)': + '@ai-sdk/svelte@3.0.0-beta.19(svelte@5.36.3)(zod@3.25.67)': dependencies: '@ai-sdk/provider-utils': 3.0.0-beta.3(zod@3.25.67) ai: 5.0.0-beta.19(zod@3.25.67) - svelte: 5.25.3 + svelte: 5.36.3 optionalDependencies: zod: 3.25.67 @@ -3750,13 +3750,13 @@ snapshots: '@appwrite.io/console@https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@a5e5564': {} - '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.36.3)': dependencies: - svelte: 5.25.3 + svelte: 5.36.3 - '@appwrite.io/pink-icons-svelte@https://try-module.cloud/module/@appwrite/%40appwrite.io%2Fpink-icons-svelte@12707b9(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://try-module.cloud/module/@appwrite/%40appwrite.io%2Fpink-icons-svelte@12707b9(svelte@5.36.3)': dependencies: - svelte: 5.25.3 + svelte: 5.36.3 '@appwrite.io/pink-icons@0.25.0': {} @@ -3767,20 +3767,20 @@ snapshots: '@appwrite.io/pink-icons': 1.0.0 the-new-css-reset: 1.11.3 - '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.25.3)': + '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@ee1b778(svelte@5.36.3)': dependencies: - '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.25.3) + '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@ee1b7788cd3f877c9aa1b6487976bd63a6196870(svelte@5.36.3) '@floating-ui/dom': 1.6.13 - '@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3) - '@melt-ui/svelte': 0.86.6(svelte@5.25.3) + '@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.36.3))(svelte@5.36.3) + '@melt-ui/svelte': 0.86.6(svelte@5.36.3) ansicolor: 2.0.3 d3: 7.9.0 fuse.js: 7.1.0 pretty-bytes: 6.1.1 shiki: 1.29.2 - svelte: 5.25.3 - svelte-motion: 0.12.2(svelte@5.25.3) - svelte-sonner: 0.3.28(svelte@5.25.3) + svelte: 5.36.3 + svelte-motion: 0.12.2(svelte@5.36.3) + svelte-sonner: 0.3.28(svelte@5.36.3) '@asamuzakjp/css-color@3.1.1': dependencies: @@ -4082,21 +4082,21 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.5(svelte@5.25.3))(svelte@5.25.3)': + '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.5(svelte@5.36.3))(svelte@5.36.3)': dependencies: - '@melt-ui/svelte': 0.86.5(svelte@5.25.3) + '@melt-ui/svelte': 0.86.5(svelte@5.36.3) estree-walker: 3.0.3 magic-string: 0.30.17 - svelte: 5.25.3 + svelte: 5.36.3 - '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)': + '@melt-ui/pp@0.3.2(@melt-ui/svelte@0.86.6(svelte@5.36.3))(svelte@5.36.3)': dependencies: - '@melt-ui/svelte': 0.86.6(svelte@5.25.3) + '@melt-ui/svelte': 0.86.6(svelte@5.36.3) estree-walker: 3.0.3 magic-string: 0.30.17 - svelte: 5.25.3 + svelte: 5.36.3 - '@melt-ui/svelte@0.86.5(svelte@5.25.3)': + '@melt-ui/svelte@0.86.5(svelte@5.36.3)': dependencies: '@floating-ui/core': 1.6.9 '@floating-ui/dom': 1.6.13 @@ -4104,9 +4104,9 @@ snapshots: dequal: 2.0.3 focus-trap: 7.6.4 nanoid: 5.1.5 - svelte: 5.25.3 + svelte: 5.36.3 - '@melt-ui/svelte@0.86.6(svelte@5.25.3)': + '@melt-ui/svelte@0.86.6(svelte@5.36.3)': dependencies: '@floating-ui/core': 1.6.9 '@floating-ui/dom': 1.6.13 @@ -4114,7 +4114,7 @@ snapshots: dequal: 2.0.3 focus-trap: 7.6.4 nanoid: 5.1.5 - svelte: 5.25.3 + svelte: 5.36.3 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -4696,21 +4696,21 @@ snapshots: '@opentelemetry/semantic-conventions': 1.30.0 '@sentry/core': 8.55.0 - '@sentry/svelte@8.55.0(svelte@5.25.3)': + '@sentry/svelte@8.55.0(svelte@5.36.3)': dependencies: '@sentry/browser': 8.55.0 '@sentry/core': 8.55.0 magic-string: 0.30.7 - svelte: 5.25.3 + svelte: 5.36.3 - '@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': + '@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': dependencies: '@sentry/core': 8.55.0 '@sentry/node': 8.55.0 '@sentry/opentelemetry': 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0) - '@sentry/svelte': 8.55.0(svelte@5.25.3) + '@sentry/svelte': 8.55.0(svelte@5.36.3) '@sentry/vite-plugin': 2.22.6 - '@sveltejs/kit': 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + '@sveltejs/kit': 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) magic-string: 0.30.7 magicast: 0.2.8 sorcery: 1.0.0 @@ -4778,13 +4778,13 @@ snapshots: dependencies: acorn: 8.14.1 - '@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))': + '@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))': dependencies: - '@sveltejs/kit': 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + '@sveltejs/kit': 2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) - '@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': + '@sveltejs/kit@2.20.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -4796,26 +4796,26 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.1 - svelte: 5.25.3 + svelte: 5.36.3 vite: 6.2.3(@types/node@22.13.14)(sass@1.86.0) - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) debug: 4.4.0 - svelte: 5.25.3 + svelte: 5.36.3 vite: 6.2.3(@types/node@22.13.14)(sass@1.86.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 - svelte: 5.25.3 + svelte: 5.36.3 vite: 6.2.3(@types/node@22.13.14)(sass@1.86.0) vitefu: 1.0.6(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0)) transitivePeerDependencies: @@ -4846,10 +4846,10 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/svelte@5.2.7(svelte@5.25.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))(vitest@3.0.9)': + '@testing-library/svelte@5.2.7(svelte@5.36.3)(vite@6.2.3(@types/node@22.13.14)(sass@1.86.0))(vitest@3.0.9)': dependencies: '@testing-library/dom': 10.4.0 - svelte: 5.25.3 + svelte: 5.36.3 optionalDependencies: vite: 6.2.3(@types/node@22.13.14)(sass@1.86.0) vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.14)(@vitest/ui@3.0.9)(jsdom@26.0.0)(sass@1.86.0) @@ -5732,7 +5732,7 @@ snapshots: dependencies: eslint: 9.23.0 - eslint-plugin-svelte@3.3.3(eslint@9.23.0)(svelte@5.25.3): + eslint-plugin-svelte@3.3.3(eslint@9.23.0)(svelte@5.36.3): dependencies: '@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0) '@jridgewell/sourcemap-codec': 1.5.0 @@ -5744,9 +5744,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.3) postcss-safe-parser: 7.0.1(postcss@8.5.3) semver: 7.7.1 - svelte-eslint-parser: 1.1.1(svelte@5.25.3) + svelte-eslint-parser: 1.1.1(svelte@5.36.3) optionalDependencies: - svelte: 5.25.3 + svelte: 5.36.3 transitivePeerDependencies: - ts-node @@ -5813,7 +5813,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.5: + esrap@2.1.0: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -6799,10 +6799,10 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.25.3): + prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.36.3): dependencies: prettier: 3.5.3 - svelte: 5.25.3 + svelte: 5.36.3 prettier@3.5.3: {} @@ -7116,23 +7116,23 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.25.3)(typescript@5.8.2): + svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.36.3)(typescript@5.8.2): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 fdir: 6.4.3(picomatch@4.0.2) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.25.3 + svelte: 5.36.3 typescript: 5.8.2 transitivePeerDependencies: - picomatch - svelte-confetti@1.4.0(svelte@5.25.3): + svelte-confetti@1.4.0(svelte@5.36.3): dependencies: - svelte: 5.25.3 + svelte: 5.36.3 - svelte-eslint-parser@1.1.1(svelte@5.25.3): + svelte-eslint-parser@1.1.1(svelte@5.36.3): dependencies: eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -7141,30 +7141,30 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.3) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.25.3 + svelte: 5.36.3 - svelte-exmarkdown@5.0.1(svelte@5.25.3): + svelte-exmarkdown@5.0.1(svelte@5.36.3): dependencies: remark-gfm: 4.0.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 - svelte: 5.25.3 + svelte: 5.36.3 unified: 11.0.5 transitivePeerDependencies: - supports-color - svelte-motion@0.12.2(svelte@5.25.3): + svelte-motion@0.12.2(svelte@5.36.3): dependencies: '@types/react': 18.3.22 framesync: 6.1.2 popmotion: 11.0.5 style-value-types: 5.1.2 - svelte: 5.25.3 + svelte: 5.36.3 tslib: 2.8.1 - svelte-preprocess@6.0.3(@babel/core@7.26.10)(postcss-load-config@3.1.4(postcss@8.5.3))(postcss@8.5.3)(sass@1.86.0)(svelte@5.25.3)(typescript@5.8.2): + svelte-preprocess@6.0.3(@babel/core@7.26.10)(postcss-load-config@3.1.4(postcss@8.5.3))(postcss@8.5.3)(sass@1.86.0)(svelte@5.36.3)(typescript@5.8.2): dependencies: - svelte: 5.25.3 + svelte: 5.36.3 optionalDependencies: '@babel/core': 7.26.10 postcss: 8.5.3 @@ -7177,9 +7177,9 @@ snapshots: svelte: 4.2.19 tslib: 2.7.0 - svelte-sonner@0.3.28(svelte@5.25.3): + svelte-sonner@0.3.28(svelte@5.36.3): dependencies: - svelte: 5.25.3 + svelte: 5.36.3 svelte@4.2.19: dependencies: @@ -7198,7 +7198,7 @@ snapshots: magic-string: 0.30.17 periscopic: 3.1.0 - svelte@5.25.3: + svelte@5.36.3: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -7209,7 +7209,7 @@ snapshots: axobject-query: 4.1.0 clsx: 2.1.1 esm-env: 1.2.2 - esrap: 1.4.5 + esrap: 2.1.0 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.17 diff --git a/src/lib/ai-service/ai-service-api-client.ts b/src/lib/ai-service/ai-service-api-client.ts new file mode 100644 index 000000000..bcf2017c1 --- /dev/null +++ b/src/lib/ai-service/ai-service-api-client.ts @@ -0,0 +1,7 @@ +import { VARS } from "../system"; + +export class AIService { + private baseUrl = `${VARS.AI_SERVICE_BASE_URL}/api`; + + constructor() {} +} diff --git a/src/lib/components/studio/chat/chat.svelte b/src/lib/components/studio/chat/chat.svelte index 739391f08..a963c1da5 100644 --- a/src/lib/components/studio/chat/chat.svelte +++ b/src/lib/components/studio/chat/chat.svelte @@ -10,7 +10,6 @@ } from '@appwrite.io/pink-icons-svelte'; import { isSmallViewport } from '$lib/stores/viewport'; import Conversation from './conversation.svelte'; - import type { StreamParser } from './parser'; import type { EventHandler } from 'svelte/elements'; import { page } from '$app/state'; import { studio } from '../studio.svelte'; @@ -18,44 +17,32 @@ import { sdk } from '$lib/stores/sdk'; import { conversation, showChat } from '$lib/stores/chat'; import { DefaultChatTransport } from 'ai'; + import type { ImagineUIMessage } from '$shared-types'; + import { VARS } from '../../../system'; type Props = { width: number; hasSubNavigation: boolean; - parser: StreamParser; }; - let { width, hasSubNavigation, parser }: Props = $props(); + let { width, hasSubNavigation }: Props = $props(); let minimizeChat = $state($isSmallViewport ? true : false); let message = $state(''); - let firstByteReceived = $state(true); let chatTextareaRef: HTMLTextAreaElement | null = $state(null); - const chatBody: { - token: string | null; - projectId: string; - artifactId: string | null; - conversationId: string | null; - } = { - token: null, - projectId: page.params.project, - artifactId: null, - conversationId: null - }; - - const chat = new Chat({ + const chat = new Chat({ + id: $conversation.data?.$id, transport: new DefaultChatTransport({ - api: 'http://localhost:8889/api/chat', - // body: chatBody + api: `${VARS.AI_SERVICE_BASE_URL}/api/chat`, }) }); - // const chat = new Chat({ - // transport: new DefaultChatTransport({ - // api: 'http://localhost:8889/api/chat', - // body: chatBody - // }) - // }); + + $effect(() => { + if ($conversation.data?.messages) { + chat.messages = $conversation.data.messages; + } + }); const onkeydown: EventHandler = (event) => { if (event.key === 'Enter') { @@ -77,116 +64,34 @@ if (chatTextareaRef && !$isSmallViewport) { chatTextareaRef.focus(); } + + refreshToken(); // TODO: remove }); async function refreshToken() { - // const token = await sdk.forConsoleIn(page.params.region).account.createJWT(); const token = await sdk .forProject(page.params.region, page.params.project) .account.createJWT(); - chatBody.token = token.jwt; + return token; } - // let controller: AbortController; - async function createMessage() { - await refreshToken(); + const token = await refreshToken(); - console.log('$conversation', $conversation); - chatBody.artifactId = $conversation?.data?.artifactId ?? null; - chatBody.conversationId = $conversation?.data?.$id ?? null; - - const group = Symbol(); - if (message.startsWith('!')) { - const [type, ...segments] = message.split(' '); - const content = segments.join(' '); - message = ''; - const value = `${content}`; - parser.chunk(value, 'system', { group }); - parser.end(); - return; - } - - console.log('artifactId', $conversation?.data?.artifactId); - - // chatIns + chat.sendMessage({ + role: "user", + text: message, + }, { + body: { + projectId: page.params.project, + artifactId: page.params.artifact, + }, + headers: { + "X-Imagine-Token": token.jwt + } + }); message = ''; - - // const initialMessage = message; - - // try { - // parser.chunk(message, 'user'); - // firstByteReceived = false; - // message = ''; - // controller = new AbortController(); - // studio.streaming = true; - - // const url = `${sdk.forProject(page.params.region, page.params.project).client.config.endpoint}/imagine/artifacts/${$conversation.data.artifactId}/conversations/${$conversation.data.$id}/messages`; - // const response = await fetch( - // url, - // { - // method: "POST", - // headers: { - // 'Content-Type': 'application/json', - // 'X-Appwrite-Project': page.params.project, - // 'X-Appwrite-Mode': 'admin' - // }, - // credentials: 'include', - // body: JSON.stringify({ - // content: initialMessage, - // type: 'text' - // }), - // signal: controller.signal - // } - // ) - - // const response = await fetch( - // url, - // { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // 'X-Appwrite-Project': page.params.project, - // 'X-Appwrite-Mode': 'admin' - // }, - // credentials: 'include', - // body: JSON.stringify({ - // content: initialMessage, - // type: 'text' - // }), - // signal: controller.signal - // } - // ); - - // if (!response.ok) { - // throw new Error(`${response.status} Error: ${await response.text()}`); - // } - - // invalidate(Dependencies.ARTIFACTS); - - // const reader = response.body.getReader(); - // const decoder = new TextDecoder(); - - // let chunk = await reader.read(); - // while (!chunk.done) { - // if (!firstByteReceived) firstByteReceived = true; - // parser.chunk(decoder.decode(chunk.value), 'system', { - // group - // }); - // chunk = await reader.read(); - // } - // parser.end(); - // } catch (error) { - // if (error instanceof Error) { - // parser.chunk(error.message, 'error'); - // } - // message = initialMessage; - // } finally { - // firstByteReceived = true; - // studio.streaming = false; - // } - // } } let tokens = $state(2); @@ -226,7 +131,7 @@ - + {#if tokens < 2} @@ -242,7 +147,6 @@ {/if} {/if} - {/if}
import 'highlight.js/styles/atom-one-light.css'; - import { StreamParser, type ParsedItem } from './parser'; - import { Icon, Layout, ShimmerText, Tag, Typography } from '@appwrite.io/pink-svelte'; + import { Icon, Spinner, Layout, Tag, Typography } from '@appwrite.io/pink-svelte'; import { IconArrowDown } from '@appwrite.io/pink-icons-svelte'; import { slide } from 'svelte/transition'; import type { UIEventHandler, WheelEventHandler } from 'svelte/elements'; import Message from './message.svelte'; import { studio } from '../studio.svelte'; import { Chat } from '@ai-sdk/svelte'; + import type { ImagineUIMessage } from '$shared-types'; type Props = { - parser: StreamParser; autoscroll?: boolean; - thinking?: boolean; - chat: Chat; + chat: Chat; }; - let { autoscroll = $bindable(true), thinking = false, chat }: Props = $props(); - - // const chunks = writable([ - // { - // id: Symbol(), - // from: 'user', - // group: null, - // content: "Test", - // complete: true, - // } - // ]); + let { autoscroll = $bindable(true), chat }: Props = $props(); function scrollToBottom(smooth: boolean = true) { document @@ -33,7 +21,7 @@ .scrollIntoView({ behavior: smooth ? 'smooth' : 'instant' }); } - const onwheel: WheelEventHandler = (event) => { + const onwheel: WheelEventHandler = () => { if (studio.streaming) autoscroll = false; }; @@ -51,35 +39,22 @@ autoscroll = false; } }; - - $effect(() => { - console.log("DATA", chat.data); - }); -
- - {#each chat.messages as message, messageIndex (messageIndex)} - - {/each} - {#if thinking} - - thinking... - + {#if chat.status === "submitted" || chat.status === "streaming"} + {/if} + + {#if chat.status === "error"} + {chat.error?.message} + {/if} +
diff --git a/src/lib/components/studio/chat/message copy.svelte b/src/lib/components/studio/chat/message copy.svelte new file mode 100644 index 000000000..36671ab3b --- /dev/null +++ b/src/lib/components/studio/chat/message copy.svelte @@ -0,0 +1,301 @@ + + +{#if 'type' in message} + {#key message.content} + {#if message.type === 'actions'} + + + + Version 0 + {#if !message.complete} + + {/if} + + +
+
+ {#each message.actions as action} + {@const actionInQueue = queue.lists[action.group]?.find( + (n) => n.data.id === action.id + )} + {#if actionInQueue} + + {#if action.type === 'file'} + + + {#if actionInQueue.status === 'done'} + + {:else} + + {/if} + + {action.src} + + {#if actionInQueue.status === 'waiting'} + Waiting + {:else if actionInQueue.status === 'processing'} + Generating + {:else if actionInQueue.status === 'done'} + Generated + {:else if actionInQueue.status === 'failed'} + Failed + {/if} + + {:else if action.type === 'shell'} + + + {#if actionInQueue.status === 'done'} + + {:else} + + {/if} + + {action.content} + + {#if actionInQueue.status === 'waiting'} + Waiting + {:else if actionInQueue.status === 'processing'} + Running + {:else if actionInQueue.status === 'done'} + Completed + {:else if actionInQueue.status === 'failed'} + Failed + {/if} + + {/if} + + {/if} + {/each} + {#if !message.complete} + + thinking... + + {/if} +
+
+ {/if} + {/key} +{:else} + {#snippet text()} + + {/snippet} + {#if message.from === 'user'} +
+ {@render text()} +
+ {:else if message.from === 'error'} +
+ {@render text()} +
+ {:else} + {@render text()} + {/if} +{/if} + + diff --git a/src/lib/components/studio/chat/message.svelte b/src/lib/components/studio/chat/message.svelte index 36671ab3b..040dad1bd 100644 --- a/src/lib/components/studio/chat/message.svelte +++ b/src/lib/components/studio/chat/message.svelte @@ -1,8 +1,5 @@ -{#if 'type' in message} - {#key message.content} - {#if message.type === 'actions'} - - - - Version 0 - {#if !message.complete} - - {/if} - - -
-
- {#each message.actions as action} - {@const actionInQueue = queue.lists[action.group]?.find( - (n) => n.data.id === action.id - )} - {#if actionInQueue} - - {#if action.type === 'file'} - - - {#if actionInQueue.status === 'done'} - - {:else} - - {/if} - - {action.src} - - {#if actionInQueue.status === 'waiting'} - Waiting - {:else if actionInQueue.status === 'processing'} - Generating - {:else if actionInQueue.status === 'done'} - Generated - {:else if actionInQueue.status === 'failed'} - Failed - {/if} - - {:else if action.type === 'shell'} - - - {#if actionInQueue.status === 'done'} - - {:else} - - {/if} - - {action.content} - - {#if actionInQueue.status === 'waiting'} - Waiting - {:else if actionInQueue.status === 'processing'} - Running - {:else if actionInQueue.status === 'done'} - Completed - {:else if actionInQueue.status === 'failed'} - Failed - {/if} - - {/if} - - {/if} - {/each} - {#if !message.complete} - - thinking... - - {/if} -
-
+ +{#if message.role === 'user'} + {#each message.parts as part, partIndex (partIndex)} + {#if part.type === 'text'} +
+ {part.text} +
{/if} - {/key} -{:else} - {#snippet text()} - - {/snippet} - {#if message.from === 'user'} -
- {@render text()} -
- {:else if message.from === 'error'} -
- {@render text()} -
- {:else} - {@render text()} + {/each} +{/if} + + +{#if message.role === 'assistant'} + + {#each organizedParts().nonTextParts as part, partIndex (partIndex)} + {#if part.type === 'data-thinking'} + 0} /> + {/if} + {/each} + + + {#if organizedParts().toolCalls.length > 0} + + {/if} + + + {#if organizedParts().finalText} + {/if} {/if} diff --git a/src/lib/components/studio/chat/toolCalls.svelte b/src/lib/components/studio/chat/toolCalls.svelte new file mode 100644 index 000000000..fb0f3bcc9 --- /dev/null +++ b/src/lib/components/studio/chat/toolCalls.svelte @@ -0,0 +1,196 @@ + + +{#if filteredParts().parts.length > 0} +
+
+ Version 1 +
+ +
EXPANDED_SHOW_ITEMS}> + {#each filteredParts().parts as toolCall, i (i)} + {@const isLoading = toolCall.state === 'input-available'} + +
+
+ {#if isLoading} + + {:else} + + {/if} +
+ +
+ {#if toolCall.type === 'tool-writeFile'} + {isLoading + ? `Writing file ${toolCall.input.path}...` + : `Wrote file ${toolCall.input.path}`} + {:else if toolCall.type === 'tool-readFile'} + {isLoading + ? `Reading file ${toolCall.input.path}...` + : `Read file ${toolCall.input.path}`} + {/if} +
+ +
+
+ {/each} +
+ + {#if filteredParts().showMoreButton} + + {/if} +
+{/if} + + diff --git a/src/lib/sdk/imagine.ts b/src/lib/sdk/imagine.ts index 7406c690f..692069fa9 100644 --- a/src/lib/sdk/imagine.ts +++ b/src/lib/sdk/imagine.ts @@ -1,3 +1,4 @@ +import type { ImagineUIMessage } from '$shared-types'; import { AppwriteException } from '@appwrite.io/console'; import type { Client, Payload } from '@appwrite.io/console'; @@ -561,6 +562,10 @@ export type Conversation = { * Search index string. */ search: string; + /** + * Messages (UI) + */ + messages: ImagineUIMessage[]; }; /** * ConversationsMessage diff --git a/src/lib/system.ts b/src/lib/system.ts index a951f58c8..fd800a54e 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -17,7 +17,8 @@ export const VARS = { APPWRITE_ENDPOINT: env.PUBLIC_APPWRITE_ENDPOINT ?? undefined, GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined, PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined, - PROJECT_PROFILE: (env.PUBLIC_PROJECT_PROFILE as Profile) ?? undefined + PROJECT_PROFILE: (env.PUBLIC_PROJECT_PROFILE as Profile) ?? undefined, + AI_SERVICE_BASE_URL: env.PUBLIC_AI_SERVICE_BASE_URL ?? undefined }; export const ENV = { diff --git a/tsconfig.json b/tsconfig.json index 900ee4e39..afe224f82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,14 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "moduleResolution": "bundler" - } + "moduleResolution": "bundler", + "paths": { + "$shared-types": [ + "./ai-service/src/shared-types" + ] + } + }, + "references": [ + { "path": "./ai-service" } + ] } diff --git a/vite.config.ts b/vite.config.ts index d0a9f9bf0..f227ab793 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -35,7 +35,10 @@ export default defineConfig({ ] }, server: { - port: 3000 + port: 3000, + watch: { + ignored: ['**/ai-service/**'] + } }, test: { workspace: [