Merge pull request #850 from tnfssc/chore/model-pricing

This commit is contained in:
Sharath 2025-06-28 00:53:46 +05:30 committed by GitHub
commit 720e31ad78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 179 additions and 97 deletions

View File

@ -18,9 +18,75 @@ import {
CardTitle, CardTitle,
} from '@/components/ui/card'; } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { useAvailableModels } from '@/hooks/react-query/subscriptions/use-billing'; import { useAvailableModels } from '@/hooks/react-query/subscriptions/use-billing';
import type { Model } from '@/lib/api'; import type { Model } from '@/lib/api';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { useState } from 'react';
// Example task data with token usage
const exampleTasks = [
{
name: 'Social Automation System',
complexity: 'Complex',
complexityVariant: 'destructive' as const,
inputTokens: 3410337,
outputTokens: 93616,
duration: '35 minutes',
originalModel: 'claude-sonnet-4',
},
{
name: 'Content Marketing Strategy',
complexity: 'Standard Complexity',
complexityVariant: 'secondary' as const,
inputTokens: 212312,
outputTokens: 3378,
duration: '11 minutes',
originalModel: 'claude-sonnet-4',
},
{
name: 'Go-to-Market Strategy',
complexity: 'Standard Complexity',
complexityVariant: 'secondary' as const,
inputTokens: 307719,
outputTokens: 24033,
duration: '16 minutes',
originalModel: 'claude-sonnet-4',
},
{
name: 'Learning Path Generator',
complexity: 'Standard Complexity',
complexityVariant: 'secondary' as const,
inputTokens: 90953,
outputTokens: 17472,
duration: '5 minutes',
originalModel: 'claude-sonnet-4',
},
{
name: 'Customer Journey Mapping',
complexity: 'Complex',
complexityVariant: 'destructive' as const,
inputTokens: 360013,
outputTokens: 17287,
duration: '20 minutes',
originalModel: 'claude-sonnet-4',
},
{
name: 'Sales Funnel Optimization',
complexity: 'Complex',
complexityVariant: 'destructive' as const,
inputTokens: 559918,
outputTokens: 33392,
duration: '14 minutes',
originalModel: 'claude-sonnet-4',
},
];
export default function PricingPage() { export default function PricingPage() {
const { const {
@ -30,6 +96,10 @@ export default function PricingPage() {
refetch, refetch,
} = useAvailableModels(); } = useAvailableModels();
const [selectedModelId, setSelectedModelId] = useState<string>(
'anthropic/claude-sonnet-4-20250514',
);
// Filter to only show models that have pricing information available // Filter to only show models that have pricing information available
const models = const models =
modelsResponse?.models?.filter((model: Model) => { modelsResponse?.models?.filter((model: Model) => {
@ -41,6 +111,30 @@ export default function PricingPage() {
); );
}) || []; }) || [];
// Find the selected model
const selectedModel = models.find((model) => model.id === selectedModelId);
// Function to calculate cost based on tokens and model pricing
const calculateCost = (
inputTokens: number,
outputTokens: number,
model: Model,
) => {
if (
!model.input_cost_per_million_tokens ||
!model.output_cost_per_million_tokens
) {
return 0;
}
const inputCost =
(inputTokens / 1000000) * model.input_cost_per_million_tokens;
const outputCost =
(outputTokens / 1000000) * model.output_cost_per_million_tokens;
return inputCost + outputCost;
};
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center min-h-[400px]"> <div className="flex items-center justify-center min-h-[400px]">
@ -139,106 +233,90 @@ export default function PricingPage() {
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> <div className="space-y-6">
{/* Example 4 */} {/* Model Selection */}
<div className="p-4 border border-border rounded-lg space-y-3"> <div className="space-y-2">
<div className="space-y-2"> <label className="text-sm font-medium text-foreground">
<h4 className="font-semibold text-foreground"> Select a model to see pricing:
Social Automation System </label>
</h4> <Select
<Badge variant="destructive">Complex</Badge> value={selectedModelId}
</div> onValueChange={setSelectedModelId}
<div className="space-y-2 text-sm mt-6"> >
<div className="flex justify-between"> <SelectTrigger className="w-full max-w-md">
<span className="text-muted-foreground">Model:</span> <SelectValue placeholder="Choose a model to calculate costs" />
<span>claude-sonnet-4</span> </SelectTrigger>
</div> <SelectContent>
<div className="flex justify-between"> {models.map((model) => (
<span className="text-muted-foreground">Duration:</span> <SelectItem key={model.id} value={model.id}>
<span>35 minutes</span> {model.display_name}
</div> </SelectItem>
<div className="flex justify-between font-semibold"> ))}
<span className="text-muted-foreground">Cost:</span> </SelectContent>
<span className="text-blue-600">$17.45</span> </Select>
</div>
</div>
</div> </div>
{/* Example 6 */} {/* Example Tasks Grid */}
<div className="p-4 border border-border rounded-lg space-y-3"> <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
<div className="space-y-2"> {exampleTasks.map((task, index) => {
<h4 className="font-semibold text-foreground"> const calculatedCost = selectedModel
Content Marketing Strategy ? calculateCost(
</h4> task.inputTokens,
<Badge variant="secondary">Standard Complexity</Badge> task.outputTokens,
</div> selectedModel,
<div className="space-y-2 text-sm mt-6"> )
<div className="flex justify-between"> : null;
<span className="text-muted-foreground">Model:</span>
<span>claude-sonnet-4</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Duration:</span>
<span>11 minutes</span>
</div>
<div className="flex justify-between font-semibold">
<span className="text-muted-foreground">Cost:</span>
<span className="text-blue-600">$1.93</span>
</div>
</div>
</div>
{/* Example 5 */} return (
<div className="p-4 border border-border rounded-lg space-y-3"> <div
<div className="space-y-2"> key={index}
<h4 className="font-semibold text-foreground"> className="p-4 border border-border rounded-lg space-y-3"
Go-to-Market Strategy >
</h4> <div className="space-y-2">
<Badge variant="secondary">Standard Complexity</Badge> <h4 className="font-semibold text-foreground">
</div> {task.name}
<div className="space-y-2 text-sm mt-6"> </h4>
<div className="flex justify-between"> </div>
<span className="text-muted-foreground">Model:</span> <div className="space-y-2 text-sm mt-6">
<span>deepseek-chat</span> <div className="flex justify-between">
</div> <span className="text-muted-foreground">Model:</span>
<div className="flex justify-between"> <span>
<span className="text-muted-foreground">Duration:</span> {selectedModel?.display_name || task.originalModel}
<span>3 minutes</span> </span>
</div> </div>
<div className="flex justify-between font-semibold"> <div className="flex justify-between">
<span className="text-muted-foreground">Cost:</span> <span className="text-muted-foreground">
<span className="text-blue-600">$0.13</span> Input Tokens:
</div> </span>
</div> <span>{task.inputTokens.toLocaleString()}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">
Output Tokens:
</span>
<span>{task.outputTokens.toLocaleString()}</span>
</div>
{/* <div className="flex justify-between">
<span className="text-muted-foreground">Duration:</span>
<span>{task.duration}</span>
</div> */}
<div className="flex justify-between font-semibold">
<span className="text-muted-foreground">Cost:</span>
{calculatedCost !== null ? (
<span className="text-blue-600">
${calculatedCost.toFixed(2)}
</span>
) : (
<span className="text-muted-foreground">
Select model above
</span>
)}
</div>
</div>
</div>
);
})}
</div> </div>
{/* Example 7 */}
{/* <div className="p-4 border border-border rounded-lg space-y-3">
<div className="space-y-2">
<h4 className="font-semibold text-foreground">
6-Month Content Marketing Strategy
</h4>
<Badge variant="secondary">Standard Complexity</Badge>
</div>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Task type:</span>
<span>Marketing SEO Content</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Model:</span>
<span>deepseek-chat</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Duration:</span>
<span>4 minutes</span>
</div>
<div className="flex justify-between font-semibold">
<span className="text-muted-foreground">Cost:</span>
<span className="text-blue-600">$0.20</span>
</div>
</div>
</div> */}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -267,7 +345,11 @@ export default function PricingPage() {
{models.map((model, index) => ( {models.map((model, index) => (
<div <div
key={model.id} key={model.id}
className="px-6 py-4 hover:bg-muted/50 transition-colors duration-150" className={`px-6 py-4 hover:bg-muted/50 transition-colors duration-150 ${
selectedModelId === model.id
? 'bg-blue-50 dark:bg-blue-950/20 border-l-4 border-l-blue-500'
: ''
}`}
> >
<div className="grid grid-cols-3 gap-4 items-center"> <div className="grid grid-cols-3 gap-4 items-center">
{/* Model Name */} {/* Model Name */}