mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-23 11:50:42 +01:00
Compare commits
1 Commits
fix/dashbo
...
chore/chec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4949ed76d |
@@ -23,3 +23,4 @@ We **recommend** (almost enforce) reviewing these guides before contributing to
|
||||
- [SQL](sql.md) - Database and SQL patterns
|
||||
- [DSL Filtering to SQL](dslfilteringtosql.md) - Compiling the list filter DSL to relational-store WHERE clauses
|
||||
- [Types](types.md) - Domain types, request/response bodies, and storage rows in `pkg/types/`
|
||||
sdsdcdsc
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { Tabs } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useConfirmableAction } from 'hooks/useConfirmableAction';
|
||||
|
||||
import AttributeMappingActions from './components/AttributeMappingActions/AttributeMappingActions';
|
||||
@@ -21,10 +20,6 @@ function LLMObservabilityAttributeMapping(): JSX.Element {
|
||||
const groupDrawer = useGroupFormDrawer();
|
||||
const spanTest = useTestSpanMapper(editor.snapshot, editor.groups);
|
||||
|
||||
useEffect(() => {
|
||||
void logEvent('AI Observability Attribute Mapping: Page visited', {});
|
||||
}, []);
|
||||
|
||||
const { discard } = editor;
|
||||
// Discarding wipes the whole working copy, so gate it behind a confirm
|
||||
// prompt rather than firing straight from the button.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
RenderErrorResponseDTO,
|
||||
SpantypesSpanMapperTestSpanDTO,
|
||||
@@ -127,7 +126,6 @@ export function useTestSpanMapper(
|
||||
{ data: body },
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
void logEvent('AI Observability Attribute Mapping: Test run', {});
|
||||
setTestedAttributes(submittedAttributes);
|
||||
setTestedResource(submittedResource);
|
||||
setResult(response.data?.spans ?? []);
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { cloneDeep, isEqual } from 'lodash-es';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
useCreateSpanMapper,
|
||||
useCreateSpanMapperGroup,
|
||||
@@ -263,7 +262,6 @@ export function useAttributeMappingEditor(): AttributeMappingEditor {
|
||||
setSaveError(null);
|
||||
try {
|
||||
await persistDraft(snapshot, draft, mutations);
|
||||
void logEvent('AI Observability Attribute Mapping: Changes saved', {});
|
||||
// Refresh the groups list in place — it stays mounted, so this just
|
||||
// swaps in fresh data without a loading flash. Using the query's own
|
||||
// refetch keeps it scoped to the groups list; the per-group mapper
|
||||
|
||||
@@ -173,7 +173,7 @@ function Explorer(): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
logEvent('AI Observability Explorer: Page visited', {});
|
||||
logEvent('Traces Explorer: Page visited', {});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -173,7 +173,7 @@ function ListView({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isFetching && !isError && rows.length !== 0) {
|
||||
void logEvent('AI Observability Explorer: Data present', {
|
||||
void logEvent('Traces Explorer: Data present', {
|
||||
panelType,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ function TracesView({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && !isFetching && !isError && rows.length !== 0) {
|
||||
void logEvent('AI Observability Explorer: Data present', {
|
||||
void logEvent('Traces Explorer: Data present', {
|
||||
panelType: 'TRACE',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import Spinner from 'components/Spinner';
|
||||
import DashboardContainer from 'pages/DashboardPage/DashboardContainer';
|
||||
|
||||
@@ -10,10 +8,6 @@ import styles from './Overview.module.scss';
|
||||
function Overview(): JSX.Element {
|
||||
const { dashboard, isLoading, isError, error, refetch } = useSystemDashboard();
|
||||
|
||||
useEffect(() => {
|
||||
void logEvent('AI Observability Overview: Page visited', {});
|
||||
}, []);
|
||||
|
||||
const renderContent = (): JSX.Element => {
|
||||
if (isLoading) {
|
||||
return <Spinner tip="Loading dashboard..." />;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Tabs } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useListUnmappedLLMModels } from 'api/generated/services/llmpricingrules';
|
||||
import { parseAsStringEnum, useQueryState } from 'nuqs';
|
||||
|
||||
@@ -22,10 +20,6 @@ function LLMObservabilityModelPricing(): JSX.Element {
|
||||
const { data } = useListUnmappedLLMModels();
|
||||
const unpricedCount = data?.data?.items?.length ?? 0;
|
||||
|
||||
useEffect(() => {
|
||||
void logEvent('AI Observability Model Pricing: Page visited', {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.llmObservabilityModelPricing}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
getListLLMPricingRulesQueryKey,
|
||||
getListUnmappedLLMModelsQueryKey,
|
||||
@@ -95,10 +94,6 @@ export function useModelCostDrawer(): UseModelCostDrawerResult {
|
||||
await createOrUpdate({
|
||||
data: { rules: [buildRulePayload(draft)] },
|
||||
});
|
||||
void logEvent('AI Observability Model Pricing: Model cost saved', {
|
||||
mode,
|
||||
modelName: draft.modelName,
|
||||
});
|
||||
await invalidateList();
|
||||
setIsOpen(false);
|
||||
setSelectedRuleId(null);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
getListLLMPricingRulesQueryKey,
|
||||
useDeleteLLMPricingRule,
|
||||
@@ -47,9 +46,6 @@ export function useModelCostDelete(): UseModelCostDeleteResult {
|
||||
}
|
||||
try {
|
||||
await deleteRuleApi({ pathParams: { id: pendingDelete.id } });
|
||||
void logEvent('AI Observability Model Pricing: Model cost deleted', {
|
||||
modelName: pendingDelete.modelName,
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getListLLMPricingRulesQueryKey(),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import {
|
||||
getListLLMPricingRulesQueryKey,
|
||||
getListUnmappedLLMModelsQueryKey,
|
||||
@@ -46,10 +45,6 @@ export function useUnpricedModelMapping(): UseUnpricedModelMappingResult {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await createOrUpdate({ data: { rules: [payload] } });
|
||||
void logEvent('AI Observability Model Pricing: Unpriced model mapped', {
|
||||
modelName: model.modelName,
|
||||
billingModelName: rule.modelName,
|
||||
});
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getListUnmappedLLMModelsQueryKey(),
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { QueryClient, QueryClientProvider, UseQueryResult } from 'react-query';
|
||||
import { renderHook, RenderHookResult, waitFor } from '@testing-library/react';
|
||||
import { AxiosError, AxiosHeaders } from 'axios';
|
||||
import { queryRangeV5 } from 'api/generated/services/querier';
|
||||
import type {
|
||||
Querybuildertypesv5QueryRangeRequestDTO,
|
||||
QueryRangeV5200,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { useGetQueryRangeV5 } from '../useGetQueryRangeV5';
|
||||
|
||||
jest.mock('api/generated/services/querier', () => ({
|
||||
queryRangeV5: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockQueryRangeV5 = queryRangeV5 as jest.Mock;
|
||||
|
||||
const REQUEST = {} as Querybuildertypesv5QueryRangeRequestDTO;
|
||||
const KEY_A = ['query-range', 'panel-1', 'window-a'];
|
||||
const KEY_B = ['query-range', 'panel-1', 'window-b'];
|
||||
|
||||
function clientError(): AxiosError {
|
||||
return new AxiosError('bad query', 'ERR_BAD_REQUEST', undefined, undefined, {
|
||||
status: 400,
|
||||
statusText: 'Bad Request',
|
||||
data: {},
|
||||
headers: {},
|
||||
config: { headers: new AxiosHeaders() },
|
||||
});
|
||||
}
|
||||
|
||||
interface Props {
|
||||
enabled: boolean;
|
||||
queryKey?: unknown[];
|
||||
}
|
||||
|
||||
function renderQuery(
|
||||
initial: Props,
|
||||
client = new QueryClient(),
|
||||
): RenderHookResult<UseQueryResult<QueryRangeV5200, Error>, Props> {
|
||||
const wrapper = ({ children }: PropsWithChildren): JSX.Element => (
|
||||
<QueryClientProvider client={client}>{children}</QueryClientProvider>
|
||||
);
|
||||
return renderHook(
|
||||
({ enabled, queryKey = KEY_A }: Props) =>
|
||||
useGetQueryRangeV5({ requestPayload: REQUEST, queryKey, enabled }),
|
||||
{ wrapper, initialProps: initial },
|
||||
);
|
||||
}
|
||||
|
||||
describe('useGetQueryRangeV5 enabled gating', () => {
|
||||
beforeEach(() => {
|
||||
mockQueryRangeV5.mockReset();
|
||||
});
|
||||
|
||||
it('does not fetch while disabled and fetches once when enabled', async () => {
|
||||
mockQueryRangeV5.mockResolvedValue({ status: 'success', data: {} });
|
||||
const { rerender } = renderQuery({ enabled: false });
|
||||
expect(mockQueryRangeV5).not.toHaveBeenCalled();
|
||||
|
||||
rerender({ enabled: true });
|
||||
await waitFor(() => expect(mockQueryRangeV5).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
it('serves a successful key from cache when re-enabled', async () => {
|
||||
mockQueryRangeV5.mockResolvedValue({ status: 'success', data: {} });
|
||||
const { result, rerender } = renderQuery({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
|
||||
rerender({ enabled: false });
|
||||
rerender({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isFetching).toBe(false));
|
||||
expect(mockQueryRangeV5).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not re-run an errored key when re-enabled', async () => {
|
||||
mockQueryRangeV5.mockRejectedValue(clientError());
|
||||
const { result, rerender } = renderQuery({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isError).toBe(true));
|
||||
|
||||
rerender({ enabled: false });
|
||||
rerender({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isFetching).toBe(false));
|
||||
expect(mockQueryRangeV5).toHaveBeenCalledTimes(1);
|
||||
expect(result.current.isError).toBe(true);
|
||||
});
|
||||
|
||||
it('still gates a new key while disabled after a prior key errored', async () => {
|
||||
mockQueryRangeV5.mockRejectedValue(clientError());
|
||||
const { result, rerender } = renderQuery({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isError).toBe(true));
|
||||
|
||||
rerender({ enabled: false });
|
||||
rerender({ enabled: false, queryKey: KEY_B });
|
||||
await waitFor(() => expect(result.current.isFetching).toBe(false));
|
||||
expect(mockQueryRangeV5).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender({ enabled: true, queryKey: KEY_B });
|
||||
await waitFor(() => expect(mockQueryRangeV5).toHaveBeenCalledTimes(2));
|
||||
});
|
||||
|
||||
it('re-runs an errored key on manual refetch', async () => {
|
||||
mockQueryRangeV5.mockRejectedValue(clientError());
|
||||
const { result } = renderQuery({ enabled: true });
|
||||
await waitFor(() => expect(result.current.isError).toBe(true));
|
||||
|
||||
mockQueryRangeV5.mockResolvedValue({ status: 'success', data: {} });
|
||||
await result.current.refetch();
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
expect(mockQueryRangeV5).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useQuery, useQueryClient, UseQueryResult } from 'react-query';
|
||||
import { useQuery, UseQueryResult } from 'react-query';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { queryRangeV5 } from 'api/generated/services/querier';
|
||||
import type {
|
||||
@@ -50,15 +50,10 @@ export function useGetQueryRangeV5({
|
||||
keepPreviousData,
|
||||
cacheTime,
|
||||
}: UseGetQueryRangeV5Args): UseQueryResult<QueryRangeV5200, Error> {
|
||||
const queryClient = useQueryClient();
|
||||
// An errored key has no data, so it is always stale; keep it enabled so
|
||||
// re-enabling doesn't re-run it.
|
||||
const hasErrored = queryClient.getQueryState(queryKey)?.status === 'error';
|
||||
|
||||
return useQuery<QueryRangeV5200, Error>({
|
||||
queryKey,
|
||||
queryFn: ({ signal }) => queryRangeV5(requestPayload, signal),
|
||||
enabled: enabled || hasErrored,
|
||||
enabled,
|
||||
retry: retryUnlessClientError,
|
||||
keepPreviousData,
|
||||
cacheTime,
|
||||
|
||||
@@ -9,7 +9,6 @@ import { authToken } from '../../helpers/common';
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
const GROUP_NAME = 'e2e-attr-mapping-happy';
|
||||
const GROUP_CONDITION = 'my_company.llm.';
|
||||
|
||||
const TARGET_ATTR = 'gen_ai.content.prompt';
|
||||
const SOURCE_ATTR = 'my_company.llm.input';
|
||||
@@ -35,8 +34,6 @@ test.describe('LLM Observability — Attribute Mapping', () => {
|
||||
const groupDrawer = page.getByTestId('group-form-drawer');
|
||||
await expect(groupDrawer).toBeVisible();
|
||||
await page.getByTestId('group-form-name').fill(GROUP_NAME);
|
||||
await page.getByTestId('group-form-attribute-add').click();
|
||||
await page.getByTestId('group-form-attribute-0').fill(GROUP_CONDITION);
|
||||
await page.getByTestId('group-form-save').click();
|
||||
await expect(groupDrawer).toBeHidden();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user