Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
850447a
Initial commit
Sg312 Jul 8, 2025
76c0c56
Initial lint
Sg312 Jul 8, 2025
b9fa50b
Add db migration
Sg312 Jul 8, 2025
70a5f4e
Lint fix
Sg312 Jul 8, 2025
17513d7
Initial chatbot ui
Sg312 Jul 8, 2025
70a5100
Better formatting
Sg312 Jul 8, 2025
7bc644a
Better formatting
Sg312 Jul 8, 2025
840a028
Add footer fullscreen option
Sg312 Jul 8, 2025
3af1a6e
Lint
Sg312 Jul 8, 2025
b58d877
Spacing
Sg312 Jul 8, 2025
767b63c
Fix spacing
Sg312 Jul 8, 2025
d75751b
Convo update
Sg312 Jul 9, 2025
3460a7b
Convo update
Sg312 Jul 9, 2025
537fbdb
UI fixes
Sg312 Jul 9, 2025
3c7e794
Lint
Sg312 Jul 9, 2025
caccb61
Tool call version
Sg312 Jul 9, 2025
2354909
Lint
Sg312 Jul 9, 2025
a3159bc
Fix streaming bug
Sg312 Jul 9, 2025
4fffc66
Lint
Sg312 Jul 9, 2025
ee66c15
some fixes
Sg312 Jul 9, 2025
d1fe209
Big refactor
Sg312 Jul 9, 2025
02c4112
Lint
Sg312 Jul 9, 2025
ccf5c2f
Works?
Sg312 Jul 9, 2025
776ae06
Lint
Sg312 Jul 9, 2025
4aaa68d
Better
Sg312 Jul 9, 2025
4b60bba
Lint
Sg312 Jul 9, 2025
1b3b85f
Checkpoint
Sg312 Jul 9, 2025
8828237
Lint
Sg312 Jul 9, 2025
c53e950
Remove dead code
Sg312 Jul 9, 2025
07cd6f9
Better ui
Sg312 Jul 9, 2025
82cb609
Lint
Sg312 Jul 9, 2025
c0b8e1a
Modal fixes
Sg312 Jul 9, 2025
c7b77bd
Yaml language basics
Sg312 Jul 9, 2025
6cb15a6
Lint
Sg312 Jul 9, 2025
2a0224f
Initial yaml
Sg312 Jul 9, 2025
bacb6f3
Lint
Sg312 Jul 9, 2025
684a802
Closer
Sg312 Jul 9, 2025
5dc3ba3
Lint
Sg312 Jul 9, 2025
bb9291a
It works??
Sg312 Jul 9, 2025
e37f362
Lint
Sg312 Jul 9, 2025
f173476
Remove logs
Sg312 Jul 9, 2025
cc249c2
Lint
Sg312 Jul 9, 2025
aa343fb
Checkpoint
Sg312 Jul 9, 2025
f6b25bf
Lint
Sg312 Jul 9, 2025
a588317
Get user workflow tool
Sg312 Jul 9, 2025
0b01d4b
Lint
Sg312 Jul 9, 2025
37c4f83
Read workflow checkpoint
Sg312 Jul 9, 2025
6ca8311
Lint
Sg312 Jul 9, 2025
a2827a5
Checkpoint
Sg312 Jul 9, 2025
218041d
Handle loops/parallel
Sg312 Jul 9, 2025
3c1914c
Fix loop/parallel yaml
Sg312 Jul 9, 2025
610ea0b
Yaml fixes
Sg312 Jul 9, 2025
8176b37
Lint
Sg312 Jul 9, 2025
eade867
Comment instead of ff
Sg312 Jul 9, 2025
763d0de
Lint
Sg312 Jul 9, 2025
cfc261d
Move upload button
Sg312 Jul 9, 2025
c45da7b
Lint
Sg312 Jul 9, 2025
5c487f5
Remove json export
Sg312 Jul 9, 2025
df4971a
fix(reddit): fixed reddit missing refresh token for oauth
waleedlatif1 Jul 9, 2025
ef681d8
Greptile fixes
Sg312 Jul 9, 2025
3c61bc1
lint
Sg312 Jul 9, 2025
50595c5
Merge pull request #646 from simstudioai/feat/ask-docs
icecrasher321 Jul 9, 2025
e102b6c
improve logging ui
Jul 10, 2025
f27cb18
fix lint
Jul 10, 2025
14f422e
fix frozen canvas trace span interpretation issue
Jul 10, 2025
148f0a6
fix lint
Jul 10, 2025
8aa86e0
remove duplicate info in trace span info for tool calls
Jul 10, 2025
717b4dd
revert
Jul 10, 2025
529fd44
Merge pull request #650 from simstudioai/improvement/logging-ui
icecrasher321 Jul 10, 2025
e5080fe
feat(billing): add comprehensive usage-based billing system (#625)
waleedlatif1 Jul 10, 2025
31d9e2a
feat(kb-tags-filtering): filter kb docs using pre-set tags (#648)
icecrasher321 Jul 10, 2025
209d822
fix response format json extraction issues + add warning for invalid …
Jul 10, 2025
9ede001
lint
Jul 10, 2025
6149489
Update apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components…
icecrasher321 Jul 10, 2025
30538d9
fix(docs): fixed docs script to reflect the new output format for all…
waleedlatif1 Jul 10, 2025
1f6dcd8
remove regex handling never hit
Jul 10, 2025
a0a4b21
remove useless paths
Jul 10, 2025
614d826
Merge pull request #652 from simstudioai/fix/resp-format-json-extraction
icecrasher321 Jul 10, 2025
3887733
feat(kb): added cost for kb blocks (#654)
waleedlatif1 Jul 10, 2025
e83745f
feat(models): add grok-4 (#655)
waleedlatif1 Jul 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Lint
  • Loading branch information
Sg312 committed Jul 9, 2025
commit 2354909ef9fdb0fe6cfd4bbd940dbbbcf5c00a03
98 changes: 53 additions & 45 deletions apps/sim/app/api/copilot/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { eq, and } from 'drizzle-orm'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console-logger'
import { getRotatingApiKey } from '@/lib/utils'
import { getSession } from '@/lib/auth'
import { db } from '@/db'
import { copilotChats } from '@/db/schema'
import { executeProviderRequest } from '@/providers'
import type { Message } from '@/providers/types'
import { executeTool } from '@/tools'

const logger = createLogger('CopilotChat')

Expand All @@ -34,10 +33,11 @@ const CopilotChatSchema = z.object({
async function generateChatTitle(userMessage: string): Promise<string> {
try {
const apiKey = getRotatingApiKey('anthropic')

const response = await executeProviderRequest('anthropic', {
model: 'claude-3-haiku-20240307',
systemPrompt: 'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
systemPrompt:
'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
context: `Generate a concise title for a conversation that starts with this user message: "${userMessage}"

Return only the title text, nothing else.`,
Expand All @@ -58,8 +58,6 @@ Return only the title text, nothing else.`,
}
}



/**
* Generate chat response with tool calling support
*/
Expand All @@ -84,8 +82,8 @@ function extractCitationsFromResponse(response: any): Array<{
return []
}

const docsSearchResult = response.toolResults.find((result: any) =>
result.sources && Array.isArray(result.sources)
const docsSearchResult = response.toolResults.find(
(result: any) => result.sources && Array.isArray(result.sources)
)

if (!docsSearchResult || !docsSearchResult.sources) {
Expand All @@ -109,15 +107,16 @@ async function generateChatResponse(

// Build conversation context
const messages: Message[] = []

// Add conversation history
for (const msg of conversationHistory.slice(-10)) { // Keep last 10 messages
for (const msg of conversationHistory.slice(-10)) {
// Keep last 10 messages
messages.push({
role: msg.role as 'user' | 'assistant' | 'system',
content: msg.content,
})
}

// Add current user message
messages.push({
role: 'user',
Expand Down Expand Up @@ -170,7 +169,8 @@ MAKE SURE YOU FULLY ANSWER THE USER'S QUESTION.
{
id: 'docs_search_internal',
name: 'Search Documentation',
description: 'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
description:
'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
params: {},
parameters: {
type: 'object',
Expand Down Expand Up @@ -204,43 +204,42 @@ MAKE SURE YOU FULLY ANSWER THE USER'S QUESTION.
stream: false, // Always start with non-streaming to handle tool calls
})



// If this is a streaming request and we got a regular response,
// If this is a streaming request and we got a regular response,
// we need to create a streaming response from the content
if (stream && typeof response === 'object' && 'content' in response) {
const content = response.content || 'Sorry, I could not generate a response.'

// Extract citations from the provider response for later use
const responseCitations = extractCitationsFromResponse(response)

// Create a ReadableStream that emits the content in character chunks
const streamResponse = new ReadableStream({
start(controller) {
// Use character-based streaming for more reliable transmission
const chunkSize = 8 // Stream 8 characters at a time for smooth experience
let index = 0

const pushNext = () => {
if (index < content.length) {
const chunk = content.slice(index, index + chunkSize)
controller.enqueue(new TextEncoder().encode(chunk))
index += chunkSize

// Add a small delay to simulate streaming
setTimeout(pushNext, 25)
} else {
controller.close()
}
}

pushNext()
}
},
})

// Store citations for later use in the main streaming handler

;(streamResponse as any)._citations = responseCitations

return streamResponse
}

Expand All @@ -252,7 +251,9 @@ MAKE SURE YOU FULLY ANSWER THE USER'S QUESTION.
return 'Sorry, I could not generate a response.'
} catch (error) {
logger.error('Failed to generate chat response:', error)
throw new Error(`Failed to generate response: ${error instanceof Error ? error.message : 'Unknown error'}`)
throw new Error(
`Failed to generate response: ${error instanceof Error ? error.message : 'Unknown error'}`
)
}
}

Expand All @@ -268,7 +269,7 @@ export async function POST(req: NextRequest) {
const { message, chatId, workflowId, createNewChat, stream } = CopilotChatSchema.parse(body)

const session = await getSession()

logger.info(`[${requestId}] Copilot chat message: "${message}"`, {
chatId,
workflowId,
Expand All @@ -285,12 +286,7 @@ export async function POST(req: NextRequest) {
const [existingChat] = await db
.select()
.from(copilotChats)
.where(
and(
eq(copilotChats.id, chatId),
eq(copilotChats.userId, session.user.id)
)
)
.where(and(eq(copilotChats.id, chatId), eq(copilotChats.userId, session.user.id)))
.limit(1)

if (existingChat) {
Expand Down Expand Up @@ -326,11 +322,11 @@ export async function POST(req: NextRequest) {
const encoder = new TextEncoder()
// Extract citations from the stream object if available
const citations = (response as any)._citations || []
return new Response(
new ReadableStream({
async start(controller) {
const reader = response.getReader()

return new Response(
new ReadableStream({
async start(controller) {
const reader = response.getReader()
let accumulatedResponse = ''

// Send initial metadata
Expand All @@ -352,7 +348,7 @@ export async function POST(req: NextRequest) {

const chunkText = new TextDecoder().decode(value)
accumulatedResponse += chunkText

const contentChunk = {
type: 'content',
content: chunkText,
Expand Down Expand Up @@ -430,14 +426,22 @@ export async function POST(req: NextRequest) {
}

// Extract citations from response if available
const citations = typeof response === 'object' && 'citations' in response ? response.citations :
typeof response === 'object' && 'toolResults' in response ? extractCitationsFromResponse(response) : []
const citations =
typeof response === 'object' && 'citations' in response
? response.citations
: typeof response === 'object' && 'toolResults' in response
? extractCitationsFromResponse(response)
: []

const assistantMessage = {
id: crypto.randomUUID(),
role: 'assistant',
content: typeof response === 'string' ? response :
'content' in response ? response.content : '[Error generating response]',
content:
typeof response === 'string'
? response
: 'content' in response
? response.content
: '[Error generating response]',
timestamp: new Date().toISOString(),
citations: citations.length > 0 ? citations : undefined,
}
Expand Down Expand Up @@ -466,8 +470,12 @@ export async function POST(req: NextRequest) {

return NextResponse.json({
success: true,
response: typeof response === 'string' ? response :
'content' in response ? response.content : '[Error generating response]',
response:
typeof response === 'string'
? response
: 'content' in response
? response.content
: '[Error generating response]',
chatId: currentChat?.id,
metadata: {
requestId,
Expand All @@ -485,4 +493,4 @@ export async function POST(req: NextRequest) {
logger.error(`[${requestId}] Copilot chat error:`, error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
}
18 changes: 13 additions & 5 deletions apps/sim/app/api/docs/search/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,18 @@ export async function POST(req: NextRequest) {
// Step 3: Format the response with context and sources
const context = chunks
.map((chunk, index) => {
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}
Expand Down Expand Up @@ -138,4 +146,4 @@ Content: ${chunkText}`
logger.error(`[${requestId}] Docs search error:`, error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
const decoder = new TextDecoder()
let accumulatedContent = ''
let newChatId: string | undefined
let responseCitations: Array<{id: number, title: string, url: string}> = []
let responseCitations: Array<{ id: number; title: string; url: string }> = []

while (true) {
const { done, value } = await reader.read()
Expand Down Expand Up @@ -241,7 +241,8 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
? {
...msg,
content: accumulatedContent,
citations: responseCitations.length > 0 ? responseCitations : undefined,
citations:
responseCitations.length > 0 ? responseCitations : undefined,
}
: msg
)
Expand All @@ -254,12 +255,13 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
? {
...msg,
content: accumulatedContent,
citations: responseCitations.length > 0 ? responseCitations : undefined,
citations:
responseCitations.length > 0 ? responseCitations : undefined,
}
: msg
)
)

// Update current chat state with the chatId from response
if (newChatId && !currentChat) {
// For new chats, create a temporary chat object and reload the full chat list
Expand Down
6 changes: 4 additions & 2 deletions apps/sim/tools/docs/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export const docsSearchTool: ToolConfig<DocsSearchParams, DocsSearchResponse> =
},

transformError: (error) => {
return error instanceof Error ? error.message : 'An error occurred while searching documentation'
return error instanceof Error
? error.message
: 'An error occurred while searching documentation'
},
}
}
2 changes: 1 addition & 1 deletion apps/sim/tools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console-logger'
import { useCustomToolsStore } from '@/stores/custom-tools/store'
import { useEnvironmentStore } from '@/stores/settings/environment/store'
import { tools } from './registry'
import { docsSearchTool } from './docs/search'
import { tools } from './registry'
import type { TableRow, ToolConfig, ToolResponse } from './types'

const logger = createLogger('ToolsUtils')
Expand Down