feat: implement AI gateway phase one runtime
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
)
|
||||
|
||||
func (s *Service) rateLimitReservations(ctx context.Context, user *auth.User, candidate store.RuntimeModelCandidate, body map[string]any) []store.RateLimitReservation {
|
||||
out := make([]store.RateLimitReservation, 0)
|
||||
out = append(out, reservationsFromPolicy("platform_model", candidate.PlatformModelID, effectiveRateLimitPolicy(candidate), body)...)
|
||||
if group, err := s.store.ResolveUserGroupPolicy(ctx, user); err == nil && group.ID != "" {
|
||||
out = append(out, reservationsFromPolicy("user_group", group.ID, group.RateLimitPolicy, body)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func effectiveRateLimitPolicy(candidate store.RuntimeModelCandidate) map[string]any {
|
||||
if hasRules(candidate.ModelRateLimitPolicy) {
|
||||
return candidate.ModelRateLimitPolicy
|
||||
}
|
||||
if hasRules(candidate.PlatformRateLimitPolicy) {
|
||||
return candidate.PlatformRateLimitPolicy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func reservationsFromPolicy(scopeType string, scopeKey string, policy map[string]any, body map[string]any) []store.RateLimitReservation {
|
||||
if scopeKey == "" || !hasRules(policy) {
|
||||
return nil
|
||||
}
|
||||
rules, _ := policy["rules"].([]any)
|
||||
out := make([]store.RateLimitReservation, 0, len(rules))
|
||||
estimatedTokens := estimateRequestTokens(body)
|
||||
for _, rawRule := range rules {
|
||||
rule, _ := rawRule.(map[string]any)
|
||||
metric := strings.TrimSpace(stringFromMap(rule, "metric"))
|
||||
limit := floatFromAny(rule["limit"])
|
||||
amount := 1.0
|
||||
if strings.HasPrefix(metric, "tpm") {
|
||||
amount = float64(estimatedTokens)
|
||||
}
|
||||
out = append(out, store.RateLimitReservation{
|
||||
ScopeType: scopeType,
|
||||
ScopeKey: scopeKey,
|
||||
Metric: metric,
|
||||
Limit: limit,
|
||||
Amount: amount,
|
||||
WindowSeconds: int(floatFromAny(rule["windowSeconds"])),
|
||||
LeaseTTLSeconds: int(floatFromAny(rule["leaseTtlSeconds"])),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func hasRules(policy map[string]any) bool {
|
||||
rules, _ := policy["rules"].([]any)
|
||||
return len(rules) > 0
|
||||
}
|
||||
|
||||
func estimateRequestTokens(body map[string]any) int {
|
||||
text := ""
|
||||
if prompt := stringFromMap(body, "prompt"); prompt != "" {
|
||||
text += prompt
|
||||
}
|
||||
if input := stringFromMap(body, "input"); input != "" {
|
||||
text += input
|
||||
}
|
||||
if messages, ok := body["messages"].([]any); ok {
|
||||
for _, raw := range messages {
|
||||
message, _ := raw.(map[string]any)
|
||||
switch content := message["content"].(type) {
|
||||
case string:
|
||||
text += content
|
||||
case []any:
|
||||
for _, rawPart := range content {
|
||||
part, _ := rawPart.(map[string]any)
|
||||
text += stringFromMap(part, "text")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if text == "" {
|
||||
return 1
|
||||
}
|
||||
return len([]rune(text))/4 + 1
|
||||
}
|
||||
Reference in New Issue
Block a user