pit-manager
Version:
Centralized prompt management system for Human Behavior AI agents
389 lines (387 loc) • 14.5 kB
JavaScript
;
/**
* 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