mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
Better ui
This commit is contained in:
@@ -137,39 +137,7 @@ export async function POST(req: NextRequest) {
|
||||
logger.info(`[${requestId}] StreamingExecution detected`)
|
||||
streamToRead = (result.response as any).stream
|
||||
|
||||
// Extract citations from StreamingExecution at API level
|
||||
const execution = (result.response as any).execution
|
||||
logger.info(`[${requestId}] Extracting citations from StreamingExecution`, {
|
||||
hasExecution: !!execution,
|
||||
hasToolResults: !!execution?.toolResults,
|
||||
toolResultsLength: execution?.toolResults?.length || 0,
|
||||
})
|
||||
|
||||
if (execution?.toolResults) {
|
||||
for (const toolResult of execution.toolResults) {
|
||||
logger.info(`[${requestId}] Processing tool result for citations`, {
|
||||
hasResult: !!toolResult,
|
||||
resultKeys: toolResult && typeof toolResult === 'object' ? Object.keys(toolResult) : [],
|
||||
hasResultsArray: !!(toolResult && typeof toolResult === 'object' && toolResult.results),
|
||||
})
|
||||
|
||||
if (toolResult && typeof toolResult === 'object' && toolResult.results) {
|
||||
// Convert documentation search results to citations
|
||||
const extractedCitations = toolResult.results.map((res: any, index: number) => ({
|
||||
id: index + 1,
|
||||
title: res.title || 'Documentation',
|
||||
url: res.url || '#',
|
||||
similarity: res.similarity,
|
||||
}))
|
||||
result.citations = extractedCitations
|
||||
logger.info(
|
||||
`[${requestId}] Extracted ${extractedCitations.length} citations from tool results:`,
|
||||
extractedCitations
|
||||
)
|
||||
break // Use first set of results found
|
||||
}
|
||||
}
|
||||
}
|
||||
// No need to extract citations - LLM generates direct markdown links
|
||||
}
|
||||
|
||||
if (streamToRead) {
|
||||
@@ -187,7 +155,6 @@ export async function POST(req: NextRequest) {
|
||||
const metadata = {
|
||||
type: 'metadata',
|
||||
chatId: result.chatId,
|
||||
citations: result.citations || [],
|
||||
metadata: {
|
||||
requestId,
|
||||
message,
|
||||
@@ -245,7 +212,6 @@ export async function POST(req: NextRequest) {
|
||||
success: true,
|
||||
response: result.response,
|
||||
chatId: result.chatId,
|
||||
citations: result.citations || [],
|
||||
metadata: {
|
||||
requestId,
|
||||
message,
|
||||
|
||||
@@ -152,39 +152,17 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
|
||||
return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
|
||||
// Function to render content with inline hyperlinked citations and basic markdown
|
||||
const renderContentWithCitations = (
|
||||
content: string,
|
||||
citations: CopilotMessage['citations'] = []
|
||||
) => {
|
||||
// Function to render content with basic markdown (including direct links from LLM)
|
||||
const renderMarkdownContent = (content: string) => {
|
||||
if (!content) return content
|
||||
|
||||
let processedContent = content
|
||||
|
||||
// Replace [1], [2], [3] etc. with clickable citation icons
|
||||
processedContent = processedContent.replace(/\[(\d+)\]/g, (match, num) => {
|
||||
const citationIndex = Number.parseInt(num) - 1
|
||||
const citation = citations?.[citationIndex]
|
||||
|
||||
if (citation) {
|
||||
return `<a href="${citation.url}" target="_blank" rel="noopener noreferrer" class="inline-flex items-center ml-1 text-primary hover:text-primary/80 transition-colors text-sm" title="${citation.title}">↗</a>`
|
||||
}
|
||||
|
||||
return match
|
||||
})
|
||||
|
||||
// Also replace standalone ↗ symbols with clickable citation links
|
||||
if (citations && citations.length > 0) {
|
||||
let citationIndex = 0
|
||||
processedContent = processedContent.replace(/↗/g, () => {
|
||||
if (citationIndex < citations.length) {
|
||||
const citation = citations[citationIndex]
|
||||
citationIndex++
|
||||
return `<a href="${citation.url}" target="_blank" rel="noopener noreferrer" class="inline-flex items-center text-primary hover:text-primary/80 transition-colors text-sm" title="${citation.title}">↗</a>`
|
||||
}
|
||||
return '↗'
|
||||
})
|
||||
}
|
||||
// Process markdown links: [text](url)
|
||||
processedContent = processedContent.replace(
|
||||
/\[([^\]]+)\]\(([^\)]+)\)/g,
|
||||
'<a href="$2" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:text-blue-800 font-semibold underline transition-colors">$1</a>'
|
||||
)
|
||||
|
||||
// Basic markdown processing
|
||||
processedContent = processedContent
|
||||
@@ -244,12 +222,12 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Enhanced content rendering with inline citations */}
|
||||
{/* Enhanced content rendering with markdown links */}
|
||||
<div className='prose prose-sm dark:prose-invert max-w-none'>
|
||||
<div
|
||||
className='text-foreground text-sm leading-normal'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderContentWithCitations(message.content, message.citations),
|
||||
__html: renderMarkdownContent(message.content),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -67,12 +67,12 @@ WHEN NOT TO SEARCH:
|
||||
|
||||
CITATION FORMAT:
|
||||
When you reference information from documentation sources, use this format:
|
||||
- Use [1], [2], [3] etc. to cite sources
|
||||
- Place citations at the end of sentences that reference specific information
|
||||
- Each source should only be cited once in your response
|
||||
- Continue your full response after adding citations - don't stop mid-answer
|
||||
- Include direct links using markdown format: [link text](URL)
|
||||
- Use descriptive link text (e.g., "workflow documentation" not "here")
|
||||
- Place links naturally in context, not clustered at the end
|
||||
- Only link when it adds value - don't over-link basic concepts
|
||||
|
||||
IMPORTANT: Always provide complete, helpful responses. If you add citations, continue writing your full answer. Do not stop your response after adding a citation.`,
|
||||
IMPORTANT: Always provide complete, helpful responses. Include relevant links to help users find more detailed information.`,
|
||||
},
|
||||
rag: {
|
||||
defaultProvider: 'anthropic',
|
||||
|
||||
@@ -271,15 +271,14 @@ Content: ${result.content}`
|
||||
|
||||
const systemPrompt = `You are a helpful assistant that answers questions about Sim Studio documentation. You are having a conversation with the user, so refer to the conversation history when relevant.
|
||||
|
||||
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.
|
||||
IMPORTANT: When referencing information from sources, include direct links using markdown format: [link text](URL)
|
||||
|
||||
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
|
||||
- When mentioning specific features or concepts, link directly to the relevant documentation
|
||||
- Use the exact URLs provided in the source context
|
||||
- Make link text descriptive (e.g., "workflow documentation" not "here")
|
||||
- Place links naturally in context, not clustered at the end
|
||||
- Only link when it adds value - don't over-link basic concepts
|
||||
|
||||
Content Guidelines:
|
||||
- Answer the user's question accurately using the provided documentation
|
||||
@@ -291,7 +290,7 @@ Content Guidelines:
|
||||
- NEVER include object representations like "[object Object]" - always use proper text
|
||||
- When mentioning tool names, use their actual names from the documentation
|
||||
|
||||
The sources are numbered [1] through [${searchResults.length}] in the context below.`
|
||||
Each source in the context below includes a URL that you can reference directly.`
|
||||
|
||||
const userPrompt = `${conversationContext}Current Question: ${query}
|
||||
|
||||
@@ -692,7 +691,6 @@ export async function deleteChat(chatId: string, userId: string): Promise<boolea
|
||||
export async function sendMessage(request: SendMessageRequest): Promise<{
|
||||
response: string | ReadableStream | any
|
||||
chatId?: string
|
||||
citations?: Array<{ id: number; title: string; url: string; similarity?: number }>
|
||||
}> {
|
||||
const { message, chatId, workflowId, createNewChat, stream, userId } = request
|
||||
|
||||
@@ -718,40 +716,7 @@ export async function sendMessage(request: SendMessageRequest): Promise<{
|
||||
workflowId,
|
||||
})
|
||||
|
||||
// Extract citations from StreamingExecution if available
|
||||
let citations: Array<{ id: number; title: string; url: string; similarity?: number }> = []
|
||||
|
||||
if (typeof response === 'object' && response && 'execution' in response) {
|
||||
// This is a StreamingExecution - extract citations from tool calls
|
||||
const execution = (response as any).execution
|
||||
logger.info('Extracting citations from StreamingExecution', {
|
||||
hasExecution: !!execution,
|
||||
hasToolResults: !!execution?.toolResults,
|
||||
toolResultsLength: execution?.toolResults?.length || 0,
|
||||
})
|
||||
|
||||
if (execution?.toolResults) {
|
||||
for (const toolResult of execution.toolResults) {
|
||||
logger.info('Processing tool result for citations', {
|
||||
hasResult: !!toolResult,
|
||||
resultKeys: toolResult && typeof toolResult === 'object' ? Object.keys(toolResult) : [],
|
||||
hasResultsArray: !!(toolResult && typeof toolResult === 'object' && toolResult.results),
|
||||
})
|
||||
|
||||
if (toolResult && typeof toolResult === 'object' && toolResult.results) {
|
||||
// Convert documentation search results to citations
|
||||
citations = toolResult.results.map((result: any, index: number) => ({
|
||||
id: index + 1,
|
||||
title: result.title || 'Documentation',
|
||||
url: result.url || '#',
|
||||
similarity: result.similarity,
|
||||
}))
|
||||
logger.info(`Extracted ${citations.length} citations from tool results`)
|
||||
break // Use first set of results found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No need to extract citations - LLM generates direct markdown links
|
||||
|
||||
// For non-streaming responses, save immediately
|
||||
// For streaming responses, save will be handled by the API layer after stream completes
|
||||
@@ -768,7 +733,6 @@ export async function sendMessage(request: SendMessageRequest): Promise<{
|
||||
role: 'assistant',
|
||||
content: response,
|
||||
timestamp: new Date().toISOString(),
|
||||
citations: citations.length > 0 ? citations : undefined,
|
||||
}
|
||||
|
||||
const updatedMessages = [...conversationHistory, userMessage, assistantMessage]
|
||||
@@ -788,7 +752,6 @@ export async function sendMessage(request: SendMessageRequest): Promise<{
|
||||
return {
|
||||
response,
|
||||
chatId: currentChat?.id,
|
||||
citations,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to send message:', error)
|
||||
|
||||
@@ -363,7 +363,7 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
const decoder = new TextDecoder()
|
||||
let accumulatedContent = ''
|
||||
let newChatId: string | undefined
|
||||
let responseCitations: Array<{ id: number; title: string; url: string }> = []
|
||||
// Citations no longer needed - LLM generates direct markdown links
|
||||
let streamComplete = false
|
||||
|
||||
try {
|
||||
@@ -381,21 +381,11 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
const data = JSON.parse(line.slice(6))
|
||||
|
||||
if (data.type === 'metadata') {
|
||||
// Get chatId and citations from metadata
|
||||
// Get chatId from metadata
|
||||
if (data.chatId) {
|
||||
newChatId = data.chatId
|
||||
}
|
||||
if (data.citations) {
|
||||
responseCitations = data.citations
|
||||
}
|
||||
if (data.sources) {
|
||||
// Convert sources to citations format
|
||||
responseCitations = data.sources.map((source: any, index: number) => ({
|
||||
id: index + 1,
|
||||
title: source.title,
|
||||
url: source.link,
|
||||
}))
|
||||
}
|
||||
// Citations no longer needed - LLM generates direct markdown links
|
||||
} else if (data.type === 'content') {
|
||||
console.log('[CopilotStore] Received content chunk:', data.content)
|
||||
accumulatedContent += data.content
|
||||
@@ -411,8 +401,6 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
? {
|
||||
...msg,
|
||||
content: accumulatedContent,
|
||||
citations:
|
||||
responseCitations.length > 0 ? responseCitations : undefined,
|
||||
}
|
||||
: msg
|
||||
),
|
||||
@@ -427,8 +415,6 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
? {
|
||||
...msg,
|
||||
content: accumulatedContent,
|
||||
citations:
|
||||
responseCitations.length > 0 ? responseCitations : undefined,
|
||||
}
|
||||
: msg
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user