mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-25 12:50:47 +01:00
Compare commits
3 Commits
fix/dashbo
...
fix/opaque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7443ab1cd | ||
|
|
7ce73f3470 | ||
|
|
5aca8b0d3c |
@@ -80,15 +80,6 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
aiObservability := ah.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureEnableAIObservability, evalCtx)
|
||||
featureSet = append(featureSet, &licensetypes.Feature{
|
||||
Name: valuer.NewString(flagger.FeatureEnableAIObservability.String()),
|
||||
Active: aiObservability,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
})
|
||||
|
||||
metricsReduction := ah.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, evalCtx)
|
||||
featureSet = append(featureSet, &licensetypes.Feature{
|
||||
Name: valuer.NewString(flagger.FeatureEnableMetricsReduction.String()),
|
||||
|
||||
@@ -8,7 +8,6 @@ import { ORG_PREFERENCES } from 'constants/orgPreferences';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { useIsAIAssistantEnabled } from 'hooks/useIsAIAssistantEnabled';
|
||||
import { useIsAIObservabilityEnabled } from 'hooks/useIsAIObservabilityEnabled';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
@@ -44,7 +43,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
const isAIAssistantEnabled = useIsAIAssistantEnabled();
|
||||
const isAIObservabilityEnabled = useIsAIObservabilityEnabled();
|
||||
const mapRoutes = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
@@ -135,14 +133,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
return <Redirect to={ROUTES.HOME} />;
|
||||
}
|
||||
|
||||
if (
|
||||
(pathname.startsWith(`${ROUTES.AI_OBSERVABILITY_BASE}/`) ||
|
||||
pathname === ROUTES.AI_OBSERVABILITY_BASE) &&
|
||||
!isAIObservabilityEnabled
|
||||
) {
|
||||
return <Redirect to={ROUTES.HOME} />;
|
||||
}
|
||||
|
||||
// Check for workspace access restriction (cloud only)
|
||||
const isCloudPlatform = activeLicense?.platform === LicensePlatform.CLOUD;
|
||||
|
||||
|
||||
@@ -8,6 +8,5 @@ export enum FeatureKeys {
|
||||
PREMIUM_SUPPORT = 'premium_support',
|
||||
ANOMALY_DETECTION = 'anomaly_detection',
|
||||
USE_JSON_BODY = 'use_json_body',
|
||||
ENABLE_AI_OBSERVABILITY = 'enable_ai_observability',
|
||||
ENABLE_METRICS_REDUCTION = 'enable_metrics_reduction',
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { useIsAIAssistantEnabled } from 'hooks/useIsAIAssistantEnabled';
|
||||
import { useIsAIObservabilityEnabled } from 'hooks/useIsAIObservabilityEnabled';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import { isArray } from 'lodash-es';
|
||||
@@ -255,7 +254,6 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
const isEditor = user.role === USER_ROLES.EDITOR;
|
||||
const isAIAssistantEnabled = useIsAIAssistantEnabled();
|
||||
const isAIObservabilityEnabled = useIsAIObservabilityEnabled();
|
||||
const aiAssistantActiveConversationId = useAIAssistantStore(
|
||||
(s) => s.activeConversationId,
|
||||
);
|
||||
@@ -295,9 +293,6 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
|
||||
if (item.key === ROUTES.INTEGRATIONS) {
|
||||
return shouldShowIntegrationsValue;
|
||||
}
|
||||
if (item.key === ROUTES.AI_OBSERVABILITY_OVERVIEW) {
|
||||
return isAIObservabilityEnabled;
|
||||
}
|
||||
return item.isEnabled;
|
||||
};
|
||||
|
||||
@@ -314,7 +309,6 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
|
||||
isEnterpriseSelfHostedUser,
|
||||
isAdmin,
|
||||
isEditor,
|
||||
isAIObservabilityEnabled,
|
||||
]);
|
||||
|
||||
// Track if we've done the initial sync (to avoid overwriting user actions during session)
|
||||
|
||||
@@ -293,9 +293,7 @@ export const defaultMoreMenuItems: SidebarItem[] = [
|
||||
label: 'AI Observability',
|
||||
icon: <Brain size={16} />,
|
||||
isBeta: true,
|
||||
// Gated behind the `enable_ai_observability` feature flag in
|
||||
// SideNav's `computedSecondaryMenuItems`; disabled by default.
|
||||
isEnabled: false,
|
||||
isEnabled: true,
|
||||
itemKey: 'ai-observability',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
|
||||
export function useIsAIObservabilityEnabled(): boolean {
|
||||
const { featureFlags } = useAppContext();
|
||||
return (
|
||||
featureFlags?.find(
|
||||
(flag) => flag.name === FeatureKeys.ENABLE_AI_OBSERVABILITY,
|
||||
)?.active || false
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
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, UseQueryResult } from 'react-query';
|
||||
import { useQuery, useQueryClient, UseQueryResult } from 'react-query';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { queryRangeV5 } from 'api/generated/services/querier';
|
||||
import type {
|
||||
@@ -50,10 +50,15 @@ 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: enabled || hasErrored,
|
||||
retry: retryUnlessClientError,
|
||||
keepPreviousData,
|
||||
cacheTime,
|
||||
|
||||
@@ -9,7 +9,6 @@ var (
|
||||
FeaturePutMetersInZeus = featuretypes.MustNewName("put_meters_in_zeus")
|
||||
FeatureUseMeterReporter = featuretypes.MustNewName("use_meter_reporter")
|
||||
FeatureUseJSONBody = featuretypes.MustNewName("use_json_body")
|
||||
FeatureEnableAIObservability = featuretypes.MustNewName("enable_ai_observability")
|
||||
FeatureEnableMetricsReduction = featuretypes.MustNewName("enable_metrics_reduction")
|
||||
FeatureResolveSemconvFamilies = featuretypes.MustNewName("resolve_semconv_families")
|
||||
)
|
||||
@@ -64,14 +63,6 @@ func MustNewRegistry() featuretypes.Registry {
|
||||
DefaultVariant: featuretypes.MustNewName("disabled"),
|
||||
Variants: featuretypes.NewBooleanVariants(),
|
||||
},
|
||||
&featuretypes.Feature{
|
||||
Name: FeatureEnableAIObservability,
|
||||
Kind: featuretypes.KindBoolean,
|
||||
Stage: featuretypes.StageExperimental,
|
||||
Description: "Controls whether ai observability is enabled",
|
||||
DefaultVariant: featuretypes.MustNewName("disabled"),
|
||||
Variants: featuretypes.NewBooleanVariants(),
|
||||
},
|
||||
&featuretypes.Feature{
|
||||
Name: FeatureEnableMetricsReduction,
|
||||
Kind: featuretypes.KindBoolean,
|
||||
|
||||
@@ -1478,15 +1478,6 @@ func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
aiObservability := aH.Signoz.Flagger.BooleanOrEmpty(r.Context(), flagger.FeatureEnableAIObservability, evalCtx)
|
||||
featureSet = append(featureSet, &licensetypes.Feature{
|
||||
Name: valuer.NewString(flagger.FeatureEnableAIObservability.String()),
|
||||
Active: aiObservability,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
})
|
||||
|
||||
aH.Respond(w, featureSet)
|
||||
}
|
||||
|
||||
|
||||
@@ -364,12 +364,26 @@ func (provider *provider) gc(ctx context.Context, org *types.Organization) error
|
||||
}
|
||||
|
||||
func (provider *provider) flushLastObservedAt(ctx context.Context, org *types.Organization) error {
|
||||
accessTokenToLastObservedAt, err := provider.listLastObservedAtDesc(ctx, org.ID)
|
||||
tokens, err := provider.tokenStore.ListByOrgID(ctx, org.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provider.tokenStore.UpdateLastObservedAtByAccessToken(ctx, accessTokenToLastObservedAt); err != nil {
|
||||
observedTokens := make([]*authtypes.StorableToken, 0, len(tokens))
|
||||
for _, token := range tokens {
|
||||
cachedLastObservedAt, ok := provider.lastObservedAtCache.Get(lastObservedAtCacheKey(token.AccessToken, token.UserID))
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := token.UpdateLastObservedAt(cachedLastObservedAt); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
observedTokens = append(observedTokens, token)
|
||||
}
|
||||
|
||||
if err := provider.tokenStore.UpdateLastObservedAt(ctx, observedTokens); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -232,15 +232,16 @@ func (store *store) ListByUserID(ctx context.Context, userID valuer.UUID) ([]*au
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (store *store) UpdateLastObservedAtByAccessToken(ctx context.Context, accessTokenToLastObservedAt []map[string]any) error {
|
||||
if len(accessTokenToLastObservedAt) == 0 {
|
||||
func (store *store) UpdateLastObservedAt(ctx context.Context, tokens []*authtypes.StorableToken) error {
|
||||
if len(tokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
values := store.
|
||||
sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewValues(&accessTokenToLastObservedAt)
|
||||
NewValues(&tokens).
|
||||
Column("id", "last_observed_at", "updated_at")
|
||||
|
||||
_, err := store.
|
||||
sqlstore.
|
||||
@@ -250,8 +251,8 @@ func (store *store) UpdateLastObservedAtByAccessToken(ctx context.Context, acces
|
||||
Model((*authtypes.StorableToken)(nil)).
|
||||
TableExpr("update_cte").
|
||||
Set("last_observed_at = update_cte.last_observed_at").
|
||||
Where("auth_token.access_token = update_cte.access_token").
|
||||
Where("auth_token.user_id = update_cte.user_id").
|
||||
Set("updated_at = update_cte.updated_at").
|
||||
Where("auth_token.id = update_cte.id").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
74
pkg/tokenizer/tokenizerstore/sqltokenizerstore/store_test.go
Normal file
74
pkg/tokenizer/tokenizerstore/sqltokenizerstore/store_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package sqltokenizerstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstoretest"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUpdateLastObservedAt(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
provider string
|
||||
tokens []*authtypes.StorableToken
|
||||
expectedQuery string
|
||||
}{
|
||||
{
|
||||
name: "Sqlite_Empty",
|
||||
provider: "sqlite",
|
||||
tokens: nil,
|
||||
expectedQuery: "",
|
||||
},
|
||||
{
|
||||
name: "Postgres_Empty",
|
||||
provider: "postgres",
|
||||
tokens: []*authtypes.StorableToken{},
|
||||
expectedQuery: "",
|
||||
},
|
||||
{
|
||||
name: "Sqlite_OneToken",
|
||||
provider: "sqlite",
|
||||
tokens: []*authtypes.StorableToken{
|
||||
{ID: valuer.MustNewUUID("019984d1-0000-7000-8000-000000000001"), AccessToken: "access-one", RefreshToken: "refresh-one", LastObservedAt: time.Date(2026, 9, 22, 10, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 9, 22, 10, 0, 1, 0, time.UTC)},
|
||||
},
|
||||
expectedQuery: `WITH "update_cte" ("id", "last_observed_at", "updated_at") AS (VALUES ('019984d1-0000-7000-8000-000000000001', '2026-09-22 10:00:00+00:00', '2026-09-22 10:00:01+00:00')) UPDATE "auth_token" AS "auth_token" SET last_observed_at = update_cte.last_observed_at, updated_at = update_cte.updated_at FROM update_cte WHERE (auth_token.id = update_cte.id)`,
|
||||
},
|
||||
{
|
||||
name: "Postgres_TwoTokens",
|
||||
provider: "postgres",
|
||||
tokens: []*authtypes.StorableToken{
|
||||
{ID: valuer.MustNewUUID("019984d1-0000-7000-8000-000000000002"), AccessToken: "access-two", RefreshToken: "refresh-two", LastObservedAt: time.Date(2026, 9, 22, 11, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 9, 22, 11, 0, 1, 0, time.UTC)},
|
||||
{ID: valuer.MustNewUUID("019984d1-0000-7000-8000-000000000003"), AccessToken: "access-three", RefreshToken: "refresh-three", LastObservedAt: time.Date(2026, 9, 22, 12, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 9, 22, 12, 0, 1, 0, time.UTC)},
|
||||
},
|
||||
expectedQuery: `WITH "update_cte" ("id", "last_observed_at", "updated_at") AS (VALUES ('019984d1-0000-7000-8000-000000000002'::text, '2026-09-22 11:00:00+00:00'::TIMESTAMPTZ, '2026-09-22 11:00:01+00:00'::TIMESTAMPTZ), ('019984d1-0000-7000-8000-000000000003'::text, '2026-09-22 12:00:00+00:00'::TIMESTAMPTZ, '2026-09-22 12:00:01+00:00'::TIMESTAMPTZ)) UPDATE "auth_token" AS "auth_token" SET last_observed_at = update_cte.last_observed_at, updated_at = update_cte.updated_at FROM update_cte WHERE (auth_token.id = update_cte.id)`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
var executedQuery string
|
||||
matcher := sqlmock.QueryMatcherFunc(func(_, actual string) error {
|
||||
executedQuery = actual
|
||||
return nil
|
||||
})
|
||||
|
||||
sqlStore := sqlstoretest.New(sqlstore.Config{Provider: testCase.provider}, matcher)
|
||||
if testCase.expectedQuery != "" {
|
||||
sqlStore.Mock().ExpectExec("").WillReturnResult(sqlmock.NewResult(0, int64(len(testCase.tokens))))
|
||||
}
|
||||
|
||||
err := NewStore(sqlStore).UpdateLastObservedAt(context.Background(), testCase.tokens)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, sqlStore.Mock().ExpectationsWereMet())
|
||||
assert.Equal(t, testCase.expectedQuery, executedQuery)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,6 @@ type TokenStore interface {
|
||||
// Delete a token by userID.
|
||||
DeleteByUserID(context.Context, valuer.UUID) error
|
||||
|
||||
// Update last observed at by access token.
|
||||
UpdateLastObservedAtByAccessToken(context.Context, []map[string]any) error
|
||||
// Update last observed at of the given tokens.
|
||||
UpdateLastObservedAt(context.Context, []*StorableToken) error
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# TODO: remove this file once enable_ai_observability flag is removed or defaulted to True
|
||||
import pytest
|
||||
from testcontainers.core.container import Network
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.signoz import create_signoz
|
||||
|
||||
|
||||
@pytest.fixture(name="signoz", scope="package")
|
||||
def signoz_e2e( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
network: Network,
|
||||
zeus: types.TestContainerDocker,
|
||||
gateway: types.TestContainerDocker,
|
||||
sqlstore: types.TestContainerSQL,
|
||||
clickhouse: types.TestContainerClickhouse,
|
||||
request: pytest.FixtureRequest,
|
||||
pytestconfig: pytest.Config,
|
||||
) -> types.SigNoz:
|
||||
"""
|
||||
E2E-scoped SigNoz override. Enables the experimental AI/LLM Observability
|
||||
module (disabled by default in pkg/flagger/registry.go) so its routes render
|
||||
instead of redirecting to /home — required by the llm-o11y e2e specs. Scoped
|
||||
to the e2e package via this conftest so normal integration tests keep the
|
||||
stock feature set. Follows the same pattern as
|
||||
tests/integration/tests/metricreduction/conftest.py.
|
||||
"""
|
||||
return create_signoz(
|
||||
network=network,
|
||||
zeus=zeus,
|
||||
gateway=gateway,
|
||||
sqlstore=sqlstore,
|
||||
clickhouse=clickhouse,
|
||||
request=request,
|
||||
pytestconfig=pytestconfig,
|
||||
cache_key="signoz_e2e",
|
||||
env_overrides={
|
||||
"SIGNOZ_FLAGGER_CONFIG_BOOLEAN_ENABLE__AI__OBSERVABILITY": True,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user