Compare commits

..

5 Commits

Author SHA1 Message Date
SagarRajput-7
679a7aaced chore: remove the snapshot update since no migration is done under this pr 2026-04-11 00:20:52 +05:30
SagarRajput-7
b4798adf7a Merge branch 'main' into asset-import-config 2026-04-10 23:42:25 +05:30
SagarRajput-7
aca7e80c8a chore: jest module resolution fix 2026-04-10 23:42:02 +05:30
SagarRajput-7
e684d09e2c chore: added modulewrapper for jests 2026-04-10 14:43:59 +05:30
SagarRajput-7
0b88938362 chore: config setup for asset migration, allow use of @ alias 2026-04-10 14:31:50 +05:30
17 changed files with 101 additions and 1188 deletions

View File

@@ -1,169 +1,5 @@
components:
schemas:
Aio11YtypesCacheMode:
enum:
- subtract
- additive
- unknown
nullable: true
type: string
Aio11YtypesGettablePricingRule:
properties:
cache_mode:
$ref: '#/components/schemas/Aio11YtypesCacheMode'
cost_cache_read:
format: double
type: number
cost_cache_write:
format: double
type: number
cost_input:
format: double
type: number
cost_output:
format: double
type: number
created_at:
format: date-time
type: string
created_by:
type: string
enabled:
type: boolean
id:
type: string
is_override:
type: boolean
model_name:
type: string
model_pattern:
items:
type: string
nullable: true
type: array
synced_at:
format: date-time
nullable: true
type: string
unit:
$ref: '#/components/schemas/Aio11YtypesUnit'
updated_at:
format: date-time
type: string
updated_by:
type: string
required:
- id
- model_name
- model_pattern
- unit
- cache_mode
- cost_input
- cost_output
- cost_cache_read
- cost_cache_write
- is_override
- enabled
- created_at
- updated_at
- created_by
- updated_by
type: object
Aio11YtypesListPricingRulesResponse:
properties:
items:
items:
$ref: '#/components/schemas/Aio11YtypesGettablePricingRule'
nullable: true
type: array
limit:
type: integer
offset:
type: integer
total:
type: integer
required:
- items
- total
- offset
- limit
type: object
Aio11YtypesPostablePricingRule:
properties:
cache_mode:
$ref: '#/components/schemas/Aio11YtypesCacheMode'
cost_cache_read:
format: double
type: number
cost_cache_write:
format: double
type: number
cost_input:
format: double
type: number
cost_output:
format: double
type: number
enabled:
type: boolean
model_name:
type: string
model_pattern:
items:
type: string
nullable: true
type: array
unit:
$ref: '#/components/schemas/Aio11YtypesUnit'
required:
- model_name
- model_pattern
- unit
- cache_mode
- cost_input
- cost_output
- cost_cache_read
- cost_cache_write
- enabled
type: object
Aio11YtypesUnit:
enum:
- per_million_tokens
- per_token
nullable: true
type: string
Aio11YtypesUpdatablePricingRule:
properties:
cache_mode:
$ref: '#/components/schemas/Aio11YtypesCacheMode'
cost_cache_read:
nullable: true
type: number
cost_cache_write:
nullable: true
type: number
cost_input:
nullable: true
type: number
cost_output:
nullable: true
type: number
enabled:
nullable: true
type: boolean
is_override:
nullable: true
type: boolean
model_name:
nullable: true
type: string
model_pattern:
items:
type: string
type: array
unit:
$ref: '#/components/schemas/Aio11YtypesUnit'
type: object
AuthtypesAttributeMapping:
properties:
email:
@@ -3222,303 +3058,6 @@ 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/Aio11YtypesListPricingRulesResponse'
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/Aio11YtypesPostablePricingRule'
responses:
"201":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YtypesGettablePricingRule'
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/Aio11YtypesGettablePricingRule'
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
put:
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/Aio11YtypesUpdatablePricingRule'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/Aio11YtypesGettablePricingRule'
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/authz/check:
post:
deprecated: false

View File

@@ -0,0 +1 @@
export default 'test-file-stub';

View File

@@ -11,6 +11,9 @@ const config: Config.InitialOptions = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
modulePathIgnorePatterns: ['dist'],
moduleNameMapper: {
'\\.(png|jpg|jpeg|gif|svg|webp|avif|ico|bmp|tiff)$':
'<rootDir>/__mocks__/fileMock.ts',
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss)$': '<rootDir>/__mocks__/cssMock.ts',
'\\.md$': '<rootDir>/__mocks__/cssMock.ts',
'^uplot$': '<rootDir>/__mocks__/uplotMock.ts',

View File

@@ -12,7 +12,11 @@
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowSyntheticDefaultImports": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "bundler",
@@ -21,8 +25,15 @@
"noEmit": true,
"baseUrl": "./src",
"paths": {
"*": ["./*"],
"@constants/*": ["./container/OnboardingContainer/constants/*"]
"*": [
"./*"
],
"@constants/*": [
"./container/OnboardingContainer/constants/*"
],
"@/*": [
"./*"
]
},
"downlevelIteration": true,
"plugins": [
@@ -30,9 +41,18 @@
"name": "typescript-plugin-css-modules"
}
],
"types": ["vite/client", "node", "jest"]
"types": [
"vite/client",
"node",
"jest"
]
},
"exclude": ["node_modules", "src/parser/*.ts", "src/parser/TraceOperatorParser/*.ts", "orval.config.ts"],
"exclude": [
"node_modules",
"src/parser/*.ts",
"src/parser/TraceOperatorParser/*.ts",
"orval.config.ts"
],
"include": [
"./src",
"./src/**/*.ts",

View File

@@ -83,6 +83,7 @@ export default defineConfig(
plugins,
resolve: {
alias: {
'@': resolve(__dirname, './src'),
utils: resolve(__dirname, './src/utils'),
types: resolve(__dirname, './src/types'),
constants: resolve(__dirname, './src/constants'),

View File

@@ -1,115 +0,0 @@
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/{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.MethodPut).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

@@ -11,7 +11,6 @@ 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"
@@ -33,32 +32,31 @@ 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
aiO11yPricingRuleHandler aio11ypricingrule.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
}
func NewFactory(
@@ -85,7 +83,6 @@ func NewFactory(
factoryHandler factory.Handler,
cloudIntegrationHandler cloudintegration.Handler,
ruleStateHistoryHandler rulestatehistory.Handler,
aiO11yPricingRuleHandler aio11ypricingrule.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) {
return newProvider(
@@ -115,7 +112,6 @@ func NewFactory(
factoryHandler,
cloudIntegrationHandler,
ruleStateHistoryHandler,
aiO11yPricingRuleHandler,
)
})
}
@@ -147,37 +143,35 @@ func newProvider(
factoryHandler factory.Handler,
cloudIntegrationHandler cloudintegration.Handler,
ruleStateHistoryHandler rulestatehistory.Handler,
aiO11yPricingRuleHandler aio11ypricingrule.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,
aiO11yPricingRuleHandler: aiO11yPricingRuleHandler,
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,
}
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
@@ -278,10 +272,6 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
return err
}
if err := provider.addAIO11yRoutes(router); err != nil {
return err
}
return nil
}

View File

@@ -1,26 +0,0 @@
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
}
// 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)
}

View File

@@ -1,226 +0,0 @@
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)
}
// 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

@@ -10,7 +10,6 @@ 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/apdex"
"github.com/SigNoz/signoz/pkg/modules/apdex/implapdex"
"github.com/SigNoz/signoz/pkg/modules/cloudintegration"
@@ -43,27 +42,26 @@ import (
)
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
AIO11yPricingRuleHandler aio11ypricingrule.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
}
func NewHandlers(

View File

@@ -16,7 +16,6 @@ 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"
@@ -70,7 +69,6 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
struct{ factory.Handler }{},
struct{ cloudintegration.Handler }{},
struct{ rulestatehistory.Handler }{},
struct{ aio11ypricingrule.Handler }{},
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
if err != nil {
return nil, err

View File

@@ -3,6 +3,8 @@ 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"
@@ -10,8 +12,6 @@ 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"
@@ -288,7 +288,6 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
handlers.RegistryHandler,
handlers.CloudIntegrationHandler,
handlers.RuleStateHistory,
handlers.AIO11yPricingRuleHandler,
),
)
}

View File

@@ -1,9 +0,0 @@
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

@@ -1,91 +0,0 @@
package aio11ypricingruletypes
import "time"
// GettablePricingRule is the HTTP response representation of a pricing rule.
type GettablePricingRule struct {
ID string `json:"id" required:"true"`
Model string `json:"model_name" required:"true"`
ModelPattern []string `json:"model_pattern" required:"true"`
Unit Unit `json:"unit" required:"true"`
CacheMode CacheMode `json:"cache_mode" required:"true"`
CostInput float64 `json:"cost_input" required:"true"`
CostOutput float64 `json:"cost_output" required:"true"`
CostCacheRead float64 `json:"cost_cache_read" required:"true"`
CostCacheWrite float64 `json:"cost_cache_write" required:"true"`
IsOverride bool `json:"is_override" required:"true"`
SyncedAt *time.Time `json:"synced_at,omitempty"`
Enabled bool `json:"enabled" required:"true"`
CreatedAt time.Time `json:"created_at" required:"true"`
UpdatedAt time.Time `json:"updated_at" required:"true"`
CreatedBy string `json:"created_by" required:"true"`
UpdatedBy string `json:"updated_by" required:"true"`
}
// NewGettablePricingRule converts a domain PricingRule to a GettablePricingRule.
func NewGettablePricingRule(r *PricingRule) *GettablePricingRule {
pattern := make([]string, len(r.ModelPattern))
copy(pattern, r.ModelPattern)
return &GettablePricingRule{
ID: r.ID,
Model: r.Model,
ModelPattern: pattern,
Unit: r.Unit,
CacheMode: r.CacheMode,
CostInput: r.CostInput,
CostOutput: r.CostOutput,
CostCacheRead: r.CostCacheRead,
CostCacheWrite: r.CostCacheWrite,
IsOverride: r.IsOverride,
SyncedAt: r.SyncedAt,
Enabled: r.Enabled,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
CreatedBy: r.CreatedBy,
UpdatedBy: r.UpdatedBy,
}
}
// PostablePricingRule is the HTTP request body for creating a pricing rule.
// All fields are required.
type PostablePricingRule struct {
Model string `json:"model_name" required:"true"`
ModelPattern []string `json:"model_pattern" required:"true"`
Unit Unit `json:"unit" required:"true"`
CacheMode CacheMode `json:"cache_mode" required:"true"`
CostInput float64 `json:"cost_input" required:"true"`
CostOutput float64 `json:"cost_output" required:"true"`
CostCacheRead float64 `json:"cost_cache_read" required:"true"`
CostCacheWrite float64 `json:"cost_cache_write" required:"true"`
Enabled bool `json:"enabled" required:"true"`
}
// UpdatablePricingRule is the HTTP request body for updating a pricing rule.
// All fields are optional; only non-nil fields are applied.
type UpdatablePricingRule struct {
Model *string `json:"model_name,omitempty"`
ModelPattern []string `json:"model_pattern,omitempty"`
Unit *Unit `json:"unit,omitempty"`
CacheMode *CacheMode `json:"cache_mode,omitempty"`
CostInput *float64 `json:"cost_input,omitempty"`
CostOutput *float64 `json:"cost_output,omitempty"`
CostCacheRead *float64 `json:"cost_cache_read,omitempty"`
CostCacheWrite *float64 `json:"cost_cache_write,omitempty"`
IsOverride *bool `json:"is_override,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
}
// ListPricingRulesQuery holds the pagination parameters for listing pricing rules.
type ListPricingRulesQuery struct {
Offset int `query:"offset" json:"offset"`
Limit int `query:"limit" json:"limit"`
}
// ListPricingRulesResponse is the paginated response for listing pricing rules.
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

@@ -1,80 +0,0 @@
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 string
OrgID valuer.UUID
Model string
ModelPattern []string
Unit Unit
CacheMode CacheMode
CostInput float64
CostOutput float64
CostCacheRead float64
CostCacheWrite float64
// IsOverride marks that cost fields were manually edited.
// When true the sync job skips this rule.
IsOverride bool
SyncedAt *time.Time
Enabled bool
}
// 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.StringValue(),
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

@@ -1,70 +0,0 @@
package aio11ypricingruletypes
import (
"database/sql/driver"
"encoding/json"
"fmt"
"time"
"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 fmt.Errorf("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"`
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

@@ -1,19 +0,0 @@
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
}