mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
feat(copilot): add context7 (#2779)
* Add context7 * Fix edit diff block ring color * Remove server side impl * Fix duplicated message on edit old message * Tables in markdown
This commit is contained in:
committed by
GitHub
parent
684ad5aeec
commit
d55072a45f
@@ -326,8 +326,8 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
),
|
||||
|
||||
table: ({ children }: React.TableHTMLAttributes<HTMLTableElement>) => (
|
||||
<div className='my-4 max-w-full overflow-x-auto'>
|
||||
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-sm'>
|
||||
<div className='my-3 max-w-full overflow-x-auto'>
|
||||
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-xs'>
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
@@ -346,12 +346,12 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
</tr>
|
||||
),
|
||||
th: ({ children }: React.ThHTMLAttributes<HTMLTableCellElement>) => (
|
||||
<th className='border-[var(--border-1)] border-r px-4 py-2 align-top font-base text-[var(--text-secondary)] last:border-r-0 dark:font-[470]'>
|
||||
<th className='border-[var(--border-1)] border-r px-2.5 py-1.5 align-top font-base text-[var(--text-secondary)] last:border-r-0 dark:font-[470]'>
|
||||
{children}
|
||||
</th>
|
||||
),
|
||||
td: ({ children }: React.TdHTMLAttributes<HTMLTableCellElement>) => (
|
||||
<td className='break-words border-[var(--border-1)] border-r px-4 py-2 align-top font-base text-[var(--text-primary)] last:border-r-0 dark:font-[470]'>
|
||||
<td className='break-words border-[var(--border-1)] border-r px-2.5 py-1.5 align-top font-base text-[var(--text-primary)] last:border-r-0 dark:font-[470]'>
|
||||
{children}
|
||||
</td>
|
||||
),
|
||||
|
||||
@@ -246,7 +246,7 @@ export function ThinkingBlock({
|
||||
)}
|
||||
>
|
||||
{/* Render markdown during streaming with thinking text styling */}
|
||||
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-none [&_*]:!m-0 [&_*]:!p-0 [&_*]:!mb-0 [&_*]:!mt-0 [&_p]:!m-0 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_code]:!text-[11px] [&_ul]:!pl-4 [&_ul]:!my-0 [&_ol]:!pl-4 [&_ol]:!my-0 [&_li]:!my-0 [&_li]:!py-0 [&_br]:!leading-[0.5] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)] leading-none'>
|
||||
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-[1.3] [&_p]:!m-0 [&_p]:!mb-1 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h1]:!m-0 [&_h1]:!mb-1 [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h2]:!m-0 [&_h2]:!mb-1 [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_h3]:!m-0 [&_h3]:!mb-1 [&_code]:!text-[11px] [&_ul]:!pl-5 [&_ul]:!my-1 [&_ol]:!pl-6 [&_ol]:!my-1 [&_li]:!my-0.5 [&_li]:!py-0 [&_br]:!leading-[0.5] [&_table]:!my-2 [&_th]:!px-2 [&_th]:!py-1 [&_th]:!text-[11px] [&_td]:!px-2 [&_td]:!py-1 [&_td]:!text-[11px] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)]'>
|
||||
<CopilotMarkdownRenderer content={content} />
|
||||
<span className='ml-1 inline-block h-2 w-1 animate-pulse bg-[var(--text-muted)]' />
|
||||
</div>
|
||||
@@ -286,7 +286,7 @@ export function ThinkingBlock({
|
||||
)}
|
||||
>
|
||||
{/* Use markdown renderer for completed content */}
|
||||
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-none [&_*]:!m-0 [&_*]:!p-0 [&_*]:!mb-0 [&_*]:!mt-0 [&_p]:!m-0 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_code]:!text-[11px] [&_ul]:!pl-4 [&_ul]:!my-0 [&_ol]:!pl-4 [&_ol]:!my-0 [&_li]:!my-0 [&_li]:!py-0 [&_br]:!leading-[0.5] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)] leading-none'>
|
||||
<div className='[&_*]:!text-[var(--text-muted)] [&_*]:!text-[12px] [&_*]:!leading-[1.3] [&_p]:!m-0 [&_p]:!mb-1 [&_h1]:!text-[12px] [&_h1]:!font-semibold [&_h1]:!m-0 [&_h1]:!mb-1 [&_h2]:!text-[12px] [&_h2]:!font-semibold [&_h2]:!m-0 [&_h2]:!mb-1 [&_h3]:!text-[12px] [&_h3]:!font-semibold [&_h3]:!m-0 [&_h3]:!mb-1 [&_code]:!text-[11px] [&_ul]:!pl-5 [&_ul]:!my-1 [&_ol]:!pl-6 [&_ol]:!my-1 [&_li]:!my-0.5 [&_li]:!py-0 [&_br]:!leading-[0.5] [&_table]:!my-2 [&_th]:!px-2 [&_th]:!py-1 [&_th]:!text-[11px] [&_td]:!px-2 [&_td]:!py-1 [&_td]:!text-[11px] whitespace-pre-wrap font-[470] font-season text-[12px] text-[var(--text-muted)]'>
|
||||
<CopilotMarkdownRenderer content={content} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@ export function getBlockRingStyles(options: BlockRingOptions): {
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
diffStatus === 'new' &&
|
||||
'ring-[var(--brand-tertiary)]',
|
||||
'ring-[var(--brand-tertiary-2)]',
|
||||
!isActive &&
|
||||
!isPending &&
|
||||
!isDeletedBlock &&
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { BookOpen, Loader2, MinusCircle, XCircle } from 'lucide-react'
|
||||
import {
|
||||
BaseClientTool,
|
||||
type BaseClientToolMetadata,
|
||||
ClientToolCallState,
|
||||
} from '@/lib/copilot/tools/client/base-tool'
|
||||
|
||||
export class SearchLibraryDocsClientTool extends BaseClientTool {
|
||||
static readonly id = 'search_library_docs'
|
||||
|
||||
constructor(toolCallId: string) {
|
||||
super(toolCallId, SearchLibraryDocsClientTool.id, SearchLibraryDocsClientTool.metadata)
|
||||
}
|
||||
|
||||
static readonly metadata: BaseClientToolMetadata = {
|
||||
displayNames: {
|
||||
[ClientToolCallState.generating]: { text: 'Reading docs', icon: Loader2 },
|
||||
[ClientToolCallState.pending]: { text: 'Reading docs', icon: Loader2 },
|
||||
[ClientToolCallState.executing]: { text: 'Reading docs', icon: Loader2 },
|
||||
[ClientToolCallState.success]: { text: 'Read docs', icon: BookOpen },
|
||||
[ClientToolCallState.error]: { text: 'Failed to read docs', icon: XCircle },
|
||||
[ClientToolCallState.aborted]: { text: 'Aborted reading docs', icon: XCircle },
|
||||
[ClientToolCallState.rejected]: { text: 'Skipped reading docs', icon: MinusCircle },
|
||||
},
|
||||
getDynamicText: (params, state) => {
|
||||
const libraryName = params?.library_name
|
||||
if (libraryName && typeof libraryName === 'string') {
|
||||
switch (state) {
|
||||
case ClientToolCallState.success:
|
||||
return `Read ${libraryName} docs`
|
||||
case ClientToolCallState.executing:
|
||||
case ClientToolCallState.generating:
|
||||
case ClientToolCallState.pending:
|
||||
return `Reading ${libraryName} docs`
|
||||
case ClientToolCallState.error:
|
||||
return `Failed to read ${libraryName} docs`
|
||||
case ClientToolCallState.aborted:
|
||||
return `Aborted reading ${libraryName} docs`
|
||||
case ClientToolCallState.rejected:
|
||||
return `Skipped reading ${libraryName} docs`
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
|
||||
async execute(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import { RememberDebugClientTool } from '@/lib/copilot/tools/client/other/rememb
|
||||
import { ResearchClientTool } from '@/lib/copilot/tools/client/other/research'
|
||||
import { SearchDocumentationClientTool } from '@/lib/copilot/tools/client/other/search-documentation'
|
||||
import { SearchErrorsClientTool } from '@/lib/copilot/tools/client/other/search-errors'
|
||||
import { SearchLibraryDocsClientTool } from '@/lib/copilot/tools/client/other/search-library-docs'
|
||||
import { SearchOnlineClientTool } from '@/lib/copilot/tools/client/other/search-online'
|
||||
import { SearchPatternsClientTool } from '@/lib/copilot/tools/client/other/search-patterns'
|
||||
import { SleepClientTool } from '@/lib/copilot/tools/client/other/sleep'
|
||||
@@ -116,6 +117,7 @@ const CLIENT_TOOL_INSTANTIATORS: Record<string, (id: string) => any> = {
|
||||
get_trigger_blocks: (id) => new GetTriggerBlocksClientTool(id),
|
||||
search_online: (id) => new SearchOnlineClientTool(id),
|
||||
search_documentation: (id) => new SearchDocumentationClientTool(id),
|
||||
search_library_docs: (id) => new SearchLibraryDocsClientTool(id),
|
||||
search_patterns: (id) => new SearchPatternsClientTool(id),
|
||||
search_errors: (id) => new SearchErrorsClientTool(id),
|
||||
remember_debug: (id) => new RememberDebugClientTool(id),
|
||||
@@ -174,6 +176,7 @@ export const CLASS_TOOL_METADATA: Record<string, BaseClientToolMetadata | undefi
|
||||
get_trigger_blocks: (GetTriggerBlocksClientTool as any)?.metadata,
|
||||
search_online: (SearchOnlineClientTool as any)?.metadata,
|
||||
search_documentation: (SearchDocumentationClientTool as any)?.metadata,
|
||||
search_library_docs: (SearchLibraryDocsClientTool as any)?.metadata,
|
||||
search_patterns: (SearchPatternsClientTool as any)?.metadata,
|
||||
search_errors: (SearchErrorsClientTool as any)?.metadata,
|
||||
remember_debug: (RememberDebugClientTool as any)?.metadata,
|
||||
@@ -2433,9 +2436,10 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
|
||||
// If already sending a message, queue this one instead
|
||||
if (isSendingMessage) {
|
||||
get().addToQueue(message, { fileAttachments, contexts })
|
||||
get().addToQueue(message, { fileAttachments, contexts, messageId })
|
||||
logger.info('[Copilot] Message queued (already sending)', {
|
||||
queueLength: get().messageQueue.length + 1,
|
||||
originalMessageId: messageId,
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -3161,8 +3165,12 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
// Process next message in queue if any
|
||||
const nextInQueue = get().messageQueue[0]
|
||||
if (nextInQueue) {
|
||||
// Use originalMessageId if available (from edit/resend), otherwise use queue entry id
|
||||
const messageIdToUse = nextInQueue.originalMessageId || nextInQueue.id
|
||||
logger.info('[Queue] Processing next queued message', {
|
||||
id: nextInQueue.id,
|
||||
originalMessageId: nextInQueue.originalMessageId,
|
||||
messageIdToUse,
|
||||
queueLength: get().messageQueue.length,
|
||||
})
|
||||
// Remove from queue and send
|
||||
@@ -3173,7 +3181,7 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
stream: true,
|
||||
fileAttachments: nextInQueue.fileAttachments,
|
||||
contexts: nextInQueue.contexts,
|
||||
messageId: nextInQueue.id,
|
||||
messageId: messageIdToUse,
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
@@ -3615,10 +3623,12 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
fileAttachments: options?.fileAttachments,
|
||||
contexts: options?.contexts,
|
||||
queuedAt: Date.now(),
|
||||
originalMessageId: options?.messageId,
|
||||
}
|
||||
set({ messageQueue: [...get().messageQueue, queuedMessage] })
|
||||
logger.info('[Queue] Message added to queue', {
|
||||
id: queuedMessage.id,
|
||||
originalMessageId: options?.messageId,
|
||||
queueLength: get().messageQueue.length,
|
||||
})
|
||||
},
|
||||
@@ -3659,12 +3669,15 @@ export const useCopilotStore = create<CopilotStore>()(
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
}
|
||||
|
||||
// Use originalMessageId if available (from edit/resend), otherwise use queue entry id
|
||||
const messageIdToUse = message.originalMessageId || message.id
|
||||
|
||||
// Send the message
|
||||
await get().sendMessage(message.content, {
|
||||
stream: true,
|
||||
fileAttachments: message.fileAttachments,
|
||||
contexts: message.contexts,
|
||||
messageId: message.id,
|
||||
messageId: messageIdToUse,
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ export interface QueuedMessage {
|
||||
fileAttachments?: MessageFileAttachment[]
|
||||
contexts?: ChatContext[]
|
||||
queuedAt: number
|
||||
/** Original messageId to use when processing (for edit/resend flows) */
|
||||
originalMessageId?: string
|
||||
}
|
||||
|
||||
// Contexts attached to a user message
|
||||
@@ -249,6 +251,8 @@ export interface CopilotActions {
|
||||
options?: {
|
||||
fileAttachments?: MessageFileAttachment[]
|
||||
contexts?: ChatContext[]
|
||||
/** Original messageId to preserve (for edit/resend flows) */
|
||||
messageId?: string
|
||||
}
|
||||
) => void
|
||||
removeFromQueue: (id: string) => void
|
||||
|
||||
Reference in New Issue
Block a user