From 70a51006f6d7ed48b69e50f8366b9aa3c5bd1dbc Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Tue, 8 Jul 2025 16:42:03 -0700 Subject: [PATCH] Better formatting --- apps/sim/app/api/docs/ask/route.ts | 140 +++-- .../panel/components/copilot/copilot.tsx | 519 ++++++++++-------- .../w/[workflowId]/components/panel/panel.tsx | 4 +- apps/sim/lib/documents/docs-chunker.ts | 3 +- apps/sim/scripts/process-docs-embeddings.ts | 6 +- 5 files changed, 370 insertions(+), 302 deletions(-) diff --git a/apps/sim/app/api/docs/ask/route.ts b/apps/sim/app/api/docs/ask/route.ts index 489e355a8d..7768907e5a 100644 --- a/apps/sim/app/api/docs/ask/route.ts +++ b/apps/sim/app/api/docs/ask/route.ts @@ -1,14 +1,14 @@ -import { NextRequest, NextResponse } from 'next/server' +import { sql } from 'drizzle-orm' +import { type NextRequest, NextResponse } from 'next/server' import { z } from 'zod' +import { env } from '@/lib/env' +import { createLogger } from '@/lib/logs/console-logger' +import { getRotatingApiKey } from '@/lib/utils' +import { generateEmbeddings } from '@/app/api/knowledge/utils' import { db } from '@/db' import { docsEmbeddings } from '@/db/schema' -import { generateEmbeddings } from '@/app/api/knowledge/utils' -import { createLogger } from '@/lib/logs/console-logger' -import { sql } from 'drizzle-orm' -import { env } from '@/lib/env' import { executeProviderRequest } from '@/providers' import { getProviderDefaultModel } from '@/providers/models' -import { getRotatingApiKey } from '@/lib/utils' const logger = createLogger('DocsRAG') @@ -74,10 +74,17 @@ async function searchDocs(queryEmbedding: number[], topK: number) { /** * Generate response using LLM with retrieved context */ -async function generateResponse(query: string, chunks: any[], provider?: string, model?: string, stream: boolean = false): Promise { +async function generateResponse( + query: string, + chunks: any[], + provider?: string, + model?: string, + stream = false +): Promise { // Determine which provider and model to use const selectedProvider = provider || DOCS_RAG_CONFIG.defaultProvider - const selectedModel = model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(selectedProvider) + const selectedModel = + model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(selectedProvider) // Get API key for the selected provider let apiKey: string @@ -103,11 +110,19 @@ async function generateResponse(query: string, chunks: any[], provider?: string, const context = chunks .map((chunk, index) => { // Ensure all chunk properties are strings to avoid object serialization - const headerText = typeof chunk.headerText === 'string' ? chunk.headerText : String(chunk.headerText || 'Untitled Section') - const sourceDocument = typeof chunk.sourceDocument === 'string' ? chunk.sourceDocument : String(chunk.sourceDocument || 'Unknown Document') - const sourceLink = typeof chunk.sourceLink === 'string' ? chunk.sourceLink : String(chunk.sourceLink || '#') - const chunkText = typeof chunk.chunkText === 'string' ? chunk.chunkText : String(chunk.chunkText || '') - + const headerText = + typeof chunk.headerText === 'string' + ? chunk.headerText + : String(chunk.headerText || 'Untitled Section') + const sourceDocument = + typeof chunk.sourceDocument === 'string' + ? chunk.sourceDocument + : String(chunk.sourceDocument || 'Unknown Document') + const sourceLink = + typeof chunk.sourceLink === 'string' ? chunk.sourceLink : String(chunk.sourceLink || '#') + const chunkText = + typeof chunk.chunkText === 'string' ? chunk.chunkText : String(chunk.chunkText || '') + return `[${index + 1}] ${headerText} Document: ${sourceDocument} URL: ${sourceLink} @@ -117,15 +132,20 @@ Content: ${chunkText}` const systemPrompt = `You are a helpful assistant that answers questions about Sim Studio documentation. -IMPORTANT: Use inline citations throughout your response. When referencing information from the sources, include the citation number in square brackets like [1], [2], etc. +IMPORTANT: Use inline citations strategically and sparingly. When referencing information from the sources, include the citation number in curly braces like {cite:1}, {cite:2}, etc. -Guidelines: +Citation Guidelines: +- Cite each source only ONCE at the specific header or topic that relates to that source +- Do NOT repeatedly cite the same source throughout your response +- Place citations directly after the header or concept that the source specifically addresses +- If multiple sources support the same specific topic, cite them together like {cite:1}{cite:2}{cite:3} +- Each citation should be placed at the relevant header/topic it supports, not grouped at the beginning +- Avoid cluttering the text with excessive citations + +Content Guidelines: - Answer the user's question accurately using the provided documentation -- Include inline citations [1], [2], etc. when referencing specific information -- Use multiple citations for comprehensive answers - Format your response in clean, readable markdown - Use bullet points, code blocks, and headers where appropriate -- If information spans multiple sources, cite all relevant ones - If the question cannot be answered from the context, say so clearly - Be conversational but precise - NEVER include object representations like "[object Object]" - always use proper text @@ -162,33 +182,33 @@ ${context}` if (response instanceof ReadableStream) { if (stream) { return response // Return the stream directly for streaming requests - } else { - throw new Error('Unexpected streaming response when non-streaming was requested') } + throw new Error('Unexpected streaming response when non-streaming was requested') } if ('stream' in response && 'execution' in response) { // Handle StreamingExecution for providers like Anthropic if (stream) { return response.stream // Return the stream from StreamingExecution - } else { - throw new Error('Unexpected streaming execution response when non-streaming was requested') } + throw new Error('Unexpected streaming execution response when non-streaming was requested') } // At this point, we have a ProviderResponse const content = response.content || 'Sorry, I could not generate a response.' - + // Clean up any object serialization artifacts const cleanedContent = content .replace(/\[object Object\],?/g, '') // Remove [object Object] artifacts .replace(/\s+/g, ' ') // Normalize whitespace .trim() - + return cleanedContent } catch (error) { logger.error('Failed to generate LLM response:', error) - throw new Error(`Failed to generate response using ${selectedProvider}: ${error instanceof Error ? error.message : 'Unknown error'}`) + throw new Error( + `Failed to generate response using ${selectedProvider}: ${error instanceof Error ? error.message : 'Unknown error'}` + ) } } @@ -198,14 +218,17 @@ ${context}` */ export async function POST(req: NextRequest) { const requestId = crypto.randomUUID() - + try { const body = await req.json() const { query, topK, provider, model, stream } = DocsQuerySchema.parse(body) logger.info(`[${requestId}] Docs RAG query: "${query}"`, { provider: provider || DOCS_RAG_CONFIG.defaultProvider, - model: model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), + model: + model || + DOCS_RAG_CONFIG.defaultModel || + getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), topK, }) @@ -214,10 +237,7 @@ export async function POST(req: NextRequest) { const queryEmbedding = await generateSearchEmbedding(query) if (queryEmbedding.length === 0) { - return NextResponse.json( - { error: 'Failed to generate query embedding' }, - { status: 500 } - ) + return NextResponse.json({ error: 'Failed to generate query embedding' }, { status: 500 }) } // Step 2: Search for relevant docs chunks @@ -227,14 +247,18 @@ export async function POST(req: NextRequest) { if (chunks.length === 0) { return NextResponse.json({ success: true, - response: "I couldn't find any relevant documentation for your question. Please try rephrasing your query or check if you're asking about a feature that exists in Sim Studio.", + response: + "I couldn't find any relevant documentation for your question. Please try rephrasing your query or check if you're asking about a feature that exists in Sim Studio.", sources: [], metadata: { requestId, chunksFound: 0, query, provider: provider || DOCS_RAG_CONFIG.defaultProvider, - model: model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), + model: + model || + DOCS_RAG_CONFIG.defaultModel || + getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), }, }) } @@ -254,16 +278,16 @@ export async function POST(req: NextRequest) { // Handle streaming response if (response instanceof ReadableStream) { logger.info(`[${requestId}] Returning streaming response`) - + // Create a new stream that includes metadata const encoder = new TextEncoder() const decoder = new TextDecoder() - + return new Response( new ReadableStream({ async start(controller) { const reader = response.getReader() - + // Send initial metadata const metadata = { type: 'metadata', @@ -274,27 +298,30 @@ export async function POST(req: NextRequest) { query, topSimilarity: sources[0]?.similarity, provider: provider || DOCS_RAG_CONFIG.defaultProvider, - model: model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), + model: + model || + DOCS_RAG_CONFIG.defaultModel || + getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), }, } controller.enqueue(encoder.encode(`data: ${JSON.stringify(metadata)}\n\n`)) - + try { while (true) { const { done, value } = await reader.read() if (done) break - - // Forward the chunk with content type - const chunkText = decoder.decode(value) - // Clean up any object serialization artifacts in streaming content - const cleanedChunk = chunkText.replace(/\[object Object\],?/g, '') - const contentChunk = { - type: 'content', - content: cleanedChunk, - } - controller.enqueue(encoder.encode(`data: ${JSON.stringify(contentChunk)}\n\n`)) + + // Forward the chunk with content type + const chunkText = decoder.decode(value) + // Clean up any object serialization artifacts in streaming content + const cleanedChunk = chunkText.replace(/\[object Object\],?/g, '') + const contentChunk = { + type: 'content', + content: cleanedChunk, + } + controller.enqueue(encoder.encode(`data: ${JSON.stringify(contentChunk)}\n\n`)) } - + // Send end marker controller.enqueue(encoder.encode(`data: {"type":"done"}\n\n`)) } catch (error) { @@ -313,7 +340,7 @@ export async function POST(req: NextRequest) { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', + Connection: 'keep-alive', }, } ) @@ -331,10 +358,12 @@ export async function POST(req: NextRequest) { query, topSimilarity: sources[0]?.similarity, provider: provider || DOCS_RAG_CONFIG.defaultProvider, - model: model || DOCS_RAG_CONFIG.defaultModel || getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), + model: + model || + DOCS_RAG_CONFIG.defaultModel || + getProviderDefaultModel(provider || DOCS_RAG_CONFIG.defaultProvider), }, }) - } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( @@ -344,9 +373,6 @@ export async function POST(req: NextRequest) { } logger.error(`[${requestId}] RAG error:`, error) - return NextResponse.json( - { error: 'Internal server error' }, - { status: 500 } - ) + return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } -} \ No newline at end of file +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/copilot.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/copilot.tsx index 9281ff34d6..d263e65526 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/copilot.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/copilot.tsx @@ -1,13 +1,12 @@ 'use client' -import { useState, useRef, useEffect, useImperativeHandle, forwardRef, useCallback, useMemo } from 'react' -import { Send, Bot, User, ExternalLink, Loader2, Expand, X } from 'lucide-react' +import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react' +import { Bot, Expand, Loader2, Send, User, X } from 'lucide-react' import { Button } from '@/components/ui/button' +import { Dialog, DialogContent } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { ScrollArea } from '@/components/ui/scroll-area' -import { Dialog, DialogContent } from '@/components/ui/dialog' import { createLogger } from '@/lib/logs/console-logger' -import ReactMarkdown from 'react-markdown' const logger = createLogger('Copilot') @@ -43,183 +42,199 @@ export const Copilot = forwardRef(({ panelWidth }, ref const inputRef = useRef(null) // Expose clear function to parent - useImperativeHandle(ref, () => ({ - clearMessages: () => { - setMessages([]) - logger.info('Copilot messages cleared') - } - }), []) + useImperativeHandle( + ref, + () => ({ + clearMessages: () => { + setMessages([]) + logger.info('Copilot messages cleared') + }, + }), + [] + ) // Auto-scroll to bottom when new messages are added useEffect(() => { if (scrollAreaRef.current) { - const scrollContainer = scrollAreaRef.current.querySelector('[data-radix-scroll-area-viewport]') + const scrollContainer = scrollAreaRef.current.querySelector( + '[data-radix-scroll-area-viewport]' + ) if (scrollContainer) { scrollContainer.scrollTop = scrollContainer.scrollHeight } } }, [messages]) + const handleSubmit = useCallback( + async (e: React.FormEvent) => { + e.preventDefault() + if (!input.trim() || isLoading) return - - const handleSubmit = useCallback(async (e: React.FormEvent) => { - e.preventDefault() - if (!input.trim() || isLoading) return - - const userMessage: Message = { - id: crypto.randomUUID(), - role: 'user', - content: input.trim(), - timestamp: new Date(), - } - - const streamingMessage: Message = { - id: crypto.randomUUID(), - role: 'assistant', - content: '', - timestamp: new Date(), - isStreaming: true, - } - - setMessages(prev => [...prev, userMessage, streamingMessage]) - const query = input.trim() - setInput('') - setIsLoading(true) - - try { - logger.info('Sending docs RAG query:', { query }) - - const response = await fetch('/api/docs/ask', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query, - topK: 5, - stream: true, - }), - }) - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${await response.text()}`) + const userMessage: Message = { + id: crypto.randomUUID(), + role: 'user', + content: input.trim(), + timestamp: new Date(), } - // Handle streaming response - if (response.headers.get('content-type')?.includes('text/event-stream')) { - const reader = response.body?.getReader() - const decoder = new TextDecoder() - let accumulatedContent = '' - let sources: any[] = [] + const streamingMessage: Message = { + id: crypto.randomUUID(), + role: 'assistant', + content: '', + timestamp: new Date(), + isStreaming: true, + } - if (!reader) { - throw new Error('Failed to get response reader') + setMessages((prev) => [...prev, userMessage, streamingMessage]) + const query = input.trim() + setInput('') + setIsLoading(true) + + try { + logger.info('Sending docs RAG query:', { query }) + + const response = await fetch('/api/docs/ask', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query, + topK: 5, + stream: true, + }), + }) + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${await response.text()}`) } - while (true) { - const { done, value } = await reader.read() - if (done) break + // Handle streaming response + if (response.headers.get('content-type')?.includes('text/event-stream')) { + const reader = response.body?.getReader() + const decoder = new TextDecoder() + let accumulatedContent = '' + let sources: any[] = [] - const chunk = decoder.decode(value, { stream: true }) - const lines = chunk.split('\n') + if (!reader) { + throw new Error('Failed to get response reader') + } - for (const line of lines) { - if (line.startsWith('data: ')) { - try { - const data = JSON.parse(line.slice(6)) - - if (data.type === 'metadata') { - sources = data.sources || [] - } else if (data.type === 'content') { - accumulatedContent += data.content - - // Update the streaming message with accumulated content - setMessages(prev => prev.map(msg => - msg.id === streamingMessage.id - ? { ...msg, content: accumulatedContent, sources } - : msg - )) - } else if (data.type === 'done') { - // Finish streaming - setMessages(prev => prev.map(msg => - msg.id === streamingMessage.id - ? { ...msg, isStreaming: false, sources } - : msg - )) - } else if (data.type === 'error') { - throw new Error(data.error || 'Streaming error') + while (true) { + const { done, value } = await reader.read() + if (done) break + + const chunk = decoder.decode(value, { stream: true }) + const lines = chunk.split('\n') + + for (const line of lines) { + if (line.startsWith('data: ')) { + try { + const data = JSON.parse(line.slice(6)) + + if (data.type === 'metadata') { + sources = data.sources || [] + } else if (data.type === 'content') { + accumulatedContent += data.content + + // Update the streaming message with accumulated content + setMessages((prev) => + prev.map((msg) => + msg.id === streamingMessage.id + ? { ...msg, content: accumulatedContent, sources } + : msg + ) + ) + } else if (data.type === 'done') { + // Finish streaming + setMessages((prev) => + prev.map((msg) => + msg.id === streamingMessage.id + ? { ...msg, isStreaming: false, sources } + : msg + ) + ) + } else if (data.type === 'error') { + throw new Error(data.error || 'Streaming error') + } + } catch (parseError) { + logger.warn('Failed to parse SSE data:', parseError) } - } catch (parseError) { - logger.warn('Failed to parse SSE data:', parseError) } } } + + logger.info('Received docs RAG response:', { + contentLength: accumulatedContent.length, + sourcesCount: sources.length, + }) + } else { + // Fallback to non-streaming response + const data = await response.json() + + const assistantMessage: Message = { + id: streamingMessage.id, + role: 'assistant', + content: data.response || 'Sorry, I could not generate a response.', + timestamp: new Date(), + sources: data.sources || [], + isStreaming: false, + } + + setMessages((prev) => prev.slice(0, -1).concat(assistantMessage)) } + } catch (error) { + logger.error('Docs RAG error:', error) - logger.info('Received docs RAG response:', { - contentLength: accumulatedContent.length, - sourcesCount: sources.length, - }) - - } else { - // Fallback to non-streaming response - const data = await response.json() - - const assistantMessage: Message = { + const errorMessage: Message = { id: streamingMessage.id, role: 'assistant', - content: data.response || 'Sorry, I could not generate a response.', + content: + 'Sorry, I encountered an error while searching the documentation. Please try again.', timestamp: new Date(), - sources: data.sources || [], isStreaming: false, } - setMessages(prev => prev.slice(0, -1).concat(assistantMessage)) + setMessages((prev) => prev.slice(0, -1).concat(errorMessage)) + } finally { + setIsLoading(false) } - - } catch (error) { - logger.error('Docs RAG error:', error) - - const errorMessage: Message = { - id: streamingMessage.id, - role: 'assistant', - content: 'Sorry, I encountered an error while searching the documentation. Please try again.', - timestamp: new Date(), - isStreaming: false, - } - - setMessages(prev => prev.slice(0, -1).concat(errorMessage)) - } finally { - setIsLoading(false) - } - }, [input, isLoading]) + }, + [input, isLoading] + ) const formatTimestamp = (date: Date) => { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } - // Function to render content with inline hyperlinked citations and basic markdown + // Function to render content with inline hyperlinked citations and basic markdown const renderContentWithCitations = (content: string, sources: Message['sources'] = []) => { if (!content) return content let processedContent = content - // Replace [1], [2], etc. with hyperlinked citations - processedContent = processedContent.replace(/\[(\d+)\]/g, (match, num) => { - const sourceIndex = parseInt(num) - 1 + // Replace {cite:1}, {cite:2}, etc. with clickable citation icons + processedContent = processedContent.replace(/\{cite:(\d+)\}/g, (match, num) => { + const sourceIndex = Number.parseInt(num) - 1 const source = sources[sourceIndex] if (source) { - return `${match}` + return `↗` } return match }) - + // Basic markdown processing for better formatting processedContent = processedContent // Handle code blocks - .replace(/```(\w+)?\n([\s\S]*?)```/g, '
$2
') + .replace( + /```(\w+)?\n([\s\S]*?)```/g, + '
$2
' + ) // Handle inline code - .replace(/`([^`]+)`/g, '$1') + .replace( + /`([^`]+)`/g, + '$1' + ) // Handle bold text .replace(/\*\*(.*?)\*\*/g, '$1') // Handle italic text @@ -234,30 +249,37 @@ export const Copilot = forwardRef(({ panelWidth }, ref // Handle line breaks .replace(/\n\n/g, '

') .replace(/\n/g, '
') - + // Wrap in paragraph tags if not already wrapped - if (!processedContent.includes('

') && !processedContent.includes('

') && !processedContent.includes('

') && !processedContent.includes('

')) { + if ( + !processedContent.includes('

') && + !processedContent.includes('

') && + !processedContent.includes('

') && + !processedContent.includes('

') + ) { processedContent = `

${processedContent}

` } - + return processedContent } const renderMessage = (message: Message) => { if (message.isStreaming && !message.content) { return ( -
-
- +
+
+
-
-
- Copilot - {formatTimestamp(message.timestamp)} +
+
+ Copilot + + {formatTimestamp(message.timestamp)} +
-
- - Searching documentation... +
+ + Searching documentation...
@@ -265,205 +287,220 @@ export const Copilot = forwardRef(({ panelWidth }, ref } return ( -
-
+
+
{message.role === 'user' ? ( - + ) : ( - + )}
-
-
- +
+
+ {message.role === 'user' ? 'You' : 'Copilot'} - {formatTimestamp(message.timestamp)} + + {formatTimestamp(message.timestamp)} + {message.isStreaming && ( -
- - Responding... +
+ + Responding...
)}
- + {/* Enhanced content rendering with inline citations */} -
-
+
- + {/* Streaming cursor */} {message.isStreaming && message.content && ( - + )}
) } - return ( + return ( <> {/* Main Panel Content */} -
+
{/* Header */} -
-
-
- +
+
+
+
-

Documentation Copilot

-

Ask questions about Sim Studio

+

Documentation Copilot

+

Ask questions about Sim Studio

-
+
{/* Messages */} - + {messages.length === 0 ? ( -
- -

Welcome to Documentation Copilot

-

+

+ +

Welcome to Documentation Copilot

+

Ask me anything about Sim Studio features, workflows, tools, or how to get started.

-
-
Try asking:
-
-
"How do I create a workflow?"
-
"What tools are available?"
-
"How do I deploy my workflow?"
+
+
Try asking:
+
+
+ "How do I create a workflow?" +
+
+ "What tools are available?" +
+
+ "How do I deploy my workflow?" +
) : ( -
- {messages.map(renderMessage)} -
+
{messages.map(renderMessage)}
)} {/* Input */} -
-
+
+ setInput(e.target.value)} - placeholder="Ask about Sim Studio documentation..." + placeholder='Ask about Sim Studio documentation...' disabled={isLoading} - className="flex-1" - autoComplete="off" + className='flex-1' + autoComplete='off' />
- + {/* Fullscreen Modal */} {isFullscreen && ( - -
+ +
{/* Header */} -
-
-
- +
+
+
+
-

Documentation Copilot

-

Ask questions about Sim Studio

+

Documentation Copilot

+

+ Ask questions about Sim Studio +

-
+
{/* Messages */} - + {messages.length === 0 ? ( -
- -

Welcome to Documentation Copilot

-

- Ask me anything about Sim Studio features, workflows, tools, or how to get started. +

+ +

Welcome to Documentation Copilot

+

+ Ask me anything about Sim Studio features, workflows, tools, or how to get + started.

-
-
Try asking:
-
-
"How do I create a workflow?"
-
"What tools are available?"
-
"How do I deploy my workflow?"
+
+
Try asking:
+
+
+ "How do I create a workflow?" +
+
+ "What tools are available?" +
+
+ "How do I deploy my workflow?" +
) : ( -
- {messages.map(renderMessage)} -
+
{messages.map(renderMessage)}
)} {/* Input */} -
-
+
+ setInput(e.target.value)} - placeholder="Ask about Sim Studio documentation..." + placeholder='Ask about Sim Studio documentation...' disabled={isLoading} - className="flex-1" - autoComplete="off" + className='flex-1' + autoComplete='off' /> @@ -476,4 +513,4 @@ export const Copilot = forwardRef(({ panelWidth }, ref ) }) -Copilot.displayName = 'Copilot' \ No newline at end of file +Copilot.displayName = 'Copilot' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 17dbfee994..8398d5bb56 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useState, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { Expand, PanelRight } from 'lucide-react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { useChatStore } from '@/stores/panel/chat/store' @@ -10,8 +10,8 @@ import { useWorkflowRegistry } from '@/stores/workflows/registry/store' import { Chat } from './components/chat/chat' import { ChatModal } from './components/chat/components/chat-modal/chat-modal' import { Console } from './components/console/console' -import { Variables } from './components/variables/variables' import { Copilot } from './components/copilot/copilot' +import { Variables } from './components/variables/variables' export function Panel() { const [width, setWidth] = useState(336) // 84 * 4 = 336px (default width) diff --git a/apps/sim/lib/documents/docs-chunker.ts b/apps/sim/lib/documents/docs-chunker.ts index c4ea6cd1b4..091c70cb3f 100644 --- a/apps/sim/lib/documents/docs-chunker.ts +++ b/apps/sim/lib/documents/docs-chunker.ts @@ -29,7 +29,8 @@ export class DocsChunker { }) // Use localhost docs in development, production docs otherwise const isDev = process.env.NODE_ENV === 'development' - this.baseUrl = options.baseUrl ?? (isDev ? 'http://localhost:3001' : 'https://docs.simstudio.ai') + this.baseUrl = + options.baseUrl ?? (isDev ? 'http://localhost:3001' : 'https://docs.simstudio.ai') } /** diff --git a/apps/sim/scripts/process-docs-embeddings.ts b/apps/sim/scripts/process-docs-embeddings.ts index a38c36676c..5ff181996f 100644 --- a/apps/sim/scripts/process-docs-embeddings.ts +++ b/apps/sim/scripts/process-docs-embeddings.ts @@ -38,7 +38,11 @@ async function processDocsEmbeddings(options: ProcessingOptions = {}) { clearExisting: options.clearExisting ?? false, docsPath: options.docsPath ?? path.join(process.cwd(), '../../apps/docs/content/docs'), // Use localhost docs in development, production docs otherwise - baseUrl: options.baseUrl ?? (process.env.NODE_ENV === 'development' ? 'http://localhost:3001' : 'https://docs.simstudio.ai'), + baseUrl: + options.baseUrl ?? + (process.env.NODE_ENV === 'development' + ? 'http://localhost:3001' + : 'https://docs.simstudio.ai'), chunkSize: options.chunkSize ?? 300, // Max 300 tokens per chunk minChunkSize: options.minChunkSize ?? 100, overlap: options.overlap ?? 50,