Compare commits

...

4 Commits

Author SHA1 Message Date
Nityananda Gohain
a58a3d4a68 Merge branch 'main' into issue_4360 2026-04-20 17:54:38 +05:30
nityanandagohain
6899eb0124 fix: changes 2026-04-20 17:53:42 +05:30
nityanandagohain
de5bec0195 Merge remote-tracking branch 'origin/main' into issue_4360 2026-04-20 16:41:58 +05:30
nityanandagohain
e359b03c25 feat: 1.Types for ai-o11y ricing rules 2026-04-12 17:14:18 +05:30
15 changed files with 2243 additions and 103 deletions

View File

@@ -1,5 +1,226 @@
components:
schemas:
Aio11YpricingruletypesCacheMode:
enum:
- subtract
- additive
- unknown
type: string
Aio11YpricingruletypesListPricingRulesResponse:
properties:
items:
items:
$ref: '#/components/schemas/Aio11YpricingruletypesPricingRule'
nullable: true
type: array
limit:
type: integer
offset:
type: integer
total:
type: integer
required:
- items
- total
- offset
- limit
type: object
Aio11YpricingruletypesPostablePricingRule:
properties:
cacheMode:
$ref: '#/components/schemas/Aio11YpricingruletypesCacheMode'
costCacheRead:
format: double
type: number
costCacheWrite:
format: double
type: number
costInput:
format: double
type: number
costOutput:
format: double
type: number
enabled:
type: boolean
modelName:
type: string
modelPattern:
items:
type: string
nullable: true
type: array
unit:
$ref: '#/components/schemas/Aio11YpricingruletypesUnit'
required:
- modelName
- modelPattern
- unit
- cacheMode
- costInput
- costOutput
- costCacheRead
- costCacheWrite
- enabled
type: object
Aio11YpricingruletypesPricingRule:
properties:
cacheMode:
$ref: '#/components/schemas/Aio11YpricingruletypesCacheMode'
costCacheRead:
format: double
type: number
costCacheWrite:
format: double
type: number
costInput:
format: double
type: number
costOutput:
format: double
type: number
createdAt:
format: date-time
type: string
createdBy:
type: string
enabled:
type: boolean
id:
type: string
isOverride:
type: boolean
model:
type: string
modelPattern:
items:
type: string
nullable: true
type: array
orgId:
type: string
syncedAt:
format: date-time
nullable: true
type: string
unit:
$ref: '#/components/schemas/Aio11YpricingruletypesUnit'
updatedAt:
format: date-time
type: string
updatedBy:
type: string
required:
- id
- orgId
- model
- modelPattern
- unit
- cacheMode
- costInput
- costOutput
- costCacheRead
- costCacheWrite
- isOverride
- enabled
type: object
Aio11YpricingruletypesSyncPricingRulesRequest:
properties:
rules:
items:
$ref: '#/components/schemas/Aio11YpricingruletypesSyncablePricingRule'
nullable: true
type: array
required:
- rules
type: object
Aio11YpricingruletypesSyncPricingRulesResponse:
properties:
refreshed:
type: integer
synced:
type: integer
required:
- synced
- refreshed
type: object
Aio11YpricingruletypesSyncablePricingRule:
properties:
cacheMode:
$ref: '#/components/schemas/Aio11YpricingruletypesCacheMode'
costCacheRead:
format: double
type: number
costCacheWrite:
format: double
type: number
costInput:
format: double
type: number
costOutput:
format: double
type: number
enabled:
type: boolean
modelName:
type: string
modelPattern:
items:
type: string
nullable: true
type: array
sourceId:
type: string
unit:
$ref: '#/components/schemas/Aio11YpricingruletypesUnit'
required:
- modelName
- modelPattern
- unit
- cacheMode
- costInput
- costOutput
- costCacheRead
- costCacheWrite
- enabled
- sourceId
type: object
Aio11YpricingruletypesUnit:
enum:
- per_million_tokens
type: string
Aio11YpricingruletypesUpdatablePricingRule:
properties:
cacheMode:
$ref: '#/components/schemas/Aio11YpricingruletypesCacheMode'
costCacheRead:
nullable: true
type: number
costCacheWrite:
nullable: true
type: number
costInput:
nullable: true
type: number
costOutput:
nullable: true
type: number
enabled:
nullable: true
type: boolean
isOverride:
nullable: true
type: boolean
modelName:
nullable: true
type: string
modelPattern:
items:
type: string
type: array
unit:
$ref: '#/components/schemas/Aio11YpricingruletypesUnit'
type: object
AlertmanagertypesChannel:
properties:
createdAt:
@@ -4644,6 +4865,361 @@ info:
version: ""
openapi: 3.0.3
paths:
/api/v1/ai-o11y/pricing_rules:
get:
deprecated: false
description: Returns all LLM pricing rules for the authenticated org, with pagination.
operationId: ListPricingRules
parameters:
- in: query
name: offset
schema:
type: integer
- in: query
name: limit
schema:
type: integer
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YpricingruletypesListPricingRulesResponse'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: List pricing rules
tags:
- ai-o11y
post:
deprecated: false
description: Creates a new LLM pricing rule for the org. Always sets is_override
= true.
operationId: CreatePricingRule
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Aio11YpricingruletypesPostablePricingRule'
responses:
"201":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YpricingruletypesPricingRule'
status:
type: string
required:
- status
- data
type: object
description: Created
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"409":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Conflict
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Create a pricing rule
tags:
- ai-o11y
/api/v1/ai-o11y/pricing_rules/{id}:
delete:
deprecated: false
description: Hard-deletes a pricing rule. If auto-synced, it will be recreated
on the next sync cycle.
operationId: DeletePricingRule
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"204":
description: No Content
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Delete a pricing rule
tags:
- ai-o11y
get:
deprecated: false
description: Returns a single LLM pricing rule by ID.
operationId: GetPricingRule
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YpricingruletypesPricingRule'
status:
type: string
required:
- status
- data
type: object
description: OK
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: Get a pricing rule
tags:
- ai-o11y
patch:
deprecated: false
description: Partially updates an existing pricing rule. Changing any cost field
sets is_override = true.
operationId: UpdatePricingRule
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Aio11YpricingruletypesUpdatablePricingRule'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YpricingruletypesPricingRule'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Update a pricing rule
tags:
- ai-o11y
/api/v1/ai-o11y/pricing_rules/sync:
put:
deprecated: false
description: Zeus bulk-upserts upstream pricing. Non-override rules get costs
updated; override rules get only SourceConfig refreshed.
operationId: SyncPricingRules
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Aio11YpricingruletypesSyncPricingRulesRequest'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YpricingruletypesSyncPricingRulesResponse'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Bulk sync pricing rules
tags:
- ai-o11y
/api/v1/alerts:
get:
deprecated: false

View File

@@ -0,0 +1,577 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useMutation, useQuery } from 'react-query';
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type {
Aio11YpricingruletypesPostablePricingRuleDTO,
Aio11YpricingruletypesSyncPricingRulesRequestDTO,
Aio11YpricingruletypesUpdatablePricingRuleDTO,
CreatePricingRule201,
DeletePricingRulePathParameters,
GetPricingRule200,
GetPricingRulePathParameters,
ListPricingRules200,
ListPricingRulesParams,
RenderErrorResponseDTO,
SyncPricingRules200,
UpdatePricingRule200,
UpdatePricingRulePathParameters,
} from '../sigNoz.schemas';
/**
* Returns all LLM pricing rules for the authenticated org, with pagination.
* @summary List pricing rules
*/
export const listPricingRules = (
params?: ListPricingRulesParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ListPricingRules200>({
url: `/api/v1/ai-o11y/pricing_rules`,
method: 'GET',
params,
signal,
});
};
export const getListPricingRulesQueryKey = (
params?: ListPricingRulesParams,
) => {
return [`/api/v1/ai-o11y/pricing_rules`, ...(params ? [params] : [])] as const;
};
export const getListPricingRulesQueryOptions = <
TData = Awaited<ReturnType<typeof listPricingRules>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
params?: ListPricingRulesParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPricingRules>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getListPricingRulesQueryKey(params);
const queryFn: QueryFunction<Awaited<ReturnType<typeof listPricingRules>>> = ({
signal,
}) => listPricingRules(params, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listPricingRules>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListPricingRulesQueryResult = NonNullable<
Awaited<ReturnType<typeof listPricingRules>>
>;
export type ListPricingRulesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List pricing rules
*/
export function useListPricingRules<
TData = Awaited<ReturnType<typeof listPricingRules>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
params?: ListPricingRulesParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPricingRules>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListPricingRulesQueryOptions(params, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary List pricing rules
*/
export const invalidateListPricingRules = async (
queryClient: QueryClient,
params?: ListPricingRulesParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListPricingRulesQueryKey(params) },
options,
);
return queryClient;
};
/**
* Creates a new LLM pricing rule for the org. Always sets is_override = true.
* @summary Create a pricing rule
*/
export const createPricingRule = (
aio11YpricingruletypesPostablePricingRuleDTO: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreatePricingRule201>({
url: `/api/v1/ai-o11y/pricing_rules`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: aio11YpricingruletypesPostablePricingRuleDTO,
signal,
});
};
export const getCreatePricingRuleMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createPricingRule>>,
TError,
{ data: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createPricingRule>>,
TError,
{ data: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO> },
TContext
> => {
const mutationKey = ['createPricingRule'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createPricingRule>>,
{ data: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO> }
> = (props) => {
const { data } = props ?? {};
return createPricingRule(data);
};
return { mutationFn, ...mutationOptions };
};
export type CreatePricingRuleMutationResult = NonNullable<
Awaited<ReturnType<typeof createPricingRule>>
>;
export type CreatePricingRuleMutationBody = BodyType<Aio11YpricingruletypesPostablePricingRuleDTO>;
export type CreatePricingRuleMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Create a pricing rule
*/
export const useCreatePricingRule = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createPricingRule>>,
TError,
{ data: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createPricingRule>>,
TError,
{ data: BodyType<Aio11YpricingruletypesPostablePricingRuleDTO> },
TContext
> => {
const mutationOptions = getCreatePricingRuleMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Hard-deletes a pricing rule. If auto-synced, it will be recreated on the next sync cycle.
* @summary Delete a pricing rule
*/
export const deletePricingRule = ({ id }: DeletePricingRulePathParameters) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/ai-o11y/pricing_rules/${id}`,
method: 'DELETE',
});
};
export const getDeletePricingRuleMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deletePricingRule>>,
TError,
{ pathParams: DeletePricingRulePathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deletePricingRule>>,
TError,
{ pathParams: DeletePricingRulePathParameters },
TContext
> => {
const mutationKey = ['deletePricingRule'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deletePricingRule>>,
{ pathParams: DeletePricingRulePathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deletePricingRule(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeletePricingRuleMutationResult = NonNullable<
Awaited<ReturnType<typeof deletePricingRule>>
>;
export type DeletePricingRuleMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete a pricing rule
*/
export const useDeletePricingRule = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deletePricingRule>>,
TError,
{ pathParams: DeletePricingRulePathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deletePricingRule>>,
TError,
{ pathParams: DeletePricingRulePathParameters },
TContext
> => {
const mutationOptions = getDeletePricingRuleMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Returns a single LLM pricing rule by ID.
* @summary Get a pricing rule
*/
export const getPricingRule = (
{ id }: GetPricingRulePathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetPricingRule200>({
url: `/api/v1/ai-o11y/pricing_rules/${id}`,
method: 'GET',
signal,
});
};
export const getGetPricingRuleQueryKey = ({
id,
}: GetPricingRulePathParameters) => {
return [`/api/v1/ai-o11y/pricing_rules/${id}`] as const;
};
export const getGetPricingRuleQueryOptions = <
TData = Awaited<ReturnType<typeof getPricingRule>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetPricingRulePathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getPricingRule>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetPricingRuleQueryKey({ id });
const queryFn: QueryFunction<Awaited<ReturnType<typeof getPricingRule>>> = ({
signal,
}) => getPricingRule({ id }, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getPricingRule>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetPricingRuleQueryResult = NonNullable<
Awaited<ReturnType<typeof getPricingRule>>
>;
export type GetPricingRuleQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get a pricing rule
*/
export function useGetPricingRule<
TData = Awaited<ReturnType<typeof getPricingRule>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetPricingRulePathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getPricingRule>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetPricingRuleQueryOptions({ id }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get a pricing rule
*/
export const invalidateGetPricingRule = async (
queryClient: QueryClient,
{ id }: GetPricingRulePathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetPricingRuleQueryKey({ id }) },
options,
);
return queryClient;
};
/**
* Partially updates an existing pricing rule. Changing any cost field sets is_override = true.
* @summary Update a pricing rule
*/
export const updatePricingRule = (
{ id }: UpdatePricingRulePathParameters,
aio11YpricingruletypesUpdatablePricingRuleDTO: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>,
) => {
return GeneratedAPIInstance<UpdatePricingRule200>({
url: `/api/v1/ai-o11y/pricing_rules/${id}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: aio11YpricingruletypesUpdatablePricingRuleDTO,
});
};
export const getUpdatePricingRuleMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updatePricingRule>>,
TError,
{
pathParams: UpdatePricingRulePathParameters;
data: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updatePricingRule>>,
TError,
{
pathParams: UpdatePricingRulePathParameters;
data: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
},
TContext
> => {
const mutationKey = ['updatePricingRule'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updatePricingRule>>,
{
pathParams: UpdatePricingRulePathParameters;
data: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updatePricingRule(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdatePricingRuleMutationResult = NonNullable<
Awaited<ReturnType<typeof updatePricingRule>>
>;
export type UpdatePricingRuleMutationBody = BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
export type UpdatePricingRuleMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update a pricing rule
*/
export const useUpdatePricingRule = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updatePricingRule>>,
TError,
{
pathParams: UpdatePricingRulePathParameters;
data: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updatePricingRule>>,
TError,
{
pathParams: UpdatePricingRulePathParameters;
data: BodyType<Aio11YpricingruletypesUpdatablePricingRuleDTO>;
},
TContext
> => {
const mutationOptions = getUpdatePricingRuleMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Zeus bulk-upserts upstream pricing. Non-override rules get costs updated; override rules get only SourceConfig refreshed.
* @summary Bulk sync pricing rules
*/
export const syncPricingRules = (
aio11YpricingruletypesSyncPricingRulesRequestDTO: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO>,
) => {
return GeneratedAPIInstance<SyncPricingRules200>({
url: `/api/v1/ai-o11y/pricing_rules/sync`,
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
data: aio11YpricingruletypesSyncPricingRulesRequestDTO,
});
};
export const getSyncPricingRulesMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof syncPricingRules>>,
TError,
{ data: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof syncPricingRules>>,
TError,
{ data: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO> },
TContext
> => {
const mutationKey = ['syncPricingRules'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof syncPricingRules>>,
{ data: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO> }
> = (props) => {
const { data } = props ?? {};
return syncPricingRules(data);
};
return { mutationFn, ...mutationOptions };
};
export type SyncPricingRulesMutationResult = NonNullable<
Awaited<ReturnType<typeof syncPricingRules>>
>;
export type SyncPricingRulesMutationBody = BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO>;
export type SyncPricingRulesMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Bulk sync pricing rules
*/
export const useSyncPricingRules = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof syncPricingRules>>,
TError,
{ data: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof syncPricingRules>>,
TError,
{ data: BodyType<Aio11YpricingruletypesSyncPricingRulesRequestDTO> },
TContext
> => {
const mutationOptions = getSyncPricingRulesMutationOptions(options);
return useMutation(mutationOptions);
};

View File

@@ -4,6 +4,251 @@
* * regenerate with 'yarn generate:api'
* SigNoz
*/
export enum Aio11YpricingruletypesCacheModeDTO {
subtract = 'subtract',
additive = 'additive',
unknown = 'unknown',
}
export interface Aio11YpricingruletypesListPricingRulesResponseDTO {
/**
* @type array
* @nullable true
*/
items: Aio11YpricingruletypesPricingRuleDTO[] | null;
/**
* @type integer
*/
limit: number;
/**
* @type integer
*/
offset: number;
/**
* @type integer
*/
total: number;
}
export interface Aio11YpricingruletypesPostablePricingRuleDTO {
cacheMode: Aio11YpricingruletypesCacheModeDTO;
/**
* @type number
* @format double
*/
costCacheRead: number;
/**
* @type number
* @format double
*/
costCacheWrite: number;
/**
* @type number
* @format double
*/
costInput: number;
/**
* @type number
* @format double
*/
costOutput: number;
/**
* @type boolean
*/
enabled: boolean;
/**
* @type string
*/
modelName: string;
/**
* @type array
* @nullable true
*/
modelPattern: string[] | null;
unit: Aio11YpricingruletypesUnitDTO;
}
export interface Aio11YpricingruletypesPricingRuleDTO {
cacheMode: Aio11YpricingruletypesCacheModeDTO;
/**
* @type number
* @format double
*/
costCacheRead: number;
/**
* @type number
* @format double
*/
costCacheWrite: number;
/**
* @type number
* @format double
*/
costInput: number;
/**
* @type number
* @format double
*/
costOutput: number;
/**
* @type string
* @format date-time
*/
createdAt?: Date;
/**
* @type string
*/
createdBy?: string;
/**
* @type boolean
*/
enabled: boolean;
/**
* @type string
*/
id: string;
/**
* @type boolean
*/
isOverride: boolean;
/**
* @type string
*/
model: string;
/**
* @type array
* @nullable true
*/
modelPattern: string[] | null;
/**
* @type string
*/
orgId: string;
/**
* @type string
* @format date-time
* @nullable true
*/
syncedAt?: Date | null;
unit: Aio11YpricingruletypesUnitDTO;
/**
* @type string
* @format date-time
*/
updatedAt?: Date;
/**
* @type string
*/
updatedBy?: string;
}
export interface Aio11YpricingruletypesSyncPricingRulesRequestDTO {
/**
* @type array
* @nullable true
*/
rules: Aio11YpricingruletypesSyncablePricingRuleDTO[] | null;
}
export interface Aio11YpricingruletypesSyncPricingRulesResponseDTO {
/**
* @type integer
*/
refreshed: number;
/**
* @type integer
*/
synced: number;
}
export interface Aio11YpricingruletypesSyncablePricingRuleDTO {
cacheMode: Aio11YpricingruletypesCacheModeDTO;
/**
* @type number
* @format double
*/
costCacheRead: number;
/**
* @type number
* @format double
*/
costCacheWrite: number;
/**
* @type number
* @format double
*/
costInput: number;
/**
* @type number
* @format double
*/
costOutput: number;
/**
* @type boolean
*/
enabled: boolean;
/**
* @type string
*/
modelName: string;
/**
* @type array
* @nullable true
*/
modelPattern: string[] | null;
/**
* @type string
*/
sourceId: string;
unit: Aio11YpricingruletypesUnitDTO;
}
export enum Aio11YpricingruletypesUnitDTO {
per_million_tokens = 'per_million_tokens',
}
export interface Aio11YpricingruletypesUpdatablePricingRuleDTO {
cacheMode?: Aio11YpricingruletypesCacheModeDTO;
/**
* @type number
* @nullable true
*/
costCacheRead?: number | null;
/**
* @type number
* @nullable true
*/
costCacheWrite?: number | null;
/**
* @type number
* @nullable true
*/
costInput?: number | null;
/**
* @type number
* @nullable true
*/
costOutput?: number | null;
/**
* @type boolean
* @nullable true
*/
enabled?: boolean | null;
/**
* @type boolean
* @nullable true
*/
isOverride?: boolean | null;
/**
* @type string
* @nullable true
*/
modelName?: string | null;
/**
* @type array
*/
modelPattern?: string[];
unit?: Aio11YpricingruletypesUnitDTO;
}
export interface AlertmanagertypesChannelDTO {
/**
* @type string
@@ -5691,6 +5936,68 @@ export interface ZeustypesPostableProfileDTO {
where_did_you_discover_signoz: string;
}
export type ListPricingRulesParams = {
/**
* @type integer
* @description undefined
*/
offset?: number;
/**
* @type integer
* @description undefined
*/
limit?: number;
};
export type ListPricingRules200 = {
data: Aio11YpricingruletypesListPricingRulesResponseDTO;
/**
* @type string
*/
status: string;
};
export type CreatePricingRule201 = {
data: Aio11YpricingruletypesPricingRuleDTO;
/**
* @type string
*/
status: string;
};
export type DeletePricingRulePathParameters = {
id: string;
};
export type GetPricingRulePathParameters = {
id: string;
};
export type GetPricingRule200 = {
data: Aio11YpricingruletypesPricingRuleDTO;
/**
* @type string
*/
status: string;
};
export type UpdatePricingRulePathParameters = {
id: string;
};
export type UpdatePricingRule200 = {
data: Aio11YpricingruletypesPricingRuleDTO;
/**
* @type string
*/
status: string;
};
export type SyncPricingRules200 = {
data: Aio11YpricingruletypesSyncPricingRulesResponseDTO;
/**
* @type string
*/
status: string;
};
export type GetAlerts200 = {
/**
* @type array

View File

@@ -0,0 +1,135 @@
package signozapiserver
import (
"net/http"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/aio11ypricingruletypes"
"github.com/gorilla/mux"
)
func (provider *provider) addAIO11yRoutes(router *mux.Router) error {
if err := router.Handle("/api/v1/ai-o11y/pricing_rules", handler.New(
provider.authZ.ViewAccess(provider.aiO11yPricingRuleHandler.List),
handler.OpenAPIDef{
ID: "ListPricingRules",
Tags: []string{"ai-o11y"},
Summary: "List pricing rules",
Description: "Returns all LLM pricing rules for the authenticated org, with pagination.",
Request: nil,
RequestContentType: "",
RequestQuery: new(aio11ypricingruletypes.ListPricingRulesQuery),
Response: new(aio11ypricingruletypes.ListPricingRulesResponse),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
},
)).Methods(http.MethodGet).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/ai-o11y/pricing_rules", handler.New(
provider.authZ.AdminAccess(provider.aiO11yPricingRuleHandler.Create),
handler.OpenAPIDef{
ID: "CreatePricingRule",
Tags: []string{"ai-o11y"},
Summary: "Create a pricing rule",
Description: "Creates a new LLM pricing rule for the org. Always sets is_override = true.",
Request: new(aio11ypricingruletypes.PostablePricingRule),
RequestContentType: "application/json",
Response: new(aio11ypricingruletypes.GettablePricingRule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusCreated,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
},
)).Methods(http.MethodPost).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/ai-o11y/pricing_rules/sync", handler.New(
provider.authZ.AdminAccess(provider.aiO11yPricingRuleHandler.Sync),
handler.OpenAPIDef{
ID: "SyncPricingRules",
Tags: []string{"ai-o11y"},
Summary: "Bulk sync pricing rules",
Description: "Zeus bulk-upserts upstream pricing. Non-override rules get costs updated; override rules get only SourceConfig refreshed.",
Request: new(aio11ypricingruletypes.SyncPricingRulesRequest),
RequestContentType: "application/json",
Response: new(aio11ypricingruletypes.SyncPricingRulesResponse),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin), // not sure of this
},
)).Methods(http.MethodPut).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/ai-o11y/pricing_rules/{id}", handler.New(
provider.authZ.ViewAccess(provider.aiO11yPricingRuleHandler.Get),
handler.OpenAPIDef{
ID: "GetPricingRule",
Tags: []string{"ai-o11y"},
Summary: "Get a pricing rule",
Description: "Returns a single LLM pricing rule by ID.",
Request: nil,
RequestContentType: "",
Response: new(aio11ypricingruletypes.GettablePricingRule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusNotFound},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
},
)).Methods(http.MethodGet).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/ai-o11y/pricing_rules/{id}", handler.New(
provider.authZ.AdminAccess(provider.aiO11yPricingRuleHandler.Update),
handler.OpenAPIDef{
ID: "UpdatePricingRule",
Tags: []string{"ai-o11y"},
Summary: "Update a pricing rule",
Description: "Partially updates an existing pricing rule. Changing any cost field sets is_override = true.",
Request: new(aio11ypricingruletypes.UpdatablePricingRule),
RequestContentType: "application/json",
Response: new(aio11ypricingruletypes.GettablePricingRule),
ResponseContentType: "application/json",
SuccessStatusCode: http.StatusOK,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
},
)).Methods(http.MethodPatch).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v1/ai-o11y/pricing_rules/{id}", handler.New(
provider.authZ.AdminAccess(provider.aiO11yPricingRuleHandler.Delete),
handler.OpenAPIDef{
ID: "DeletePricingRule",
Tags: []string{"ai-o11y"},
Summary: "Delete a pricing rule",
Description: "Hard-deletes a pricing rule. If auto-synced, it will be recreated on the next sync cycle.",
Request: nil,
RequestContentType: "",
Response: nil,
ResponseContentType: "",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusNotFound},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
},
)).Methods(http.MethodDelete).GetError(); err != nil {
return err
}
return nil
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/SigNoz/signoz/pkg/global"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/modules/aio11ypricingrule"
"github.com/SigNoz/signoz/pkg/modules/authdomain"
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
@@ -34,33 +35,34 @@ import (
)
type provider struct {
config apiserver.Config
settings factory.ScopedProviderSettings
router *mux.Router
authZ *middleware.AuthZ
orgHandler organization.Handler
userHandler user.Handler
sessionHandler session.Handler
authDomainHandler authdomain.Handler
preferenceHandler preference.Handler
globalHandler global.Handler
promoteHandler promote.Handler
flaggerHandler flagger.Handler
dashboardModule dashboard.Module
dashboardHandler dashboard.Handler
metricsExplorerHandler metricsexplorer.Handler
gatewayHandler gateway.Handler
fieldsHandler fields.Handler
authzHandler authz.Handler
rawDataExportHandler rawdataexport.Handler
zeusHandler zeus.Handler
querierHandler querier.Handler
serviceAccountHandler serviceaccount.Handler
factoryHandler factory.Handler
cloudIntegrationHandler cloudintegration.Handler
ruleStateHistoryHandler rulestatehistory.Handler
alertmanagerHandler alertmanager.Handler
rulerHandler ruler.Handler
config apiserver.Config
settings factory.ScopedProviderSettings
router *mux.Router
authZ *middleware.AuthZ
orgHandler organization.Handler
userHandler user.Handler
sessionHandler session.Handler
authDomainHandler authdomain.Handler
preferenceHandler preference.Handler
globalHandler global.Handler
promoteHandler promote.Handler
flaggerHandler flagger.Handler
dashboardModule dashboard.Module
dashboardHandler dashboard.Handler
metricsExplorerHandler metricsexplorer.Handler
gatewayHandler gateway.Handler
fieldsHandler fields.Handler
authzHandler authz.Handler
rawDataExportHandler rawdataexport.Handler
zeusHandler zeus.Handler
querierHandler querier.Handler
serviceAccountHandler serviceaccount.Handler
factoryHandler factory.Handler
cloudIntegrationHandler cloudintegration.Handler
ruleStateHistoryHandler rulestatehistory.Handler
alertmanagerHandler alertmanager.Handler
aiO11yPricingRuleHandler aio11ypricingrule.Handler
rulerHandler ruler.Handler
}
func NewFactory(
@@ -88,6 +90,7 @@ func NewFactory(
cloudIntegrationHandler cloudintegration.Handler,
ruleStateHistoryHandler rulestatehistory.Handler,
alertmanagerHandler alertmanager.Handler,
aiO11yPricingRuleHandler aio11ypricingrule.Handler,
rulerHandler ruler.Handler,
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
@@ -119,6 +122,7 @@ func NewFactory(
cloudIntegrationHandler,
ruleStateHistoryHandler,
alertmanagerHandler,
aiO11yPricingRuleHandler,
rulerHandler,
)
})
@@ -152,38 +156,41 @@ func newProvider(
cloudIntegrationHandler cloudintegration.Handler,
ruleStateHistoryHandler rulestatehistory.Handler,
alertmanagerHandler alertmanager.Handler,
aiO11yPricingRuleHandler aio11ypricingrule.Handler,
rulerHandler ruler.Handler,
) (apiserver.APIServer, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
router := mux.NewRouter().UseEncodedPath()
provider := &provider{
config: config,
settings: settings,
router: router,
orgHandler: orgHandler,
userHandler: userHandler,
sessionHandler: sessionHandler,
authDomainHandler: authDomainHandler,
preferenceHandler: preferenceHandler,
globalHandler: globalHandler,
promoteHandler: promoteHandler,
flaggerHandler: flaggerHandler,
dashboardModule: dashboardModule,
dashboardHandler: dashboardHandler,
metricsExplorerHandler: metricsExplorerHandler,
gatewayHandler: gatewayHandler,
fieldsHandler: fieldsHandler,
authzHandler: authzHandler,
rawDataExportHandler: rawDataExportHandler,
zeusHandler: zeusHandler,
querierHandler: querierHandler,
serviceAccountHandler: serviceAccountHandler,
factoryHandler: factoryHandler,
cloudIntegrationHandler: cloudIntegrationHandler,
ruleStateHistoryHandler: ruleStateHistoryHandler,
alertmanagerHandler: alertmanagerHandler,
rulerHandler: rulerHandler,
config: config,
settings: settings,
router: router,
orgHandler: orgHandler,
userHandler: userHandler,
sessionHandler: sessionHandler,
authDomainHandler: authDomainHandler,
preferenceHandler: preferenceHandler,
globalHandler: globalHandler,
promoteHandler: promoteHandler,
flaggerHandler: flaggerHandler,
dashboardModule: dashboardModule,
dashboardHandler: dashboardHandler,
metricsExplorerHandler: metricsExplorerHandler,
gatewayHandler: gatewayHandler,
fieldsHandler: fieldsHandler,
authzHandler: authzHandler,
rawDataExportHandler: rawDataExportHandler,
zeusHandler: zeusHandler,
querierHandler: querierHandler,
serviceAccountHandler: serviceAccountHandler,
factoryHandler: factoryHandler,
cloudIntegrationHandler: cloudIntegrationHandler,
ruleStateHistoryHandler: ruleStateHistoryHandler,
alertmanagerHandler: alertmanagerHandler,
aiO11yPricingRuleHandler: aiO11yPricingRuleHandler,
rulerHandler: rulerHandler,
}
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
@@ -288,6 +295,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
return err
}
if err := provider.addAIO11yRoutes(router); err != nil {
return nil
}
if err := provider.addRulerRoutes(router); err != nil {
return err
}

View File

@@ -0,0 +1,28 @@
package aio11ypricingrule
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/types/aio11ypricingruletypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Module interface {
List(ctx context.Context, orgID valuer.UUID, offset, limit int) ([]*aio11ypricingruletypes.PricingRule, int, error)
Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*aio11ypricingruletypes.PricingRule, error)
Create(ctx context.Context, orgID valuer.UUID, createdBy string, req *aio11ypricingruletypes.PostablePricingRule) (*aio11ypricingruletypes.PricingRule, error)
Update(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, req *aio11ypricingruletypes.UpdatablePricingRule) (*aio11ypricingruletypes.PricingRule, error)
Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error
Sync(ctx context.Context, orgID valuer.UUID, req *aio11ypricingruletypes.SyncPricingRulesRequest) (*aio11ypricingruletypes.SyncPricingRulesResponse, error)
}
// Handler defines the HTTP handler interface for pricing rule endpoints.
type Handler interface {
List(rw http.ResponseWriter, r *http.Request)
Get(rw http.ResponseWriter, r *http.Request)
Create(rw http.ResponseWriter, r *http.Request)
Update(rw http.ResponseWriter, r *http.Request)
Delete(rw http.ResponseWriter, r *http.Request)
Sync(rw http.ResponseWriter, r *http.Request)
}

View File

@@ -0,0 +1,261 @@
package implaio11ypricingrule
import (
"context"
"net/http"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/http/binding"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/modules/aio11ypricingrule"
"github.com/SigNoz/signoz/pkg/types/aio11ypricingruletypes"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/gorilla/mux"
)
const maxLimit = 100
type handler struct {
module aio11ypricingrule.Module
providerSettings factory.ProviderSettings
}
func NewHandler(module aio11ypricingrule.Module, providerSettings factory.ProviderSettings) aio11ypricingrule.Handler {
return &handler{module: module, providerSettings: providerSettings}
}
// List handles GET /api/v1/ai-o11y/pricing_rules.
func (h *handler) List(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
var q aio11ypricingruletypes.ListPricingRulesQuery
if err := binding.Query.BindQuery(r.URL.Query(), &q); err != nil {
render.Error(rw, err)
return
}
if q.Limit <= 0 {
q.Limit = 20
} else if q.Limit > maxLimit {
q.Limit = maxLimit
}
if q.Offset < 0 {
render.Error(rw, errors.Newf(errors.TypeInvalidInput, aio11ypricingruletypes.ErrCodePricingRuleInvalidInput, "offset must be a non-negative integer"))
return
}
rules, total, err := h.module.List(ctx, orgID, q.Offset, q.Limit)
if err != nil {
render.Error(rw, err)
return
}
items := make([]*aio11ypricingruletypes.GettablePricingRule, len(rules))
for i, r := range rules {
items[i] = aio11ypricingruletypes.NewGettablePricingRule(r)
}
render.Success(rw, http.StatusOK, &aio11ypricingruletypes.ListPricingRulesResponse{
Items: items,
Total: total,
Offset: q.Offset,
Limit: q.Limit,
})
}
// Get handles GET /api/v1/ai-o11y/pricing_rules/{id}.
func (h *handler) Get(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
id, err := ruleIDFromPath(r)
if err != nil {
render.Error(rw, err)
return
}
rule, err := h.module.Get(ctx, orgID, id)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, aio11ypricingruletypes.NewGettablePricingRule(rule))
}
// Create handles POST /api/v1/ai-o11y/pricing_rules.
func (h *handler) Create(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
req := new(aio11ypricingruletypes.PostablePricingRule)
if err := binding.JSON.BindBody(r.Body, req); err != nil {
render.Error(rw, err)
return
}
rule, err := h.module.Create(ctx, orgID, claims.Email, req)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusCreated, aio11ypricingruletypes.NewGettablePricingRule(rule))
}
// Update handles PUT /api/v1/ai-o11y/pricing_rules/{id}.
func (h *handler) Update(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
id, err := ruleIDFromPath(r)
if err != nil {
render.Error(rw, err)
return
}
req := new(aio11ypricingruletypes.UpdatablePricingRule)
if err := binding.JSON.BindBody(r.Body, req); err != nil {
render.Error(rw, err)
return
}
rule, err := h.module.Update(ctx, orgID, id, claims.Email, req)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, aio11ypricingruletypes.NewGettablePricingRule(rule))
}
// Delete handles DELETE /api/v1/ai-o11y/pricing_rules/{id}.
func (h *handler) Delete(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
id, err := ruleIDFromPath(r)
if err != nil {
render.Error(rw, err)
return
}
if err := h.module.Delete(ctx, orgID, id); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}
// Sync handles PUT /api/v1/ai-o11y/pricing_rules/sync.
// Zeus sends a bulk payload of rules across any number of sources.
// Rules with isOverride=false get costs updated; rules with isOverride=true
// get only SourceConfig refreshed so users can revert to the new upstream value.
func (h *handler) Sync(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
orgID, err := valuer.NewUUID(claims.OrgID)
if err != nil {
render.Error(rw, err)
return
}
req := new(aio11ypricingruletypes.SyncPricingRulesRequest)
if err := binding.JSON.BindBody(r.Body, req); err != nil {
render.Error(rw, err)
return
}
resp, err := h.module.Sync(ctx, orgID, req)
if err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusOK, resp)
}
// ruleIDFromPath extracts and validates the {id} path variable.
func ruleIDFromPath(r *http.Request) (valuer.UUID, error) {
raw := mux.Vars(r)["id"]
if raw == "" {
return valuer.UUID{}, errors.Newf(errors.TypeInvalidInput, aio11ypricingruletypes.ErrCodePricingRuleInvalidInput, "id is missing from the path")
}
id, err := valuer.NewUUID(raw)
if err != nil {
return valuer.UUID{}, errors.Wrapf(err, errors.TypeInvalidInput, aio11ypricingruletypes.ErrCodePricingRuleInvalidInput, "id is not a valid uuid")
}
return id, nil
}

View File

@@ -3,8 +3,6 @@ package signoz
import (
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/ruler"
"github.com/SigNoz/signoz/pkg/ruler/signozruler"
"github.com/SigNoz/signoz/pkg/analytics"
"github.com/SigNoz/signoz/pkg/authz"
"github.com/SigNoz/signoz/pkg/authz/signozauthzapi"
@@ -14,6 +12,8 @@ import (
"github.com/SigNoz/signoz/pkg/global"
"github.com/SigNoz/signoz/pkg/global/signozglobal"
"github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/modules/aio11ypricingrule"
"github.com/SigNoz/signoz/pkg/modules/aio11ypricingrule/implaio11ypricingrule"
"github.com/SigNoz/signoz/pkg/modules/apdex"
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
@@ -41,33 +41,36 @@ import (
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/ruler"
"github.com/SigNoz/signoz/pkg/ruler/signozruler"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/zeus"
)
type Handlers struct {
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler
QuickFilter quickfilter.Handler
TraceFunnel tracefunnel.Handler
RawDataExport rawdataexport.Handler
SpanPercentile spanpercentile.Handler
Services services.Handler
MetricsExplorer metricsexplorer.Handler
Global global.Handler
FlaggerHandler flagger.Handler
GatewayHandler gateway.Handler
Fields fields.Handler
AuthzHandler authz.Handler
ZeusHandler zeus.Handler
QuerierHandler querier.Handler
ServiceAccountHandler serviceaccount.Handler
RegistryHandler factory.Handler
CloudIntegrationHandler cloudintegration.Handler
RuleStateHistory rulestatehistory.Handler
AlertmanagerHandler alertmanager.Handler
RulerHandler ruler.Handler
SavedView savedview.Handler
Apdex apdex.Handler
Dashboard dashboard.Handler
QuickFilter quickfilter.Handler
TraceFunnel tracefunnel.Handler
RawDataExport rawdataexport.Handler
SpanPercentile spanpercentile.Handler
Services services.Handler
MetricsExplorer metricsexplorer.Handler
Global global.Handler
FlaggerHandler flagger.Handler
GatewayHandler gateway.Handler
Fields fields.Handler
AuthzHandler authz.Handler
ZeusHandler zeus.Handler
QuerierHandler querier.Handler
ServiceAccountHandler serviceaccount.Handler
RegistryHandler factory.Handler
CloudIntegrationHandler cloudintegration.Handler
RuleStateHistory rulestatehistory.Handler
AlertmanagerHandler alertmanager.Handler
AIO11yPricingRuleHandler aio11ypricingrule.Handler
RulerHandler ruler.Handler
}
func NewHandlers(
@@ -87,27 +90,28 @@ func NewHandlers(
rulerService ruler.Ruler,
) Handlers {
return Handlers{
SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings, authz),
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
Services: implservices.NewHandler(modules.Services),
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
Global: signozglobal.NewHandler(global),
FlaggerHandler: flagger.NewHandler(flaggerService),
GatewayHandler: gateway.NewHandler(gatewayService),
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
AuthzHandler: signozauthzapi.NewHandler(authz),
ZeusHandler: zeus.NewHandler(zeusService, licensing),
QuerierHandler: querierHandler,
ServiceAccountHandler: implserviceaccount.NewHandler(modules.ServiceAccount),
RegistryHandler: registryHandler,
RuleStateHistory: implrulestatehistory.NewHandler(modules.RuleStateHistory),
CloudIntegrationHandler: implcloudintegration.NewHandler(modules.CloudIntegration),
AlertmanagerHandler: signozalertmanager.NewHandler(alertmanagerService),
RulerHandler: signozruler.NewHandler(rulerService),
SavedView: implsavedview.NewHandler(modules.SavedView),
Apdex: implapdex.NewHandler(modules.Apdex),
Dashboard: impldashboard.NewHandler(modules.Dashboard, providerSettings, authz),
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
Services: implservices.NewHandler(modules.Services),
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
Global: signozglobal.NewHandler(global),
FlaggerHandler: flagger.NewHandler(flaggerService),
GatewayHandler: gateway.NewHandler(gatewayService),
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
AuthzHandler: signozauthzapi.NewHandler(authz),
ZeusHandler: zeus.NewHandler(zeusService, licensing),
QuerierHandler: querierHandler,
ServiceAccountHandler: implserviceaccount.NewHandler(modules.ServiceAccount),
RegistryHandler: registryHandler,
RuleStateHistory: implrulestatehistory.NewHandler(modules.RuleStateHistory),
CloudIntegrationHandler: implcloudintegration.NewHandler(modules.CloudIntegration),
AlertmanagerHandler: signozalertmanager.NewHandler(alertmanagerService),
AIO11yPricingRuleHandler: implaio11ypricingrule.NewHandler(nil, providerSettings),
RulerHandler: signozruler.NewHandler(rulerService),
}
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/SigNoz/signoz/pkg/global"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/instrumentation"
"github.com/SigNoz/signoz/pkg/modules/aio11ypricingrule"
"github.com/SigNoz/signoz/pkg/modules/authdomain"
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
"github.com/SigNoz/signoz/pkg/modules/dashboard"
@@ -72,6 +73,7 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
struct{ cloudintegration.Handler }{},
struct{ rulestatehistory.Handler }{},
struct{ alertmanager.Handler }{},
struct{ aio11ypricingrule.Handler }{},
struct{ ruler.Handler }{},
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
if err != nil {

View File

@@ -3,8 +3,6 @@ package signoz
import (
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/auditor/noopauditor"
"github.com/SigNoz/signoz/pkg/alertmanager/nfmanager/rulebasednotification"
"github.com/SigNoz/signoz/pkg/alertmanager/signozalertmanager"
"github.com/SigNoz/signoz/pkg/analytics"
@@ -12,6 +10,8 @@ import (
"github.com/SigNoz/signoz/pkg/analytics/segmentanalytics"
"github.com/SigNoz/signoz/pkg/apiserver"
"github.com/SigNoz/signoz/pkg/apiserver/signozapiserver"
"github.com/SigNoz/signoz/pkg/auditor"
"github.com/SigNoz/signoz/pkg/auditor/noopauditor"
"github.com/SigNoz/signoz/pkg/authz"
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/cache/memorycache"
@@ -226,8 +226,6 @@ func NewAlertmanagerProviderFactories(sqlstore sqlstore.SQLStore, orgGetter orga
)
}
func NewEmailingProviderFactories() factory.NamedMap[factory.ProviderFactory[emailing.Emailing, emailing.Config]] {
return factory.MustNewNamedMap(
noopemailing.NewFactory(),
@@ -282,6 +280,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
handlers.CloudIntegrationHandler,
handlers.RuleStateHistory,
handlers.AlertmanagerHandler,
handlers.AIO11yPricingRuleHandler,
handlers.RulerHandler,
),
)

View File

@@ -0,0 +1,9 @@
package aio11ypricingruletypes
import "github.com/SigNoz/signoz/pkg/errors"
var (
ErrCodePricingRuleNotFound = errors.MustNewCode("pricing_rule_not_found")
ErrCodePricingRuleAlreadyExists = errors.MustNewCode("pricing_rule_already_exists")
ErrCodePricingRuleInvalidInput = errors.MustNewCode("pricing_rule_invalid_input")
)

View File

@@ -0,0 +1,65 @@
package aio11ypricingruletypes
import "github.com/SigNoz/signoz/pkg/valuer"
type GettablePricingRule = PricingRule
func NewGettablePricingRule(r *PricingRule) *GettablePricingRule {
return r
}
type PostablePricingRule struct {
Model string `json:"modelName" required:"true"`
ModelPattern []string `json:"modelPattern" required:"true"`
Unit Unit `json:"unit" required:"true"`
CacheMode CacheMode `json:"cacheMode" required:"true"`
CostInput float64 `json:"costInput" required:"true"`
CostOutput float64 `json:"costOutput" required:"true"`
CostCacheRead float64 `json:"costCacheRead" required:"true"`
CostCacheWrite float64 `json:"costCacheWrite" required:"true"`
Enabled bool `json:"enabled" required:"true"`
}
type UpdatablePricingRule struct {
Model *string `json:"modelName,omitempty"`
ModelPattern []string `json:"modelPattern,omitempty"`
Unit Unit `json:"unit,omitempty"`
CacheMode CacheMode `json:"cacheMode,omitempty"`
CostInput *float64 `json:"costInput,omitempty"`
CostOutput *float64 `json:"costOutput,omitempty"`
CostCacheRead *float64 `json:"costCacheRead,omitempty"`
CostCacheWrite *float64 `json:"costCacheWrite,omitempty"`
IsOverride *bool `json:"isOverride,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
}
// SyncablePricingRule is one entry in a Zeus bulk pricing sync payload.
// isOverride is not caller-supplied; the server always forces it to false.
type SyncablePricingRule struct {
PostablePricingRule
SourceID valuer.UUID `json:"sourceId" required:"true"`
}
type SyncPricingRulesRequest struct {
Rules []SyncablePricingRule `json:"rules" required:"true"`
}
// SyncPricingRulesResponse reports how many rows were affected.
// Synced: non-override rows whose cost fields were updated.
// Refreshed: override rows whose SourceConfig was updated (cost fields preserved).
type SyncPricingRulesResponse struct {
Synced int `json:"synced" required:"true"`
Refreshed int `json:"refreshed" required:"true"`
}
type ListPricingRulesQuery struct {
Offset int `query:"offset" json:"offset"`
Limit int `query:"limit" json:"limit"`
}
type ListPricingRulesResponse struct {
Items []*GettablePricingRule `json:"items" required:"true" nullable:"true"`
Total int `json:"total" required:"true"`
Offset int `json:"offset" required:"true"`
Limit int `json:"limit" required:"true"`
}

View File

@@ -0,0 +1,80 @@
package aio11ypricingruletypes
import (
"time"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Unit string
const (
UnitPerMillionTokens Unit = "per_million_tokens"
)
func (Unit) Enum() []any {
return []any{UnitPerMillionTokens}
}
type CacheMode string
const (
// CacheModeSubtract: cached tokens are inside input_tokens (OpenAI-style).
CacheModeSubtract CacheMode = "subtract"
// CacheModeAdditive: cached tokens are reported separately (Anthropic-style).
CacheModeAdditive CacheMode = "additive"
// CacheModeUnknown: provider behaviour is unknown; falls back to subtract.
CacheModeUnknown CacheMode = "unknown"
)
func (CacheMode) Enum() []any {
return []any{CacheModeSubtract, CacheModeAdditive, CacheModeUnknown}
}
// PricingRule is the domain model for an LLM pricing rule.
// It has no serialisation concerns — use GettablePricingRule for HTTP responses.
type PricingRule struct {
types.TimeAuditable
types.UserAuditable
ID valuer.UUID `json:"id" required:"true"`
OrgID valuer.UUID `json:"orgId" required:"true"`
Model string `json:"model" required:"true"`
ModelPattern []string `json:"modelPattern" required:"true"`
Unit Unit `json:"unit" required:"true"`
CacheMode CacheMode `json:"cacheMode" required:"true"`
CostInput float64 `json:"costInput" required:"true"`
CostOutput float64 `json:"costOutput" required:"true"`
CostCacheRead float64 `json:"costCacheRead" required:"true"`
CostCacheWrite float64 `json:"costCacheWrite" required:"true"`
// IsOverride marks that cost fields were manually edited.
// When true the sync job skips this rule.
IsOverride bool `json:"isOverride" required:"true"`
SyncedAt *time.Time `json:"syncedAt,omitempty"`
Enabled bool `json:"enabled" required:"true"`
}
// NewPricingRuleFromStorable converts a StorablePricingRule to a PricingRule.
func NewPricingRuleFromStorable(s *StorablePricingRule) *PricingRule {
pattern := make([]string, len(s.ModelPattern))
copy(pattern, s.ModelPattern)
return &PricingRule{
TimeAuditable: s.TimeAuditable,
UserAuditable: s.UserAuditable,
ID: s.ID,
OrgID: s.OrgID,
Model: s.Model,
ModelPattern: pattern,
Unit: s.Unit,
CacheMode: s.CacheMode,
CostInput: s.CostInput,
CostOutput: s.CostOutput,
CostCacheRead: s.CostCacheRead,
CostCacheWrite: s.CostCacheWrite,
IsOverride: s.IsOverride,
SyncedAt: s.SyncedAt,
Enabled: s.Enabled,
}
}

View File

@@ -0,0 +1,71 @@
package aio11ypricingruletypes
import (
"database/sql/driver"
"encoding/json"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/uptrace/bun"
)
// StringSlice is a []string that is stored as a JSON text column.
// It is compatible with both SQLite and PostgreSQL.
type StringSlice []string
func (s StringSlice) Value() (driver.Value, error) {
if s == nil {
return "[]", nil
}
b, err := json.Marshal(s)
if err != nil {
return nil, err
}
return string(b), nil
}
func (s *StringSlice) Scan(src any) error {
var raw []byte
switch v := src.(type) {
case string:
raw = []byte(v)
case []byte:
raw = v
case nil:
*s = nil
return nil
default:
return errors.NewInternalf(errors.CodeInternal, "aio11ypricingruletypes: cannot scan %T into StringSlice", src)
}
return json.Unmarshal(raw, s)
}
// StorablePricingRule is the bun/DB representation of an LLM pricing rule.
type StorablePricingRule struct {
bun.BaseModel `bun:"table:llm_pricing_rules,alias:llm_pricing_rules"`
types.Identifiable
types.TimeAuditable
types.UserAuditable
OrgID valuer.UUID `bun:"org_id,type:text,notnull"`
SourceID valuer.UUID `bun:"source_id,type:text,notnull"`
Model string `bun:"model,type:text,notnull"`
ModelPattern StringSlice `bun:"model_pattern,type:text,notnull"`
Unit Unit `bun:"unit,type:text,notnull"`
CacheMode CacheMode `bun:"cache_mode,type:text,notnull"`
CostInput float64 `bun:"cost_input,notnull"`
CostOutput float64 `bun:"cost_output,notnull"`
CostCacheRead float64 `bun:"cost_cache_read,notnull"`
CostCacheWrite float64 `bun:"cost_cache_write,notnull"`
// IsOverride marks that cost fields were manually edited.
// When true the sync job skips this rule.
IsOverride bool `bun:"is_override,notnull,default:false"`
// SourceConfig holds the last upstream pricing snapshot; used to revert
// when IsOverride is cleared.
SourceConfig string `bun:"source_config,type:text,notnull,default:'{}'"`
SyncedAt *time.Time `bun:"synced_at"`
Enabled bool `bun:"enabled,notnull,default:true"`
}

View File

@@ -0,0 +1,15 @@
package aio11ypricingruletypes
import (
"context"
"github.com/SigNoz/signoz/pkg/valuer"
)
type Store interface {
List(ctx context.Context, orgID valuer.UUID, offset, limit int) ([]*StorablePricingRule, int, error)
Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*StorablePricingRule, error)
Create(ctx context.Context, rule *StorablePricingRule) error
Update(ctx context.Context, rule *StorablePricingRule) error
Delete(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error
}