Skip to content

Commit aa31b19

Browse files
authored
Merge pull request #12 from tinybirdco/table
connect table
2 parents fdeff2e + 9ae5284 commit aa31b19

File tree

6 files changed

+149
-213
lines changed

6 files changed

+149
-213
lines changed

dashboard/ai-analytics/src/app/components/DataTable.tsx

Lines changed: 72 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -1,225 +1,84 @@
11
'use client';
22

3-
// import { useState } from 'react';
43
import {
5-
// MultiSelect,
6-
// MultiSelectItem,
7-
// Select,
8-
// SelectItem,
94
Table,
105
TableBody,
116
TableCell,
127
TableHead,
138
TableHeaderCell,
149
TableRow,
1510
} from '@tremor/react';
11+
import { format } from 'date-fns';
1612

17-
// Mock data
13+
// Define the shape of the LLM message data
14+
interface LLMMessage {
15+
timestamp: string;
16+
organization: string;
17+
project: string;
18+
environment: string;
19+
user: string;
20+
chat_id: string;
21+
model: string;
22+
provider: string;
23+
prompt_tokens: number;
24+
completion_tokens: number;
25+
total_tokens: number;
26+
duration: number;
27+
cost: number;
28+
response_status: string;
29+
exception: string | null;
30+
}
31+
32+
interface DataTableProps {
33+
data?: { data: LLMMessage[] };
34+
isLoading?: boolean;
35+
}
36+
37+
// Mock data for development and testing
1838
const MOCK_DATA = {
1939
data: [
2040
{
21-
date: '2024-01-01',
22-
model: 'gpt-4',
23-
provider: 'OpenAI',
24-
organization: 'Acme Inc',
25-
project: 'chatbot',
26-
environment: 'production',
27-
28-
total_tokens: 15000,
29-
total_requests: 100,
30-
avg_duration: 250.5
31-
},
32-
{
33-
date: '2024-01-01',
34-
model: 'gpt-3.5-turbo',
35-
provider: 'OpenAI',
36-
organization: 'Beta Corp',
37-
project: 'support',
38-
environment: 'staging',
39-
40-
total_tokens: 8000,
41-
total_requests: 50,
42-
avg_duration: 150.2
43-
},
44-
{
45-
date: '2024-01-01',
46-
model: 'gpt-4',
47-
provider: 'OpenAI',
48-
organization: 'Acme Inc',
49-
project: 'chatbot',
50-
environment: 'production',
51-
52-
total_tokens: 15000,
53-
total_requests: 100,
54-
avg_duration: 250.5
55-
},
56-
{
57-
date: '2024-01-01',
58-
model: 'gpt-3.5-turbo',
59-
provider: 'OpenAI',
60-
organization: 'Beta Corp',
61-
project: 'support',
62-
environment: 'staging',
63-
64-
total_tokens: 8000,
65-
total_requests: 50,
66-
avg_duration: 150.2
67-
},
68-
{
69-
date: '2024-01-01',
70-
model: 'gpt-4',
71-
provider: 'OpenAI',
72-
organization: 'Acme Inc',
73-
project: 'chatbot',
74-
environment: 'production',
75-
76-
total_tokens: 15000,
77-
total_requests: 100,
78-
avg_duration: 250.5
79-
},
80-
{
81-
date: '2024-01-01',
82-
model: 'gpt-3.5-turbo',
83-
provider: 'OpenAI',
84-
organization: 'Beta Corp',
85-
project: 'support',
86-
environment: 'staging',
87-
88-
total_tokens: 8000,
89-
total_requests: 50,
90-
avg_duration: 150.2
91-
},
92-
{
93-
date: '2024-01-01',
94-
model: 'gpt-4',
95-
provider: 'OpenAI',
41+
timestamp: '2024-01-01T12:00:00',
9642
organization: 'Acme Inc',
9743
project: 'chatbot',
9844
environment: 'production',
9945
100-
total_tokens: 15000,
101-
total_requests: 100,
102-
avg_duration: 250.5
103-
},
104-
{
105-
date: '2024-01-01',
106-
model: 'gpt-3.5-turbo',
107-
provider: 'OpenAI',
108-
organization: 'Beta Corp',
109-
project: 'support',
110-
environment: 'staging',
111-
112-
total_tokens: 8000,
113-
total_requests: 50,
114-
avg_duration: 150.2
115-
},
116-
{
117-
date: '2024-01-01',
118-
model: 'gpt-4',
119-
provider: 'OpenAI',
120-
organization: 'Acme Inc',
121-
project: 'chatbot',
122-
environment: 'production',
123-
124-
total_tokens: 15000,
125-
total_requests: 100,
126-
avg_duration: 250.5
127-
},
128-
{
129-
date: '2024-01-01',
130-
model: 'gpt-3.5-turbo',
131-
provider: 'OpenAI',
132-
organization: 'Beta Corp',
133-
project: 'support',
134-
environment: 'staging',
135-
136-
total_tokens: 8000,
137-
total_requests: 50,
138-
avg_duration: 150.2
139-
},
140-
{
141-
date: '2024-01-01',
46+
chat_id: 'chat_123',
14247
model: 'gpt-4',
14348
provider: 'OpenAI',
144-
organization: 'Acme Inc',
145-
project: 'chatbot',
146-
environment: 'production',
147-
49+
prompt_tokens: 10000,
50+
completion_tokens: 5000,
14851
total_tokens: 15000,
149-
total_requests: 100,
150-
avg_duration: 250.5
52+
duration: 250.5,
53+
cost: 0.12,
54+
response_status: 'success',
55+
exception: null
15156
},
152-
{
153-
date: '2024-01-01',
154-
model: 'gpt-3.5-turbo',
155-
provider: 'OpenAI',
156-
organization: 'Beta Corp',
157-
project: 'support',
158-
environment: 'staging',
159-
160-
total_tokens: 8000,
161-
total_requests: 50,
162-
avg_duration: 150.2
163-
},
164-
// Add more mock entries as needed
57+
// More mock data entries...
16558
]
16659
};
16760

168-
interface DataTableProps {
169-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
170-
data?: any; // We'll ignore the passed data for now and use mock data
171-
}
172-
173-
export default function DataTable({ data = MOCK_DATA }: DataTableProps) {
174-
// const [selectedStatus, setSelectedStatus] = useState<string[]>([]);
175-
// const [selectedOwners, setSelectedOwners] = useState<string[]>([]);
176-
177-
// const isStatusSelected = (item: any) =>
178-
// (selectedStatus.includes(item.model) || selectedStatus.length === 0) &&
179-
// (selectedOwners.includes(item.organization) || selectedOwners.length === 0);
180-
181-
// const filteredData = data.data.filter((item) => isStatusSelected(item));
182-
183-
// Get unique values for filters
184-
// const models = Array.from(new Set(MOCK_DATA.data.map(item => item.model)));
185-
// const organizations = Array.from(new Set(MOCK_DATA.data.map(item => item.organization)));
61+
export default function DataTable({ data = MOCK_DATA, isLoading = false }: DataTableProps) {
62+
if (isLoading) {
63+
return (
64+
<div className="flex items-center justify-center h-full">
65+
<div className="text-white">Loading messages...</div>
66+
</div>
67+
);
68+
}
18669

18770
return (
18871
<div className="flex flex-col h-full">
18972
<div className="flex-none">
19073
<div className="md:flex md:items-center md:justify-between md:space-x-8">
19174
{/* <div>
19275
<h3 className="font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong">
193-
Recent Activity
76+
Recent Messages
19477
</h3>
19578
<p className="mt-1 text-tremor-default leading-6 text-tremor-content dark:text-dark-tremor-content">
196-
Overview of recent LLM usage across your organization.
79+
Detailed view of recent LLM interactions
19780
</p>
19881
</div> */}
199-
{/* <div className="mt-4 sm:flex sm:items-center sm:space-x-2 md:mt-0">
200-
<MultiSelect
201-
onValueChange={setSelectedStatus}
202-
placeholder="Select model..."
203-
className="w-full sm:w-44 [&>button]:rounded-tremor-small"
204-
>
205-
{models.map(model => (
206-
<MultiSelectItem key={model} value={model}>
207-
{model}
208-
</MultiSelectItem>
209-
))}
210-
</MultiSelect>
211-
<Select
212-
onValueChange={(value) => setSelectedOwners([value])}
213-
placeholder="Select organization..."
214-
className="mt-2 w-full sm:mt-0 sm:w-44 [&>button]:rounded-tremor-small"
215-
>
216-
{organizations.map(org => (
217-
<SelectItem key={org} value={org}>
218-
{org}
219-
</SelectItem>
220-
))}
221-
</Select>
222-
</div> */}
22382
</div>
22483
</div>
22584

@@ -228,37 +87,50 @@ export default function DataTable({ data = MOCK_DATA }: DataTableProps) {
22887
<Table>
22988
<TableHead className="sticky top-0 bg-gray-900 z-10">
23089
<TableRow>
231-
<TableHeaderCell>Date</TableHeaderCell>
90+
<TableHeaderCell>Timestamp</TableHeaderCell>
23291
<TableHeaderCell>Model</TableHeaderCell>
92+
<TableHeaderCell>Provider</TableHeaderCell>
23393
<TableHeaderCell>Organization</TableHeaderCell>
23494
<TableHeaderCell>Project</TableHeaderCell>
235-
<TableHeaderCell>Environment</TableHeaderCell>
23695
<TableHeaderCell>User</TableHeaderCell>
237-
<TableHeaderCell>Tokens</TableHeaderCell>
238-
<TableHeaderCell>Requests</TableHeaderCell>
239-
<TableHeaderCell>Avg Duration</TableHeaderCell>
96+
<TableHeaderCell>Prompt Tokens</TableHeaderCell>
97+
<TableHeaderCell>Completion Tokens</TableHeaderCell>
98+
<TableHeaderCell>Total Tokens</TableHeaderCell>
99+
<TableHeaderCell>Duration (ms)</TableHeaderCell>
100+
<TableHeaderCell>Cost ($)</TableHeaderCell>
101+
<TableHeaderCell>Status</TableHeaderCell>
240102
</TableRow>
241103
</TableHead>
242104
<TableBody>
243-
{data.data.length > 0 ? (
244-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
245-
data.data.map((item: any, idx: number) => (
105+
{data.data && data.data.length > 0 ? (
106+
data.data.map((item, idx) => (
246107
<TableRow key={idx}>
247-
<TableCell>{new Date(item.date).toLocaleDateString()}</TableCell>
108+
<TableCell>{format(new Date(item.timestamp), 'MMM d, yyyy HH:mm:ss')}</TableCell>
248109
<TableCell>{item.model}</TableCell>
110+
<TableCell>{item.provider}</TableCell>
249111
<TableCell>{item.organization}</TableCell>
250112
<TableCell>{item.project}</TableCell>
251-
<TableCell>{item.environment}</TableCell>
252113
<TableCell>{item.user}</TableCell>
114+
<TableCell>{item.prompt_tokens.toLocaleString()}</TableCell>
115+
<TableCell>{item.completion_tokens.toLocaleString()}</TableCell>
253116
<TableCell>{item.total_tokens.toLocaleString()}</TableCell>
254-
<TableCell>{item.total_requests.toLocaleString()}</TableCell>
255-
<TableCell>{item.avg_duration.toFixed(2)}ms</TableCell>
117+
<TableCell>{item.duration.toFixed(2)}</TableCell>
118+
<TableCell>${item.cost.toFixed(4)}</TableCell>
119+
<TableCell>
120+
<span className={`px-2 py-1 rounded-full text-xs ${
121+
item.response_status === 'success'
122+
? 'bg-green-100 text-green-800'
123+
: 'bg-red-100 text-red-800'
124+
}`}>
125+
{item.response_status}
126+
</span>
127+
</TableCell>
256128
</TableRow>
257129
))
258130
) : (
259131
<TableRow>
260-
<TableCell colSpan={9} className="text-center">
261-
No results.
132+
<TableCell colSpan={12} className="text-center">
133+
No messages found.
262134
</TableCell>
263135
</TableRow>
264136
)}
Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
1-
import { Card } from '@tremor/react';
1+
'use client';
2+
23
import DataTable from '../components/DataTable';
4+
import { useLLMMessages } from '@/hooks/useTinybirdData';
35

46
interface DataTableContainerProps {
5-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6-
data: any;
7-
isLoading: boolean;
7+
filters: Record<string, string>;
8+
isLoading?: boolean;
89
}
910

10-
export default function DataTableContainer({ data, isLoading }: DataTableContainerProps) {
11-
if (isLoading) return <div>Loading...</div>;
11+
export default function DataTableContainer({ filters, isLoading: parentLoading }: DataTableContainerProps) {
12+
// Use the LLM messages hook
13+
const { data: messagesData, isLoading: messagesLoading } = useLLMMessages(filters);
14+
15+
// Combine loading states
16+
const isLoading = parentLoading || messagesLoading;
1217

1318
return (
14-
<Card className="h-full rounded-none flex flex-col overflow-hidden" style={{ boxShadow: 'none' }}>
15-
<div className="flex-1 overflow-auto">
16-
<DataTable data={data} />
17-
</div>
18-
</Card>
19+
<div className="h-full p-4">
20+
<DataTable
21+
data={messagesData}
22+
isLoading={isLoading}
23+
/>
24+
</div>
1925
);
2026
}

dashboard/ai-analytics/src/app/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ function DashboardContent() {
135135
/>
136136
</div>
137137
<div className="h-[35vh] border-r border-gray-700 overflow-hidden">
138-
<DataTableContainer data={llmData} isLoading={isLoading} />
138+
<DataTableContainer
139+
isLoading={isLoading}
140+
filters={filters}
141+
/>
139142
</div>
140143
</div>
141144

0 commit comments

Comments
 (0)