From 660353360f2c867dacc1ebe49e00a770d29ff86e Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 25 Jan 2025 03:52:26 +0530 Subject: [PATCH 1/5] fix: docker prod env variable fix (#1170) * fix: docker prod env variable fix * lint and typecheck * removed hardcoded tag --- app/lib/modules/llm/providers/lmstudio.ts | 9 +++--- app/lib/modules/llm/providers/ollama.ts | 6 ++-- app/routes/api.models.ts | 12 ++++++-- bindings.sh | 35 +++++++++++++++++------ docker-compose.yaml | 18 ++++++++++++ worker-configuration.d.ts | 3 +- 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/app/lib/modules/llm/providers/lmstudio.ts b/app/lib/modules/llm/providers/lmstudio.ts index ba319ac8..9dabc3eb 100644 --- a/app/lib/modules/llm/providers/lmstudio.ts +++ b/app/lib/modules/llm/providers/lmstudio.ts @@ -40,7 +40,7 @@ export default class LMStudioProvider extends BaseProvider { * Running in Server * Backend: Check if we're running in Docker */ - const isDocker = process.env.RUNNING_IN_DOCKER === 'true'; + const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; @@ -58,7 +58,7 @@ export default class LMStudioProvider extends BaseProvider { } getModelInstance: (options: { model: string; - serverEnv: Env; + serverEnv?: Env; apiKeys?: Record; providerSettings?: Record; }) => LanguageModelV1 = (options) => { @@ -75,8 +75,9 @@ export default class LMStudioProvider extends BaseProvider { throw new Error('No baseUrl found for LMStudio provider'); } + const isDocker = process.env.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; + if (typeof window === 'undefined') { - const isDocker = process.env.RUNNING_IN_DOCKER === 'true'; baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; } @@ -84,7 +85,7 @@ export default class LMStudioProvider extends BaseProvider { logger.debug('LMStudio Base Url used: ', baseUrl); const lmstudio = createOpenAI({ - baseUrl: `${baseUrl}/v1`, + baseURL: `${baseUrl}/v1`, apiKey: '', }); diff --git a/app/lib/modules/llm/providers/ollama.ts b/app/lib/modules/llm/providers/ollama.ts index 11cf6a2b..a3974ab3 100644 --- a/app/lib/modules/llm/providers/ollama.ts +++ b/app/lib/modules/llm/providers/ollama.ts @@ -63,7 +63,7 @@ export default class OllamaProvider extends BaseProvider { * Running in Server * Backend: Check if we're running in Docker */ - const isDocker = process.env.RUNNING_IN_DOCKER === 'true'; + const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; @@ -83,7 +83,7 @@ export default class OllamaProvider extends BaseProvider { } getModelInstance: (options: { model: string; - serverEnv: Env; + serverEnv?: Env; apiKeys?: Record; providerSettings?: Record; }) => LanguageModelV1 = (options) => { @@ -101,7 +101,7 @@ export default class OllamaProvider extends BaseProvider { throw new Error('No baseUrl found for OLLAMA provider'); } - const isDocker = process.env.RUNNING_IN_DOCKER === 'true'; + const isDocker = process?.env?.RUNNING_IN_DOCKER === 'true' || serverEnv?.RUNNING_IN_DOCKER === 'true'; baseUrl = isDocker ? baseUrl.replace('localhost', 'host.docker.internal') : baseUrl; baseUrl = isDocker ? baseUrl.replace('127.0.0.1', 'host.docker.internal') : baseUrl; diff --git a/app/routes/api.models.ts b/app/routes/api.models.ts index 13588f90..f7512226 100644 --- a/app/routes/api.models.ts +++ b/app/routes/api.models.ts @@ -41,11 +41,17 @@ function getProviderInfo(llmManager: LLMManager) { export async function loader({ request, params, + context, }: { request: Request; params: { provider?: string }; + context: { + cloudflare?: { + env: Record; + }; + }; }): Promise { - const llmManager = LLMManager.getInstance(import.meta.env); + const llmManager = LLMManager.getInstance(context.cloudflare?.env); // Get client side maintained API keys and provider settings from cookies const cookieHeader = request.headers.get('Cookie'); @@ -63,7 +69,7 @@ export async function loader({ if (provider) { const staticModels = provider.staticModels; const dynamicModels = provider.getDynamicModels - ? await provider.getDynamicModels(apiKeys, providerSettings, import.meta.env) + ? await provider.getDynamicModels(apiKeys, providerSettings, context.cloudflare?.env) : []; modelList = [...staticModels, ...dynamicModels]; } @@ -72,7 +78,7 @@ export async function loader({ modelList = await llmManager.updateModelList({ apiKeys, providerSettings, - serverEnv: import.meta.env, + serverEnv: context.cloudflare?.env, }); } diff --git a/bindings.sh b/bindings.sh index c8a86ead..f4a67325 100755 --- a/bindings.sh +++ b/bindings.sh @@ -2,15 +2,32 @@ bindings="" -while IFS= read -r line || [ -n "$line" ]; do - if [[ ! "$line" =~ ^# ]] && [[ -n "$line" ]]; then - name=$(echo "$line" | cut -d '=' -f 1) - value=$(echo "$line" | cut -d '=' -f 2-) - value=$(echo $value | sed 's/^"\(.*\)"$/\1/') - bindings+="--binding ${name}=${value} " - fi -done < .env.local +# Function to extract variable names from the TypeScript interface +extract_env_vars() { + grep -o '[A-Z_]\+:' worker-configuration.d.ts | sed 's/://' +} + +# First try to read from .env.local if it exists +if [ -f ".env.local" ]; then + while IFS= read -r line || [ -n "$line" ]; do + if [[ ! "$line" =~ ^# ]] && [[ -n "$line" ]]; then + name=$(echo "$line" | cut -d '=' -f 1) + value=$(echo "$line" | cut -d '=' -f 2-) + value=$(echo $value | sed 's/^"\(.*\)"$/\1/') + bindings+="--binding ${name}=${value} " + fi + done < .env.local +else + # If .env.local doesn't exist, use environment variables defined in .d.ts + env_vars=($(extract_env_vars)) + # Generate bindings for each environment variable if it exists + for var in "${env_vars[@]}"; do + if [ -n "${!var}" ]; then + bindings+="--binding ${var}=${!var} " + fi + done +fi bindings=$(echo $bindings | sed 's/[[:space:]]*$//') -echo $bindings +echo $bindings \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 47938e0a..2452557d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -72,3 +72,21 @@ services: - "5173:5173" command: pnpm run dev --host 0.0.0.0 profiles: ["development", "default"] + + app-prebuild: + image: ghcr.io/stackblitz-labs/bolt.diy:latest + ports: + - "5173:5173" + environment: + - NODE_ENV=production + - COMPOSE_PROFILES=production + # No strictly needed but serving as hints for Coolify + - PORT=5173 + - OLLAMA_API_BASE_URL=http://127.0.0.1:11434 + - DEFAULT_NUM_CTX=${DEFAULT_NUM_CTX:-32768} + - RUNNING_IN_DOCKER=true + extra_hosts: + - "host.docker.internal:host-gateway" + command: pnpm run dockerstart + profiles: + - prebuilt \ No newline at end of file diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 099fba9b..b2ae1ce7 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,5 +1,6 @@ interface Env { - DEFAULT_NUM_CTX:Settings; + RUNNING_IN_DOCKER: Settings; + DEFAULT_NUM_CTX: Settings; ANTHROPIC_API_KEY: string; OPENAI_API_KEY: string; GROQ_API_KEY: string; From df766c98d4704b0076f99ee1d34e9b3a6c386b00 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 25 Jan 2025 16:16:19 +0530 Subject: [PATCH 2/5] feat: added support for reasoning content (#1168) --- app/components/chat/Markdown.tsx | 7 + app/components/chat/ThoughtBox.tsx | 43 ++++ app/lib/modules/llm/providers/deepseek.ts | 9 +- app/routes/api.chat.ts | 33 ++- app/utils/markdown.ts | 8 +- package.json | 7 +- pnpm-lock.yaml | 239 ++++++++++++---------- 7 files changed, 230 insertions(+), 116 deletions(-) create mode 100644 app/components/chat/ThoughtBox.tsx diff --git a/app/components/chat/Markdown.tsx b/app/components/chat/Markdown.tsx index 07b6a673..46cffd4e 100644 --- a/app/components/chat/Markdown.tsx +++ b/app/components/chat/Markdown.tsx @@ -7,6 +7,7 @@ import { Artifact } from './Artifact'; import { CodeBlock } from './CodeBlock'; import styles from './Markdown.module.scss'; +import ThoughtBox from './ThoughtBox'; const logger = createScopedLogger('MarkdownComponent'); @@ -22,6 +23,8 @@ export const Markdown = memo(({ children, html = false, limitedMarkdown = false const components = useMemo(() => { return { div: ({ className, children, node, ...props }) => { + console.log(className, node); + if (className?.includes('__boltArtifact__')) { const messageId = node?.properties.dataMessageId as string; @@ -32,6 +35,10 @@ export const Markdown = memo(({ children, html = false, limitedMarkdown = false return ; } + if (className?.includes('__boltThought__')) { + return {children}; + } + return (
{children} diff --git a/app/components/chat/ThoughtBox.tsx b/app/components/chat/ThoughtBox.tsx new file mode 100644 index 00000000..3c70e1a8 --- /dev/null +++ b/app/components/chat/ThoughtBox.tsx @@ -0,0 +1,43 @@ +import { useState, type PropsWithChildren } from 'react'; + +const ThoughtBox = ({ title, children }: PropsWithChildren<{ title: string }>) => { + const [isExpanded, setIsExpanded] = useState(false); + + return ( +
setIsExpanded(!isExpanded)} + className={` + bg-bolt-elements-background-depth-2 + shadow-md + rounded-lg + cursor-pointer + transition-all + duration-300 + ${isExpanded ? 'max-h-96' : 'max-h-13'} + overflow-auto + border border-bolt-elements-borderColor + `} + > +
+
+
+ {title}{' '} + {!isExpanded && - Click to expand} +
+
+
+ {children} +
+
+ ); +}; + +export default ThoughtBox; diff --git a/app/lib/modules/llm/providers/deepseek.ts b/app/lib/modules/llm/providers/deepseek.ts index d53bb26c..e0e72571 100644 --- a/app/lib/modules/llm/providers/deepseek.ts +++ b/app/lib/modules/llm/providers/deepseek.ts @@ -2,7 +2,7 @@ import { BaseProvider } from '~/lib/modules/llm/base-provider'; import type { ModelInfo } from '~/lib/modules/llm/types'; import type { IProviderSetting } from '~/types/model'; import type { LanguageModelV1 } from 'ai'; -import { createOpenAI } from '@ai-sdk/openai'; +import { createDeepSeek } from '@ai-sdk/deepseek'; export default class DeepseekProvider extends BaseProvider { name = 'Deepseek'; @@ -38,11 +38,12 @@ export default class DeepseekProvider extends BaseProvider { throw new Error(`Missing API key for ${this.name} provider`); } - const openai = createOpenAI({ - baseURL: 'https://api.deepseek.com/beta', + const deepseek = createDeepSeek({ apiKey, }); - return openai(model); + return deepseek(model, { + // simulateStreaming: true, + }); } } diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index aacaacf9..8564bafd 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -63,6 +63,8 @@ async function chatAction({ context, request }: ActionFunctionArgs) { const totalMessageContent = messages.reduce((acc, message) => acc + message.content, ''); logger.debug(`Total message length: ${totalMessageContent.split(' ').length}, words`); + let lastChunk: string | undefined = undefined; + const dataStream = createDataStream({ async execute(dataStream) { const filePaths = getFilePaths(files || {}); @@ -247,15 +249,42 @@ async function chatAction({ context, request }: ActionFunctionArgs) { } } })(); - result.mergeIntoDataStream(dataStream); }, onError: (error: any) => `Custom error: ${error.message}`, }).pipeThrough( new TransformStream({ transform: (chunk, controller) => { + if (!lastChunk) { + lastChunk = ' '; + } + + if (typeof chunk === 'string') { + if (chunk.startsWith('g') && !lastChunk.startsWith('g')) { + controller.enqueue(encoder.encode(`0: "
"\n`)); + } + + if (lastChunk.startsWith('g') && !chunk.startsWith('g')) { + controller.enqueue(encoder.encode(`0: "
\\n"\n`)); + } + } + + lastChunk = chunk; + + let transformedChunk = chunk; + + if (typeof chunk === 'string' && chunk.startsWith('g')) { + let content = chunk.split(':').slice(1).join(':'); + + if (content.endsWith('\n')) { + content = content.slice(0, content.length - 1); + } + + transformedChunk = `0:${content}\n`; + } + // Convert the string stream to a byte stream - const str = typeof chunk === 'string' ? chunk : JSON.stringify(chunk); + const str = typeof transformedChunk === 'string' ? transformedChunk : JSON.stringify(transformedChunk); controller.enqueue(encoder.encode(str)); }, }), diff --git a/app/utils/markdown.ts b/app/utils/markdown.ts index 4409b85d..262222bf 100644 --- a/app/utils/markdown.ts +++ b/app/utils/markdown.ts @@ -61,7 +61,13 @@ const rehypeSanitizeOptions: RehypeSanitizeOptions = { tagNames: allowedHTMLElements, attributes: { ...defaultSchema.attributes, - div: [...(defaultSchema.attributes?.div ?? []), 'data*', ['className', '__boltArtifact__']], + div: [ + ...(defaultSchema.attributes?.div ?? []), + 'data*', + ['className', '__boltArtifact__', '__boltThought__'], + + // ['className', '__boltThought__'] + ], }, strip: [], }; diff --git a/package.json b/package.json index 7d6dae22..c5422779 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,10 @@ "@ai-sdk/amazon-bedrock": "1.0.6", "@ai-sdk/anthropic": "^0.0.39", "@ai-sdk/cohere": "^1.0.3", + "@ai-sdk/deepseek": "^0.1.3", "@ai-sdk/google": "^0.0.52", "@ai-sdk/mistral": "^0.0.43", - "@ai-sdk/openai": "^0.0.66", + "@ai-sdk/openai": "^1.1.2", "@codemirror/autocomplete": "^6.18.3", "@codemirror/commands": "^6.7.1", "@codemirror/lang-cpp": "^6.0.2", @@ -75,7 +76,7 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", - "ai": "^4.0.13", + "ai": "^4.1.2", "chalk": "^5.4.1", "date-fns": "^3.6.0", "diff": "^5.2.0", @@ -131,7 +132,7 @@ "vite-tsconfig-paths": "^4.3.2", "vitest": "^2.1.7", "wrangler": "^3.91.0", - "zod": "^3.23.8" + "zod": "^3.24.1" }, "resolutions": { "@typescript-eslint/utils": "^8.0.0-alpha.30" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78cf9026..346f53bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,22 +13,25 @@ importers: dependencies: '@ai-sdk/amazon-bedrock': specifier: 1.0.6 - version: 1.0.6(zod@3.23.8) + version: 1.0.6(zod@3.24.1) '@ai-sdk/anthropic': specifier: ^0.0.39 - version: 0.0.39(zod@3.23.8) + version: 0.0.39(zod@3.24.1) '@ai-sdk/cohere': specifier: ^1.0.3 - version: 1.0.3(zod@3.23.8) + version: 1.0.3(zod@3.24.1) + '@ai-sdk/deepseek': + specifier: ^0.1.3 + version: 0.1.3(zod@3.24.1) '@ai-sdk/google': specifier: ^0.0.52 - version: 0.0.52(zod@3.23.8) + version: 0.0.52(zod@3.24.1) '@ai-sdk/mistral': specifier: ^0.0.43 - version: 0.0.43(zod@3.23.8) + version: 0.0.43(zod@3.24.1) '@ai-sdk/openai': - specifier: ^0.0.66 - version: 0.0.66(zod@3.23.8) + specifier: ^1.1.2 + version: 1.1.2(zod@3.24.1) '@codemirror/autocomplete': specifier: ^6.18.3 version: 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) @@ -97,7 +100,7 @@ importers: version: 13.6.2 '@openrouter/ai-sdk-provider': specifier: ^0.0.5 - version: 0.0.5(zod@3.23.8) + version: 0.0.5(zod@3.24.1) '@radix-ui/react-context-menu': specifier: ^2.2.2 version: 2.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -147,8 +150,8 @@ importers: specifier: ^5.5.0 version: 5.5.0 ai: - specifier: ^4.0.13 - version: 4.0.18(react@18.3.1)(zod@3.23.8) + specifier: ^4.1.2 + version: 4.1.2(react@18.3.1)(zod@3.24.1) chalk: specifier: ^5.4.1 version: 5.4.1 @@ -193,7 +196,7 @@ importers: version: 0.10.3 ollama-ai-provider: specifier: ^0.15.2 - version: 0.15.2(zod@3.23.8) + version: 0.15.2(zod@3.24.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -226,7 +229,7 @@ importers: version: 0.2.0(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/server-runtime@2.15.0(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remix-utils: specifier: ^7.7.0 - version: 7.7.0(@remix-run/cloudflare@2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2))(@remix-run/node@2.15.0(typescript@5.7.2))(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/router@1.21.0)(react@18.3.1)(zod@3.23.8) + version: 7.7.0(@remix-run/cloudflare@2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2))(@remix-run/node@2.15.0(typescript@5.7.2))(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/router@1.21.0)(react@18.3.1)(zod@3.24.1) shiki: specifier: ^1.24.0 version: 1.24.0 @@ -310,8 +313,8 @@ importers: specifier: ^3.91.0 version: 3.91.0(@cloudflare/workers-types@4.20241127.0) zod: - specifier: ^3.23.8 - version: 3.23.8 + specifier: ^3.24.1 + version: 3.24.1 packages: @@ -333,6 +336,12 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/deepseek@0.1.3': + resolution: {integrity: sha512-cj0uYgFk0TWWtHKtwB8v17frttquLll9hCpRWtKpiZO69SbiZOwNSjENaoyZvN1sHMLQoQkw+hnbMGtWuU2yOg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@ai-sdk/google@0.0.52': resolution: {integrity: sha512-bfsA/1Ae0SQ6NfLwWKs5SU4MBwlzJjVhK6bTVBicYFjUxg9liK/W76P1Tq/qK9OlrODACz3i1STOIWsFPpIOuQ==} engines: {node: '>=18'} @@ -345,8 +354,14 @@ packages: peerDependencies: zod: ^3.0.0 - '@ai-sdk/openai@0.0.66': - resolution: {integrity: sha512-V4XeDnlNl5/AY3GB3ozJUjqnBLU5pK3DacKTbCNH3zH8/MggJoH6B8wRGdLUPVFMcsMz60mtvh4DC9JsIVFrKw==} + '@ai-sdk/openai-compatible@0.1.3': + resolution: {integrity: sha512-3dr81jVNTd7Tg4i6JwGKHX47DnQ+jn3zOuxLvu6bM2hFylchtIFn/ut3Et7VfsdMWf4gj9tXp/9rUiQ0JokkrQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/openai@1.1.2': + resolution: {integrity: sha512-9rfcwjl4g1/Bdr2SmgFQr+aw81r62MvIKE7QDHMC4ulFd/Hej2oClROSMpDFZHXzs7RGeb32VkRyCHUWWgN3RQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -387,8 +402,8 @@ packages: zod: optional: true - '@ai-sdk/provider-utils@2.0.4': - resolution: {integrity: sha512-GMhcQCZbwM6RoZCri0MWeEWXRt/T+uCxsmHEsTwNvEH3GDjNzchfX25C8ftry2MeEOOn6KfqCLSKomcgK6RoOg==} + '@ai-sdk/provider-utils@2.0.5': + resolution: {integrity: sha512-2M7vLhYN0ThGjNlzow7oO/lsL+DyMxvGMIYmVQvEYaCWhDzxH5dOp78VNjJIVwHzVLMbBDigX3rJuzAs853idw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -396,8 +411,8 @@ packages: zod: optional: true - '@ai-sdk/provider-utils@2.0.5': - resolution: {integrity: sha512-2M7vLhYN0ThGjNlzow7oO/lsL+DyMxvGMIYmVQvEYaCWhDzxH5dOp78VNjJIVwHzVLMbBDigX3rJuzAs853idw==} + '@ai-sdk/provider-utils@2.1.2': + resolution: {integrity: sha512-ezpQT6kzy/2O4yyn/2YigMqynBYjZIOam3/EMNVzju+Ogj+Z+pf27c/Th78ce0A2ltgrXx6xN14sal/HHZNOOw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -421,16 +436,16 @@ packages: resolution: {integrity: sha512-mV+3iNDkzUsZ0pR2jG0sVzU6xtQY5DtSCBy3JFycLp6PwjyLw/iodfL3MwdmMCRJWgs3dadcHejRnMvF9nGTBg==} engines: {node: '>=18'} - '@ai-sdk/provider@1.0.2': - resolution: {integrity: sha512-YYtP6xWQyaAf5LiWLJ+ycGTOeBLWrED7LUrvc+SQIWhGaneylqbaGsyQL7VouQUeQ4JZ1qKYZuhmi3W56HADPA==} - engines: {node: '>=18'} - '@ai-sdk/provider@1.0.3': resolution: {integrity: sha512-WiuJEpHTrltOIzv3x2wx4gwksAHW0h6nK3SoDzjqCOJLu/2OJ1yASESTIX+f07ChFykHElVoP80Ol/fe9dw6tQ==} engines: {node: '>=18'} - '@ai-sdk/react@1.0.6': - resolution: {integrity: sha512-8Hkserq0Ge6AEi7N4hlv2FkfglAGbkoAXEZ8YSp255c3PbnZz6+/5fppw+aROmZMOfNwallSRuy1i/iPa2rBpQ==} + '@ai-sdk/provider@1.0.6': + resolution: {integrity: sha512-hwj/gFNxpDgEfTaYzCYoslmw01IY9kWLKl/wf8xuPvHtQIzlfXWmmUwc8PnCwxyt8cKzIuV0dfUghCf68HQ0SA==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.1.2': + resolution: {integrity: sha512-bBcRsDaNHzCKSIBbPngMeqbnwZ1RFadXQo9XzHoGrvLANYRwuphGNB8XTXYVLC/eXjoaGVGw2wWf/TYigEnCuA==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -441,8 +456,8 @@ packages: zod: optional: true - '@ai-sdk/ui-utils@1.0.5': - resolution: {integrity: sha512-DGJSbDf+vJyWmFNexSPUsS1AAy7gtsmFmoSyNbNbJjwl9hRIf2dknfA1V0ahx6pg3NNklNYFm53L8Nphjovfvg==} + '@ai-sdk/ui-utils@1.1.2': + resolution: {integrity: sha512-+0kfBF4Y9jmlg1KlbNKIxchmXx9PzuReSpgRNWhpU10vfl1eeer4xK/XL2qHnzAWhsMFe/SVZXJIQObk44zNEQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -2854,8 +2869,8 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@4.0.18: - resolution: {integrity: sha512-BTWzalLNE1LQphEka5xzJXDs5v4xXy1Uzr7dAVk+C/CnO3WNpuMBgrCymwUv0VrWaWc8xMQuh+OqsT7P7JyekQ==} + ai@4.1.2: + resolution: {integrity: sha512-11efhPorWFphIpeCgjW6r/jk4wB5RWUGjxayHblBXCq6YEc7o5ki7vlmSnESprsDkMEfmONBWb/xM8pWjR5O2g==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -6248,112 +6263,125 @@ packages: youch@3.3.4: resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==} - zod-to-json-schema@3.23.5: - resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} + zod-to-json-schema@3.24.1: + resolution: {integrity: sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==} peerDependencies: - zod: ^3.23.3 + zod: ^3.24.1 - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - '@ai-sdk/amazon-bedrock@1.0.6(zod@3.23.8)': + '@ai-sdk/amazon-bedrock@1.0.6(zod@3.24.1)': dependencies: '@ai-sdk/provider': 1.0.3 - '@ai-sdk/provider-utils': 2.0.5(zod@3.23.8) + '@ai-sdk/provider-utils': 2.0.5(zod@3.24.1) '@aws-sdk/client-bedrock-runtime': 3.716.0 - zod: 3.23.8 + zod: 3.24.1 transitivePeerDependencies: - aws-crt - '@ai-sdk/anthropic@0.0.39(zod@3.23.8)': + '@ai-sdk/anthropic@0.0.39(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.17 - '@ai-sdk/provider-utils': 1.0.9(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 1.0.9(zod@3.24.1) + zod: 3.24.1 - '@ai-sdk/cohere@1.0.3(zod@3.23.8)': + '@ai-sdk/cohere@1.0.3(zod@3.24.1)': dependencies: '@ai-sdk/provider': 1.0.1 - '@ai-sdk/provider-utils': 2.0.2(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 2.0.2(zod@3.24.1) + zod: 3.24.1 - '@ai-sdk/google@0.0.52(zod@3.23.8)': + '@ai-sdk/deepseek@0.1.3(zod@3.24.1)': + dependencies: + '@ai-sdk/openai-compatible': 0.1.3(zod@3.24.1) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + zod: 3.24.1 + + '@ai-sdk/google@0.0.52(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.20(zod@3.24.1) json-schema: 0.4.0 - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/mistral@0.0.43(zod@3.23.8)': + '@ai-sdk/mistral@0.0.43(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 1.0.20(zod@3.24.1) + zod: 3.24.1 - '@ai-sdk/openai@0.0.66(zod@3.23.8)': + '@ai-sdk/openai-compatible@0.1.3(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + zod: 3.24.1 - '@ai-sdk/provider-utils@1.0.2(zod@3.23.8)': + '@ai-sdk/openai@1.1.2(zod@3.24.1)': + dependencies: + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + zod: 3.24.1 + + '@ai-sdk/provider-utils@1.0.2(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.12 eventsource-parser: 1.1.2 nanoid: 3.3.6 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/provider-utils@1.0.20(zod@3.23.8)': + '@ai-sdk/provider-utils@1.0.20(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.24 eventsource-parser: 1.1.2 nanoid: 3.3.6 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/provider-utils@1.0.9(zod@3.23.8)': + '@ai-sdk/provider-utils@1.0.9(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.17 eventsource-parser: 1.1.2 nanoid: 3.3.6 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/provider-utils@2.0.2(zod@3.23.8)': + '@ai-sdk/provider-utils@2.0.2(zod@3.24.1)': dependencies: '@ai-sdk/provider': 1.0.1 eventsource-parser: 3.0.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/provider-utils@2.0.4(zod@3.23.8)': - dependencies: - '@ai-sdk/provider': 1.0.2 - eventsource-parser: 3.0.0 - nanoid: 3.3.8 - secure-json-parse: 2.7.0 - optionalDependencies: - zod: 3.23.8 - - '@ai-sdk/provider-utils@2.0.5(zod@3.23.8)': + '@ai-sdk/provider-utils@2.0.5(zod@3.24.1)': dependencies: '@ai-sdk/provider': 1.0.3 eventsource-parser: 3.0.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 + + '@ai-sdk/provider-utils@2.1.2(zod@3.24.1)': + dependencies: + '@ai-sdk/provider': 1.0.6 + eventsource-parser: 3.0.0 + nanoid: 3.3.8 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.24.1 '@ai-sdk/provider@0.0.12': dependencies: @@ -6371,31 +6399,31 @@ snapshots: dependencies: json-schema: 0.4.0 - '@ai-sdk/provider@1.0.2': - dependencies: - json-schema: 0.4.0 - '@ai-sdk/provider@1.0.3': dependencies: json-schema: 0.4.0 - '@ai-sdk/react@1.0.6(react@18.3.1)(zod@3.23.8)': + '@ai-sdk/provider@1.0.6': dependencies: - '@ai-sdk/provider-utils': 2.0.4(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.5(zod@3.23.8) + json-schema: 0.4.0 + + '@ai-sdk/react@1.1.2(react@18.3.1)(zod@3.24.1)': + dependencies: + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.2(zod@3.24.1) swr: 2.2.5(react@18.3.1) throttleit: 2.1.0 optionalDependencies: react: 18.3.1 - zod: 3.23.8 + zod: 3.24.1 - '@ai-sdk/ui-utils@1.0.5(zod@3.23.8)': + '@ai-sdk/ui-utils@1.1.2(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.23.8) - zod-to-json-schema: 3.23.5(zod@3.23.8) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + zod-to-json-schema: 3.24.1(zod@3.24.1) optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 '@ampproject/remapping@2.3.0': dependencies: @@ -7060,7 +7088,7 @@ snapshots: '@cloudflare/workers-shared@0.9.0': dependencies: mime: 3.0.0 - zod: 3.23.8 + zod: 3.24.1 '@cloudflare/workers-types@4.20241127.0': {} @@ -7800,11 +7828,11 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 - '@openrouter/ai-sdk-provider@0.0.5(zod@3.23.8)': + '@openrouter/ai-sdk-provider@0.0.5(zod@3.24.1)': dependencies: '@ai-sdk/provider': 0.0.12 - '@ai-sdk/provider-utils': 1.0.2(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 1.0.2(zod@3.24.1) + zod: 3.24.1 '@opentelemetry/api@1.9.0': {} @@ -9299,18 +9327,17 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@4.0.18(react@18.3.1)(zod@3.23.8): + ai@4.1.2(react@18.3.1)(zod@3.24.1): dependencies: - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.23.8) - '@ai-sdk/react': 1.0.6(react@18.3.1)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.5(zod@3.23.8) + '@ai-sdk/provider': 1.0.6 + '@ai-sdk/provider-utils': 2.1.2(zod@3.24.1) + '@ai-sdk/react': 1.1.2(react@18.3.1)(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.2(zod@3.24.1) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 - zod-to-json-schema: 3.23.5(zod@3.23.8) optionalDependencies: react: 18.3.1 - zod: 3.23.8 + zod: 3.24.1 ajv@6.12.6: dependencies: @@ -11589,7 +11616,7 @@ snapshots: workerd: 1.20241106.1 ws: 8.18.0 youch: 3.3.4 - zod: 3.23.8 + zod: 3.24.1 transitivePeerDependencies: - bufferutil - supports-color @@ -11774,13 +11801,13 @@ snapshots: ohash@1.1.4: {} - ollama-ai-provider@0.15.2(zod@3.23.8): + ollama-ai-provider@0.15.2(zod@3.24.1): dependencies: '@ai-sdk/provider': 0.0.24 - '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.20(zod@3.24.1) partial-json: 0.1.7 optionalDependencies: - zod: 3.23.8 + zod: 3.24.1 on-finished@2.4.1: dependencies: @@ -12332,7 +12359,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - remix-utils@7.7.0(@remix-run/cloudflare@2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2))(@remix-run/node@2.15.0(typescript@5.7.2))(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/router@1.21.0)(react@18.3.1)(zod@3.23.8): + remix-utils@7.7.0(@remix-run/cloudflare@2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2))(@remix-run/node@2.15.0(typescript@5.7.2))(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/router@1.21.0)(react@18.3.1)(zod@3.24.1): dependencies: type-fest: 4.30.0 optionalDependencies: @@ -12341,7 +12368,7 @@ snapshots: '@remix-run/react': 2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) '@remix-run/router': 1.21.0 react: 18.3.1 - zod: 3.23.8 + zod: 3.24.1 require-like@0.1.2: {} @@ -13352,10 +13379,10 @@ snapshots: mustache: 4.2.0 stacktracey: 2.1.8 - zod-to-json-schema@3.23.5(zod@3.23.8): + zod-to-json-schema@3.24.1(zod@3.24.1): dependencies: - zod: 3.23.8 + zod: 3.24.1 - zod@3.23.8: {} + zod@3.24.1: {} zwitch@2.0.4: {} From 6d4196a2b4faedffd170e5c366d0206ec57073e6 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 27 Jan 2025 17:58:25 +0530 Subject: [PATCH 3/5] fix: improve push to github option (#1111) * feat: better push to githubbutton * added url update on push to github --- app/components/chat/GitCloneButton.tsx | 5 +- app/components/git/GitUrlImport.client.tsx | 2 +- app/components/workbench/Workbench.client.tsx | 48 +++++++++++++++---- app/lib/hooks/useGit.ts | 3 ++ app/lib/persistence/db.ts | 26 +++++++++- app/lib/persistence/useChatHistory.ts | 26 ++++++++-- app/lib/stores/workbench.ts | 4 +- 7 files changed, 95 insertions(+), 19 deletions(-) diff --git a/app/components/chat/GitCloneButton.tsx b/app/components/chat/GitCloneButton.tsx index 376d59d6..43008970 100644 --- a/app/components/chat/GitCloneButton.tsx +++ b/app/components/chat/GitCloneButton.tsx @@ -6,6 +6,7 @@ import { generateId } from '~/utils/fileUtils'; import { useState } from 'react'; import { toast } from 'react-toastify'; import { LoadingOverlay } from '~/components/ui/LoadingOverlay'; +import type { IChatMetadata } from '~/lib/persistence'; const IGNORE_PATTERNS = [ 'node_modules/**', @@ -35,7 +36,7 @@ const ig = ignore().add(IGNORE_PATTERNS); interface GitCloneButtonProps { className?: string; - importChat?: (description: string, messages: Message[]) => Promise; + importChat?: (description: string, messages: Message[], metadata?: IChatMetadata) => Promise; } export default function GitCloneButton({ importChat }: GitCloneButtonProps) { @@ -98,7 +99,7 @@ ${file.content} messages.push(commandsMessage); } - await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages); + await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages, { gitUrl: repoUrl }); } } catch (error) { console.error('Error during import:', error); diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx index fe8b346b..578c35f2 100644 --- a/app/components/git/GitUrlImport.client.tsx +++ b/app/components/git/GitUrlImport.client.tsx @@ -94,7 +94,7 @@ ${file.content} messages.push(commandsMessage); } - await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages); + await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages, { gitUrl: repoUrl }); } } catch (error) { console.error('Error during import:', error); diff --git a/app/components/workbench/Workbench.client.tsx b/app/components/workbench/Workbench.client.tsx index 0e34b599..be74b772 100644 --- a/app/components/workbench/Workbench.client.tsx +++ b/app/components/workbench/Workbench.client.tsx @@ -18,6 +18,7 @@ import { EditorPanel } from './EditorPanel'; import { Preview } from './Preview'; import useViewport from '~/lib/hooks'; import Cookies from 'js-cookie'; +import { chatMetadata, useChatHistory } from '~/lib/persistence'; interface WorkspaceProps { chatStarted?: boolean; @@ -66,6 +67,8 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => const unsavedFiles = useStore(workbenchStore.unsavedFiles); const files = useStore(workbenchStore.files); const selectedView = useStore(workbenchStore.currentView); + const metadata = useStore(chatMetadata); + const { updateChatMestaData } = useChatHistory(); const isSmallViewport = useViewport(1024); @@ -171,18 +174,28 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => { - const repoName = prompt( - 'Please enter a name for your new GitHub repository:', - 'bolt-generated-project', - ); + let repoName = metadata?.gitUrl?.split('/').slice(-1)[0]?.replace('.git', '') || null; + let repoConfirmed: boolean = true; + + if (repoName) { + repoConfirmed = confirm(`Do you want to push to the repository ${repoName}?`); + } + + if (!repoName || !repoConfirmed) { + repoName = prompt( + 'Please enter a name for your new GitHub repository:', + 'bolt-generated-project', + ); + } else { + } if (!repoName) { alert('Repository name is required. Push to GitHub cancelled.'); return; } - const githubUsername = Cookies.get('githubUsername'); - const githubToken = Cookies.get('githubToken'); + let githubUsername = Cookies.get('githubUsername'); + let githubToken = Cookies.get('githubToken'); if (!githubUsername || !githubToken) { const usernameInput = prompt('Please enter your GitHub username:'); @@ -193,9 +206,26 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => return; } - workbenchStore.pushToGitHub(repoName, usernameInput, tokenInput); - } else { - workbenchStore.pushToGitHub(repoName, githubUsername, githubToken); + githubUsername = usernameInput; + githubToken = tokenInput; + + Cookies.set('githubUsername', usernameInput); + Cookies.set('githubToken', tokenInput); + Cookies.set( + 'git:github.com', + JSON.stringify({ username: tokenInput, password: 'x-oauth-basic' }), + ); + } + + const commitMessage = + prompt('Please enter a commit message:', 'Initial commit') || 'Initial commit'; + workbenchStore.pushToGitHub(repoName, commitMessage, githubUsername, githubToken); + + if (!metadata?.gitUrl) { + updateChatMestaData({ + ...(metadata || {}), + gitUrl: `https://github.com/${githubUsername}/${repoName}.git`, + }); } }} > diff --git a/app/lib/hooks/useGit.ts b/app/lib/hooks/useGit.ts index 2efc6e8c..82c650c3 100644 --- a/app/lib/hooks/useGit.ts +++ b/app/lib/hooks/useGit.ts @@ -92,6 +92,7 @@ export function useGit() { }, onAuthFailure: (url, _auth) => { toast.error(`Error Authenticating with ${url.split('/')[2]}`); + throw `Error Authenticating with ${url.split('/')[2]}`; }, onAuthSuccess: (url, auth) => { saveGitAuth(url, auth); @@ -107,6 +108,8 @@ export function useGit() { return { workdir: webcontainer.workdir, data }; } catch (error) { console.error('Git clone error:', error); + + // toast.error(`Git clone error ${(error as any).message||""}`); throw error; } }, diff --git a/app/lib/persistence/db.ts b/app/lib/persistence/db.ts index 64aea1cf..2f346f60 100644 --- a/app/lib/persistence/db.ts +++ b/app/lib/persistence/db.ts @@ -2,6 +2,11 @@ import type { Message } from 'ai'; import { createScopedLogger } from '~/utils/logger'; import type { ChatHistoryItem } from './useChatHistory'; +export interface IChatMetadata { + gitUrl: string; + gitBranch?: string; +} + const logger = createScopedLogger('ChatHistory'); // this is used at the top level and never rejects @@ -53,6 +58,7 @@ export async function setMessages( urlId?: string, description?: string, timestamp?: string, + metadata?: IChatMetadata, ): Promise { return new Promise((resolve, reject) => { const transaction = db.transaction('chats', 'readwrite'); @@ -69,6 +75,7 @@ export async function setMessages( urlId, description, timestamp: timestamp ?? new Date().toISOString(), + metadata, }); request.onsuccess = () => resolve(); @@ -204,6 +211,7 @@ export async function createChatFromMessages( db: IDBDatabase, description: string, messages: Message[], + metadata?: IChatMetadata, ): Promise { const newId = await getNextId(db); const newUrlId = await getUrlId(db, newId); // Get a new urlId for the duplicated chat @@ -214,6 +222,8 @@ export async function createChatFromMessages( messages, newUrlId, // Use the new urlId description, + undefined, // Use the current timestamp + metadata, ); return newUrlId; // Return the urlId instead of id for navigation @@ -230,5 +240,19 @@ export async function updateChatDescription(db: IDBDatabase, id: string, descrip throw new Error('Description cannot be empty'); } - await setMessages(db, id, chat.messages, chat.urlId, description, chat.timestamp); + await setMessages(db, id, chat.messages, chat.urlId, description, chat.timestamp, chat.metadata); +} + +export async function updateChatMetadata( + db: IDBDatabase, + id: string, + metadata: IChatMetadata | undefined, +): Promise { + const chat = await getMessages(db, id); + + if (!chat) { + throw new Error('Chat not found'); + } + + await setMessages(db, id, chat.messages, chat.urlId, chat.description, chat.timestamp, metadata); } diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index 0a8eeb58..7baefa56 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -13,6 +13,7 @@ import { setMessages, duplicateChat, createChatFromMessages, + type IChatMetadata, } from './db'; export interface ChatHistoryItem { @@ -21,6 +22,7 @@ export interface ChatHistoryItem { description?: string; messages: Message[]; timestamp: string; + metadata?: IChatMetadata; } const persistenceEnabled = !import.meta.env.VITE_DISABLE_PERSISTENCE; @@ -29,7 +31,7 @@ export const db = persistenceEnabled ? await openDatabase() : undefined; export const chatId = atom(undefined); export const description = atom(undefined); - +export const chatMetadata = atom(undefined); export function useChatHistory() { const navigate = useNavigate(); const { id: mixedId } = useLoaderData<{ id?: string }>(); @@ -65,6 +67,7 @@ export function useChatHistory() { setUrlId(storedMessages.urlId); description.set(storedMessages.description); chatId.set(storedMessages.id); + chatMetadata.set(storedMessages.metadata); } else { navigate('/', { replace: true }); } @@ -81,6 +84,21 @@ export function useChatHistory() { return { ready: !mixedId || ready, initialMessages, + updateChatMestaData: async (metadata: IChatMetadata) => { + const id = chatId.get(); + + if (!db || !id) { + return; + } + + try { + await setMessages(db, id, initialMessages, urlId, description.get(), undefined, metadata); + chatMetadata.set(metadata); + } catch (error) { + toast.error('Failed to update chat metadata'); + console.error(error); + } + }, storeMessageHistory: async (messages: Message[]) => { if (!db || messages.length === 0) { return; @@ -109,7 +127,7 @@ export function useChatHistory() { } } - await setMessages(db, chatId.get() as string, messages, urlId, description.get()); + await setMessages(db, chatId.get() as string, messages, urlId, description.get(), undefined, chatMetadata.get()); }, duplicateCurrentChat: async (listItemId: string) => { if (!db || (!mixedId && !listItemId)) { @@ -125,13 +143,13 @@ export function useChatHistory() { console.log(error); } }, - importChat: async (description: string, messages: Message[]) => { + importChat: async (description: string, messages: Message[], metadata?: IChatMetadata) => { if (!db) { return; } try { - const newId = await createChatFromMessages(db, description, messages); + const newId = await createChatFromMessages(db, description, messages, metadata); window.location.href = `/chat/${newId}`; toast.success('Chat imported successfully'); } catch (error) { diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index 92c3508c..32d5b89f 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -434,7 +434,7 @@ export class WorkbenchStore { return syncedFiles; } - async pushToGitHub(repoName: string, githubUsername?: string, ghToken?: string) { + async pushToGitHub(repoName: string, commitMessage?: string, githubUsername?: string, ghToken?: string) { try { // Use cookies if username and token are not provided const githubToken = ghToken || Cookies.get('githubToken'); @@ -523,7 +523,7 @@ export class WorkbenchStore { const { data: newCommit } = await octokit.git.createCommit({ owner: repo.owner.login, repo: repo.name, - message: 'Initial commit from your app', + message: commitMessage || 'Initial commit from your app', tree: newTree.sha, parents: [latestCommitSha], }); From bbae032a376bd447fc686f409f0071b8e004efcc Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 27 Jan 2025 18:05:55 +0530 Subject: [PATCH 4/5] fix: git import issue when importing bolt on bolt (#1020) * fix: import bolt on bolt fix * added escape on folder import * type fix --- app/components/chat/GitCloneButton.tsx | 4 +-- app/components/git/GitUrlImport.client.tsx | 6 ++-- app/lib/runtime/message-parser.ts | 6 ++++ app/utils/folderImport.ts | 4 +-- app/utils/projectCommands.ts | 36 ++++++++++++++++++++++ 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/app/components/chat/GitCloneButton.tsx b/app/components/chat/GitCloneButton.tsx index 43008970..bc98924b 100644 --- a/app/components/chat/GitCloneButton.tsx +++ b/app/components/chat/GitCloneButton.tsx @@ -1,7 +1,7 @@ import ignore from 'ignore'; import { useGit } from '~/lib/hooks/useGit'; import type { Message } from 'ai'; -import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands'; +import { detectProjectCommands, createCommandsMessage, escapeBoltTags } from '~/utils/projectCommands'; import { generateId } from '~/utils/fileUtils'; import { useState } from 'react'; import { toast } from 'react-toastify'; @@ -84,7 +84,7 @@ ${fileContents .map( (file) => ` -${file.content} +${escapeBoltTags(file.content)} `, ) .join('\n')} diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx index 578c35f2..90853701 100644 --- a/app/components/git/GitUrlImport.client.tsx +++ b/app/components/git/GitUrlImport.client.tsx @@ -7,7 +7,7 @@ import { BaseChat } from '~/components/chat/BaseChat'; import { Chat } from '~/components/chat/Chat.client'; import { useGit } from '~/lib/hooks/useGit'; import { useChatHistory } from '~/lib/persistence'; -import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands'; +import { createCommandsMessage, detectProjectCommands, escapeBoltTags } from '~/utils/projectCommands'; import { LoadingOverlay } from '~/components/ui/LoadingOverlay'; import { toast } from 'react-toastify'; @@ -74,12 +74,12 @@ export function GitUrlImport() { const filesMessage: Message = { role: 'assistant', content: `Cloning the repo ${repoUrl} into ${workdir} - + ${fileContents .map( (file) => ` -${file.content} +${escapeBoltTags(file.content)} `, ) .join('\n')} diff --git a/app/lib/runtime/message-parser.ts b/app/lib/runtime/message-parser.ts index 27571794..3b41b6d6 100644 --- a/app/lib/runtime/message-parser.ts +++ b/app/lib/runtime/message-parser.ts @@ -64,6 +64,10 @@ function cleanoutMarkdownSyntax(content: string) { return content; } } + +function cleanEscapedTags(content: string) { + return content.replace(/</g, '<').replace(/>/g, '>'); +} export class StreamingMessageParser { #messages = new Map(); @@ -110,6 +114,7 @@ export class StreamingMessageParser { // Remove markdown code block syntax if present and file is not markdown if (!currentAction.filePath.endsWith('.md')) { content = cleanoutMarkdownSyntax(content); + content = cleanEscapedTags(content); } content += '\n'; @@ -141,6 +146,7 @@ export class StreamingMessageParser { if (!currentAction.filePath.endsWith('.md')) { content = cleanoutMarkdownSyntax(content); + content = cleanEscapedTags(content); } this._options.callbacks?.onActionStream?.({ diff --git a/app/utils/folderImport.ts b/app/utils/folderImport.ts index 759df100..98bbe316 100644 --- a/app/utils/folderImport.ts +++ b/app/utils/folderImport.ts @@ -1,6 +1,6 @@ import type { Message } from 'ai'; import { generateId } from './fileUtils'; -import { detectProjectCommands, createCommandsMessage } from './projectCommands'; +import { detectProjectCommands, createCommandsMessage, escapeBoltTags } from './projectCommands'; export const createChatFromFolder = async ( files: File[], @@ -42,7 +42,7 @@ export const createChatFromFolder = async ( ${fileArtifacts .map( (file) => ` -${file.content} +${escapeBoltTags(file.content)} `, ) .join('\n\n')} diff --git a/app/utils/projectCommands.ts b/app/utils/projectCommands.ts index 050663ae..e734dffb 100644 --- a/app/utils/projectCommands.ts +++ b/app/utils/projectCommands.ts @@ -78,3 +78,39 @@ ${commands.setupCommand} createdAt: new Date(), }; } + +export function escapeBoltArtifactTags(input: string) { + // Regular expression to match boltArtifact tags and their content + const regex = /(]*>)([\s\S]*?)(<\/boltArtifact>)/g; + + return input.replace(regex, (match, openTag, content, closeTag) => { + // Escape the opening tag + const escapedOpenTag = openTag.replace(//g, '>'); + + // Escape the closing tag + const escapedCloseTag = closeTag.replace(//g, '>'); + + // Return the escaped version + return `${escapedOpenTag}${content}${escapedCloseTag}`; + }); +} + +export function escapeBoltAActionTags(input: string) { + // Regular expression to match boltArtifact tags and their content + const regex = /(]*>)([\s\S]*?)(<\/boltAction>)/g; + + return input.replace(regex, (match, openTag, content, closeTag) => { + // Escape the opening tag + const escapedOpenTag = openTag.replace(//g, '>'); + + // Escape the closing tag + const escapedCloseTag = closeTag.replace(//g, '>'); + + // Return the escaped version + return `${escapedOpenTag}${content}${escapedCloseTag}`; + }); +} + +export function escapeBoltTags(input: string) { + return escapeBoltArtifactTags(escapeBoltAActionTags(input)); +} From 68bbbd0a67486966d3b8b9a092ee25f3538b5adb Mon Sep 17 00:00:00 2001 From: Mohammad Saif Khan <63262604+saif78642@users.noreply.github.com> Date: Mon, 27 Jan 2025 18:08:46 +0530 Subject: [PATCH 5/5] feat: add deepseek-r1-distill-llama-70b to groq provider (#1187) This PR introduces a new model, deepseek-r1-distill-llama-70b, to the staticModels array and ensures compatibility with the Groq API. The changes include: Adding the deepseek-r1-distill-llama-70b model to the staticModels array with its relevant metadata. Updating the Groq API call to use the new model for chat completions. These changes enable the application to support the deepseek-r1-distill-llama-70b model, expanding the range of available models for users. --- app/lib/modules/llm/providers/groq.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/lib/modules/llm/providers/groq.ts b/app/lib/modules/llm/providers/groq.ts index 034dab12..24d4f155 100644 --- a/app/lib/modules/llm/providers/groq.ts +++ b/app/lib/modules/llm/providers/groq.ts @@ -19,6 +19,7 @@ export default class GroqProvider extends BaseProvider { { name: 'llama-3.2-3b-preview', label: 'Llama 3.2 3b (Groq)', provider: 'Groq', maxTokenAllowed: 8000 }, { name: 'llama-3.2-1b-preview', label: 'Llama 3.2 1b (Groq)', provider: 'Groq', maxTokenAllowed: 8000 }, { name: 'llama-3.3-70b-versatile', label: 'Llama 3.3 70b (Groq)', provider: 'Groq', maxTokenAllowed: 8000 }, + { name: 'deepseek-r1-distill-llama-70b', label: 'Deepseek R1 Distill Llama 70b (Groq)', provider: 'Groq', maxTokenAllowed: 131072 }, ]; getModelInstance(options: {