mirror of
https://github.com/run-llama/chat-ui.git
synced 2026-07-21 11:25:22 -04:00
refactor: cleaned annotations (#106)
* refactor: cleaned annotations --------- Co-authored-by: thucpn <thucsh2@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@llamaindex/chat-ui': patch
|
||||
'@llamaindex/chat-ui-docs': patch
|
||||
---
|
||||
|
||||
Cleaned up internal annotation retrieval
|
||||
@@ -449,7 +449,7 @@ const weatherAnnotation: WeatherAnnotation = {
|
||||
### Custom Widget Implementation
|
||||
|
||||
```tsx
|
||||
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
|
||||
import { useChatMessage, getAnnotationData } from '@llamaindex/chat-ui'
|
||||
|
||||
interface WeatherData {
|
||||
location: string
|
||||
@@ -459,79 +459,27 @@ interface WeatherData {
|
||||
windSpeed: number
|
||||
}
|
||||
|
||||
export function CustomWeatherWidget() {
|
||||
function WeatherWidget() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const weatherData = getCustomAnnotations<WeatherData>(
|
||||
message.annotations,
|
||||
'weather'
|
||||
)
|
||||
const weatherData = getAnnotationData<WeatherData>(message, 'weather')
|
||||
|
||||
if (!weatherData[0]) return null
|
||||
if (!weatherData?.[0]) return null
|
||||
|
||||
const data = weatherData[0]
|
||||
|
||||
return (
|
||||
<div className="my-4 rounded-lg border border-blue-200 bg-blue-50 p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-100">
|
||||
<WeatherIcon condition={data.condition} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-blue-900">{data.location}</h3>
|
||||
<div className="flex items-center gap-4 text-sm text-blue-700">
|
||||
<span className="text-2xl font-bold">{data.temperature}°C</span>
|
||||
<span>{data.condition}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 grid grid-cols-2 gap-4 text-sm text-blue-600">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>💧 Humidity:</span>
|
||||
<span className="font-medium">{data.humidity}%</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>🌬️ Wind:</span>
|
||||
<span className="font-medium">{data.windSpeed} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function WeatherIcon({ condition }: { condition: string }) {
|
||||
const iconMap: Record<string, string> = {
|
||||
sunny: '☀️',
|
||||
cloudy: '☁️',
|
||||
rainy: '🌧️',
|
||||
snowy: '❄️',
|
||||
stormy: '⛈️',
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="text-2xl">{iconMap[condition.toLowerCase()] || '🌤️'}</span>
|
||||
)
|
||||
// Render weather data...
|
||||
}
|
||||
```
|
||||
|
||||
### Using Custom Widgets
|
||||
### getAnnotationData
|
||||
|
||||
Extract annotation data by type from messages:
|
||||
|
||||
```tsx
|
||||
import { ChatMessage } from '@llamaindex/chat-ui'
|
||||
import { CustomWeatherWidget } from './custom-weather-widget'
|
||||
import { getAnnotationData } from '@llamaindex/chat-ui'
|
||||
|
||||
function MessageWithCustomWidgets({ message }) {
|
||||
return (
|
||||
<ChatMessage message={message}>
|
||||
<ChatMessage.Content>
|
||||
<ChatMessage.Content.Markdown />
|
||||
<ChatMessage.Content.Image />
|
||||
<ChatMessage.Content.Source />
|
||||
<CustomWeatherWidget /> {/* Add custom widget */}
|
||||
</ChatMessage.Content>
|
||||
</ChatMessage>
|
||||
)
|
||||
}
|
||||
// Usage
|
||||
return getAnnotationData<WeatherData>(message, 'weather')
|
||||
```
|
||||
|
||||
## Annotation Utilities
|
||||
|
||||
+69
-72
@@ -15,22 +15,20 @@ Renders rich text with LaTeX math support, syntax highlighting, and citations.
|
||||
import { Markdown } from '@llamaindex/chat-ui/widgets'
|
||||
|
||||
function RichText({ content }) {
|
||||
return (
|
||||
<Markdown>
|
||||
{content}
|
||||
</Markdown>
|
||||
)
|
||||
return <Markdown>{content}</Markdown>
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- **LaTeX Math** - Inline and block math rendering with KaTeX
|
||||
- **Code Highlighting** - Syntax highlighting with highlight.js
|
||||
- **Citations** - Clickable citation links
|
||||
- **Custom Renderers** - Extensible rendering pipeline
|
||||
|
||||
**Example Content:**
|
||||
```markdown
|
||||
|
||||
````markdown
|
||||
# Mathematical Formula
|
||||
|
||||
The quadratic formula is: $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$
|
||||
@@ -43,11 +41,13 @@ def fibonacci(n):
|
||||
return n
|
||||
return fibonacci(n-1) + fibonacci(n-2)
|
||||
```
|
||||
````
|
||||
|
||||
This algorithm has O(2^n) complexity [^1].
|
||||
|
||||
[^1]: Introduction to Algorithms, Chapter 15
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
### CodeBlock
|
||||
|
||||
@@ -66,9 +66,10 @@ function CodeDisplay({ code, language, filename }) {
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
**Props:**
|
||||
|
||||
- `code` - Source code string
|
||||
- `language` - Programming language for highlighting
|
||||
- `filename` - Optional filename display
|
||||
@@ -95,14 +96,16 @@ function InteractiveCode({ initialCode, language, onChange }) {
|
||||
```
|
||||
|
||||
**Supported Languages:**
|
||||
|
||||
- JavaScript/TypeScript
|
||||
- Python
|
||||
- Python
|
||||
- CSS/SCSS
|
||||
- HTML
|
||||
- JSON
|
||||
- Markdown
|
||||
|
||||
**Features:**
|
||||
|
||||
- **Syntax Highlighting** - Real-time highlighting
|
||||
- **Auto-completion** - Language-aware suggestions
|
||||
- **Code Folding** - Collapse code blocks
|
||||
@@ -129,6 +132,7 @@ function DocumentEdit({ content, onChange, title }) {
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- **WYSIWYG Editing** - Visual editing with markdown output
|
||||
- **Formatting Tools** - Bold, italic, lists, headers
|
||||
- **Link Support** - Insert and edit links
|
||||
@@ -149,16 +153,17 @@ const imageAnnotation = {
|
||||
type: 'IMAGE',
|
||||
data: {
|
||||
url: 'https://example.com/image.jpg',
|
||||
alt: 'Description of the image'
|
||||
}
|
||||
alt: 'Description of the image',
|
||||
},
|
||||
}
|
||||
|
||||
function ImageDisplay() {
|
||||
return <ChatImage /> // Automatically renders from context
|
||||
return <ChatImage /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- **Zoom & Pan** - Interactive image viewing
|
||||
- **Lazy Loading** - Performance optimization
|
||||
- **Alt Text** - Accessibility support
|
||||
@@ -181,18 +186,19 @@ const fileAnnotation = {
|
||||
name: 'report.pdf',
|
||||
type: 'application/pdf',
|
||||
url: '/files/report.pdf',
|
||||
size: 1024000
|
||||
}
|
||||
]
|
||||
}
|
||||
size: 1024000,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function FileDisplay() {
|
||||
return <ChatFiles /> // Automatically renders from context
|
||||
return <ChatFiles /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
**Supported File Types:**
|
||||
|
||||
- **PDF** - Inline viewer
|
||||
- **Images** - Thumbnail preview
|
||||
- **Text Files** - Content preview
|
||||
@@ -216,19 +222,20 @@ const sourceAnnotation = {
|
||||
url: '/documents/paper.pdf',
|
||||
metadata: {
|
||||
title: 'Research Paper',
|
||||
page_number: 5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
page_number: 5,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function SourceDisplay() {
|
||||
return <ChatSources /> // Automatically renders from context
|
||||
return <ChatSources /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- **Document Grouping** - Groups citations by document
|
||||
- **Page Numbers** - Shows specific page references
|
||||
- **Click to View** - Opens source documents
|
||||
@@ -249,13 +256,13 @@ const eventAnnotation = {
|
||||
type: 'function_call',
|
||||
name: 'search_documents',
|
||||
args: { query: 'machine learning' },
|
||||
result: 'Found 15 relevant documents'
|
||||
}
|
||||
]
|
||||
result: 'Found 15 relevant documents',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function EventDisplay() {
|
||||
return <ChatEvents /> // Automatically renders from context
|
||||
return <ChatEvents /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
@@ -276,13 +283,13 @@ const agentAnnotation = {
|
||||
events: [
|
||||
{ step: 'Searching', status: 'completed' },
|
||||
{ step: 'Analyzing', status: 'in_progress' },
|
||||
{ step: 'Summarizing', status: 'pending' }
|
||||
]
|
||||
}
|
||||
{ step: 'Summarizing', status: 'pending' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function AgentEventDisplay() {
|
||||
return <ChatAgentEvents /> // Automatically renders from context
|
||||
return <ChatAgentEvents /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
@@ -300,13 +307,13 @@ const suggestionsAnnotation = {
|
||||
questions: [
|
||||
'Can you explain this in more detail?',
|
||||
'What are the practical applications?',
|
||||
'How does this compare to other approaches?'
|
||||
]
|
||||
}
|
||||
'How does this compare to other approaches?',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function QuestionSuggestions() {
|
||||
return <SuggestedQuestions /> // Automatically renders from context
|
||||
return <SuggestedQuestions /> // Automatically renders from context
|
||||
}
|
||||
```
|
||||
|
||||
@@ -321,7 +328,7 @@ const starterQuestions = [
|
||||
'How can I improve my code?',
|
||||
'Explain machine learning concepts',
|
||||
'Help me debug this error',
|
||||
'What are best practices for React?'
|
||||
'What are best practices for React?',
|
||||
]
|
||||
|
||||
function ChatStarters() {
|
||||
@@ -349,7 +356,7 @@ function UploadArea({ onFilesSelected }) {
|
||||
accept={{
|
||||
'image/*': ['.png', '.jpg', '.jpeg'],
|
||||
'application/pdf': ['.pdf'],
|
||||
'text/*': ['.txt', '.md']
|
||||
'text/*': ['.txt', '.md'],
|
||||
}}
|
||||
maxSize={10 * 1024 * 1024} // 10MB
|
||||
onFiles={onFilesSelected}
|
||||
@@ -364,6 +371,7 @@ function UploadArea({ onFilesSelected }) {
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- **Drag & Drop** - Intuitive file selection
|
||||
- **File Validation** - Type and size checking
|
||||
- **Multiple Files** - Batch upload support
|
||||
@@ -444,9 +452,9 @@ function MessageWithWidgets({ message }) {
|
||||
<ChatMessage message={message}>
|
||||
<ChatMessage.Content>
|
||||
<ChatMessage.Content.Markdown />
|
||||
<ChatMessage.Content.Image /> {/* Renders ChatImage */}
|
||||
<ChatMessage.Content.Source /> {/* Renders ChatSources */}
|
||||
<ChatMessage.Content.Event /> {/* Renders ChatEvents */}
|
||||
<ChatMessage.Content.Image /> {/* Renders ChatImage */}
|
||||
<ChatMessage.Content.Source /> {/* Renders ChatSources */}
|
||||
<ChatMessage.Content.Event /> {/* Renders ChatEvents */}
|
||||
</ChatMessage.Content>
|
||||
</ChatMessage>
|
||||
)
|
||||
@@ -458,35 +466,25 @@ function MessageWithWidgets({ message }) {
|
||||
Use widgets independently for custom layouts:
|
||||
|
||||
```tsx
|
||||
import {
|
||||
Markdown,
|
||||
CodeBlock,
|
||||
import {
|
||||
Markdown,
|
||||
CodeBlock,
|
||||
ChatImage,
|
||||
SuggestedQuestions
|
||||
SuggestedQuestions,
|
||||
} from '@llamaindex/chat-ui/widgets'
|
||||
|
||||
function CustomMessageLayout({ message }) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Markdown>{message.content}</Markdown>
|
||||
|
||||
{message.code && (
|
||||
<CodeBlock
|
||||
code={message.code}
|
||||
language="python"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
{message.code && <CodeBlock code={message.code} language="python" />}
|
||||
|
||||
{message.imageUrl && (
|
||||
<ChatImage
|
||||
src={message.imageUrl}
|
||||
alt="Generated image"
|
||||
/>
|
||||
<ChatImage src={message.imageUrl} alt="Generated image" />
|
||||
)}
|
||||
|
||||
<SuggestedQuestions
|
||||
questions={message.suggestions}
|
||||
/>
|
||||
|
||||
<SuggestedQuestions questions={message.suggestions} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -497,26 +495,25 @@ function CustomMessageLayout({ message }) {
|
||||
Create custom widgets by following the pattern:
|
||||
|
||||
```tsx
|
||||
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
|
||||
import { useChatMessage, getAnnotationData } from '@llamaindex/chat-ui'
|
||||
|
||||
interface WeatherData {
|
||||
location: string
|
||||
temperature: number
|
||||
condition: string
|
||||
humidity: number
|
||||
windSpeed: number
|
||||
}
|
||||
|
||||
function CustomWeatherWidget() {
|
||||
export function CustomWeatherWidget() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const weatherData = getCustomAnnotations<WeatherData>(
|
||||
message.annotations,
|
||||
'weather'
|
||||
)
|
||||
|
||||
if (!weatherData[0]) return null
|
||||
|
||||
|
||||
const weatherData = getAnnotationData<WeatherData>(message, 'weather')
|
||||
|
||||
if (!weatherData?.[0]) return null
|
||||
|
||||
const data = weatherData[0]
|
||||
|
||||
|
||||
return (
|
||||
<div className="rounded-lg bg-blue-50 p-4">
|
||||
<h3 className="font-semibold">{data.location}</h3>
|
||||
@@ -532,4 +529,4 @@ function CustomWeatherWidget() {
|
||||
- [Annotations](./annotations.mdx) - Learn how to create and send annotation data
|
||||
- [Artifacts](./artifacts.mdx) - Implement interactive code and document artifacts
|
||||
- [Hooks](./hooks.mdx) - Understand the widget hook system
|
||||
- [Customization](./customization.mdx) - Style and customize widget appearance
|
||||
- [Customization](./customization.mdx) - Style and customize widget appearance
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
|
||||
import { useChatMessage, getAnnotationData } from '@llamaindex/chat-ui'
|
||||
|
||||
interface WeatherData {
|
||||
location: string
|
||||
@@ -13,12 +13,9 @@ interface WeatherData {
|
||||
export function CustomWeatherAnnotation() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const weatherData = getCustomAnnotations<WeatherData>(
|
||||
message.annotations,
|
||||
'weather'
|
||||
)
|
||||
const weatherData = getAnnotationData<WeatherData>(message, 'weather')
|
||||
|
||||
if (!weatherData[0]) return null
|
||||
if (!weatherData?.[0]) return null
|
||||
|
||||
const data = weatherData[0]
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useChatMessage, getCustomAnnotations } from '@llamaindex/chat-ui'
|
||||
import { useChatMessage, getAnnotationData } from '@llamaindex/chat-ui'
|
||||
|
||||
interface WeatherData {
|
||||
location: string
|
||||
@@ -13,12 +13,9 @@ interface WeatherData {
|
||||
export function CustomWeatherAnnotation() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const weatherData = getCustomAnnotations<WeatherData>(
|
||||
message.annotations,
|
||||
'weather'
|
||||
)
|
||||
const weatherData = getAnnotationData<WeatherData>(message, 'weather')
|
||||
|
||||
if (!weatherData[0]) return null
|
||||
if (!weatherData?.[0]) return null
|
||||
|
||||
const data = weatherData[0]
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
import { JSONValue, Message } from './chat.interface'
|
||||
|
||||
export enum MessageAnnotationType {
|
||||
IMAGE = 'image',
|
||||
DOCUMENT_FILE = 'document_file',
|
||||
SOURCES = 'sources',
|
||||
EVENTS = 'events',
|
||||
SUGGESTED_QUESTIONS = 'suggested_questions',
|
||||
AGENT_EVENTS = 'agent',
|
||||
ARTIFACT = 'artifact',
|
||||
}
|
||||
|
||||
export type ImageData = {
|
||||
url: string
|
||||
}
|
||||
|
||||
export type DocumentFileType = 'csv' | 'pdf' | 'txt' | 'docx'
|
||||
export const DOCUMENT_FILE_TYPES: DocumentFileType[] = [
|
||||
'csv',
|
||||
'pdf',
|
||||
'txt',
|
||||
'docx',
|
||||
]
|
||||
|
||||
export type DocumentFile = {
|
||||
id: string
|
||||
name: string // The uploaded file name in the backend
|
||||
size: number // The file size in bytes
|
||||
type: DocumentFileType
|
||||
url: string // The URL of the uploaded file in the backend
|
||||
refs?: string[] // DocumentIDs of the uploaded file in the vector index
|
||||
}
|
||||
|
||||
export type DocumentFileData = {
|
||||
files: DocumentFile[]
|
||||
}
|
||||
|
||||
export type SourceNode = {
|
||||
id: string
|
||||
metadata: Record<string, unknown>
|
||||
score?: number
|
||||
text: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type SourceData = {
|
||||
nodes: SourceNode[]
|
||||
}
|
||||
|
||||
export type EventData = {
|
||||
title: string
|
||||
}
|
||||
|
||||
export type ProgressData = {
|
||||
id: string
|
||||
total: number
|
||||
current: number
|
||||
}
|
||||
|
||||
export type AgentEventData = {
|
||||
agent: string
|
||||
text: string
|
||||
type: 'text' | 'progress'
|
||||
data?: ProgressData
|
||||
}
|
||||
|
||||
export type SuggestedQuestionsData = string[]
|
||||
|
||||
export type Artifact<T = unknown> = {
|
||||
created_at: number
|
||||
type: 'code' | 'document'
|
||||
data: T
|
||||
}
|
||||
|
||||
export type CodeArtifact = Artifact<{
|
||||
file_name: string
|
||||
code: string
|
||||
language: string
|
||||
}>
|
||||
|
||||
export type DocumentArtifact = Artifact<{
|
||||
title: string
|
||||
content: string
|
||||
type: string
|
||||
}>
|
||||
|
||||
export type AnnotationData =
|
||||
| ImageData
|
||||
| DocumentFileData
|
||||
| SourceData
|
||||
| EventData
|
||||
| AgentEventData
|
||||
| SuggestedQuestionsData
|
||||
| Artifact
|
||||
|
||||
export type MessageAnnotation = {
|
||||
type: MessageAnnotationType
|
||||
data: AnnotationData
|
||||
}
|
||||
|
||||
export type CustomAnnotation<T = unknown> = {
|
||||
type: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export type AnyAnnotation<T = unknown> = MessageAnnotation | CustomAnnotation<T>
|
||||
|
||||
/**
|
||||
* Gets custom message annotations that don't match any standard MessageAnnotationType
|
||||
* @param annotations - Array of message annotations to filter
|
||||
* @param filter - Optional custom filter function to apply after filtering out standard annotations
|
||||
* @returns Filtered array of custom message annotations
|
||||
*
|
||||
* First filters out any annotations that match MessageAnnotationType values,
|
||||
* then applies the optional custom filter if provided.
|
||||
* @deprecated Use getCustomAnnotations instead
|
||||
*/
|
||||
export function getCustomAnnotation<T = JSONValue>(
|
||||
annotations: JSONValue[] | undefined,
|
||||
filterFn?: (a: T) => boolean
|
||||
): T[] {
|
||||
if (!Array.isArray(annotations) || !annotations.length) return [] as T[]
|
||||
const customAnnotations = annotations.filter(
|
||||
a => !isSupportedAnnotation(a)
|
||||
) as T[]
|
||||
return filterFn ? customAnnotations.filter(filterFn) : customAnnotations
|
||||
}
|
||||
|
||||
function isSupportedAnnotation(a: JSONValue): boolean {
|
||||
return (
|
||||
typeof a === 'object' &&
|
||||
a !== null &&
|
||||
a !== undefined &&
|
||||
'type' in a &&
|
||||
'data' in a &&
|
||||
Object.values(MessageAnnotationType).includes(
|
||||
a.type as MessageAnnotationType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function getChatUIAnnotation<T extends AnnotationData>(
|
||||
annotations: MessageAnnotation[],
|
||||
type: string
|
||||
): T[] {
|
||||
if (!annotations?.length) return []
|
||||
return annotations
|
||||
.filter(a => a && 'type' in a && a.type.toString() === type)
|
||||
.map(a => a.data as T)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets custom annotations by type from message annotations
|
||||
* @param annotations - Array of message annotations (can be undefined)
|
||||
* @param type - The annotation type to filter by
|
||||
* @returns Array of data from custom annotations of the specified type
|
||||
*/
|
||||
export function getCustomAnnotations<T = unknown>(
|
||||
annotations: JSONValue[] | undefined,
|
||||
type: string
|
||||
): T[] {
|
||||
if (!annotations?.length) return []
|
||||
return annotations
|
||||
.filter(
|
||||
a =>
|
||||
a &&
|
||||
typeof a === 'object' &&
|
||||
a !== null &&
|
||||
'type' in a &&
|
||||
a.type === type &&
|
||||
'data' in a
|
||||
)
|
||||
.map(a => (a as CustomAnnotation<T>).data)
|
||||
}
|
||||
|
||||
export function getSourceAnnotationData(
|
||||
annotations: MessageAnnotation[]
|
||||
): SourceData[] {
|
||||
const data = getChatUIAnnotation<SourceData>(
|
||||
annotations,
|
||||
MessageAnnotationType.SOURCES
|
||||
)
|
||||
if (!data.length) return []
|
||||
return data.map(item => ({
|
||||
...item,
|
||||
nodes: item.nodes ? preprocessSourceNodes(item.nodes) : [],
|
||||
}))
|
||||
}
|
||||
|
||||
function preprocessSourceNodes(nodes: SourceNode[]): SourceNode[] {
|
||||
// Filter source nodes has lower score
|
||||
const processedNodes = nodes.map(node => {
|
||||
// remove trailing slash for node url if exists
|
||||
if (node.url) {
|
||||
node.url = node.url.replace(/\/$/, '')
|
||||
}
|
||||
return node
|
||||
})
|
||||
return processedNodes
|
||||
}
|
||||
|
||||
export type CodeArtifactError = {
|
||||
artifact: CodeArtifact
|
||||
errors: string[]
|
||||
}
|
||||
|
||||
export function extractArtifactsFromMessage(message: Message): Artifact[] {
|
||||
const artifacts = getChatUIAnnotation<Artifact>(
|
||||
message.annotations as MessageAnnotation[],
|
||||
MessageAnnotationType.ARTIFACT
|
||||
)
|
||||
return artifacts ?? []
|
||||
}
|
||||
|
||||
// extract artifacts from all messages (sort ascending by created_at)
|
||||
export function extractArtifactsFromAllMessages(messages: Message[]) {
|
||||
return messages
|
||||
.flatMap(extractArtifactsFromMessage)
|
||||
.sort((a, b) => a.created_at - b.created_at)
|
||||
}
|
||||
|
||||
// check if two artifacts are equal by comparing their type and created time
|
||||
export function isEqualArtifact(a: Artifact, b: Artifact) {
|
||||
return a.type === b.type && a.created_at === b.created_at
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Message } from '../chat.interface'
|
||||
|
||||
// TODO: add different methods to retrieve annotations from a message - e.g. by parsing its content for inline annotations
|
||||
|
||||
export type MessageAnnotation<T = unknown> = {
|
||||
type: string
|
||||
data: T
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets annotation data directly from a message by type
|
||||
* @param message - The message to extract annotations from
|
||||
* @param type - The annotation type to filter by (can be standard or custom)
|
||||
* @returns Array of data from annotations of the specified type, or null if none found
|
||||
*/
|
||||
|
||||
export function getAnnotationData<T = unknown>(
|
||||
message: Message,
|
||||
type: string
|
||||
): T[] | null {
|
||||
const annotations = message.annotations
|
||||
if (!annotations) return []
|
||||
|
||||
const matchingAnnotations = annotations
|
||||
.filter(
|
||||
a =>
|
||||
a &&
|
||||
typeof a === 'object' &&
|
||||
a !== null &&
|
||||
'type' in a &&
|
||||
a.type === type &&
|
||||
'data' in a
|
||||
)
|
||||
.map(a => (a as { data: T }).data)
|
||||
|
||||
return matchingAnnotations
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Message } from '../chat.interface'
|
||||
import { MessageAnnotationType } from './data'
|
||||
import { getAnnotationData } from './annotations'
|
||||
|
||||
// check if two artifacts are equal by comparing their type and created time
|
||||
export function isEqualArtifact(a: Artifact, b: Artifact) {
|
||||
return a.type === b.type && a.created_at === b.created_at
|
||||
}
|
||||
|
||||
// extract artifacts from all messages (sort ascending by created_at)
|
||||
export function extractArtifactsFromAllMessages(messages: Message[]) {
|
||||
return messages
|
||||
.flatMap(extractArtifactsFromMessage)
|
||||
.sort((a, b) => a.created_at - b.created_at)
|
||||
}
|
||||
|
||||
export function extractArtifactsFromMessage(message: Message): Artifact[] {
|
||||
const artifacts = getAnnotationData<Artifact>(
|
||||
message,
|
||||
MessageAnnotationType.ARTIFACT
|
||||
)
|
||||
return artifacts ?? []
|
||||
}
|
||||
|
||||
export type CodeArtifactError = {
|
||||
artifact: CodeArtifact
|
||||
errors: string[]
|
||||
}
|
||||
export type Artifact<T = unknown> = {
|
||||
created_at: number
|
||||
type: 'code' | 'document'
|
||||
data: T
|
||||
}
|
||||
export type CodeArtifact = Artifact<{
|
||||
file_name: string
|
||||
code: string
|
||||
language: string
|
||||
}>
|
||||
export type DocumentArtifact = Artifact<{
|
||||
title: string
|
||||
content: string
|
||||
type: string
|
||||
}>
|
||||
@@ -0,0 +1,9 @@
|
||||
export enum MessageAnnotationType {
|
||||
IMAGE = 'image',
|
||||
DOCUMENT_FILE = 'document_file',
|
||||
SOURCES = 'sources',
|
||||
EVENTS = 'events',
|
||||
SUGGESTED_QUESTIONS = 'suggested_questions',
|
||||
AGENT_EVENTS = 'agent',
|
||||
ARTIFACT = 'artifact',
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './annotations'
|
||||
export * from './data'
|
||||
export * from './artifacts'
|
||||
export * from './sources'
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Message } from '../chat.interface'
|
||||
import { MessageAnnotationType } from './data'
|
||||
import { getAnnotationData } from './annotations'
|
||||
|
||||
export function preprocessSourceNodes(nodes: SourceNode[]): SourceNode[] {
|
||||
// Filter source nodes has lower score
|
||||
const processedNodes = nodes.map(node => {
|
||||
// remove trailing slash for node url if exists
|
||||
if (node.url) {
|
||||
node.url = node.url.replace(/\/$/, '')
|
||||
}
|
||||
return node
|
||||
})
|
||||
return processedNodes
|
||||
}
|
||||
export function getSourceAnnotationData(message: Message): SourceData[] {
|
||||
const data = getAnnotationData<SourceData>(
|
||||
message,
|
||||
MessageAnnotationType.SOURCES
|
||||
)
|
||||
if (!data?.length) return []
|
||||
return data.map(item => ({
|
||||
...item,
|
||||
nodes: item.nodes ? preprocessSourceNodes(item.nodes) : [],
|
||||
}))
|
||||
}
|
||||
export type SourceNode = {
|
||||
id: string
|
||||
metadata: Record<string, unknown>
|
||||
score?: number
|
||||
text: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
export type SourceData = {
|
||||
nodes: SourceNode[]
|
||||
}
|
||||
@@ -7,13 +7,13 @@ import { cn } from '../../lib/utils'
|
||||
import { Badge } from '../../ui/badge'
|
||||
import { Button } from '../../ui/button'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover'
|
||||
import { useChatCanvas } from './context'
|
||||
import {
|
||||
Artifact,
|
||||
CodeArtifact,
|
||||
DocumentArtifact,
|
||||
CodeArtifact,
|
||||
isEqualArtifact,
|
||||
} from '../annotation'
|
||||
import { useChatCanvas } from './context'
|
||||
} from '../annotations/artifacts'
|
||||
|
||||
interface ChatCanvasActionsProps {
|
||||
children?: React.ReactNode
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { FileCode, FileText, LucideIcon } from 'lucide-react'
|
||||
import { cn } from '../../lib/utils'
|
||||
import { Badge } from '../../ui/badge'
|
||||
import { Button } from '../../ui/button'
|
||||
import {
|
||||
DocumentArtifact,
|
||||
CodeArtifact,
|
||||
isEqualArtifact,
|
||||
Artifact,
|
||||
} from '../annotations/artifacts'
|
||||
import { useChatCanvas } from './context'
|
||||
|
||||
const IconMap: Record<Artifact['type'], LucideIcon> = {
|
||||
code: FileCode,
|
||||
document: FileText,
|
||||
}
|
||||
|
||||
export function ArtifactCard({ data }: { data: Artifact }) {
|
||||
const {
|
||||
openArtifactInCanvas,
|
||||
getArtifactVersion,
|
||||
restoreArtifact,
|
||||
displayedArtifact,
|
||||
} = useChatCanvas()
|
||||
const { versionNumber, isLatest } = getArtifactVersion(data)
|
||||
|
||||
const Icon = IconMap[data.type]
|
||||
const title = getCardTitle(data)
|
||||
const isDisplayed =
|
||||
displayedArtifact && isEqualArtifact(data, displayedArtifact)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'border-border flex w-full max-w-72 cursor-pointer items-center justify-between gap-2 rounded-lg border-2 p-2 hover:border-blue-500',
|
||||
isDisplayed && 'border-blue-500'
|
||||
)}
|
||||
onClick={() => openArtifactInCanvas(data)}
|
||||
>
|
||||
<div className="flex flex-1 items-center gap-2">
|
||||
<Icon className="size-7 shrink-0 text-blue-500" />
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm font-semibold">Version {versionNumber}</div>
|
||||
{title && <div className="text-xs text-gray-600">{title}</div>}
|
||||
</div>
|
||||
</div>
|
||||
{isLatest ? (
|
||||
<Badge className="ml-2 bg-blue-500 hover:bg-blue-600">Latest</Badge>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 shrink-0 cursor-pointer text-xs"
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
restoreArtifact(data)
|
||||
}}
|
||||
>
|
||||
Restore
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const getCardTitle = (artifact: Artifact) => {
|
||||
if (artifact.type === 'code') {
|
||||
const { file_name } = artifact.data as CodeArtifact['data']
|
||||
return file_name
|
||||
}
|
||||
if (artifact.type === 'document') {
|
||||
const { title } = artifact.data as DocumentArtifact['data']
|
||||
return title
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { cn } from '../../../lib/utils'
|
||||
import { Button } from '../../../ui/button'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../../ui/tabs'
|
||||
import { CodeEditor, fileExtensionToEditorLang } from '../../../widgets'
|
||||
import { CodeArtifact } from '../../annotation'
|
||||
import { CodeArtifact } from '../../annotations/artifacts'
|
||||
import { ChatCanvasActions } from '../actions'
|
||||
import { useChatCanvas } from '../context'
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { FileText } from 'lucide-react'
|
||||
import { cn } from '../../../lib/utils'
|
||||
import { DocumentEditor } from '../../../widgets'
|
||||
import { DocumentArtifact } from '../../annotation'
|
||||
import { DocumentArtifact } from '../../annotations/artifacts'
|
||||
import { ChatCanvasActions } from '../actions'
|
||||
import { useChatCanvas } from '../context'
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -3,11 +3,11 @@ import { cn } from '../../lib/utils'
|
||||
import { Badge } from '../../ui/badge'
|
||||
import { Button } from '../../ui/button'
|
||||
import {
|
||||
isEqualArtifact,
|
||||
DocumentArtifact,
|
||||
Artifact,
|
||||
CodeArtifact,
|
||||
DocumentArtifact,
|
||||
isEqualArtifact,
|
||||
} from '../annotation'
|
||||
} from '../annotations/artifacts'
|
||||
import { useChatCanvas } from './context'
|
||||
|
||||
const IconMap: Record<Artifact['type'], LucideIcon> = {
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
extractArtifactsFromAllMessages,
|
||||
isEqualArtifact,
|
||||
DocumentArtifact,
|
||||
} from '../annotation'
|
||||
} from '../annotations/artifacts'
|
||||
import { Message } from '../chat.interface'
|
||||
import { useChatUI } from '../chat.context'
|
||||
|
||||
|
||||
@@ -5,20 +5,17 @@ import {
|
||||
ChatFiles,
|
||||
ChatImage,
|
||||
ChatSources,
|
||||
EventData,
|
||||
ImageData,
|
||||
DocumentFileData,
|
||||
AgentEventData,
|
||||
SuggestedQuestionsData,
|
||||
SuggestedQuestions,
|
||||
} from '../widgets/index.js' // this import needs the file extension as it's importing the widget bundle
|
||||
import {
|
||||
AgentEventData,
|
||||
DocumentFileData,
|
||||
EventData,
|
||||
extractArtifactsFromMessage,
|
||||
getChatUIAnnotation,
|
||||
getSourceAnnotationData,
|
||||
ImageData,
|
||||
MessageAnnotation,
|
||||
MessageAnnotationType,
|
||||
SuggestedQuestionsData,
|
||||
} from './annotation'
|
||||
import { MessageAnnotationType } from './annotations/data.js'
|
||||
import { getAnnotationData } from './annotations/annotations.js'
|
||||
import { getSourceAnnotationData } from './annotations/sources.js'
|
||||
import { extractArtifactsFromMessage } from './annotations/artifacts.js'
|
||||
import { useChatMessage } from './chat-message.context.js'
|
||||
import { useChatUI } from './chat.context.js'
|
||||
import { ArtifactCard } from './canvas/card.js'
|
||||
@@ -27,14 +24,10 @@ export function EventAnnotations() {
|
||||
const { message, isLast, isLoading } = useChatMessage()
|
||||
const showLoading = (isLast && isLoading) ?? false
|
||||
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
const eventData =
|
||||
annotations && annotations.length > 0
|
||||
? getChatUIAnnotation<EventData>(
|
||||
annotations,
|
||||
MessageAnnotationType.EVENTS
|
||||
)
|
||||
: null
|
||||
const eventData = getAnnotationData<EventData>(
|
||||
message,
|
||||
MessageAnnotationType.EVENTS
|
||||
)
|
||||
if (!eventData?.length) return null
|
||||
return <ChatEvents data={eventData} showLoading={showLoading} />
|
||||
}
|
||||
@@ -42,14 +35,10 @@ export function EventAnnotations() {
|
||||
export function AgentEventAnnotations() {
|
||||
const { message, isLast } = useChatMessage()
|
||||
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
const agentEventData =
|
||||
annotations && annotations.length > 0
|
||||
? getChatUIAnnotation<AgentEventData>(
|
||||
annotations,
|
||||
MessageAnnotationType.AGENT_EVENTS
|
||||
)
|
||||
: null
|
||||
const agentEventData = getAnnotationData<AgentEventData>(
|
||||
message,
|
||||
MessageAnnotationType.AGENT_EVENTS
|
||||
)
|
||||
if (!agentEventData?.length) return null
|
||||
return (
|
||||
<ChatAgentEvents
|
||||
@@ -63,11 +52,7 @@ export function AgentEventAnnotations() {
|
||||
export function ImageAnnotations() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
const imageData =
|
||||
annotations && annotations.length > 0
|
||||
? getChatUIAnnotation<ImageData>(annotations, 'image')
|
||||
: null
|
||||
const imageData = getAnnotationData<ImageData>(message, 'image')
|
||||
if (!imageData) return null
|
||||
return imageData[0] ? <ChatImage data={imageData[0]} /> : null
|
||||
}
|
||||
@@ -75,14 +60,10 @@ export function ImageAnnotations() {
|
||||
export function DocumentFileAnnotations() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
const contentFileData =
|
||||
annotations && annotations.length > 0
|
||||
? getChatUIAnnotation<DocumentFileData>(
|
||||
annotations,
|
||||
MessageAnnotationType.DOCUMENT_FILE
|
||||
)
|
||||
: null
|
||||
const contentFileData = getAnnotationData<DocumentFileData>(
|
||||
message,
|
||||
MessageAnnotationType.DOCUMENT_FILE
|
||||
)
|
||||
if (!contentFileData) return null
|
||||
return contentFileData[0] ? <ChatFiles data={contentFileData[0]} /> : null
|
||||
}
|
||||
@@ -90,8 +71,7 @@ export function DocumentFileAnnotations() {
|
||||
export function SourceAnnotations() {
|
||||
const { message } = useChatMessage()
|
||||
|
||||
const annotations = (message.annotations ?? []) as MessageAnnotation[]
|
||||
const sourceData = getSourceAnnotationData(annotations)
|
||||
const sourceData = getSourceAnnotationData(message)
|
||||
|
||||
if (!sourceData?.length) return null
|
||||
const allNodes = sourceData.flatMap(item => item.nodes)
|
||||
@@ -103,14 +83,10 @@ export function SuggestedQuestionsAnnotations() {
|
||||
const { message, isLast } = useChatMessage()
|
||||
if (!isLast || !append) return null
|
||||
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
const suggestedQuestionsData =
|
||||
annotations && annotations.length > 0
|
||||
? getChatUIAnnotation<SuggestedQuestionsData>(
|
||||
annotations,
|
||||
MessageAnnotationType.SUGGESTED_QUESTIONS
|
||||
)
|
||||
: null
|
||||
const suggestedQuestionsData = getAnnotationData<SuggestedQuestionsData>(
|
||||
message,
|
||||
MessageAnnotationType.SUGGESTED_QUESTIONS
|
||||
)
|
||||
if (!suggestedQuestionsData?.[0]) return null
|
||||
return (
|
||||
<SuggestedQuestions
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Markdown,
|
||||
LanguageRendererProps,
|
||||
} from '../widgets/index.js'
|
||||
import { getSourceAnnotationData, MessageAnnotation } from './annotation'
|
||||
import { getSourceAnnotationData } from './annotations/sources.js'
|
||||
import {
|
||||
AgentEventAnnotations,
|
||||
ArtifactAnnotations,
|
||||
@@ -138,12 +138,11 @@ function ChatMessageContent(props: ChatMessageContentProps) {
|
||||
|
||||
function ChatMarkdown(props: ChatMarkdownProps) {
|
||||
const { message } = useChatMessage()
|
||||
const annotations = message.annotations as MessageAnnotation[] | undefined
|
||||
|
||||
const allNodes = useMemo(() => {
|
||||
const sourceData = getSourceAnnotationData(annotations ?? [])
|
||||
const sourceData = getSourceAnnotationData(message)
|
||||
return sourceData.flatMap(item => item.nodes)
|
||||
}, [annotations])
|
||||
}, [message])
|
||||
|
||||
return (
|
||||
<Markdown
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { DocumentFileType, DocumentFile } from '../chat/annotation'
|
||||
import { DocumentFileType, DocumentFile } from '../widgets'
|
||||
|
||||
const docMineTypeMap: Record<string, DocumentFileType> = {
|
||||
'text/csv': 'csv',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Chat components
|
||||
export * from './chat/chat.interface'
|
||||
export * from './chat/annotation'
|
||||
export * from './chat/annotations'
|
||||
export { default as ChatSection } from './chat/chat-section'
|
||||
export { default as ChatCanvas } from './chat/canvas'
|
||||
export { default as ChatInput } from './chat/chat-input'
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
DrawerTrigger,
|
||||
} from '../ui/drawer'
|
||||
import { Markdown } from './markdown'
|
||||
import { AgentEventData, ProgressData } from '../chat/annotation'
|
||||
import { Progress } from '../ui/progress'
|
||||
|
||||
const AgentIcons: Record<string, LucideIcon> = {
|
||||
@@ -36,6 +35,18 @@ type MergedEvent = {
|
||||
steps: (StepText | StepProgress)[]
|
||||
}
|
||||
|
||||
export type ProgressData = {
|
||||
id: string
|
||||
total: number
|
||||
current: number
|
||||
}
|
||||
|
||||
export type AgentEventData = {
|
||||
agent: string
|
||||
text: string
|
||||
type: 'text' | 'progress'
|
||||
data?: ProgressData
|
||||
}
|
||||
export function ChatAgentEvents({
|
||||
data,
|
||||
isFinished,
|
||||
|
||||
@@ -6,7 +6,10 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '../ui/collapsible'
|
||||
import { EventData } from '../chat/annotation'
|
||||
|
||||
export type EventData = {
|
||||
title: string
|
||||
}
|
||||
|
||||
export function ChatEvents({
|
||||
data,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { DocumentFileData } from '../chat/annotation'
|
||||
import { DocumentInfo } from './document-info'
|
||||
import { DocumentInfo, DocumentFile } from './document-info'
|
||||
|
||||
export type DocumentFileData = {
|
||||
files: DocumentFile[]
|
||||
}
|
||||
|
||||
export function ChatFiles({ data }: { data: DocumentFileData }) {
|
||||
if (!data.files.length) return null
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { type ImageData } from '../index'
|
||||
export type ImageData = {
|
||||
url: string
|
||||
}
|
||||
|
||||
export function ChatImage({ data }: { data: ImageData }) {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMemo } from 'react'
|
||||
import { SourceData, SourceNode } from '../chat/annotation'
|
||||
import { SourceData, SourceNode } from '../chat/annotations/sources'
|
||||
import { DocumentInfo } from './document-info'
|
||||
|
||||
type Document = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SourceNode } from '../chat/annotation'
|
||||
import { SourceNode } from '../chat/annotations/sources'
|
||||
import { SourceNumberButton } from './source-number-button'
|
||||
|
||||
export interface CitationComponentProps {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
HoverCardTrigger,
|
||||
} from '@radix-ui/react-hover-card'
|
||||
import { Check, Copy, FileIcon, XCircleIcon } from 'lucide-react'
|
||||
import { DocumentFileType, SourceNode } from '../chat/annotation'
|
||||
import { SourceNode } from '../chat/annotations/sources'
|
||||
import { useCopyToClipboard } from '../hook/use-copy-to-clipboard'
|
||||
import { Button } from '../ui/button'
|
||||
import { PdfDialog } from './pdf-dialog'
|
||||
@@ -16,6 +16,22 @@ import { PDFIcon } from '../ui/icons/pdf'
|
||||
import { SheetIcon } from '../ui/icons/sheet'
|
||||
import { TxtIcon } from '../ui/icons/txt'
|
||||
|
||||
export type DocumentFileType = 'csv' | 'pdf' | 'txt' | 'docx'
|
||||
export const DOCUMENT_FILE_TYPES: DocumentFileType[] = [
|
||||
'csv',
|
||||
'pdf',
|
||||
'txt',
|
||||
'docx',
|
||||
]
|
||||
export type DocumentFile = {
|
||||
id: string
|
||||
name: string // The uploaded file name in the backend
|
||||
size: number // The file size in bytes
|
||||
type: DocumentFileType
|
||||
url: string // The URL of the uploaded file in the backend
|
||||
refs?: string[] // DocumentIDs of the uploaded file in the vector index
|
||||
}
|
||||
|
||||
type Document = {
|
||||
url: string
|
||||
sources: SourceNode[]
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Other useful components
|
||||
export { ChatAgentEvents } from './chat-agent-events'
|
||||
export { ChatEvents } from './chat-events'
|
||||
export { ChatFiles } from './chat-files'
|
||||
export { ChatImage } from './chat-image'
|
||||
export { ChatSources } from './chat-sources'
|
||||
export { Markdown, type LanguageRendererProps } from './markdown'
|
||||
export { CodeBlock } from './codeblock'
|
||||
export { PdfDialog } from './pdf-dialog'
|
||||
export { SuggestedQuestions } from './suggested-questions'
|
||||
export { StarterQuestions } from './starter-questions'
|
||||
export { DocumentInfo } from './document-info'
|
||||
export { ImagePreview } from './image-preview'
|
||||
export { FileUploader } from './file-uploader'
|
||||
export { Citation, type CitationComponentProps } from './citation'
|
||||
export { CodeEditor, fileExtensionToEditorLang } from './code-editor'
|
||||
export { DocumentEditor } from './document-editor'
|
||||
export * from './chat-agent-events'
|
||||
export * from './chat-events'
|
||||
export * from './chat-files'
|
||||
export * from './chat-image'
|
||||
export * from './chat-sources'
|
||||
export * from './markdown'
|
||||
export * from './codeblock'
|
||||
export * from './pdf-dialog'
|
||||
export * from './suggested-questions'
|
||||
export * from './starter-questions'
|
||||
export * from './document-info'
|
||||
export * from './image-preview'
|
||||
export * from './file-uploader'
|
||||
export * from './citation'
|
||||
export * from './code-editor'
|
||||
export * from './document-editor'
|
||||
|
||||
@@ -7,9 +7,9 @@ import { CodeBlock } from './codeblock'
|
||||
import {
|
||||
DOCUMENT_FILE_TYPES,
|
||||
DocumentFileType,
|
||||
SourceData,
|
||||
} from '../chat/annotation'
|
||||
import { DocumentInfo } from './document-info'
|
||||
DocumentInfo,
|
||||
} from './document-info'
|
||||
import { SourceData } from '../chat/annotations/sources'
|
||||
import { Citation, CitationComponentProps } from './citation'
|
||||
import { cn } from '../lib/utils'
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SuggestedQuestionsData } from '../chat/annotation'
|
||||
import { ChatHandler } from '../chat/chat.interface'
|
||||
|
||||
export type SuggestedQuestionsData = string[]
|
||||
|
||||
export function SuggestedQuestions({
|
||||
questions,
|
||||
append,
|
||||
|
||||
Reference in New Issue
Block a user