mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Feat(references) add at to reference sim resources(#3560)
* feat(chat) add at sign * Address bugbot issues * Remove extra chatcontext defs * Add table and file to schema * Add icon to chip for files --------- Co-authored-by: Theodore Li <theo@sim.ai>
This commit is contained in:
@@ -55,6 +55,8 @@ const MothershipMessageSchema = z.object({
|
||||
'knowledge',
|
||||
'templates',
|
||||
'docs',
|
||||
'table',
|
||||
'file',
|
||||
]),
|
||||
label: z.string(),
|
||||
chatId: z.string().optional(),
|
||||
@@ -64,6 +66,8 @@ const MothershipMessageSchema = z.object({
|
||||
blockIds: z.array(z.string()).optional(),
|
||||
templateId: z.string().optional(),
|
||||
executionId: z.string().optional(),
|
||||
tableId: z.string().optional(),
|
||||
fileId: z.string().optional(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
@@ -162,6 +166,17 @@ export async function POST(req: NextRequest) {
|
||||
size: f.size,
|
||||
})),
|
||||
}),
|
||||
...(contexts &&
|
||||
contexts.length > 0 && {
|
||||
contexts: contexts.map((c) => ({
|
||||
kind: c.kind,
|
||||
label: c.label,
|
||||
...(c.workflowId && { workflowId: c.workflowId }),
|
||||
...(c.knowledgeId && { knowledgeId: c.knowledgeId }),
|
||||
...(c.tableId && { tableId: c.tableId }),
|
||||
...(c.fileId && { fileId: c.fileId }),
|
||||
})),
|
||||
}),
|
||||
}
|
||||
|
||||
const [updated] = await db
|
||||
|
||||
@@ -2,3 +2,4 @@ export { MessageContent } from './message-content'
|
||||
export { MothershipView } from './mothership-view'
|
||||
export { TemplatePrompts } from './template-prompts'
|
||||
export { UserInput } from './user-input'
|
||||
export { UserMessageContent } from './user-message-content'
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
'use client'
|
||||
|
||||
import { X } from 'lucide-react'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { Database, File as FileIcon, Table as TableIcon } from '@/components/emcn/icons'
|
||||
import { WorkflowIcon } from '@/components/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import type { ChatContext } from '@/stores/panel'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
interface ContextPillsProps {
|
||||
contexts: ChatContext[]
|
||||
onRemoveContext: (context: ChatContext) => void
|
||||
}
|
||||
|
||||
function WorkflowPillIcon({ workflowId, className }: { workflowId: string; className?: string }) {
|
||||
const color = useWorkflowRegistry((state) => state.workflows[workflowId]?.color ?? '#888')
|
||||
return (
|
||||
<div
|
||||
className={cn('flex-shrink-0 rounded-[3px] border-[2px]', className)}
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderColor: `${color}60`,
|
||||
backgroundClip: 'padding-box',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function getContextIcon(ctx: ChatContext) {
|
||||
switch (ctx.kind) {
|
||||
case 'workflow':
|
||||
case 'current_workflow':
|
||||
return (
|
||||
<WorkflowPillIcon
|
||||
workflowId={ctx.workflowId}
|
||||
className='mr-[4px] h-[10px] w-[10px]'
|
||||
/>
|
||||
)
|
||||
case 'workflow_block':
|
||||
return (
|
||||
<WorkflowPillIcon
|
||||
workflowId={ctx.workflowId}
|
||||
className='mr-[4px] h-[10px] w-[10px]'
|
||||
/>
|
||||
)
|
||||
case 'knowledge':
|
||||
return <Database className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'templates':
|
||||
return <WorkflowIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'past_chat':
|
||||
return null
|
||||
case 'logs':
|
||||
return <FileIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'blocks':
|
||||
return <TableIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'table':
|
||||
return <TableIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'file':
|
||||
return <FileIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
case 'docs':
|
||||
return <FileIcon className='mr-[4px] h-[10px] w-[10px] text-[var(--text-icon)]' />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function ContextPills({ contexts, onRemoveContext }: ContextPillsProps) {
|
||||
const visibleContexts = contexts.filter((c) => c.kind !== 'current_workflow')
|
||||
|
||||
if (visibleContexts.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{visibleContexts.map((ctx, idx) => (
|
||||
<Badge
|
||||
key={`selctx-${idx}-${ctx.label}`}
|
||||
variant='outline'
|
||||
className='inline-flex items-center gap-1 rounded-[6px] px-2 py-[4.5px] text-xs leading-[12px]'
|
||||
title={ctx.label}
|
||||
>
|
||||
{getContextIcon(ctx)}
|
||||
<span className='max-w-[140px] truncate leading-[12px]'>{ctx.label}</span>
|
||||
<button
|
||||
type='button'
|
||||
onClick={() => onRemoveContext(ctx)}
|
||||
className='text-muted-foreground transition-colors hover:text-foreground'
|
||||
title='Remove context'
|
||||
aria-label='Remove context'
|
||||
>
|
||||
<X className='h-3 w-3' strokeWidth={1.75} />
|
||||
</button>
|
||||
</Badge>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { ContextPills } from './context-pills'
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,115 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import type { ChatMessageContext } from '../types'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
interface UserMessageContentProps {
|
||||
content: string
|
||||
contexts?: ChatMessageContext[]
|
||||
}
|
||||
|
||||
function escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
interface MentionRange {
|
||||
start: number
|
||||
end: number
|
||||
token: string
|
||||
context: ChatMessageContext
|
||||
}
|
||||
|
||||
function computeMentionRanges(text: string, contexts: ChatMessageContext[]): MentionRange[] {
|
||||
const ranges: MentionRange[] = []
|
||||
|
||||
for (const ctx of contexts) {
|
||||
if (!ctx.label) continue
|
||||
const token = `@${ctx.label}`
|
||||
const pattern = new RegExp(`(^|\\s)(${escapeRegex(token)})(\\s|$)`, 'g')
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(text)) !== null) {
|
||||
const leadingSpace = match[1]
|
||||
const tokenStart = match.index + leadingSpace.length
|
||||
const tokenEnd = tokenStart + token.length
|
||||
ranges.push({ start: tokenStart, end: tokenEnd, token, context: ctx })
|
||||
}
|
||||
}
|
||||
|
||||
ranges.sort((a, b) => a.start - b.start)
|
||||
return ranges
|
||||
}
|
||||
|
||||
function MentionHighlight({ context, token }: { context: ChatMessageContext; token: string }) {
|
||||
const workflowColor = useWorkflowRegistry((state) => {
|
||||
if (context.kind === 'workflow' || context.kind === 'current_workflow') {
|
||||
return state.workflows[context.workflowId || '']?.color ?? null
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
const bgColor = workflowColor
|
||||
? `${workflowColor}40`
|
||||
: 'rgba(50, 189, 126, 0.4)'
|
||||
|
||||
return (
|
||||
<span
|
||||
className='rounded-[4px] py-[1px] px-[2px]'
|
||||
style={{ backgroundColor: bgColor }}
|
||||
>
|
||||
{token}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function UserMessageContent({ content, contexts }: UserMessageContentProps) {
|
||||
if (!contexts || contexts.length === 0) {
|
||||
return (
|
||||
<p className='whitespace-pre-wrap font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'>
|
||||
{content}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const ranges = computeMentionRanges(content, contexts)
|
||||
|
||||
if (ranges.length === 0) {
|
||||
return (
|
||||
<p className='whitespace-pre-wrap font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'>
|
||||
{content}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const elements: React.ReactNode[] = []
|
||||
let lastIndex = 0
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const range = ranges[i]
|
||||
|
||||
if (range.start > lastIndex) {
|
||||
const before = content.slice(lastIndex, range.start)
|
||||
elements.push(<span key={`text-${i}-${lastIndex}`}>{before}</span>)
|
||||
}
|
||||
|
||||
elements.push(
|
||||
<MentionHighlight
|
||||
key={`mention-${i}-${range.start}`}
|
||||
context={range.context}
|
||||
token={range.token}
|
||||
/>
|
||||
)
|
||||
lastIndex = range.end
|
||||
}
|
||||
|
||||
const tail = content.slice(lastIndex)
|
||||
if (tail) {
|
||||
elements.push(<span key={`tail-${lastIndex}`}>{tail}</span>)
|
||||
}
|
||||
|
||||
return (
|
||||
<p className='whitespace-pre-wrap font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'>
|
||||
{elements}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
@@ -15,8 +15,10 @@ import {
|
||||
} from '@/lib/core/utils/browser-storage'
|
||||
import { persistImportedWorkflow } from '@/lib/workflows/operations/import-export'
|
||||
import { useChatHistory, useMarkTaskRead } from '@/hooks/queries/tasks'
|
||||
import { MessageContent, MothershipView, TemplatePrompts, UserInput } from './components'
|
||||
import { MessageContent, MothershipView, TemplatePrompts, UserInput, UserMessageContent } from './components'
|
||||
import type { FileAttachmentForApi } from './components/user-input/user-input'
|
||||
import type { ChatContext } from '@/stores/panel'
|
||||
import type { MothershipResource, MothershipResourceType } from './types'
|
||||
import { useAutoScroll, useChat } from './hooks'
|
||||
|
||||
const logger = createLogger('Home')
|
||||
@@ -231,14 +233,61 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
}, [skipResourceTransition])
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(text: string, fileAttachments?: FileAttachmentForApi[]) => {
|
||||
(text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => {
|
||||
const trimmed = text.trim()
|
||||
if (!trimmed && !(fileAttachments && fileAttachments.length > 0)) return
|
||||
sendMessage(trimmed || 'Analyze the attached file(s).', fileAttachments)
|
||||
sendMessage(trimmed || 'Analyze the attached file(s).', fileAttachments, contexts)
|
||||
},
|
||||
[sendMessage]
|
||||
)
|
||||
|
||||
const handleContextAdd = useCallback(
|
||||
(context: ChatContext) => {
|
||||
let resourceType: MothershipResourceType | null = null
|
||||
let resourceId: string | null = null
|
||||
let resourceTitle: string = context.label
|
||||
|
||||
switch (context.kind) {
|
||||
case 'workflow':
|
||||
case 'current_workflow':
|
||||
resourceType = 'workflow'
|
||||
resourceId = context.workflowId
|
||||
break
|
||||
case 'knowledge':
|
||||
if (context.knowledgeId) {
|
||||
resourceType = 'knowledgebase'
|
||||
resourceId = context.knowledgeId
|
||||
}
|
||||
break
|
||||
case 'table':
|
||||
if (context.tableId) {
|
||||
resourceType = 'table'
|
||||
resourceId = context.tableId
|
||||
}
|
||||
break
|
||||
case 'file':
|
||||
if (context.fileId) {
|
||||
resourceType = 'file'
|
||||
resourceId = context.fileId
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (resourceType && resourceId) {
|
||||
const resource: MothershipResource = {
|
||||
type: resourceType,
|
||||
id: resourceId,
|
||||
title: resourceTitle,
|
||||
}
|
||||
addResource(resource)
|
||||
handleResourceEvent()
|
||||
}
|
||||
},
|
||||
[addResource, handleResourceEvent]
|
||||
)
|
||||
|
||||
const scrollContainerRef = useAutoScroll(isSending)
|
||||
|
||||
const hasMessages = messages.length > 0
|
||||
@@ -268,6 +317,7 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
onStopGeneration={stopGeneration}
|
||||
isInitialView={false}
|
||||
userId={session?.user?.id}
|
||||
onContextAdd={handleContextAdd}
|
||||
/>
|
||||
</ChatSkeleton>
|
||||
)
|
||||
@@ -288,6 +338,7 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
isSending={isSending}
|
||||
onStopGeneration={stopGeneration}
|
||||
userId={session?.user?.id}
|
||||
onContextAdd={handleContextAdd}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -337,9 +388,7 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
</div>
|
||||
)}
|
||||
<div className='max-w-[70%] rounded-[16px] bg-[var(--surface-5)] px-3.5 py-2'>
|
||||
<p className='whitespace-pre-wrap font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'>
|
||||
{msg.content}
|
||||
</p>
|
||||
<UserMessageContent content={msg.content} contexts={msg.contexts} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -384,6 +433,7 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
onStopGeneration={stopGeneration}
|
||||
isInitialView={false}
|
||||
userId={session?.user?.id}
|
||||
onContextAdd={handleContextAdd}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
taskKeys,
|
||||
useChatHistory,
|
||||
} from '@/hooks/queries/tasks'
|
||||
import type { ChatContext } from '@/stores/panel'
|
||||
import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
|
||||
import { useExecutionStream } from '@/hooks/use-execution-stream'
|
||||
import { useExecutionStore } from '@/stores/execution/store'
|
||||
@@ -45,7 +46,7 @@ export interface UseChatReturn {
|
||||
isSending: boolean
|
||||
error: string | null
|
||||
resolvedChatId: string | undefined
|
||||
sendMessage: (message: string, fileAttachments?: FileAttachmentForApi[]) => Promise<void>
|
||||
sendMessage: (message: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => Promise<void>
|
||||
stopGeneration: () => Promise<void>
|
||||
resources: MothershipResource[]
|
||||
activeResourceId: string | null
|
||||
@@ -145,6 +146,17 @@ function mapStoredMessage(msg: TaskStoredMessage): ChatMessage {
|
||||
mapped.attachments = msg.fileAttachments.map(toDisplayAttachment)
|
||||
}
|
||||
|
||||
if (Array.isArray(msg.contexts) && msg.contexts.length > 0) {
|
||||
mapped.contexts = msg.contexts.map((c) => ({
|
||||
kind: c.kind,
|
||||
label: c.label,
|
||||
...(c.workflowId && { workflowId: c.workflowId }),
|
||||
...(c.knowledgeId && { knowledgeId: c.knowledgeId }),
|
||||
...(c.tableId && { tableId: c.tableId }),
|
||||
...(c.fileId && { fileId: c.fileId }),
|
||||
}))
|
||||
}
|
||||
|
||||
return mapped
|
||||
}
|
||||
|
||||
@@ -257,6 +269,18 @@ export function useChat(
|
||||
return [...prev, resource]
|
||||
})
|
||||
setActiveResourceId(resource.id)
|
||||
|
||||
// Persist to database if we have a chat ID
|
||||
const currentChatId = chatIdRef.current
|
||||
if (currentChatId) {
|
||||
fetch('/api/copilot/chat/resources', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ chatId: currentChatId, resource }),
|
||||
}).catch((err) => {
|
||||
logger.warn('Failed to persist resource', err)
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const removeResource = useCallback((resourceType: MothershipResourceType, resourceId: string) => {
|
||||
@@ -695,7 +719,7 @@ export function useChat(
|
||||
}, [chatHistory?.activeStreamId, processSSEStream, finalize])
|
||||
|
||||
const sendMessage = useCallback(
|
||||
async (message: string, fileAttachments?: FileAttachmentForApi[]) => {
|
||||
async (message: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => {
|
||||
if (!message.trim() || !workspaceId) return
|
||||
|
||||
if (sendingRef.current) {
|
||||
@@ -746,9 +770,24 @@ export function useChat(
|
||||
|
||||
const userAttachments = storedAttachments?.map(toDisplayAttachment)
|
||||
|
||||
const messageContexts = contexts?.map((c) => ({
|
||||
kind: c.kind,
|
||||
label: c.label,
|
||||
...('workflowId' in c && c.workflowId ? { workflowId: c.workflowId } : {}),
|
||||
...('knowledgeId' in c && c.knowledgeId ? { knowledgeId: c.knowledgeId } : {}),
|
||||
...('tableId' in c && c.tableId ? { tableId: c.tableId } : {}),
|
||||
...('fileId' in c && c.fileId ? { fileId: c.fileId } : {}),
|
||||
}))
|
||||
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{ id: userMessageId, role: 'user', content: message, attachments: userAttachments },
|
||||
{
|
||||
id: userMessageId,
|
||||
role: 'user',
|
||||
content: message,
|
||||
attachments: userAttachments,
|
||||
...(messageContexts && messageContexts.length > 0 ? { contexts: messageContexts } : {}),
|
||||
},
|
||||
{ id: assistantId, role: 'assistant', content: '', contentBlocks: [] },
|
||||
])
|
||||
|
||||
@@ -776,6 +815,7 @@ export function useChat(
|
||||
...(chatIdRef.current ? { chatId: chatIdRef.current } : {}),
|
||||
...(fileAttachments && fileAttachments.length > 0 ? { fileAttachments } : {}),
|
||||
...(resourceAttachments ? { resourceAttachments } : {}),
|
||||
...(contexts && contexts.length > 0 ? { contexts } : {}),
|
||||
userTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
}),
|
||||
signal: abortController.signal,
|
||||
|
||||
@@ -141,12 +141,22 @@ export interface ChatMessageAttachment {
|
||||
previewUrl?: string
|
||||
}
|
||||
|
||||
export interface ChatMessageContext {
|
||||
kind: string
|
||||
label: string
|
||||
workflowId?: string
|
||||
knowledgeId?: string
|
||||
tableId?: string
|
||||
fileId?: string
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
contentBlocks?: ContentBlock[]
|
||||
attachments?: ChatMessageAttachment[]
|
||||
contexts?: ChatMessageContext[]
|
||||
}
|
||||
|
||||
export const SUBAGENT_LABELS: Record<SubagentName, string> = {
|
||||
|
||||
@@ -283,7 +283,7 @@ export function useMentionMenu({
|
||||
// Add leading space only if not at start and previous char isn't whitespace
|
||||
const needsLeadingSpace = before.length > 0 && !before.endsWith(' ')
|
||||
// Always add trailing space for easy continued typing
|
||||
const insertion = `${needsLeadingSpace ? ' ' : ''}@${label} `
|
||||
const insertion = `${needsLeadingSpace ? ' ' : ''}@${label} `
|
||||
|
||||
const next = `${before}${insertion}${after}`
|
||||
onMessageChange(next)
|
||||
|
||||
@@ -148,6 +148,8 @@ type CurrentWorkflowContext = Extract<ChatContext, { kind: 'current_workflow' }>
|
||||
type BlocksContext = Extract<ChatContext, { kind: 'blocks' }>
|
||||
type WorkflowBlockContext = Extract<ChatContext, { kind: 'workflow_block' }>
|
||||
type KnowledgeContext = Extract<ChatContext, { kind: 'knowledge' }>
|
||||
type TableContext = Extract<ChatContext, { kind: 'table' }>
|
||||
type FileContext = Extract<ChatContext, { kind: 'file' }>
|
||||
type TemplatesContext = Extract<ChatContext, { kind: 'templates' }>
|
||||
type LogsContext = Extract<ChatContext, { kind: 'logs' }>
|
||||
type SlashCommandContext = Extract<ChatContext, { kind: 'slash_command' }>
|
||||
@@ -184,6 +186,14 @@ export function areContextsEqual(c: ChatContext, context: ChatContext): boolean
|
||||
const ctx = context as KnowledgeContext
|
||||
return c.knowledgeId === ctx.knowledgeId
|
||||
}
|
||||
case 'table': {
|
||||
const ctx = context as TableContext
|
||||
return c.tableId === ctx.tableId
|
||||
}
|
||||
case 'file': {
|
||||
const ctx = context as FileContext
|
||||
return c.fileId === ctx.fileId
|
||||
}
|
||||
case 'templates': {
|
||||
const ctx = context as TemplatesContext
|
||||
return c.templateId === ctx.templateId
|
||||
|
||||
@@ -35,6 +35,15 @@ export interface TaskStoredFileAttachment {
|
||||
size: number
|
||||
}
|
||||
|
||||
export interface TaskStoredMessageContext {
|
||||
kind: string
|
||||
label: string
|
||||
workflowId?: string
|
||||
knowledgeId?: string
|
||||
tableId?: string
|
||||
fileId?: string
|
||||
}
|
||||
|
||||
export interface TaskStoredMessage {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
@@ -42,6 +51,7 @@ export interface TaskStoredMessage {
|
||||
toolCalls?: TaskStoredToolCall[]
|
||||
contentBlocks?: TaskStoredContentBlock[]
|
||||
fileAttachments?: TaskStoredFileAttachment[]
|
||||
contexts?: TaskStoredMessageContext[]
|
||||
}
|
||||
|
||||
export interface TaskStoredContentBlock {
|
||||
|
||||
@@ -21,6 +21,8 @@ export type AgentContextType =
|
||||
| 'blocks'
|
||||
| 'logs'
|
||||
| 'knowledge'
|
||||
| 'table'
|
||||
| 'file'
|
||||
| 'templates'
|
||||
| 'workflow_block'
|
||||
| 'docs'
|
||||
@@ -120,6 +122,16 @@ export async function processContextsServer(
|
||||
if (ctx.kind === 'workflow_block' && ctx.workflowId && ctx.blockId) {
|
||||
return await processWorkflowBlockFromDb(ctx.workflowId, ctx.blockId, ctx.label)
|
||||
}
|
||||
if (ctx.kind === 'table' && ctx.tableId) {
|
||||
const result = await resolveTableResource(ctx.tableId)
|
||||
if (!result) return null
|
||||
return { type: 'table', tag: ctx.label ? `@${ctx.label}` : '@', content: result.content }
|
||||
}
|
||||
if (ctx.kind === 'file' && ctx.fileId && workspaceId) {
|
||||
const result = await resolveFileResource(ctx.fileId, workspaceId)
|
||||
if (!result) return null
|
||||
return { type: 'file', tag: ctx.label ? `@${ctx.label}` : '@', content: result.content }
|
||||
}
|
||||
if (ctx.kind === 'docs') {
|
||||
try {
|
||||
const { searchDocumentationServerTool } = await import(
|
||||
|
||||
@@ -101,6 +101,8 @@ export type ChatContext =
|
||||
| { kind: 'logs'; executionId?: string; label: string }
|
||||
| { kind: 'workflow_block'; workflowId: string; blockId: string; label: string }
|
||||
| { kind: 'knowledge'; knowledgeId?: string; label: string }
|
||||
| { kind: 'table'; tableId: string; label: string }
|
||||
| { kind: 'file'; fileId: string; label: string }
|
||||
| { kind: 'templates'; templateId?: string; label: string }
|
||||
| { kind: 'docs'; label: string }
|
||||
| { kind: 'slash_command'; command: string; label: string }
|
||||
|
||||
Reference in New Issue
Block a user