UNPKG

pit-manager

Version:

Centralized prompt management system for Human Behavior AI agents

389 lines (387 loc) 14.5 kB
"use strict"; /** * Supabase client wrapper for PIT TypeScript online storage. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SupabaseManager = void 0; const supabase_js_1 = require("@supabase/supabase-js"); const crypto = __importStar(require("crypto")); class SupabaseManager { client; url; serviceKey; constructor(url, serviceKey) { // Load environment variables this.url = url || process.env.PIT_SUPABASE_URL || ''; this.serviceKey = serviceKey || process.env.PIT_SUPABASE_SERVICE_ROLE_KEY || ''; if (!this.url || !this.serviceKey) { throw new Error('Supabase credentials not found. Please set PIT_SUPABASE_URL and ' + 'PIT_SUPABASE_SERVICE_ROLE_KEY in your .env file'); } this.client = (0, supabase_js_1.createClient)(this.url, this.serviceKey, { auth: { autoRefreshToken: false, persistSession: false } }); } /** * Get the SQL schema for PIT database tables including chain execution groups. */ getDatabaseSchema() { return ` -- Chain Execution Groups Table (NEW) CREATE TABLE IF NOT EXISTS chain_execution_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id VARCHAR(255) NOT NULL, -- e.g., "session-analysis-pipeline-2024-08-12-06:52" chain_id VARCHAR(255) NOT NULL, -- e.g., "session-analysis-pipeline" repo_id UUID NOT NULL, session_id VARCHAR(255), -- Optional: for user session tracking process_id VARCHAR(255), -- Process ID for concurrent execution tracking start_time TIMESTAMP NOT NULL, end_time TIMESTAMP, status VARCHAR(50) DEFAULT 'running', -- running, completed, failed total_executions INTEGER DEFAULT 0, total_tokens INTEGER DEFAULT 0, metadata JSONB, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- Add foreign key to executions table ALTER TABLE executions ADD COLUMN IF NOT EXISTS chain_group_id UUID REFERENCES chain_execution_groups(id); -- Indexes for performance CREATE INDEX IF NOT EXISTS idx_chain_groups_chain_id ON chain_execution_groups(chain_id); CREATE INDEX IF NOT EXISTS idx_chain_groups_repo_id ON chain_execution_groups(repo_id); CREATE INDEX IF NOT EXISTS idx_chain_groups_group_id ON chain_execution_groups(group_id); CREATE INDEX IF NOT EXISTS idx_executions_chain_group_id ON executions(chain_group_id); CREATE INDEX IF NOT EXISTS idx_chain_groups_session_id ON chain_execution_groups(session_id); CREATE INDEX IF NOT EXISTS idx_chain_groups_process_id ON chain_execution_groups(process_id); `; } /** * Generate a secure repository key. */ generateRepoKey() { const randomBytes = crypto.randomBytes(32); const keyHash = crypto.createHash('sha256').update(randomBytes).digest('hex'); return `pit_repo_${keyHash.substring(0, 32)}`; } /** * Hash a repository key for secure storage. */ hashRepoKey(repoKey) { return crypto.createHash('sha256').update(repoKey).digest('hex'); } /** * Create a new repository and return its ID and key. */ async createRepository(name, metadata) { try { // Create repository const repoData = { name, metadata: metadata || {} }; const { data: repoResult, error: repoError } = await this.client .from('repositories') .insert(repoData) .select() .single(); if (repoError || !repoResult) { throw new Error('Failed to create repository'); } const repoId = repoResult.id; // Generate and store repo key const repoKey = this.generateRepoKey(); const keyHash = this.hashRepoKey(repoKey); const keyData = { repo_id: repoId, key_hash: keyHash }; const { error: keyError } = await this.client .from('repo_keys') .insert(keyData); if (keyError) { throw new Error('Failed to create repository key'); } return { repoId, repoKey }; } catch (error) { throw new Error(`Failed to create repository: ${error}`); } } /** * Get repository by key. */ async getRepositoryByKey(repoKey) { try { const keyHash = this.hashRepoKey(repoKey); const { data: keyResult, error: keyError } = await this.client .from('repo_keys') .select('repo_id') .eq('key_hash', keyHash) .single(); if (keyError || !keyResult) { return null; } const repoId = keyResult.repo_id; // Get repository details const { data: repoResult, error: repoError } = await this.client .from('repositories') .select('*') .eq('id', repoId) .single(); if (repoError || !repoResult) { return null; } return repoResult; } catch (error) { throw new Error(`Failed to get repository by key: ${error}`); } } /** * Create a new chain execution group for process-unique tracking. */ async createChainExecutionGroup(repoId, chainId, sessionId, processId, metadata) { try { // Generate unique group ID const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const uniqueSuffix = crypto.randomBytes(4).toString('hex'); let groupId; if (sessionId) { groupId = `${chainId}-${sessionId}-${timestamp}-${uniqueSuffix}`; } else if (processId) { groupId = `${chainId}-process-${processId}-${timestamp}-${uniqueSuffix}`; } else { groupId = `${chainId}-${timestamp}-${uniqueSuffix}`; } const groupData = { group_id: groupId, chain_id: chainId, repo_id: repoId, session_id: sessionId, process_id: processId, start_time: new Date().toISOString(), status: 'running', metadata: metadata || {} }; const { data, error } = await this.client .from('chain_execution_groups') .insert(groupData) .select() .single(); if (error || !data) { throw new Error('Failed to create chain execution group'); } return data.id; } catch (error) { throw new Error(`Failed to create chain execution group: ${error}`); } } /** * Get a chain execution group by ID. */ async getChainExecutionGroup(groupId) { try { const { data, error } = await this.client .from('chain_execution_groups') .select('*') .eq('id', groupId) .single(); if (error || !data) { return null; } return data; } catch (error) { throw new Error(`Failed to get chain execution group: ${error}`); } } /** * Update a chain execution group. */ async updateChainExecutionGroup(groupId, updates) { try { const updateData = { ...updates, updated_at: new Date().toISOString() }; const { error } = await this.client .from('chain_execution_groups') .update(updateData) .eq('id', groupId); if (error) { throw new Error(`Failed to update chain execution group: ${error.message}`); } } catch (error) { throw new Error(`Failed to update chain execution group: ${error}`); } } /** * Get chain execution groups for a repository. */ async getChainExecutionGroups(repoId, chainId, limit = 100) { try { let query = this.client .from('chain_execution_groups') .select('*') .eq('repo_id', repoId) .order('created_at', { ascending: false }) .limit(limit); if (chainId) { query = query.eq('chain_id', chainId); } const { data, error } = await query; if (error) { throw new Error(`Failed to get chain execution groups: ${error.message}`); } return (data || []); } catch (error) { throw new Error(`Failed to get chain execution groups: ${error}`); } } /** * Track an execution in the database with optional chain group association. */ async trackExecution(repoId, executionData, chainGroupId) { try { // Prepare execution data for Supabase const supabaseData = { repo_id: repoId, execution_id: executionData.execution_id, model: executionData.model, prompt_hash: executionData.prompt_hash, tag: executionData.tag, tokens: executionData.tokens, latency_ms: executionData.latency_ms, cost: executionData.cost, chain_id: executionData.chain_id, parent_execution_id: executionData.parent_execution_id, chain_group_id: chainGroupId, // NEW: Associate with chain group metadata: executionData.metadata || {}, created_at: executionData.created_at || new Date().toISOString() }; const { error } = await this.client .from('executions') .insert(supabaseData); if (error) { throw new Error(`Failed to track execution: ${error.message}`); } // Update chain group statistics if provided if (chainGroupId) { await this.updateChainGroupStats(chainGroupId, executionData.tokens.total); } } catch (error) { throw new Error(`Failed to track execution: ${error}`); } } /** * Update chain group statistics when an execution is tracked. */ async updateChainGroupStats(chainGroupId, tokens) { try { // Get current group stats const group = await this.getChainExecutionGroup(chainGroupId); if (!group) { return; } // Calculate new totals const currentExecutions = group.total_executions + 1; const currentTokens = group.total_tokens + tokens; // Update group await this.updateChainExecutionGroup(chainGroupId, { total_executions: currentExecutions, total_tokens: currentTokens }); } catch (error) { // Don't fail execution tracking if group stats update fails console.warn(`Warning: Failed to update chain group stats: ${error}`); } } /** * Get executions for a repository with optional chain group filtering. */ async getExecutions(repoId, limit = 100, chainGroupId) { try { let query = this.client .from('executions') .select('*') .eq('repo_id', repoId) .order('created_at', { ascending: false }) .limit(limit); if (chainGroupId) { query = query.eq('chain_group_id', chainGroupId); } const { data, error } = await query; if (error) { throw new Error(`Failed to get executions: ${error.message}`); } return (data || []); } catch (error) { throw new Error(`Failed to get executions: ${error}`); } } /** * Get all executions for a specific chain group. */ async getExecutionsByChainGroup(chainGroupId) { try { const { data, error } = await this.client .from('executions') .select('*') .eq('chain_group_id', chainGroupId) .order('created_at', { ascending: true }); // Chronological order if (error) { throw new Error(`Failed to get executions by chain group: ${error.message}`); } return (data || []); } catch (error) { throw new Error(`Failed to get executions by chain group: ${error}`); } } } exports.SupabaseManager = SupabaseManager; //# sourceMappingURL=supabase-client.js.map