mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-01 19:19:07 +02:00
[management, proxy] Management-owned LLM pricing: file-backed defaults + (#6965)
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
)
|
||||
@@ -74,6 +75,13 @@ func (c *Combined) DeleteProvider(ctx context.Context, id string) error {
|
||||
return anDelete(ctx, c, "/api/agent-network/providers/"+id)
|
||||
}
|
||||
|
||||
// UpdateProvider replaces a provider by id (PUT). The API key may be omitted on
|
||||
// the request to keep the stored one; Models replaces the enumerated list, so
|
||||
// this is the path a test uses to change a model's price mid-run.
|
||||
func (c *Combined) UpdateProvider(ctx context.Context, id string, req api.AgentNetworkProviderRequest) (api.AgentNetworkProvider, error) {
|
||||
return anRequest[api.AgentNetworkProvider](ctx, c, http.MethodPut, "/api/agent-network/providers/"+id, req)
|
||||
}
|
||||
|
||||
// SetProviderEnabled toggles a provider's enabled flag, preserving its other
|
||||
// fields (the API key is omitted, which keeps the stored one). Used to run one
|
||||
// provider at a time so model→provider routing is unambiguous.
|
||||
@@ -139,3 +147,16 @@ func (c *Combined) ListConsumption(ctx context.Context) ([]api.AgentNetworkConsu
|
||||
func (c *Combined) ListAccessLogs(ctx context.Context) (api.AgentNetworkAccessLogsResponse, error) {
|
||||
return anRequest[api.AgentNetworkAccessLogsResponse](ctx, c, http.MethodGet, "/api/agent-network/access-logs", nil)
|
||||
}
|
||||
|
||||
// ListAccessLogsFiltered returns the access-log page narrowed by the given
|
||||
// query parameters (e.g. model=..., session_id=..., provider_id=...). This
|
||||
// exercises management's server-side filtering rather than filtering client
|
||||
// side, so a row that is ingested but not indexed under the filtered column
|
||||
// surfaces as an empty page.
|
||||
func (c *Combined) ListAccessLogsFiltered(ctx context.Context, query url.Values) (api.AgentNetworkAccessLogsResponse, error) {
|
||||
path := "/api/agent-network/access-logs"
|
||||
if encoded := query.Encode(); encoded != "" {
|
||||
path += "?" + encoded
|
||||
}
|
||||
return anRequest[api.AgentNetworkAccessLogsResponse](ctx, c, http.MethodGet, path, nil)
|
||||
}
|
||||
|
||||
+10
-1
@@ -93,10 +93,19 @@ func StartCombined(ctx context.Context) (*Combined, error) {
|
||||
_ = net.Remove(ctx)
|
||||
return nil, fmt.Errorf("write combined config: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(workDir, "data"), 0o755); err != nil {
|
||||
dataDir := filepath.Join(workDir, "data")
|
||||
if err := os.MkdirAll(dataDir, 0o755); err != nil {
|
||||
_ = net.Remove(ctx)
|
||||
return nil, fmt.Errorf("create datadir: %w", err)
|
||||
}
|
||||
// The config's agentNetwork.pricingDefaultsFile is a bare filename, so the
|
||||
// server resolves it against the datadir; write it there. It is an explicitly
|
||||
// configured path, so a failure to load fails the server's startup — which
|
||||
// surfaces here as the /api/instance readiness wait timing out.
|
||||
if err := os.WriteFile(filepath.Join(dataDir, PricingDefaultsFileName), []byte(pricingDefaultsYAML), 0o644); err != nil { //nolint:gosec // non-secret config, bind-mounted and read by the container
|
||||
_ = net.Remove(ctx)
|
||||
return nil, fmt.Errorf("write pricing defaults: %w", err)
|
||||
}
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: combinedImage,
|
||||
|
||||
@@ -8,6 +8,13 @@ package harness
|
||||
// embedded IdP, local signal/relay/STUN, and a sqlite store under the mounted
|
||||
// data dir. exposedAddress is the address peers use to reach this container; it
|
||||
// is overridden per-run so the value matches the container's network alias.
|
||||
//
|
||||
// pricingDefaultsFile is deliberately a BARE FILENAME, not an absolute path: it
|
||||
// must resolve against dataDir (→ /nb/data/<name>), which is the resolution rule
|
||||
// the combined server applies. It is also an EXPLICITLY configured path, so the
|
||||
// server is required to load it — a broken path or malformed file fails startup
|
||||
// rather than silently falling back to the compiled-in rates, and TestMain then
|
||||
// fails with the container logs.
|
||||
const combinedConfigYAML = `server:
|
||||
listenAddress: ":8080"
|
||||
exposedAddress: "%s"
|
||||
@@ -23,4 +30,36 @@ const combinedConfigYAML = `server:
|
||||
issuer: "%s"
|
||||
store:
|
||||
engine: "sqlite"
|
||||
agentNetwork:
|
||||
pricingDefaultsFile: "` + PricingDefaultsFileName + `"
|
||||
`
|
||||
|
||||
const (
|
||||
// PricingDefaultsFileName is the basename of the operator-supplied LLM
|
||||
// pricing defaults file the combined server is configured to load. Written
|
||||
// into the bind-mounted datadir by StartCombined.
|
||||
PricingDefaultsFileName = "e2e_llm_pricing.yaml"
|
||||
|
||||
// PricedDefaultModel is a real catalog model (openai surface) whose rates the
|
||||
// defaults file below REPLACES. Tests drive it against the mock vLLM upstream
|
||||
// and assert the file's rates were billed, which is only true if the file
|
||||
// travelled: config → LoadFile → DefaultTable → synthesizer → the proxy's
|
||||
// cost_meter defaults table.
|
||||
PricedDefaultModel = "gpt-4.1-mini"
|
||||
// PricedDefaultInputPer1k / PricedDefaultOutputPer1k are deliberately odd
|
||||
// values that no compiled-in catalog entry carries (gpt-4.1-mini ships as
|
||||
// 0.0004 / 0.0016), so a test asserting them cannot pass on the built-in
|
||||
// table.
|
||||
PricedDefaultInputPer1k = 0.0123
|
||||
PricedDefaultOutputPer1k = 0.0456
|
||||
)
|
||||
|
||||
// pricingDefaultsYAML is the operator-supplied pricing defaults file. Its schema
|
||||
// is surface -> model -> per-1k rates. Entries replace the compiled-in entry for
|
||||
// the same surface+model whole; every other model keeps its built-in rates, so
|
||||
// this file overriding one model must not disturb the rest of the table.
|
||||
const pricingDefaultsYAML = `openai:
|
||||
gpt-4.1-mini:
|
||||
input_per_1k: 0.0123
|
||||
output_per_1k: 0.0456
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user