feat: add openai monitor
This commit is contained in:
parent
604e6ef454
commit
ef00122752
@ -124,7 +124,7 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<div className="text-black text-opacity-75">
|
<div className="text-black dark:text-gray-200 text-opacity-75">
|
||||||
Monitored for {dayjs().diff(dayjs(monitorInfo.createdAt), 'days')}{' '}
|
Monitored for {dayjs().diff(dayjs(monitorInfo.createdAt), 'days')}{' '}
|
||||||
days
|
days
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,10 +3,12 @@ import { MonitorInfo } from '../../../../types';
|
|||||||
import { pingProvider } from './ping';
|
import { pingProvider } from './ping';
|
||||||
import { httpProvider } from './http';
|
import { httpProvider } from './http';
|
||||||
import { MonitorProvider } from './types';
|
import { MonitorProvider } from './types';
|
||||||
|
import { openaiProvider } from './openai';
|
||||||
|
|
||||||
export const monitorProviders: MonitorProvider[] = [
|
export const monitorProviders: MonitorProvider[] = [
|
||||||
pingProvider, // ping
|
pingProvider, // ping
|
||||||
httpProvider, // http
|
httpProvider, // http
|
||||||
|
openaiProvider, // http
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getMonitorProvider(type: string) {
|
export function getMonitorProvider(type: string) {
|
||||||
@ -20,7 +22,7 @@ export function getMonitorProvider(type: string) {
|
|||||||
|
|
||||||
export function getMonitorLink(info: MonitorInfo): React.ReactNode {
|
export function getMonitorLink(info: MonitorInfo): React.ReactNode {
|
||||||
const provider = getMonitorProvider(info.type);
|
const provider = getMonitorProvider(info.type);
|
||||||
if (!provider) {
|
if (!provider || !provider.link) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
63
src/client/components/monitor/provider/openai.tsx
Normal file
63
src/client/components/monitor/provider/openai.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { Form, Input } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { MonitorOverviewComponent, MonitorProvider } from './types';
|
||||||
|
import { useCurrentWorkspaceId } from '../../../store/user';
|
||||||
|
import { trpc } from '../../../api/trpc';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { MonitorStatsBlock } from '../MonitorStatsBlock';
|
||||||
|
import { isEmpty } from 'lodash-es';
|
||||||
|
|
||||||
|
export const MonitorOpenai: React.FC = React.memo(() => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label="Session Key"
|
||||||
|
name={['payload', 'sessionKey']}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Input.Password
|
||||||
|
placeholder="sess-************"
|
||||||
|
visibilityToggle={false}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
MonitorOpenai.displayName = 'MonitorOpenai';
|
||||||
|
|
||||||
|
export const MonitorOpenaiOverview: MonitorOverviewComponent = React.memo(
|
||||||
|
(props) => {
|
||||||
|
const workspaceId = useCurrentWorkspaceId();
|
||||||
|
const { data } = trpc.monitor.getStatus.useQuery({
|
||||||
|
workspaceId,
|
||||||
|
monitorId: props.monitorId,
|
||||||
|
statusName: 'credit',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data || !data.payload || typeof data.payload !== 'object') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = data.payload as Record<string, any>;
|
||||||
|
|
||||||
|
if (isEmpty(payload.certInfo)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MonitorStatsBlock
|
||||||
|
title="Usage"
|
||||||
|
desc={dayjs(data.updatedAt).format('YYYY-MM-DD')}
|
||||||
|
text={`${payload.totalUsed} / ${payload.totalGranted}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
MonitorOpenaiOverview.displayName = 'MonitorOpenaiOverview';
|
||||||
|
|
||||||
|
export const openaiProvider: MonitorProvider = {
|
||||||
|
label: 'OpenAI',
|
||||||
|
name: 'openai',
|
||||||
|
form: MonitorOpenai,
|
||||||
|
overview: [MonitorOpenaiOverview],
|
||||||
|
};
|
@ -3,7 +3,7 @@ import { MonitorInfo } from '../../../../types';
|
|||||||
export interface MonitorProvider {
|
export interface MonitorProvider {
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
link: (info: MonitorInfo) => React.ReactNode;
|
link?: (info: MonitorInfo) => React.ReactNode;
|
||||||
form: React.ComponentType;
|
form: React.ComponentType;
|
||||||
overview?: MonitorOverviewComponent[];
|
overview?: MonitorOverviewComponent[];
|
||||||
}
|
}
|
||||||
|
32
src/server/model/monitor/provider/_utils.ts
Normal file
32
src/server/model/monitor/provider/_utils.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { prisma } from '../../_client';
|
||||||
|
|
||||||
|
export async function saveMonitorStatus(
|
||||||
|
monitorId: string,
|
||||||
|
statusName: string,
|
||||||
|
payload: Record<string, any>
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const res = await prisma.monitorStatus.upsert({
|
||||||
|
where: {
|
||||||
|
monitorId_statusName: {
|
||||||
|
monitorId,
|
||||||
|
statusName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
payload,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
monitorId,
|
||||||
|
statusName,
|
||||||
|
payload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { logger } from '../../../utils/logger';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { prisma } from '../../_client';
|
import { prisma } from '../../_client';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
|
import { saveMonitorStatus } from './_utils';
|
||||||
|
|
||||||
export const http: MonitorProvider<{
|
export const http: MonitorProvider<{
|
||||||
url: string;
|
url: string;
|
||||||
@ -81,27 +82,9 @@ export const http: MonitorProvider<{
|
|||||||
try {
|
try {
|
||||||
const { valid, certInfo } = checkCertificate(res);
|
const { valid, certInfo } = checkCertificate(res);
|
||||||
|
|
||||||
await prisma.monitorStatus.upsert({
|
await saveMonitorStatus(monitor.id, 'tls', {
|
||||||
where: {
|
|
||||||
monitorId_statusName: {
|
|
||||||
monitorId: monitor.id,
|
|
||||||
statusName: 'tls',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
payload: {
|
|
||||||
valid,
|
valid,
|
||||||
certInfo,
|
certInfo,
|
||||||
},
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
monitorId: monitor.id,
|
|
||||||
statusName: 'tls',
|
|
||||||
payload: {
|
|
||||||
valid,
|
|
||||||
certInfo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { http } from './http';
|
import { http } from './http';
|
||||||
import { ping } from './ping';
|
import { ping } from './ping';
|
||||||
|
import { openai } from './openai';
|
||||||
import type { MonitorProvider } from './type';
|
import type { MonitorProvider } from './type';
|
||||||
|
|
||||||
export const monitorProviders: Record<string, MonitorProvider<any>> = {
|
export const monitorProviders: Record<string, MonitorProvider<any>> = {
|
||||||
ping,
|
ping,
|
||||||
http,
|
http,
|
||||||
|
openai,
|
||||||
};
|
};
|
||||||
|
68
src/server/model/monitor/provider/openai.ts
Normal file
68
src/server/model/monitor/provider/openai.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { MonitorProvider } from './type';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { saveMonitorStatus } from './_utils';
|
||||||
|
|
||||||
|
const openaiCreditGrantsSchema = z.object({
|
||||||
|
object: z.string(),
|
||||||
|
total_granted: z.number(),
|
||||||
|
total_used: z.number(),
|
||||||
|
total_available: z.number(),
|
||||||
|
total_paid_available: z.number(),
|
||||||
|
grants: z.object({
|
||||||
|
object: z.string(),
|
||||||
|
data: z.array(
|
||||||
|
z.object({
|
||||||
|
object: z.string(),
|
||||||
|
id: z.string(),
|
||||||
|
grant_amount: z.number(),
|
||||||
|
used_amount: z.number(),
|
||||||
|
effective_at: z.number(),
|
||||||
|
expires_at: z.number(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const openai: MonitorProvider<{
|
||||||
|
sessionKey: string;
|
||||||
|
}> = {
|
||||||
|
run: async (monitor) => {
|
||||||
|
if (typeof monitor.payload !== 'object') {
|
||||||
|
throw new Error('monitor.payload should be object');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sessionKey } = monitor.payload;
|
||||||
|
|
||||||
|
const res = await getBillingCreditGrants(sessionKey);
|
||||||
|
|
||||||
|
const balance = res.total_granted - res.total_used;
|
||||||
|
|
||||||
|
await saveMonitorStatus(monitor.id, 'credit', {
|
||||||
|
totalGranted: res.total_granted,
|
||||||
|
totalUsed: res.total_used,
|
||||||
|
totalAvailable: res.total_available,
|
||||||
|
totalPaidAvailable: res.total_paid_available,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (balance <= 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return balance;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getBillingCreditGrants(sessionKey: string) {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
'https://api.openai.com/dashboard/billing/credit_grants',
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${sessionKey}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return openaiCreditGrantsSchema.parse(data);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user