| import { useQuery } from '@tanstack/react-query';
|
| import { graphqlRequest } from '@/gql/graphql';
|
| import { useTranslation } from 'react-i18next';
|
| import { useSelectedProjectId } from '@/stores/projectStore';
|
| import { useErrorHandler } from '@/hooks/use-error-handler';
|
| import { useRequestPermissions } from '../../../hooks/useRequestPermissions';
|
| import {
|
| Request,
|
| RequestConnection,
|
| RequestExecutionConnection,
|
| requestConnectionSchema,
|
| requestExecutionConnectionSchema,
|
| requestSchema,
|
| } from './schema';
|
|
|
|
|
| function buildRequestsQuery(permissions: { canViewApiKeys: boolean; canViewChannels: boolean }) {
|
| const apiKeyFields = permissions.canViewApiKeys
|
| ? `
|
| apiKey {
|
| id
|
| name
|
| }`
|
| : '';
|
|
|
| const channelFields = permissions.canViewChannels
|
| ? `
|
| channel {
|
| id
|
| name
|
| }`
|
| : '';
|
|
|
| return `
|
| query GetRequests(
|
| $first: Int
|
| $after: Cursor
|
| $last: Int
|
| $before: Cursor
|
| $orderBy: RequestOrder
|
| $where: RequestWhereInput
|
| ) {
|
| requests(first: $first, after: $after, last: $last, before: $before, orderBy: $orderBy, where: $where) {
|
| edges {
|
| node {
|
| id
|
| createdAt
|
| updatedAt${apiKeyFields}${channelFields}
|
| source
|
| modelID
|
| stream
|
| status
|
| clientIP
|
| metricsLatencyMs
|
| metricsFirstTokenLatencyMs
|
| executions(first: 10, orderBy: { field: CREATED_AT, direction: DESC }) {
|
| edges {
|
| node {
|
| modelID
|
| status
|
| channel {
|
| id
|
| name
|
| }
|
| }
|
| cursor
|
| }
|
| pageInfo {
|
| hasNextPage
|
| hasPreviousPage
|
| startCursor
|
| endCursor
|
| }
|
| totalCount
|
| }
|
| usageLogs(first: 1) {
|
| edges {
|
| node {
|
| id
|
| promptTokens
|
| completionTokens
|
| totalTokens
|
| promptCachedTokens
|
| promptWriteCachedTokens
|
| totalCost
|
| }
|
| }
|
| }
|
| }
|
| cursor
|
| }
|
| pageInfo {
|
| hasNextPage
|
| hasPreviousPage
|
| startCursor
|
| endCursor
|
| }
|
| totalCount
|
| }
|
| }
|
| `;
|
| }
|
|
|
| function buildRequestDetailQuery(permissions: { canViewApiKeys: boolean; canViewChannels: boolean }) {
|
| const apiKeyFields = permissions.canViewApiKeys
|
| ? `
|
| apiKey {
|
| id
|
| name
|
| }`
|
| : '';
|
|
|
| const requestChannelFields = permissions.canViewChannels
|
| ? `
|
| channel {
|
| id
|
| name
|
| }`
|
| : '';
|
|
|
| return `
|
| query GetRequestDetail($id: ID!) {
|
| node(id: $id) {
|
| ... on Request {
|
| id
|
| createdAt
|
| updatedAt${apiKeyFields}${requestChannelFields}
|
| source
|
| modelID
|
| stream
|
| clientIP
|
| projectID
|
| dataStorageID
|
| requestHeaders
|
| requestBody
|
| responseBody
|
| responseChunks
|
| status
|
| format
|
| usageLogs(first: 1) {
|
| edges {
|
| node {
|
| id
|
| promptTokens
|
| completionTokens
|
| totalTokens
|
| promptCachedTokens
|
| promptWriteCachedTokens
|
| totalCost
|
| }
|
| }
|
| }
|
| }
|
| }
|
| }
|
| `;
|
| }
|
|
|
| function buildRequestExecutionsQuery(permissions: { canViewChannels: boolean }) {
|
| const channelFields = permissions.canViewChannels
|
| ? `
|
| channel {
|
| id
|
| name
|
| type
|
| baseURL
|
| }`
|
| : '';
|
|
|
| return `
|
| query GetRequestExecutions(
|
| $requestID: ID!
|
| $first: Int
|
| $after: Cursor
|
| $orderBy: RequestExecutionOrder
|
| $where: RequestExecutionWhereInput
|
| ) {
|
| node(id: $requestID) {
|
| ... on Request {
|
| executions(first: $first, after: $after, orderBy: $orderBy, where: $where) {
|
| edges {
|
| node {
|
| id
|
| createdAt
|
| updatedAt
|
| requestID${channelFields}
|
| modelID
|
| projectID
|
| dataStorageID
|
| requestHeaders
|
| requestBody
|
| responseBody
|
| responseChunks
|
| errorMessage
|
| status
|
| format
|
| stream
|
| metricsFirstTokenLatencyMs
|
| }
|
| cursor
|
| }
|
| pageInfo {
|
| hasNextPage
|
| hasPreviousPage
|
| startCursor
|
| endCursor
|
| }
|
| totalCount
|
| }
|
| }
|
| }
|
| }
|
| `;
|
| }
|
|
|
|
|
| export function useRequests(variables?: {
|
| first?: number;
|
| after?: string;
|
| last?: number;
|
| before?: string;
|
| orderBy?: { field: 'CREATED_AT'; direction: 'ASC' | 'DESC' };
|
| where?: {
|
| status?: string;
|
| source?: string;
|
| channelID?: string;
|
| channelIDIn?: string[];
|
| statusIn?: string[];
|
| sourceIn?: string[];
|
| projectID?: string;
|
| [key: string]: any;
|
| };
|
| }) {
|
| const { handleError } = useErrorHandler();
|
| const { t } = useTranslation();
|
| const permissions = useRequestPermissions();
|
| const selectedProjectId = useSelectedProjectId();
|
|
|
| return useQuery({
|
| queryKey: ['requests', variables, permissions, selectedProjectId],
|
| queryFn: async () => {
|
| try {
|
| const query = buildRequestsQuery(permissions);
|
| const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined;
|
|
|
|
|
| const finalVariables = {
|
| ...variables,
|
| where: {
|
| ...variables?.where,
|
| ...(selectedProjectId && { projectID: selectedProjectId }),
|
| },
|
| };
|
|
|
| const data = await graphqlRequest<{ requests: RequestConnection }>(query, finalVariables, headers);
|
| return requestConnectionSchema.parse(data?.requests);
|
| } catch (error) {
|
| handleError(error, t('requests.errors.loadRequestsFailed'));
|
| throw error;
|
| }
|
| },
|
| enabled: true,
|
| });
|
| }
|
|
|
| export function useRequest(id: string) {
|
| const { handleError } = useErrorHandler();
|
| const { t } = useTranslation();
|
| const permissions = useRequestPermissions();
|
| const selectedProjectId = useSelectedProjectId();
|
|
|
| return useQuery({
|
| queryKey: ['request', id, permissions, selectedProjectId],
|
| queryFn: async () => {
|
| try {
|
| const query = buildRequestDetailQuery(permissions);
|
| const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined;
|
| const data = await graphqlRequest<{ node: Request }>(query, { id }, headers);
|
| if (!data.node) {
|
| throw new Error('Request not found');
|
| }
|
| return requestSchema.parse(data.node);
|
| } catch (error) {
|
| handleError(error, t('requests.errors.loadRequestDetailFailed'));
|
| throw error;
|
| }
|
| },
|
| enabled: !!id,
|
| });
|
| }
|
|
|
| export function useRequestExecutions(
|
| requestID: string,
|
| variables?: {
|
| first?: number;
|
| after?: string;
|
| orderBy?: { field: 'CREATED_AT'; direction: 'ASC' | 'DESC' };
|
| where?: Record<string, any>;
|
| }
|
| ) {
|
| const permissions = useRequestPermissions();
|
| const selectedProjectId = useSelectedProjectId();
|
|
|
| return useQuery({
|
| queryKey: ['request-executions', requestID, variables, permissions, selectedProjectId],
|
| queryFn: async () => {
|
| const query = buildRequestExecutionsQuery(permissions);
|
| const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined;
|
| const finalVariables = {
|
| requestID,
|
| ...variables,
|
| };
|
| const data = await graphqlRequest<{ node: { executions: RequestExecutionConnection } }>(query, finalVariables, headers);
|
| return requestExecutionConnectionSchema.parse(data?.node?.executions);
|
| },
|
| enabled: !!requestID,
|
| });
|
| }
|
|
|