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
30 changes: 30 additions & 0 deletions electron/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { trackEvent, initialize } from '@aptabase/electron/main'

/**
* Initialize Aptabase analytics
* Set the APTABASE_APP_KEY environment variable or replace the placeholder
*/
export function initAnalytics() {
const appKey = process.env.APTABASE_APP_KEY || 'YOUR_APTABASE_APP_KEY'

// Only initialize if a valid key is provided
if (appKey && appKey !== 'YOUR_APTABASE_APP_KEY') {
try {
initialize(appKey)
console.log('Analytics initialized')
} catch (error) {
console.error('Failed to initialize analytics:', error)
}
}
}

/**
* Track an analytics event
*/
export function track(eventName: string, properties?: Record<string, any>) {
try {
trackEvent(eventName, properties)
} catch (error) {
// Silently fail if analytics is not initialized
}
}
42 changes: 42 additions & 0 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
GetVectorsPaginatedParams,
} from './types'
import { initAutoUpdater, checkForUpdates } from './auto-updater'
import { initAnalytics, track } from './analytics'

// Inject stored API keys into process.env at startup
settingsStore.injectIntoProcessEnv()
Expand Down Expand Up @@ -99,6 +100,7 @@ process.env.VITE_PUBLIC = app.isPackaged
ipcMain.handle('pinecone:connect', async (_event, profileId: string, profile: ConnectionProfile) => {
try {
await pineconeConnectionPool.connect(profileId, profile)
track('pinecone_connected')
return { success: true }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to connect to Pinecone'
Expand Down Expand Up @@ -232,6 +234,7 @@ ipcMain.handle('pinecone:createVector', async (_event, profileId: string, params
const embeddingOverride = connectionStore.getEmbeddingOverride(profileId, params.indexName)
const hybridOverride = connectionStore.getHybridEmbeddingOverride(profileId, params.indexName)
await service.createVector(params, embeddingOverride || undefined, hybridOverride || undefined)
track('vector_created')
return { success: true }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to create vector'
Expand All @@ -249,6 +252,7 @@ ipcMain.handle('pinecone:updateVector', async (_event, profileId: string, params
const embeddingOverride = connectionStore.getEmbeddingOverride(profileId, params.indexName)
const hybridOverride = connectionStore.getHybridEmbeddingOverride(profileId, params.indexName)
await service.updateVector(params, embeddingOverride || undefined, hybridOverride || undefined)
track('vector_updated')
return { success: true }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to update vector'
Expand All @@ -263,6 +267,13 @@ ipcMain.handle('pinecone:deleteVectors', async (_event, profileId: string, param
return { success: false, error: 'Not connected to Pinecone' }
}
await service.deleteVectors(params)
// Track vector deletion with count if specific IDs provided
const count = params.ids?.length
if (count !== undefined) {
track('vectors_deleted', { count })
} else {
track('vectors_deleted')
}
return { success: true }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete vectors'
Expand All @@ -280,6 +291,9 @@ ipcMain.handle('pinecone:batchImport', async (_event, profileId: string, params:
const embeddingOverride = connectionStore.getEmbeddingOverride(profileId, params.indexName)
const hybridOverride = connectionStore.getHybridEmbeddingOverride(profileId, params.indexName)
const result = await service.batchImport(params, embeddingOverride || undefined, undefined, hybridOverride || undefined)
track('vectors_imported', {
count: result.upsertedCount,
})
return { success: true, data: result }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to import vectors'
Expand All @@ -294,6 +308,14 @@ ipcMain.handle('pinecone:createIndex', async (_event, profileId: string, params:
return { success: false, error: 'Not connected to Pinecone' }
}
await service.createIndex(params)
// Track index creation with generic metadata
const spec = params.spec as { serverless?: { cloud?: string; region?: string } }
track('index_created', {
cloud: spec.serverless?.cloud,
region: spec.serverless?.region,
metric: params.metric,
dimension: params.dimension,
})
return { success: true }
} catch (error) {
// Extract cloud/region from serverless spec for better error messages
Expand All @@ -313,6 +335,7 @@ ipcMain.handle('pinecone:deleteIndex', async (_event, profileId: string, indexNa
return { success: false, error: 'Not connected to Pinecone' }
}
await service.deleteIndex(indexName)
track('index_deleted')
return { success: true }
} catch (error) {
const message = error instanceof Error ? error.message : 'Failed to delete index'
Expand Down Expand Up @@ -351,6 +374,13 @@ ipcMain.handle('pinecone:cloneIndex', async (event, profileId: string, params: C
// Clean up abort controller
activeCloneOperations.delete(profileId)

// Track successful index duplication
if (result.success) {
track('index_duplicated', {
vectorsCopied: result.copiedVectors
})
}

return { success: result.success, data: result, error: result.error }
} catch (error) {
activeCloneOperations.delete(profileId)
Expand Down Expand Up @@ -418,6 +448,9 @@ ipcMain.handle('pinecone:cloneNamespace', async (event, profileId: string, param
activeNamespaceCloneOperations.delete(operationKey)

if (result.success) {
track('namespace_duplicated', {
vectorsCopied: result.copiedVectors,
})
event.sender.send('pinecone:cloneNamespaceProgress', {
phase: 'complete',
totalVectors,
Expand Down Expand Up @@ -916,6 +949,10 @@ ipcMain.handle('shell:openExternal', async (_event, url: string) => {
// ============================================================================

app.whenReady().then(() => {
// Initialize analytics
initAnalytics()
track('app_started')

// Create application menu
createApplicationMenu()

Expand All @@ -935,10 +972,15 @@ app.whenReady().then(() => {

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
track('app_closed')
app.quit()
}
})

app.on('before-quit', () => {
track('app_closed')
})

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
windowManager.createSetupWindow()
Expand Down
2 changes: 1 addition & 1 deletion electron/pinecone-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { withRetry } from './retry-utils'
* Main Pinecone service class
*/
class PineconeService {
private static readonly BATCH_SIZE = 100
private static readonly BATCH_SIZE = 50

private client: Pinecone | null = null
private embeddingService: EmbeddingService | null = null
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"vite-plugin-electron-renderer": "^0.14.6"
},
"dependencies": {
"@aptabase/electron": "^0.3.1",
"@pinecone-database/pinecone": "^6.1.3",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
Expand Down
Loading