Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
AssistantFile,
ListAssistantFilesFilter,
UploadAssistantFileParams,
ChatStreamChunk,
ChatMessage,
} from './types'

console.log('Preload script is running!')
Expand Down Expand Up @@ -387,6 +389,31 @@ contextBridge.exposeInMainWorld('electronAPI', {
}
},
},
// Chat streaming operations
chatStream: {
start: async (
profileId: string,
assistantName: string,
params: { messages: ChatMessage[]; model?: string; filter?: Record<string, unknown> }
): Promise<string> => {
const result = await ipcRenderer.invoke('assistant:chat:stream:start', profileId, assistantName, params)
if (!result.success) {
throw new Error(result.error)
}
return result.data.streamId
},
cancel: async (streamId: string): Promise<void> => {
const result = await ipcRenderer.invoke('assistant:chat:stream:cancel', streamId)
if (!result.success) {
throw new Error(result.error)
}
},
onChunk: (callback: (streamId: string, chunk: ChatStreamChunk) => void): (() => void) => {
const handler = (_event: Electron.IpcRendererEvent, streamId: string, chunk: ChatStreamChunk) => callback(streamId, chunk)
ipcRenderer.on('assistant:chat:chunk', handler)
return () => ipcRenderer.removeListener('assistant:chat:chunk', handler)
},
},
},
window: {
createConnection: async (profile: ConnectionProfile): Promise<{ windowId: string }> => {
Expand Down Expand Up @@ -469,6 +496,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
}
},
},
dialog: {
showOpenDialog: async (options: {
properties?: Array<'openFile' | 'openDirectory' | 'multiSelections'>
filters?: Array<{ name: string; extensions: string[] }>
}): Promise<{ canceled: boolean; filePaths: string[] }> => {
return await ipcRenderer.invoke('dialog:showOpenDialog', options)
},
},
updater: {
checkForUpdates: async (): Promise<any> => {
const result = await ipcRenderer.invoke('updater:check')
Expand Down
85 changes: 85 additions & 0 deletions electron/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,88 @@ export interface UploadAssistantFileParams {
multimodal?: boolean
}

// ============================================================================
// Assistant Chat Types
// ============================================================================

/**
* Chat message structure
*/
export interface ChatMessage {
role: 'user' | 'assistant'
content: string
}

/**
* A single reference within a citation
*/
export interface CitationReference {
file: {
name: string
id: string
status?: string
signedUrl?: string | null
}
pages?: number[]
}

/**
* Citation reference in assistant responses
*/
export interface Citation {
position: number
references: CitationReference[]
}

/**
* Token usage statistics for chat
*/
export interface ChatUsage {
promptTokens: number
completionTokens: number
totalTokens: number
}

/**
* Context options for chat requests
*/
export interface ChatContextOptions {
topK?: number
snippetSize?: number
}

/**
* Parameters for chat requests
*/
export interface ChatParams {
messages: ChatMessage[]
model?: string
filter?: Record<string, unknown>
jsonResponse?: boolean
includeHighlights?: boolean
temperature?: number
contextOptions?: ChatContextOptions
}

/**
* Response from non-streaming chat
*/
export interface ChatResponse {
id: string
message: ChatMessage
citations?: Citation[]
usage?: ChatUsage
finishReason?: string
model?: string
}

/**
* Stream chunk types for streaming chat responses
*/
export type ChatStreamChunk =
| { type: 'message_start'; id: string; model: string; role: 'assistant' }
| { type: 'content'; content: string }
| { type: 'citation'; citation: Citation | undefined }
| { type: 'message_end'; usage?: ChatUsage; finishReason?: string }
| { type: 'error'; error: string }

4 changes: 2 additions & 2 deletions src/components/mode/ModeSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface ModeOption {
}

const modes: ModeOption[] = [
{ value: 'index', icon: Database, label: 'Index Explorer', shortcut: '⌘1' },
{ value: 'index', icon: Database, label: 'Database Explorer', shortcut: '⌘1' },
{ value: 'assistant', icon: Bot, label: 'Assistant Explorer', shortcut: '⌘2' },
]

Expand Down Expand Up @@ -43,7 +43,7 @@ export function ModeSwitcher() {
data-testid={`mode-${value}`}
>
<Icon className="h-3.5 w-3.5" />
<span className="hidden sm:inline">{value === 'index' ? 'Index' : 'Assistant'}</span>
<span className="hidden sm:inline">{value === 'index' ? 'Database' : 'Assistant'}</span>
</button>
)
})}
Expand Down
49 changes: 49 additions & 0 deletions src/types/electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,40 @@ declare global {
metadata?: Record<string, string | number>
}

// Chat types for assistant streaming
interface ChatMessage {
role: 'user' | 'assistant'
content: string
}

interface CitationReference {
file: {
name: string
id: string
status?: string
signedUrl?: string | null
}
pages?: number[]
}

interface Citation {
position: number
references: CitationReference[]
}

interface ChatUsage {
promptTokens: number
completionTokens: number
totalTokens: number
}

type ChatStreamChunk =
| { type: 'message_start'; id: string; model: string; role: 'assistant' }
| { type: 'content'; content: string }
| { type: 'citation'; citation: Citation | undefined }
| { type: 'message_end'; usage?: ChatUsage; finishReason?: string }
| { type: 'error'; error: string }

interface ConnectionProfile {
id: string
name: string
Expand Down Expand Up @@ -377,6 +411,15 @@ declare global {
upload: (profileId: string, assistantName: string, params: UploadAssistantFileParams) => Promise<AssistantFile>
delete: (profileId: string, assistantName: string, fileId: string) => Promise<void>
}
chatStream: {
start: (profileId: string, assistantName: string, params: {
messages: Array<{ role: 'user' | 'assistant'; content: string }>;
model?: string;
filter?: Record<string, unknown>;
}) => Promise<string>
cancel: (streamId: string) => Promise<void>
onChunk: (callback: (streamId: string, chunk: ChatStreamChunk) => void) => () => void
}
}
window: {
createConnection: (profile: ConnectionProfile) => Promise<{ windowId: string }>
Expand All @@ -396,6 +439,12 @@ declare global {
shell: {
openExternal: (url: string) => Promise<void>
}
dialog: {
showOpenDialog: (options: {
properties?: Array<'openFile' | 'openDirectory' | 'multiSelections'>
filters?: Array<{ name: string; extensions: string[] }>
}) => Promise<{ canceled: boolean; filePaths: string[] }>
}
updater: {
checkForUpdates: () => Promise<UpdateInfo | undefined>
downloadUpdate: () => Promise<void>
Expand Down