| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
- package service
- import (
- "errors"
- "fmt"
- "strings"
- )
- // geminiAIStudioActions 是允许出现在上游 URL 里的 action 集合,与 ForwardNative
- // 的入站校验保持一致,同时避免 compat 侧把可变字符串直接拼进 path。
- var geminiAIStudioActions = map[string]struct{}{
- "generateContent": {},
- "streamGenerateContent": {},
- "countTokens": {},
- }
- // buildGeminiAIStudioModelActionURL 组装 AI Studio 的
- // /v1beta/models/{model}:{action} 上游 URL。
- //
- // model 是客户端可控的(native 路由取自 URL 片段,compat 路由取自请求体的 model
- // 字段,之后可能再经渠道映射),因此必须先过路径片段护栏才能拼进 path,
- // 见 upstream_path_guard.go。新增 AI Studio 端点请一律走本函数。
- func buildGeminiAIStudioModelActionURL(baseURL, model, action string, stream bool) (string, error) {
- trimmedBase := strings.TrimRight(strings.TrimSpace(baseURL), "/")
- if trimmedBase == "" {
- return "", errors.New("gemini base url is required")
- }
- trimmedModel := strings.TrimSpace(model)
- if trimmedModel == "" {
- return "", errors.New("gemini model is required")
- }
- if err := validateUpstreamPathSegment("gemini model", trimmedModel); err != nil {
- return "", err
- }
- trimmedAction := strings.TrimSpace(action)
- if _, ok := geminiAIStudioActions[trimmedAction]; !ok {
- return "", fmt.Errorf("unsupported gemini action: %s", trimmedAction)
- }
- fullURL := fmt.Sprintf("%s/v1beta/models/%s:%s", trimmedBase, trimmedModel, trimmedAction)
- if stream {
- fullURL += "?alt=sse"
- }
- return fullURL, nil
- }
- // IsSafeGeminiModelPathSegment 供 handler 层在解析出 URL 里的模型名后立刻校验,
- // 让客户端拿到明确的 400,而不是等到构造上游请求时才报错。
- func IsSafeGeminiModelPathSegment(model string) bool {
- return isSafeUpstreamPathSegment(strings.TrimSpace(model))
- }
|