mirror of
https://github.com/SigNoz/signoz.git
synced 2026-11-02 22:40:51 +00:00
Compare commits
5 Commits
refactor/s
...
issue_nerv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d1355071e | ||
|
|
dc1b3608f5 | ||
|
|
03184576bf | ||
|
|
bb1fd30b10 | ||
|
|
a6627445d0 |
@@ -13,7 +13,7 @@ interface PatternEditorProps {
|
||||
onChange: (patterns: string[]) => void;
|
||||
}
|
||||
|
||||
// Model-name prefix patterns as removable chips + an add input.
|
||||
// Model-name patterns as removable chips + an add input.
|
||||
function PatternEditor({
|
||||
patterns,
|
||||
isReadOnly,
|
||||
@@ -37,12 +37,7 @@ function PatternEditor({
|
||||
|
||||
return (
|
||||
<div className={styles.drawerSection}>
|
||||
<span className={styles.fieldLabel}>
|
||||
Model name patterns{' '}
|
||||
<Typography.Text as="span" color="muted">
|
||||
(prefix match)
|
||||
</Typography.Text>
|
||||
</span>
|
||||
<span className={styles.fieldLabel}>Model name patterns</span>
|
||||
<div className={styles.patternBox}>
|
||||
{patterns.length > 0 && (
|
||||
<div className={styles.patternChips}>
|
||||
@@ -53,7 +48,7 @@ function PatternEditor({
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
>
|
||||
{pattern}*
|
||||
{pattern}
|
||||
{!isReadOnly && (
|
||||
<button
|
||||
type="button"
|
||||
@@ -95,8 +90,9 @@ function PatternEditor({
|
||||
)}
|
||||
</div>
|
||||
<Typography.Text as="p" size="small" color="muted">
|
||||
Each pattern uses <strong>prefix matching</strong> against{' '}
|
||||
<code>gen_ai.request.model</code>.
|
||||
A pattern matches <code>gen_ai.request.model</code> exactly, plus its dated
|
||||
or platform variants such as <code>-20260921</code>, <code>-latest</code> or{' '}
|
||||
<code>@20260921</code>. End it with <code>*</code> to match any suffix.
|
||||
</Typography.Text>
|
||||
</div>
|
||||
);
|
||||
|
||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/SigNoz/clickhouse-go-mock v0.14.0
|
||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.6
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.16-0.20261102045710-f531882cb133
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1
|
||||
github.com/antonmedv/expr v1.15.3
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -100,8 +100,8 @@ github.com/SigNoz/expr v1.17.7-beta h1:FyZkleM5dTQ0O6muQfwGpoH5A2ohmN/XTasRCO72g
|
||||
github.com/SigNoz/expr v1.17.7-beta/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8=
|
||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc=
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.6 h1:AgblCIDlLKGtssS4ywvyEByH9fbJ7hFqZvPvUVJKwp8=
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.6/go.mod h1:YRQw6ETaq7fm8ts4Itgb6wC0bVhc0+nC6PFI0MaiH9k=
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.16-0.20261102045710-f531882cb133 h1:45SI7q1cTPE/rjDfLevdlTprXHiKcT5YiH5b48WdXCU=
|
||||
github.com/SigNoz/signoz-otel-collector v0.144.16-0.20261102045710-f531882cb133/go.mod h1:aOTO7iet9Ga9d3v/EkgeSZdSXEkjgVq2L0pK4tjgTEE=
|
||||
github.com/Yiling-J/theine-go v0.6.2 h1:1GeoXeQ0O0AUkiwj2S9Jc0Mzx+hpqzmqsJ4kIC4M9AY=
|
||||
github.com/Yiling-J/theine-go v0.6.2/go.mod h1:08QpMa5JZ2pKN+UJCRrCasWYO1IKCdl54Xa836rpmDU=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
|
||||
@@ -3,9 +3,9 @@ package llmpricingruletypes
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz-otel-collector/pkg/llmpricing"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
@@ -220,18 +220,14 @@ func (u *UpdatableLLMPricingRule) UnmarshalJSON(data []byte) error {
|
||||
return u.Validate()
|
||||
}
|
||||
|
||||
// Validate mirrors the collector's pattern check: at least one pattern, none
|
||||
// empty, all valid path.Match globs.
|
||||
// Validate requires at least one pattern and holds each to the collector's rules.
|
||||
func (u *UpdatableLLMPricingRule) Validate() error {
|
||||
if len(u.ModelPattern) == 0 {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern must contain at least one pattern", u.Model)
|
||||
}
|
||||
for _, p := range u.ModelPattern {
|
||||
if p == "" {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern must not contain an empty pattern", u.Model)
|
||||
}
|
||||
if _, err := path.Match(p, ""); err != nil {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: modelPattern %q is not a valid glob", u.Model, p)
|
||||
if err := llmpricing.ValidatePattern(p); err != nil {
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodePricingRuleInvalidInput, "model %q: %v", u.Model, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -270,7 +266,7 @@ func NewLLMPricingRuleFromUpdatable(u *UpdatableLLMPricingRule, orgID valuer.UUI
|
||||
func ModelMatchesAnyRule(model string, rules []*LLMPricingRule) bool {
|
||||
for _, r := range rules {
|
||||
for _, pattern := range r.ModelPattern {
|
||||
if ok, err := path.Match(pattern, model); err == nil && ok {
|
||||
if llmpricing.ParsePattern(pattern).Matches(model) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
32
pkg/types/llmpricingruletypes/pricing_test.go
Normal file
32
pkg/types/llmpricingruletypes/pricing_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package llmpricingruletypes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestModelMatchesAnyRule(t *testing.T) {
|
||||
opus5 := &LLMPricingRule{ModelPattern: StringSlice{"anthropic/claude-opus-5", "claude-opus-5"}}
|
||||
opus55 := &LLMPricingRule{ModelPattern: StringSlice{"anthropic/claude-opus-5.5", "claude-opus-5.5", "claude-opus-5-5"}}
|
||||
gpt5 := &LLMPricingRule{ModelPattern: StringSlice{"openai/gpt-5", "gpt-5"}}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
rules []*LLMPricingRule
|
||||
model string
|
||||
want bool
|
||||
}{
|
||||
{name: "ProviderQualifiedModel", rules: []*LLMPricingRule{opus55}, model: "anthropic/claude-opus-5.5", want: true},
|
||||
{name: "DatedSnapshot", rules: []*LLMPricingRule{opus55}, model: "claude-opus-5-5-20260921", want: true},
|
||||
{name: "SiblingModel_NoMatch", rules: []*LLMPricingRule{gpt5}, model: "gpt-5-mini-2025-08-07", want: false},
|
||||
{name: "SecondRuleMatches", rules: []*LLMPricingRule{opus5, opus55}, model: "claude-opus-5-5", want: true},
|
||||
{name: "NoRules", rules: nil, model: "claude-opus-5-5", want: false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assert.Equal(t, testCase.want, ModelMatchesAnyRule(testCase.model, testCase.rules))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,8 +138,7 @@ func TestUpdatableLLMPricingRuleUnmarshalJSON(t *testing.T) {
|
||||
{name: "missing", pattern: ``, wantErr: true},
|
||||
{name: "null", pattern: `null`, wantErr: true},
|
||||
{name: "empty_list", pattern: `[]`, wantErr: true},
|
||||
{name: "empty_entry", pattern: `["gpt-4o*", ""]`, wantErr: true},
|
||||
{name: "bad_glob", pattern: `["gpt-["]`, wantErr: true},
|
||||
{name: "mid_star", pattern: `["gpt-*-turbo"]`, wantErr: true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
Reference in New Issue
Block a user