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
Markdown
---
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))
// );