UNPKG

oneie

Version:

Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.

568 lines (511 loc) 19.8 kB
--- title: Service Layer dimension: connections category: service-layer.md tags: ai related_dimensions: events, people, things scope: global created: 2025-11-03 updated: 2025-11-03 version: 1.0.0 ai_context: | This document is part of the connections dimension in the service-layer.md category. Location: one/connections/service-layer.md Purpose: Provides information Related dimensions: events, people, things For AI agents: Read this to understand service layer. --- // convex/services/aiClone.ts import { Effect, Layer } from "effect"; import { ConvexDatabase } from "./database"; import { ElevenLabsProvider } from "./providers/elevenlabs"; import { OpenAIProvider } from "./providers/openai"; // ============================================================================ // ERROR TYPES: Explicit, typed errors // ============================================================================ export class InsufficientContentError { readonly _tag = "InsufficientContentError"; constructor(readonly creatorId: string, readonly contentCount: number) {} } export class VoiceCloneFailedError { readonly _tag = "VoiceCloneFailedError"; constructor(readonly reason: string) {} } export class PersonalityExtractionError { readonly _tag = "PersonalityExtractionError"; constructor(readonly reason: string) {} } // ============================================================================ // AI CLONE SERVICE: Creator personality → AI // ============================================================================ export class AICloneService extends Effect.Service<AICloneService>()( "AICloneService", { effect: Effect.gen(function* () { const db = yield* ConvexDatabase; const elevenlabs = yield* ElevenLabsProvider; const openai = yield* OpenAIProvider; return { // ==================================================================== // CREATE CLONE: Extract personality from creator content // ==================================================================== createClone: (creatorId: Id<"entities">) => Effect.gen(function* () { yield* Effect.logInfo("Starting AI clone creation", { creatorId }); // Step 1: Gather creator content with validation const content = yield* Effect.tryPromise({ try: () => db.getCreatorContent(creatorId), catch: (error) => new InsufficientContentError(creatorId, 0), }); if (content.videos.length < 3 || content.audioSamples.length < 5) { return yield* Effect.fail( new InsufficientContentError(creatorId, content.videos.length) ); } // Step 2: Clone voice (parallel with appearance) const voiceId = yield* Effect.gen(function* () { const samples = content.audioSamples.slice(0, 10); const result = yield* elevenlabs.cloneVoice({ name: `${content.creatorName}_voice`, samples, }); return result.voiceId; }).pipe( Effect.retry({ times: 3, delay: "2 seconds" }), Effect.timeout("60 seconds"), Effect.catchAll((error) => Effect.fail(new VoiceCloneFailedError(error.message)) ) ); // Step 3: Extract personality from content const personality = yield* Effect.gen(function* () { const analysis = yield* openai.analyzePersonality({ videos: content.videos, posts: content.posts, interactions: content.interactions, }); return { systemPrompt: analysis.systemPrompt, traits: analysis.traits, communicationStyle: analysis.style, values: analysis.values, }; }).pipe( Effect.catchAll((error) => Effect.fail(new PersonalityExtractionError(error.message)) ) ); // Step 4: Create AI clone entity const cloneId = yield* Effect.tryPromise(() => db.insert("entities", { type: "ai_clone", name: `${content.creatorName} AI Clone`, properties: { voiceId, voiceProvider: "elevenlabs", systemPrompt: personality.systemPrompt, temperature: 0.7, knowledgeBaseSize: content.totalItems, lastTrainingDate: Date.now(), totalInteractions: 0, }, status: "active", createdAt: Date.now(), updatedAt: Date.now(), }) ); // Step 5: Create relationships yield* Effect.all( [ // Clone belongs to creator db.insert("connections", { fromEntityId: creatorId, toEntityId: cloneId, relationshipType: "owns", createdAt: Date.now(), }), // Clone trained on content db.insert("connections", { fromEntityId: cloneId, toEntityId: content.knowledgeBaseId, relationshipType: "trained_on", createdAt: Date.now(), }), ], { concurrency: 2 } ); // Step 6: Log event yield* db.insert("events", { entityId: cloneId, eventType: "clone_created", timestamp: Date.now(), actorType: "system", metadata: { voiceId, contentCount: content.totalItems, personality: personality.traits, }, }); yield* Effect.logInfo("AI clone created successfully", { cloneId, voiceId, }); return { cloneId, voiceId, personality }; }).pipe( Effect.withSpan("createAIClone", { attributes: { creatorId } }) ), // ==================================================================== // INTERACT: User chats with AI clone // ==================================================================== interact: (cloneId: Id<"entities">, message: string, userId: Id<"entities">) => Effect.gen(function* () { // Get clone configuration const clone = yield* db.get("entities", cloneId); // Retrieve relevant knowledge via RAG const context = yield* Effect.gen(function* () { const embedding = yield* openai.embed(message); const results = yield* db.vectorSearch({ vector: embedding, limit: 5, }); return results.map((r) => r.content).join("\n"); }); // Generate response with clone's personality const response = yield* openai.chat({ systemPrompt: clone.properties.systemPrompt, context, messages: [{ role: "user", content: message }], temperature: clone.properties.temperature, }); // Save interaction yield* db.insert("events", { entityId: cloneId, eventType: "clone_interaction", timestamp: Date.now(), actorType: "user", actorId: userId, metadata: { message, response, tokensUsed: response.usage.totalTokens, }, }); // Update stats yield* db.update("entities", cloneId, { properties: { ...clone.properties, totalInteractions: clone.properties.totalInteractions + 1, }, }); return response.content; }), }; }), dependencies: [ ConvexDatabase.Default, ElevenLabsProvider.Default, OpenAIProvider.Default, ], } ) {} // ============================================================================ // BUSINESS AGENT ORCHESTRATION: Multiple agents working together // ============================================================================ export class AgentOrchestrator extends Effect.Service<AgentOrchestrator>()( "AgentOrchestrator", { effect: Effect.gen(function* () { const db = yield* ConvexDatabase; return { // ==================================================================== // LAUNCH CREATOR: All business agents setup // ==================================================================== launchCreator: (creatorId: Id<"entities">) => Effect.gen(function* () { yield* Effect.logInfo("Launching creator business", { creatorId }); // Create all business function agents in parallel const agents = yield* Effect.all( [ createAgent(db, creatorId, "strategy_agent", { systemPrompt: "You are a strategic business advisor...", capabilities: ["goal_setting", "okr_planning", "vision"], }), createAgent(db, creatorId, "marketing_agent", { systemPrompt: "You are a marketing expert...", capabilities: ["content_strategy", "seo", "distribution"], }), createAgent(db, creatorId, "sales_agent", { systemPrompt: "You are a sales optimization specialist...", capabilities: ["funnel_design", "conversion", "follow_up"], }), createAgent(db, creatorId, "finance_agent", { systemPrompt: "You are a financial analyst...", capabilities: ["revenue_tracking", "forecasting", "costs"], }), createAgent(db, creatorId, "intelligence_agent", { systemPrompt: "You are an analytics and insights expert...", capabilities: ["data_analysis", "predictions", "optimization"], }), ], { concurrency: 5 } ); // Strategy agent sets initial goals const strategyAgent = agents[0]; const initialGoals = yield* executeAgent( db, strategyAgent, "Create initial 90-day business plan" ); // Marketing agent creates content calendar based on goals const marketingAgent = agents[1]; const contentCalendar = yield* executeAgent( db, marketingAgent, `Create content calendar for: ${initialGoals.goals.join(", ")}` ); yield* Effect.logInfo("Creator business launched", { agentCount: agents.length, initialGoals: initialGoals.goals.length, }); return { agents: agents.map((a) => a.agentId), initialGoals, contentCalendar, }; }).pipe( Effect.withSpan("launchCreator", { attributes: { creatorId } }) ), // ==================================================================== // DAILY OPERATIONS: Agents execute automated tasks // ==================================================================== runDailyOperations: (creatorId: Id<"entities">) => Effect.gen(function* () { const agents = yield* db.query("connections", { fromEntityId: creatorId, relationshipType: "owns", }); // Intelligence agent analyzes yesterday's data const analytics = yield* Effect.gen(function* () { const intelligenceAgent = agents.find( (a) => a.toEntity.type === "intelligence_agent" ); return yield* executeAgent( db, intelligenceAgent, "Analyze yesterday's metrics and provide insights" ); }); // Marketing agent generates content based on insights const content = yield* Effect.gen(function* () { const marketingAgent = agents.find( (a) => a.toEntity.type === "marketing_agent" ); return yield* executeAgent( db, marketingAgent, `Create today's content. Context: ${analytics.summary}` ); }); // Design agent creates assets for content const assets = yield* Effect.gen(function* () { const designAgent = agents.find( (a) => a.toEntity.type === "design_agent" ); return yield* executeAgent( db, designAgent, `Design assets for: ${content.contentPlan}` ); }); return { analytics, content, assets }; }), }; }), dependencies: [ConvexDatabase.Default, OpenAIProvider.Default], } ) {} // ============================================================================ // TOKEN SERVICE: Atomic token operations // ============================================================================ export class TokenService extends Effect.Service<TokenService>()("TokenService", { effect: Effect.gen(function* () { const db = yield* ConvexDatabase; const blockchain = yield* BlockchainProvider; const stripe = yield* StripeProvider; return { // ==================================================================== // PURCHASE TOKENS: Atomic payment + mint + record // ==================================================================== purchaseTokens: ( userId: Id<"entities">, tokenId: Id<"entities">, amount: number, usdAmount: number ) => Effect.gen(function* () { yield* Effect.logInfo("Starting token purchase", { userId, amount, usdAmount, }); // All operations must succeed together or all fail const result = yield* Effect.all( [ // 1. Charge payment stripe.createPaymentIntent({ amount: usdAmount, currency: "usd", metadata: { userId, tokenId, tokenAmount: amount }, }), // 2. Mint tokens on blockchain blockchain.mintTokens({ contractAddress: tokenId, toAddress: userId, amount, }), ], { concurrency: 2 } ).pipe( // Automatic rollback on any failure Effect.tap(([payment, mint]) => Effect.gen(function* () { // 3. Record in database yield* db.insert("events", { entityId: tokenId, eventType: "tokens_purchased", timestamp: Date.now(), actorType: "user", actorId: userId, metadata: { amount, usdAmount, paymentId: payment.id, txHash: mint.transactionHash, }, }); // 4. Update token balance connection yield* db.upsert("connections", { fromEntityId: userId, toEntityId: tokenId, relationshipType: "holds_tokens", metadata: { balance: amount, // Will be updated by trigger }, }); }) ), Effect.catchAll((error) => Effect.gen(function* () { // Rollback: refund payment and burn tokens yield* Effect.logError("Token purchase failed, rolling back", { error, }); yield* Effect.all([ stripe.refund(payment.id), blockchain.burnTokens({ contractAddress: tokenId, amount, }), ]); return yield* Effect.fail(error); }) ) ); yield* Effect.logInfo("Token purchase completed", { txHash: result[1].transactionHash, }); return { paymentId: result[0].id, txHash: result[1].transactionHash, amount, }; }).pipe( Effect.withSpan("purchaseTokens", { attributes: { userId, tokenId, amount }, }) ), }; }), dependencies: [ConvexDatabase.Default, BlockchainProvider.Default, StripeProvider.Default], }) {} // ============================================================================ // HELPER FUNCTIONS // ============================================================================ const createAgent = ( db: ConvexDatabase, creatorId: Id<"entities">, agentType: string, config: { systemPrompt: string; capabilities: string[] } ) => Effect.gen(function* () { const agentId = yield* db.insert("entities", { type: agentType, name: `${agentType.replace("_", " ")} for creator`, properties: { agentType, systemPrompt: config.systemPrompt, model: "gpt-4-turbo", temperature: 0.7, capabilities: config.capabilities, totalExecutions: 0, successRate: 1.0, }, status: "active", createdAt: Date.now(), updatedAt: Date.now(), }); yield* db.insert("connections", { fromEntityId: creatorId, toEntityId: agentId, relationshipType: "owns", createdAt: Date.now(), }); return { agentId, agentType }; }); const executeAgent = ( db: ConvexDatabase, agent: { agentId: Id<"entities"> }, task: string ) => Effect.gen(function* () { const openai = yield* OpenAIProvider; const agentEntity = yield* db.get("entities", agent.agentId); const result = yield* openai.chat({ systemPrompt: agentEntity.properties.systemPrompt, messages: [{ role: "user", content: task }], }); yield* db.insert("events", { entityId: agent.agentId, eventType: "agent_executed", timestamp: Date.now(), actorType: "system", metadata: { task, result: result.content, tokensUsed: result.usage.totalTokens, }, }); return JSON.parse(result.content); }); // ============================================================================ // TESTING EXAMPLE: Mock services for unit tests // ============================================================================ export const MockAICloneService = Layer.succeed(AICloneService, { createClone: () => Effect.succeed({ cloneId: "mock-clone-id" as Id<"entities">, voiceId: "mock-voice-id", personality: { systemPrompt: "Mock system prompt", traits: ["friendly", "expert"], communicationStyle: "casual", values: ["authenticity"], }, }), interact: () => Effect.succeed("Mock AI response"), }); // Usage in tests: // const result = await Effect.runPromise( // createClone("creator-123").pipe(Effect.provide(MockAICloneService)) // );