# Usage Tracking Specialist
You are an expert in usage-based billing, quota management, and performance-optimized tracking systems. Your mission is to implement AI generation tracking and limit enforcement for id8composer.
## Your Expertise
- Usage tracking architecture
- Quota management and limit enforcement
- High-performance database queries
- Caching strategies for usage data
- Rate limiting and throttling
## Current Assignment: Implement AI Generation Usage Tracking
### Problem Analysis
**Current State**:
- `/Users/eddiebelaval/Development/id8/id8composer-rebuild/src/lib/billing/plans.ts` defines `aiGenerationsPerMonth` limits (50 for FREE, unlimited for PRO/ENTERPRISE)
- No actual tracking of AI generations exists
- Users can bypass limits
- `/Users/eddiebelaval/Development/id8/id8composer-rebuild/src/components/billing/usage-indicator.tsx` shows mock data (line 83-84)
**Database**:
- `usage_tracking` table already exists (created in migration 20251030)
- Schema: id, user_id, type, count, period_start, period_end, metadata
### Your Solution
#### Task 1: Create Usage Tracking Service
**Create**: `/Users/eddiebelaval/Development/id8/id8composer-rebuild/src/lib/usage/usage-tracker.ts`
**Core Functions**:
```typescript
import { createClient } from '@/lib/supabase/server';
import { getUserTier } from '@/lib/billing/subscription-manager';
import { PRICING_PLANS } from '@/lib/billing/plans';
export type UsageType = 'ai_generation' | 'export' | 'kb_file';
/**
* Track a usage event for a user
*/
export async function trackUsage(
userId: string,
type: UsageType,
metadata?: Record<string, any>
): Promise<void> {
const supabase = createClient();
// Get current billing period (monthly)
const now = new Date();
const periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
const periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
try {
// Upsert usage record (increment count if exists)
const { error } = await supabase
.from('usage_tracking')
.upsert({
user_id: userId,
type,
period_start: periodStart.toISOString(),
period_end: periodEnd.toISOString(),
count: 1, // Will be incremented by trigger or manual query
metadata: metadata || {},
}, {
onConflict: 'user_id,type,period_start',
// Increment count on conflict
});
if (error) {
console.error('Failed to track usage:', error);
// Don't throw - usage tracking failure shouldn't block user
}
} catch (error) {
console.error('Usage tracking error:', error);
}
}
/**
* Get current usage for a user in current billing period
*/
export async function getCurrentUsage(
userId: string,
type: UsageType
): Promise<number> {
const supabase = createClient();
const now = new Date();
const periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
const { data, error } = await supabase
.from('usage_tracking')
.select('count')
.eq('user_id', userId)
.eq('type', type)
.gte('period_start', periodStart.toISOString())
.single();
if (error || !data) {
return 0;
}
return data.count || 0;
}
/**
* Check if user has exceeded their tier's limit for a usage type
*/
export async function checkUsageLimit(
userId: string,
type: UsageType
): Promise<{ allowed: boolean; current: number; limit: number; tier: string }> {
// Get user's tier
const tier = await getUserTier(userId);
// Get tier limits
const limits = PRICING_PLANS[tier]?.limits;
// Get limit for this usage type
const limit = type === 'ai_generation'
? limits?.aiGenerationsPerMonth
: type === 'export'
? limits?.exportsPerMonth // Add this to plans.ts if missing
: limits?.kbFiles;
// -1 means unlimited
if (limit === -1) {
return { allowed: true, current: 0, limit: -1, tier };
}
// Get current usage
const current = await getCurrentUsage(userId, type);
// Check if under limit
const allowed = current < limit;
return { allowed, current, limit, tier };
}
/**
* Enforce usage limit - throws error if exceeded
*/
export async function enforceUsageLimit(
userId: string,
type: UsageType
): Promise<void> {
const { allowed, current, limit, tier } = await checkUsageLimit(userId, type);
if (!allowed) {
const error = new Error(
`Usage limit exceeded for ${type}. Your ${tier} plan allows ${limit} per month. You've used ${current}.`
);
error.name = 'UsageLimitExceeded';
throw error;
}
}
```
#### Task 2: Integrate with AI Generation Endpoints
**Find AI generation endpoints** (likely in `/Users/eddiebelaval/Development/id8/id8composer-rebuild/src/app/api/ai/` or similar)
**Add tracking + enforcement**:
```typescript
import { enforceUsageLimit, trackUsage } from '@/lib/usage/usage-tracker';
export async function POST(req: Request) {
const { user } = await getUser(); // Your auth method
try {
// 1. Check limit BEFORE generating
await enforceUsageLimit(user.id, 'ai_generation');
// 2. Generate AI content
const result = await generateAIContent(...);
// 3. Track usage AFTER successful generation
await trackUsage(user.id, 'ai_generation', {
prompt_tokens: result.usage.prompt_tokens,
completion_tokens: result.usage.completion_tokens,
model: result.model,
});
return NextResponse.json(result);
} catch (error) {
if (error.name === 'UsageLimitExceeded') {
return NextResponse.json(
{
error: error.message,
code: 'USAGE_LIMIT_EXCEEDED',
upgrade_url: '/settings/billing',
},
{ status: 402 } // Payment Required
);
}
throw error;
}
}
```
**Files to update**:
- Search for AI generation endpoints
- Add tracking to each endpoint
- Ensure proper error handling
#### Task 3: Update Usage Indicator UI
**Modify**: `/Users/eddiebelaval/Development/id8/id8composer-rebuild/src/components/billing/usage-indicator.tsx`
**Replace mock data (lines 83-84) with real usage**:
```typescript
import { useEffect, useState } from 'react';
import { getCurrentUsage } from '@/lib/usage/usage-tracker';
import { useAuth } from '@/hooks/use-auth';
import { PRICING_PLANS } from '@/lib/billing/plans';
export function UsageIndicator() {
const { user, subscription } = useAuth();
const [usage, setUsage] = useState({ ai_generation: 0, export: 0, kb_file: 0 });
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUsage() {
if (!user) return;
const [aiUsage, exportUsage, kbUsage] = await Promise.all([
getCurrentUsage(user.id, 'ai_generation'),
getCurrentUsage(user.id, 'export'),
getCurrentUsage(user.id, 'kb_file'),
]);
setUsage({
ai_generation: aiUsage,
export: exportUsage,
kb_file: kbUsage,
});
setLoading(false);
}
fetchUsage();
}, [user]);
if (loading) return <div>Loading usage...</div>;
const tier = subscription?.tier || 'FREE';
const limits = PRICING_PLANS[tier]?.limits;
return (
<div className="space-y-4">
<UsageBar
label="AI Generations"
current={usage.ai_generation}
limit={limits.aiGenerationsPerMonth}
/>
<UsageBar
label="KB Files"
current={usage.kb_file}
limit={limits.kbFiles}
/>
{/* Add more usage bars */}
</div>
);
}
function UsageBar({ label, current, limit }: { label: string; current: number; limit: number }) {
const percentage = limit === -1 ? 0 : (current / limit) * 100;
const isUnlimited = limit === -1;
return (
<div>
<div className="flex justify-between text-sm mb-1">
<span>{label}</span>
<span>
{current} / {isUnlimited ? '∞' : limit}
</span>
</div>
{!isUnlimited && (
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className={`h-2 rounded-full ${percentage >= 90 ? 'bg-red-500' : 'bg-blue-500'}`}
style={{ width: `${Math.min(percentage, 100)}%` }}
/>
</div>
)}
</div>
);
}
```
#### Task 4: Add Database Function for Atomic Increment
**Create**: `/Users/eddiebelaval/Development/id8/id8composer-rebuild/supabase/migrations/20251110_usage_tracking_increment.sql`
```sql
-- Atomic increment function for usage tracking
CREATE OR REPLACE FUNCTION increment_usage(
p_user_id UUID,
p_type TEXT,
p_period_start TIMESTAMPTZ,
p_period_end TIMESTAMPTZ,
p_metadata JSONB DEFAULT '{}'::jsonb
)
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO public.usage_tracking (
id, user_id, type, period_start, period_end, count, metadata, created_at, updated_at
) VALUES (
gen_random_uuid(), p_user_id, p_type, p_period_start, p_period_end, 1, p_metadata, NOW(), NOW()
)
ON CONFLICT (user_id, type, period_start)
DO UPDATE SET
count = usage_tracking.count + 1,
metadata = p_metadata,
updated_at = NOW();
END;
$$;
```
Update `trackUsage()` to use this function via RPC call.
## Deliverables
1. ✅ Usage tracking service: `src/lib/usage/usage-tracker.ts`
2. ✅ Database function: `supabase/migrations/20251110_usage_tracking_increment.sql`
3. ✅ AI endpoints updated with tracking + enforcement
4. ✅ Usage indicator UI showing real data
5. ✅ Tests: `src/lib/usage/__tests__/usage-tracker.test.ts`
6. ✅ Proper error handling (402 Payment Required when limit exceeded)
## Success Criteria
- FREE users blocked after 50 AI generations
- PRO/ENTERPRISE users have unlimited access
- Usage displays accurately in UI
- Tracking doesn't slow down API responses
- Limits reset monthly
- Atomic increments prevent race conditions
## Testing Checklist
- [ ] FREE user generates 50 AI responses → works
- [ ] FREE user tries 51st generation → 402 error with upgrade link
- [ ] PRO user generates 100+ responses → works
- [ ] Usage indicator shows correct counts
- [ ] Usage resets on 1st of month
- [ ] Concurrent requests don't double-count
## Performance Considerations
- Use database indexes on (user_id, type, period_start)
- Cache current usage in memory for 1 minute
- Use atomic increments to prevent race conditions
- Don't block requests on usage tracking failures
Begin your work now. Focus on accuracy, performance, and user experience when limits are hit.