feat: add wallet settlement audit flow

This commit is contained in:
2026-05-11 22:59:26 +08:00
parent da1e19d0a9
commit c992f1de60
22 changed files with 1452 additions and 44 deletions
+8 -4
View File
@@ -21,6 +21,13 @@ func (s *Service) Estimate(ctx context.Context, kind string, model string, body
return EstimateResult{}, err
}
candidate := candidates[0]
return EstimateResult{
Items: s.estimatedBillings(ctx, user, kind, body, candidate),
Resolver: "effective-pricing-v1",
}, nil
}
func (s *Service) estimatedBillings(ctx context.Context, user *auth.User, kind string, body map[string]any, candidate store.RuntimeModelCandidate) []any {
usage := clients.Usage{InputTokens: estimateRequestTokens(body), OutputTokens: int(floatFromAny(body["max_tokens"]))}
if usage.OutputTokens == 0 {
usage.OutputTokens = 64
@@ -31,10 +38,7 @@ func (s *Service) Estimate(ctx context.Context, kind string, model string, body
"completion_tokens": usage.OutputTokens,
"total_tokens": usage.TotalTokens,
}}}
return EstimateResult{
Items: s.billings(ctx, user, kind, body, candidate, response, true),
Resolver: "effective-pricing-v1",
}, nil
return s.billings(ctx, user, kind, body, candidate, response, true)
}
func (s *Service) billings(ctx context.Context, user *auth.User, kind string, body map[string]any, candidate store.RuntimeModelCandidate, response clients.Response, simulated bool) []any {
+13
View File
@@ -67,6 +67,19 @@ func (s *Service) execute(ctx context.Context, task store.GatewayTask, user *aut
}
return Result{Task: failed, Output: failed.Result}, err
}
if len(candidates) > 0 {
estimatedBillings := s.estimatedBillings(ctx, user, task.Kind, body, candidates[0])
if err := s.ensureWalletBalance(ctx, user, estimatedBillings); err != nil {
if errors.Is(err, store.ErrInsufficientWalletBalance) {
failed, finishErr := s.failTask(ctx, task.ID, "insufficient_balance", err.Error(), task.RunMode == "simulation", err)
if finishErr != nil {
return Result{}, finishErr
}
return Result{Task: failed, Output: failed.Result}, err
}
return Result{}, err
}
}
if err := s.store.MarkTaskRunning(ctx, task.ID, modelType, body); err != nil {
return Result{}, err
}
+38
View File
@@ -0,0 +1,38 @@
package runner
import (
"context"
"fmt"
"strings"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)
func (s *Service) ensureWalletBalance(ctx context.Context, user *auth.User, billings []any) error {
amounts := map[string]float64{}
for _, raw := range billings {
line, _ := raw.(map[string]any)
if line == nil {
continue
}
currency := strings.TrimSpace(stringFromAny(line["currency"]))
if currency == "" {
currency = "resource"
}
amounts[currency] = roundPrice(amounts[currency] + floatFromAny(line["amount"]))
}
for currency, amount := range amounts {
if amount <= 0 {
continue
}
availability, err := s.store.WalletAvailability(ctx, user, currency, amount)
if err != nil {
return err
}
if !availability.Enough {
return fmt.Errorf("%w: required %.6f %s, available %.6f", store.ErrInsufficientWalletBalance, amount, currency, availability.AvailableAmount)
}
}
return nil
}