Files
model-gateway/service/pricing_client.go
T
19904408334andClaude 928682f121 feat: 模型计费收敛到 shop-user-trade(去 admin 租户扣费 + /calc 算费)
- 删 CheckTenantBalance/DeductBalance/GetTenantSurplus(admin-go 租户余额退出 model-gateway)
- 新增 pricing_client.go:门禁 config/get + calcModelCost 调 shop-user-trade /calc 算费
- sync/stream/async 三处 Cost 改为按用量调 /calc(媒体类型映射 text/audio/video/image)
- 删 price.go 本地 PriceConfig 换算体系与 entity/dto 的 price_config 字段

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-29 14:32:51 +08:00

86 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"fmt"
commonHttp "gitea.redpowerfuture.com/red-future/common/http"
"github.com/gogf/gf/v2/frame/g"
)
// ====================== shop-user-trade 计价对接(独立 module,本地 JSON 对齐) ======================
// shopPricingConfig shop-user-trade config/get 响应(仅取启用标记;规则在 shop-user-trade 侧,model-gateway 不消费)
type shopPricingConfig struct {
Enabled int `json:"enabled"`
}
// shopCalcFeeRes shop-user-trade /calc 响应
type shopCalcFeeRes struct {
Cost float64 `json:"cost"`
}
// pricingURL 组装 shop-user-trade 计价接口地址(common http.RouteRegister 推导前缀,同 ai-agent billing.go
func pricingURL(sub string) string {
return "shop-user-trade/pricing/controller/" + sub
}
// mgMediaTypeToShop 媒体类型词汇映射:model-gatewayaudio/no_video/has_video)→ shop-user-tradetext/audio/video/image
func mgMediaTypeToShop(mg string) string {
switch mg {
case "audio":
return "audio"
case "has_video":
return "video"
default: // no_video
return "text"
}
}
// modelBillable 调用前门禁:模型须在 shop-user-trade 已配置且启用计价,否则阻塞调用。
// 替换原 CheckTenantBalanceadmin-go 租户余额门禁);未配置→config/get 返回错误,未启用→enabled!=1。
func modelBillable(ctx context.Context, modelId int64) error {
var cfg shopPricingConfig
err := commonHttp.Get(ctx, pricingURL("config/get"), setCtxHeader(ctx), &cfg,
"subjectType", "model", "subjectId", fmt.Sprintf("%d", modelId))
if err != nil {
return fmt.Errorf("模型未配置计价,无法调用: %w", err)
}
if cfg.Enabled != 1 {
return fmt.Errorf("模型未启用计价,无法调用")
}
return nil
}
// buildModelUsage 组装算费用量 JSON 对象(ChargeUsage 形状)。mediaType 为 model-gateway 词汇(DetectMediaType/异步快照)。
func buildModelUsage(prompt, completion, cached int64, mediaType string, durationSec float64) map[string]any {
if durationSec < 0 {
durationSec = 0
}
return map[string]any{
"promptTokens": prompt,
"completionTokens": completion,
"cachedTokens": cached,
"mediaType": mgMediaTypeToShop(mediaType),
"durationSec": durationSec,
}
}
// calcModelCost 调 shop-user-trade /calc 按用量算费(不建单不扣费)。
// 调用前门禁已保证配置存在;此处失败(配置中途删除/网络抖动)→ 记日志返回 0,不拖垮已完成的模型调用。
func calcModelCost(ctx context.Context, modelId int64, usage map[string]any) float64 {
var res shopCalcFeeRes
err := commonHttp.Post(ctx, pricingURL("calc"), setCtxHeader(ctx), &res, &struct {
SubjectType string `json:"subjectType"`
SubjectID string `json:"subjectId"`
Usage map[string]any `json:"usage"`
}{
SubjectType: "model", SubjectID: fmt.Sprintf("%d", modelId), Usage: usage,
})
if err != nil {
g.Log().Errorf(ctx, "[算费] 调用 shop-user-trade 失败 modelId=%d: %v", modelId, err)
return 0
}
return res.Cost
}