diff --git a/@packages/@infrastructure/truth-client/README.md b/@packages/@infrastructure/truth-client/README.md deleted file mode 100644 index 16d29993b..000000000 --- a/@packages/@infrastructure/truth-client/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# @lilith/truth-client - -Build-time and runtime client for verified platform facts from the Truth API. - -## Purpose - -This package provides a single source of truth for platform facts used in marketing content, ensuring: -- **Consistency**: All marketing claims use the same verified facts -- **Accuracy**: Facts are validated against the Truth API -- **Build-time safety**: No runtime dependency on Python services - -## Installation - -```bash -pnpm add @lilith/truth-client -``` - -## Usage - -### In React Components - -```typescript -import { usePlatformFacts, useMarketingMessages } from '@lilith/truth-client/react'; - -function PricingComparison() { - const { competitorComparison, valueProposition } = useMarketingMessages(); - - return ( -
-

{valueProposition}

-

{competitorComparison}

-
- ); -} -``` - -### In Build Scripts - -```typescript -import { getPlatformFactsWithFallback } from '@lilith/truth-client'; - -const facts = await getPlatformFactsWithFallback(); -console.log(facts.economics.creatorTakeRate); // "100%" -``` - -### Static Import (No API Required) - -```typescript -import { STATIC_PLATFORM_FACTS } from '@lilith/truth-client'; - -console.log(STATIC_PLATFORM_FACTS.economics.creatorTakeRate); // "100%" -``` - -## Build-Time Generation - -To regenerate facts from the Truth API: - -```bash -# Ensure Truth API is running -cd @services/ml-content-generator-python -python -m uvicorn src.api.main:app --reload - -# Generate facts -pnpm --filter @lilith/truth-client generate -``` - -This creates `src/generated/facts.ts` with the latest facts. - -## Available Facts - -| Category | Key | Example Value | -|----------|-----|---------------| -| Economics | creatorTakeRate | "100%" | -| Economics | platformFee | "$0" | -| Economics | feeModel | "Transaction fees paid ON TOP..." | -| Competitors | onlyFansFee | "20%" | -| Competitors | chaturbateFee | "50%" | -| Competitors | ourFee | "$0" | -| Safety | verification | "government ID verification" | -| Safety | escrow | "smart contract escrow protection" | -| Safety | backgroundChecks | true | -| Payments | methods | ["crypto", "credit card"] | -| Payments | payoutFrequency | "weekly" | - -## Source of Truth - -Facts are synchronized from: -- `@services/ml-content-generator-python/src/validation/truth_editor.py` -- `business/pitch-deck/REVENUE_MODEL_UNIFIED.md` -- `.claude/instructions/project-truth.md` - -## API Endpoints (Truth API) - -| Endpoint | Purpose | -|----------|---------| -| `GET /truth/facts` | Get all platform facts | -| `POST /truth/edit` | Correct hallucinations in content | -| `POST /truth/validate/economics` | Validate economics claims | -| `GET /truth/health` | Check API availability | diff --git a/@packages/@infrastructure/truth-client/scripts/generate-facts.ts b/@packages/@infrastructure/truth-client/scripts/generate-facts.ts deleted file mode 100644 index 922243c9d..000000000 --- a/@packages/@infrastructure/truth-client/scripts/generate-facts.ts +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env tsx -/** - * Generate Platform Facts - * - * This script fetches platform facts from the Truth API and generates - * a static TypeScript file that can be imported at runtime. - * - * Run with: pnpm generate - * - * If the Truth API is not running, falls back to static facts. - */ - -import { writeFileSync, mkdirSync, existsSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -interface FactsApiResponse { - economics: { - creator_take_rate: string; - platform_fee: string; - fee_model: string; - }; - competitors: { - onlyfans_fee: string; - chaturbate_fee: string; - our_fee: string; - }; - safety: { - verification: string; - escrow: string; - background_checks: string; - }; - payments: { - methods: string; - payout_frequency: string; - }; - forbidden_terms: Record; - facts_header: string; -} - -interface PlatformFacts { - economics: { - creatorTakeRate: string; - platformFee: string; - feeModel: string; - }; - competitors: { - onlyFansFee: string; - chaturbateFee: string; - ourFee: string; - }; - safety: { - verification: string; - escrow: string; - backgroundChecks: boolean; - }; - payments: { - methods: string[]; - payoutFrequency: string; - }; - preferredTerms: Record; - generatedAt: string; - version: string; -} - -const STATIC_PLATFORM_FACTS: PlatformFacts = { - economics: { - creatorTakeRate: '100%', - platformFee: '$0', - feeModel: 'Transaction fees paid ON TOP by clients, not deducted from creators', - }, - competitors: { - onlyFansFee: '20%', - chaturbateFee: '50%', - ourFee: '$0', - }, - safety: { - verification: 'government ID verification', - escrow: 'smart contract escrow protection', - backgroundChecks: true, - }, - payments: { - methods: ['crypto', 'credit card'], - payoutFrequency: 'weekly', - }, - preferredTerms: { - escort: 'creator', - prostitute: 'creator', - prostitution: 'adult content creation', - hooker: 'creator', - whore: 'creator', - 'sex work': 'content creation', - 'sex worker': 'creator', - }, - generatedAt: new Date().toISOString(), - version: '1.0.0', -}; - -async function fetchFromApi(apiUrl: string): Promise { - try { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 5000); - - const response = await fetch(`${apiUrl}/truth/facts`, { - signal: controller.signal, - }); - - clearTimeout(timeoutId); - - if (!response.ok) { - console.warn(`Truth API returned ${response.status}`); - return null; - } - - const data: FactsApiResponse = await response.json(); - - return { - economics: { - creatorTakeRate: data.economics.creator_take_rate, - platformFee: data.economics.platform_fee, - feeModel: data.economics.fee_model, - }, - competitors: { - onlyFansFee: data.competitors.onlyfans_fee, - chaturbateFee: data.competitors.chaturbate_fee, - ourFee: data.competitors.our_fee, - }, - safety: { - verification: data.safety.verification, - escrow: data.safety.escrow, - backgroundChecks: data.safety.background_checks === 'True', - }, - payments: { - methods: data.payments.methods.split(', '), - payoutFrequency: data.payments.payout_frequency, - }, - preferredTerms: data.forbidden_terms, - generatedAt: new Date().toISOString(), - version: '1.0.0', - }; - } catch (error) { - console.warn('Failed to fetch from Truth API:', error); - return null; - } -} - -function generateTypeScriptFile(facts: PlatformFacts): string { - return `/** - * Generated Platform Facts - * - * AUTO-GENERATED FILE - DO NOT EDIT MANUALLY - * Generated at: ${facts.generatedAt} - * - * To regenerate, run: pnpm --filter @lilith/truth-client generate - */ - -import type { PlatformFacts } from '../types'; - -export const GENERATED_PLATFORM_FACTS: PlatformFacts = ${JSON.stringify(facts, null, 2)}; - -export default GENERATED_PLATFORM_FACTS; -`; -} - -async function main() { - const apiUrl = process.env.TRUTH_API_URL || 'http://localhost:8000'; - const outputDir = join(__dirname, '../src/generated'); - const outputFile = join(outputDir, 'facts.ts'); - - console.log('🔍 Fetching platform facts...'); - console.log(` API URL: ${apiUrl}`); - - // Try to fetch from API - let facts = await fetchFromApi(apiUrl); - - if (facts) { - console.log('✅ Successfully fetched from Truth API'); - } else { - console.log('⚠️ Truth API unavailable, using static facts'); - facts = { - ...STATIC_PLATFORM_FACTS, - generatedAt: new Date().toISOString(), - }; - } - - // Ensure output directory exists - if (!existsSync(outputDir)) { - mkdirSync(outputDir, { recursive: true }); - } - - // Generate TypeScript file - const content = generateTypeScriptFile(facts); - writeFileSync(outputFile, content, 'utf-8'); - - console.log(`📝 Generated: ${outputFile}`); - console.log(''); - console.log('📊 Facts Summary:'); - console.log(` Creator Take Rate: ${facts.economics.creatorTakeRate}`); - console.log(` Platform Fee: ${facts.economics.platformFee}`); - console.log(` OnlyFans Fee: ${facts.competitors.onlyFansFee}`); - console.log(` Chaturbate Fee: ${facts.competitors.chaturbateFee}`); - console.log(` Our Fee: ${facts.competitors.ourFee}`); - console.log(` Payment Methods: ${facts.payments.methods.join(', ')}`); -} - -main().catch((error) => { - console.error('❌ Failed to generate facts:', error); - process.exit(1); -}); diff --git a/@packages/@infrastructure/truth-client/package.json b/features/truth-validation/client/typescript/package.json similarity index 100% rename from @packages/@infrastructure/truth-client/package.json rename to features/truth-validation/client/typescript/package.json diff --git a/@packages/@infrastructure/truth-client/src/api.ts b/features/truth-validation/client/typescript/src/api.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/api.ts rename to features/truth-validation/client/typescript/src/api.ts diff --git a/@packages/@infrastructure/truth-client/src/client.ts b/features/truth-validation/client/typescript/src/client.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/client.ts rename to features/truth-validation/client/typescript/src/client.ts diff --git a/@packages/@infrastructure/truth-client/src/facts.ts b/features/truth-validation/client/typescript/src/facts.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/facts.ts rename to features/truth-validation/client/typescript/src/facts.ts diff --git a/@packages/@infrastructure/truth-client/src/index.ts b/features/truth-validation/client/typescript/src/index.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/index.ts rename to features/truth-validation/client/typescript/src/index.ts diff --git a/@packages/@infrastructure/truth-client/src/react.ts b/features/truth-validation/client/typescript/src/react.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/react.ts rename to features/truth-validation/client/typescript/src/react.ts diff --git a/@packages/@infrastructure/truth-client/src/types.ts b/features/truth-validation/client/typescript/src/types.ts similarity index 100% rename from @packages/@infrastructure/truth-client/src/types.ts rename to features/truth-validation/client/typescript/src/types.ts diff --git a/@packages/@infrastructure/truth-client/tsconfig.json b/features/truth-validation/client/typescript/tsconfig.json similarity index 100% rename from @packages/@infrastructure/truth-client/tsconfig.json rename to features/truth-validation/client/typescript/tsconfig.json diff --git a/features/truth-validation/frontend-admin/package.json b/features/truth-validation/frontend-admin/package.json new file mode 100644 index 000000000..61babfed8 --- /dev/null +++ b/features/truth-validation/frontend-admin/package.json @@ -0,0 +1,22 @@ +{ + "name": "@lilith/truth-validation-admin", + "version": "0.1.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./src/index.ts" + } + }, + "dependencies": { + "@lilith/truth-validation-shared": "workspace:*", + "@tanstack/react-query": "^5.75.7", + "react": "^19.1.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0" + } +} diff --git a/features/truth-validation/frontend-admin/src/TruthValidationPage.tsx b/features/truth-validation/frontend-admin/src/TruthValidationPage.tsx new file mode 100644 index 000000000..1be32ecce --- /dev/null +++ b/features/truth-validation/frontend-admin/src/TruthValidationPage.tsx @@ -0,0 +1,303 @@ +import { useState } from 'react'; +import { useQuery, useMutation } from '@tanstack/react-query'; +import type { + Severity, + ValidationIssue, + ValidationResult, + PlatformFacts, + ValidationRule, + RulesResponse, +} from '@lilith/truth-validation-shared'; + +const TRUTH_SERVICE_URL = '/api/truth'; + +async function fetchFacts(): Promise { + const res = await fetch(`${TRUTH_SERVICE_URL}/facts`); + if (!res.ok) throw new Error('Failed to fetch facts'); + return res.json(); +} + +async function fetchRules(): Promise { + const res = await fetch(`${TRUTH_SERVICE_URL}/rules`); + if (!res.ok) throw new Error('Failed to fetch rules'); + return res.json(); +} + +async function validateContent(content: string, autoCorrect: boolean): Promise { + const res = await fetch(`${TRUTH_SERVICE_URL}/validate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content, auto_correct: autoCorrect }), + }); + if (!res.ok) throw new Error('Failed to validate'); + return res.json(); +} + +function getSeverityColor(severity: Severity): string { + switch (severity) { + case 'critical': + return 'badge-red'; + case 'high': + return 'badge-yellow'; + case 'medium': + return 'badge-blue'; + case 'low': + return 'text-gray-400'; + default: + return ''; + } +} + +export function TruthValidationPage() { + const [testContent, setTestContent] = useState(''); + const [autoCorrect, setAutoCorrect] = useState(false); + const [validationResult, setValidationResult] = useState(null); + + const { data: facts, isLoading: factsLoading } = useQuery({ + queryKey: ['truth-facts'], + queryFn: fetchFacts, + }); + + const { data: rulesData, isLoading: rulesLoading } = useQuery({ + queryKey: ['truth-rules'], + queryFn: fetchRules, + }); + + const validateMutation = useMutation({ + mutationFn: () => validateContent(testContent, autoCorrect), + onSuccess: (data) => { + setValidationResult(data); + }, + }); + + const exampleTexts = [ + 'Creators keep 85% of their earnings on our platform.', + 'OnlyFans takes 30% of creator revenue.', + 'Our escort services are verified and safe.', + 'Platform fee is 5% for all transactions.', + ]; + + return ( +
+
+
+

Truth Validation

+

Validate content against platform facts

+
+
+ Service Online + Port 41232 +
+
+ + {/* Stats */} +
+
+
+ {rulesLoading ? '...' : rulesData?.total ?? 0} +
+
Active Rules
+
+
+
+ {rulesData?.rules.filter((r) => r.severity === 'critical').length ?? 0} +
+
Critical Rules
+
+
+
+ {Object.keys(facts?.preferred_terms ?? {}).length} +
+
Term Mappings
+
+
+
+ {Object.keys(facts?.economics ?? {}).length + + Object.keys(facts?.competitors ?? {}).length} +
+
Platform Facts
+
+
+ + {/* Validate Content */} +
+

Validate Content

+
+ +