Better ui

This commit is contained in:
Siddharth Ganesan
2025-07-08 20:28:32 -07:00
parent c53e950269
commit 07cd6f9e49
5 changed files with 26 additions and 133 deletions

View File

@@ -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,

View File

@@ -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>

View File

@@ -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',

View File

@@ -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)

View File

@@ -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
),