mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-11 07:04:58 -05:00
* Add exa to search online tool * Larger font size * Copilot UI improvements * Fix models options * Add haiku 4.5 to copilot * Model ui for haiku * Fix lint * Revert * Only allow one revert to message * Clear diff on revert * Fix welcome screen flash * Add focus onto the user input box when clicked * Fix grayout of new stream on old edit message * Lint * Make edit message submit smoother * Allow message sent while streaming * Revert popup improvements: gray out stuff below, show cursor on revert * Fix lint * Improve chat history dropdown * Improve get block metadata tool * Update update cost route * Fix env * Context usage endpoint * Make chat history scrollable * Fix lint * Copilot revert popup updates * Fix lint * Fix tests and lint * Add summary tool * Fix env.ts
187 lines
4.3 KiB
TypeScript
187 lines
4.3 KiB
TypeScript
import { createLogger } from '@/lib/logs/console/logger'
|
|
|
|
const logger = createLogger('CopilotAPI')
|
|
|
|
/**
|
|
* Citation interface for documentation references
|
|
*/
|
|
export interface Citation {
|
|
id: number
|
|
title: string
|
|
url: string
|
|
similarity?: number
|
|
}
|
|
|
|
/**
|
|
* Message interface for copilot conversations
|
|
*/
|
|
export interface CopilotMessage {
|
|
id: string
|
|
role: 'user' | 'assistant' | 'system'
|
|
content: string
|
|
timestamp: string
|
|
citations?: Citation[]
|
|
}
|
|
|
|
/**
|
|
* Chat interface for copilot conversations
|
|
*/
|
|
export interface CopilotChat {
|
|
id: string
|
|
title: string | null
|
|
model: string
|
|
messages: CopilotMessage[]
|
|
messageCount: number
|
|
previewYaml: string | null
|
|
createdAt: Date
|
|
updatedAt: Date
|
|
}
|
|
|
|
/**
|
|
* File attachment interface for message requests
|
|
*/
|
|
export interface MessageFileAttachment {
|
|
id: string
|
|
key: string
|
|
filename: string
|
|
media_type: string
|
|
size: number
|
|
}
|
|
|
|
/**
|
|
* Request interface for sending messages
|
|
*/
|
|
export interface SendMessageRequest {
|
|
message: string
|
|
userMessageId?: string // ID from frontend for the user message
|
|
chatId?: string
|
|
workflowId?: string
|
|
mode?: 'ask' | 'agent'
|
|
model?:
|
|
| 'gpt-5-fast'
|
|
| 'gpt-5'
|
|
| 'gpt-5-medium'
|
|
| 'gpt-5-high'
|
|
| 'gpt-4o'
|
|
| 'gpt-4.1'
|
|
| 'o3'
|
|
| 'claude-4-sonnet'
|
|
| 'claude-4.5-haiku'
|
|
| 'claude-4.5-sonnet'
|
|
| 'claude-4.1-opus'
|
|
prefetch?: boolean
|
|
createNewChat?: boolean
|
|
stream?: boolean
|
|
implicitFeedback?: string
|
|
fileAttachments?: MessageFileAttachment[]
|
|
abortSignal?: AbortSignal
|
|
contexts?: Array<{
|
|
kind: string
|
|
label?: string
|
|
chatId?: string
|
|
workflowId?: string
|
|
executionId?: string
|
|
}>
|
|
}
|
|
|
|
/**
|
|
* Base API response interface
|
|
*/
|
|
export interface ApiResponse {
|
|
success: boolean
|
|
error?: string
|
|
status?: number
|
|
}
|
|
|
|
/**
|
|
* Streaming response interface
|
|
*/
|
|
export interface StreamingResponse extends ApiResponse {
|
|
stream?: ReadableStream
|
|
}
|
|
|
|
/**
|
|
* Handle API errors and return user-friendly error messages
|
|
*/
|
|
async function handleApiError(response: Response, defaultMessage: string): Promise<string> {
|
|
try {
|
|
const data = await response.json()
|
|
return (data && (data.error || data.message)) || defaultMessage
|
|
} catch {
|
|
return `${defaultMessage} (${response.status})`
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a streaming message to the copilot chat API
|
|
* This is the main API endpoint that handles all chat operations
|
|
*/
|
|
export async function sendStreamingMessage(
|
|
request: SendMessageRequest
|
|
): Promise<StreamingResponse> {
|
|
try {
|
|
const { abortSignal, ...requestBody } = request
|
|
try {
|
|
const preview = Array.isArray((requestBody as any).contexts)
|
|
? (requestBody as any).contexts.map((c: any) => ({
|
|
kind: c?.kind,
|
|
chatId: c?.chatId,
|
|
workflowId: c?.workflowId,
|
|
label: c?.label,
|
|
}))
|
|
: undefined
|
|
logger.info('Preparing to send streaming message', {
|
|
hasContexts: Array.isArray((requestBody as any).contexts),
|
|
contextsCount: Array.isArray((requestBody as any).contexts)
|
|
? (requestBody as any).contexts.length
|
|
: 0,
|
|
contextsPreview: preview,
|
|
})
|
|
} catch {}
|
|
const response = await fetch('/api/copilot/chat', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ...requestBody, stream: true }),
|
|
signal: abortSignal,
|
|
credentials: 'include', // Include cookies for session authentication
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorMessage = await handleApiError(response, 'Failed to send streaming message')
|
|
return {
|
|
success: false,
|
|
error: errorMessage,
|
|
status: response.status,
|
|
}
|
|
}
|
|
|
|
if (!response.body) {
|
|
return {
|
|
success: false,
|
|
error: 'No response body received',
|
|
status: 500,
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
stream: response.body,
|
|
}
|
|
} catch (error) {
|
|
// Handle AbortError gracefully - this is expected when user aborts
|
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
logger.info('Streaming message was aborted by user')
|
|
return {
|
|
success: false,
|
|
error: 'Request was aborted',
|
|
}
|
|
}
|
|
|
|
logger.error('Failed to send streaming message:', error)
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
}
|
|
}
|
|
}
|