Spaces:
Paused
Paused
| 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'; | |
| // Dynamic GraphQL query builder | |
| 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 | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| } | |
| // Query hooks | |
| 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; | |
| // Add project filter if project is selected | |
| 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, // Requests can be queried without project selection for admin users | |
| }); | |
| } | |
| 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, | |
| }); | |
| } | |