github.com/qiniu/go-sdk/v7@v7.27.0/sandbox/types_injection_rule.go (about) 1 package sandbox 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/qiniu/go-sdk/v7/sandbox/internal/apis" 8 ) 9 10 // --------------------------------------------------------------------------- 11 // SDK 自有类型 — 注入配置(含注入规则) 12 // --------------------------------------------------------------------------- 13 14 // OpenAIInjection OpenAI 兼容 API 注入配置。自动设置 Authorization: Bearer 头。 15 // 默认 host: api.openai.com 16 type OpenAIInjection struct { 17 // APIKey API 密钥,可选。 18 APIKey *string 19 20 // BaseURL 可选 base URL,未指定时使用 api.openai.com。host 精确匹配; 21 // path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。 22 BaseURL *string 23 24 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 25 IfHeaders *map[string]string 26 27 // IfQueries 可选请求 query 匹配条件,满足后才注入。 28 IfQueries *map[string]string 29 } 30 31 // AnthropicInjection Anthropic API 注入配置。自动设置 x-api-key 头。 32 // 默认 host: api.anthropic.com 33 type AnthropicInjection struct { 34 // APIKey API 密钥,可选。 35 APIKey *string 36 37 // BaseURL 可选 base URL,未指定时使用 api.anthropic.com。host 精确匹配; 38 // path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。 39 BaseURL *string 40 41 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 42 IfHeaders *map[string]string 43 44 // IfQueries 可选请求 query 匹配条件,满足后才注入。 45 IfQueries *map[string]string 46 } 47 48 // GeminiInjection Google Gemini API 注入配置。自动设置 x-goog-api-key 头。 49 // 默认 host: generativelanguage.googleapis.com 50 type GeminiInjection struct { 51 // APIKey API 密钥,可选。 52 APIKey *string 53 54 // BaseURL 可选 base URL,未指定时使用 generativelanguage.googleapis.com。 55 // host 精确匹配;path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。 56 BaseURL *string 57 58 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 59 IfHeaders *map[string]string 60 61 // IfQueries 可选请求 query 匹配条件,满足后才注入。 62 IfQueries *map[string]string 63 } 64 65 // QiniuInjection 七牛 AI API 注入配置。七牛网关同时兼容 OpenAI 和 Anthropic 协议, 66 // api_key 会注入到请求实际携带的认证头(Authorization 或 x-api-key)。 67 // 默认 host: api.qnaigc.com 68 type QiniuInjection struct { 69 // APIKey API 密钥。 70 APIKey *string 71 72 // BaseURL 可选 base URL,未指定时使用 api.qnaigc.com。host 精确匹配; 73 // path 存在时精确匹配规范化路径,尾部 * 表示前缀匹配。 74 BaseURL *string 75 76 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 77 IfHeaders *map[string]string 78 79 // IfQueries 可选请求 query 匹配条件,满足后才注入。 80 IfQueries *map[string]string 81 } 82 83 // GithubInjection GitHub 凭证注入。平台用此凭证克隆并校验仓库,且在沙箱运行期间为 84 // 匹配 github.com / api.github.com 的 HTTPS 请求自动注入认证;token 不会以明文形式 85 // 暴露到沙箱内。 86 type GithubInjection struct { 87 // BaseURL 可选 base URL,未指定时平台会匹配 github.com 和 api.github.com。 88 // 指定时 host 必须为 github.com 或 api.github.com。 89 BaseURL *string 90 91 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 92 IfHeaders *map[string]string 93 94 // IfQueries 可选请求 query 匹配条件,满足后才注入。 95 IfQueries *map[string]string 96 97 // Token GitHub token,对所有请求的仓库具备访问权限。 98 Token *string 99 } 100 101 // HTTPInjection 自定义 HTTP 注入配置。 102 type HTTPInjection struct { 103 // BaseURL 匹配 HTTPS 请求的 base URL。host 精确匹配;path 存在时精确匹配 104 // 规范化路径,尾部 * 表示前缀匹配。若未指定 scheme 默认为 https。 105 BaseURL string 106 107 // Headers 需要注入或覆盖的 HTTP Headers,可选。 108 Headers *map[string]string 109 110 // IfHeaders 可选请求 Header 匹配条件,满足后才注入。 111 IfHeaders *map[string]string 112 113 // IfQueries 可选请求 query 匹配条件,满足后才注入。 114 IfQueries *map[string]string 115 } 116 117 // InjectionSpec 注入配置(discriminated union),各字段互斥,只能设置一个。 118 type InjectionSpec struct { 119 // OpenAI OpenAI 兼容 API 注入。 120 OpenAI *OpenAIInjection 121 122 // Anthropic Anthropic API 注入。 123 Anthropic *AnthropicInjection 124 125 // Gemini Google Gemini API 注入。 126 Gemini *GeminiInjection 127 128 // Qiniu 七牛 AI API 注入。 129 Qiniu *QiniuInjection 130 131 // Github GitHub 凭证注入。 132 Github *GithubInjection 133 134 // HTTP 自定义 HTTP 注入。 135 HTTP *HTTPInjection 136 } 137 138 // SandboxInjectionSpec 沙箱注入配置(discriminated union)。 139 // 可引用已保存的注入规则 ID,或直接指定注入配置,各字段互斥。 140 type SandboxInjectionSpec struct { 141 // ByID 引用已保存的注入规则 ID。 142 ByID *string 143 144 // OpenAI OpenAI 兼容 API 注入。 145 OpenAI *OpenAIInjection 146 147 // Anthropic Anthropic API 注入。 148 Anthropic *AnthropicInjection 149 150 // Gemini Google Gemini API 注入。 151 Gemini *GeminiInjection 152 153 // Qiniu 七牛 AI API 注入。 154 Qiniu *QiniuInjection 155 156 // Github GitHub 凭证注入。 157 Github *GithubInjection 158 159 // HTTP 自定义 HTTP 注入。 160 HTTP *HTTPInjection 161 } 162 163 // MaskedSandboxInjection 沙箱当前的请求注入配置查询结果。 164 // APIKey、Token 和 Headers 等敏感字段仅包含服务端返回的脱敏值。 165 // 该类型与可写的 SandboxInjectionSpec 保持分离,不能直接用于更新配置。 166 type MaskedSandboxInjection struct { 167 // ByID 引用的已保存注入规则 ID。 168 ByID *string 169 170 // OpenAI 已脱敏的 OpenAI 兼容 API 注入配置。 171 OpenAI *OpenAIInjection 172 173 // Anthropic 已脱敏的 Anthropic API 注入配置。 174 Anthropic *AnthropicInjection 175 176 // Gemini 已脱敏的 Google Gemini API 注入配置。 177 Gemini *GeminiInjection 178 179 // Qiniu 已脱敏的七牛 AI API 注入配置。 180 Qiniu *QiniuInjection 181 182 // Github 已脱敏的 GitHub 凭证注入配置。 183 Github *GithubInjection 184 185 // HTTP 已脱敏的自定义 HTTP 注入配置。 186 HTTP *HTTPInjection 187 } 188 189 // InjectionRule 预定义的请求注入规则。 190 type InjectionRule struct { 191 // RuleID 规则唯一标识。 192 RuleID string 193 194 // Name 规则名称,同一用户下唯一。 195 Name string 196 197 // Injection 注入配置。 198 Injection InjectionSpec 199 200 // CreatedAt 创建时间。 201 CreatedAt time.Time 202 203 // UpdatedAt 最后更新时间。 204 UpdatedAt time.Time 205 } 206 207 // CreateInjectionRuleParams 创建注入规则的请求参数。 208 type CreateInjectionRuleParams struct { 209 // Name 规则名称(必填),同一用户下唯一。 210 Name string 211 212 // Injection 注入配置(必填)。 213 Injection InjectionSpec 214 } 215 216 func (p *CreateInjectionRuleParams) toAPI() (apis.PostInjectionRulesJSONRequestBody, error) { 217 inj, err := injectionSpecToAPI(p.Injection) 218 if err != nil { 219 return apis.PostInjectionRulesJSONRequestBody{}, err 220 } 221 return apis.PostInjectionRulesJSONRequestBody{ 222 Name: p.Name, 223 Injection: inj, 224 }, nil 225 } 226 227 // UpdateInjectionRuleParams 更新注入规则的请求参数。 228 type UpdateInjectionRuleParams struct { 229 // Name 规则名称,可选。 230 Name *string 231 232 // Injection 注入配置,可选。 233 Injection *InjectionSpec 234 } 235 236 func (p *UpdateInjectionRuleParams) toAPI() (apis.PutInjectionRulesRuleIDJSONRequestBody, error) { 237 body := apis.PutInjectionRulesRuleIDJSONRequestBody{ 238 Name: p.Name, 239 } 240 if p.Injection != nil { 241 inj, err := injectionSpecToAPI(*p.Injection) 242 if err != nil { 243 return body, err 244 } 245 body.Injection = &inj 246 } 247 return body, nil 248 } 249 250 // --------------------------------------------------------------------------- 251 // 转换函数 — SDK → apis 252 // --------------------------------------------------------------------------- 253 254 func injectionSpecToAPI(spec InjectionSpec) (apis.Injection, error) { 255 count := 0 256 if spec.OpenAI != nil { 257 count++ 258 } 259 if spec.Anthropic != nil { 260 count++ 261 } 262 if spec.Gemini != nil { 263 count++ 264 } 265 if spec.HTTP != nil { 266 count++ 267 } 268 if spec.Qiniu != nil { 269 count++ 270 } 271 if spec.Github != nil { 272 count++ 273 } 274 if count == 0 { 275 return apis.Injection{}, fmt.Errorf("InjectionSpec: exactly one injection type must be set (OpenAI, Anthropic, Gemini, Qiniu, GitHub, or HTTP), got none") 276 } 277 if count > 1 { 278 return apis.Injection{}, fmt.Errorf("InjectionSpec: exactly one injection type must be set, but got %d", count) 279 } 280 281 var inj apis.Injection 282 var err error 283 switch { 284 case spec.OpenAI != nil: 285 err = inj.FromOpenaiInjection(apis.OpenaiInjection{ 286 APIKey: spec.OpenAI.APIKey, 287 BaseURL: spec.OpenAI.BaseURL, 288 IfHeaders: spec.OpenAI.IfHeaders, 289 IfQueries: spec.OpenAI.IfQueries, 290 Type: apis.Openai, 291 }) 292 case spec.Anthropic != nil: 293 err = inj.FromAnthropicInjection(apis.AnthropicInjection{ 294 APIKey: spec.Anthropic.APIKey, 295 BaseURL: spec.Anthropic.BaseURL, 296 IfHeaders: spec.Anthropic.IfHeaders, 297 IfQueries: spec.Anthropic.IfQueries, 298 Type: apis.Anthropic, 299 }) 300 case spec.Gemini != nil: 301 err = inj.FromGeminiInjection(apis.GeminiInjection{ 302 APIKey: spec.Gemini.APIKey, 303 BaseURL: spec.Gemini.BaseURL, 304 IfHeaders: spec.Gemini.IfHeaders, 305 IfQueries: spec.Gemini.IfQueries, 306 Type: apis.Gemini, 307 }) 308 case spec.Qiniu != nil: 309 err = inj.FromQiniuInjection(apis.QiniuInjection{ 310 APIKey: spec.Qiniu.APIKey, 311 BaseURL: spec.Qiniu.BaseURL, 312 IfHeaders: spec.Qiniu.IfHeaders, 313 IfQueries: spec.Qiniu.IfQueries, 314 Type: apis.Qiniu, 315 }) 316 case spec.Github != nil: 317 err = inj.FromGithubInjection(apis.GithubInjection{ 318 BaseURL: spec.Github.BaseURL, 319 IfHeaders: spec.Github.IfHeaders, 320 IfQueries: spec.Github.IfQueries, 321 Token: spec.Github.Token, 322 Type: apis.Github, 323 }) 324 case spec.HTTP != nil: 325 err = inj.FromHTTPInjection(apis.HTTPInjection{ 326 BaseURL: spec.HTTP.BaseURL, 327 Headers: spec.HTTP.Headers, 328 IfHeaders: spec.HTTP.IfHeaders, 329 IfQueries: spec.HTTP.IfQueries, 330 Type: apis.HTTP, 331 }) 332 } 333 return inj, err 334 } 335 336 func sandboxInjectionSpecToAPI(spec SandboxInjectionSpec) (apis.SandboxInjection, error) { 337 count := 0 338 if spec.ByID != nil { 339 count++ 340 } 341 if spec.OpenAI != nil { 342 count++ 343 } 344 if spec.Anthropic != nil { 345 count++ 346 } 347 if spec.Gemini != nil { 348 count++ 349 } 350 if spec.Qiniu != nil { 351 count++ 352 } 353 if spec.Github != nil { 354 count++ 355 } 356 if spec.HTTP != nil { 357 count++ 358 } 359 if count == 0 { 360 return apis.SandboxInjection{}, fmt.Errorf("SandboxInjectionSpec: exactly one injection type must be set (ByID, OpenAI, Anthropic, Gemini, Qiniu, GitHub, or HTTP), got none") 361 } 362 if count > 1 { 363 return apis.SandboxInjection{}, fmt.Errorf("SandboxInjectionSpec: exactly one injection type must be set, but got %d", count) 364 } 365 366 var si apis.SandboxInjection 367 var err error 368 switch { 369 case spec.ByID != nil: 370 err = si.FromInjectionByID(apis.InjectionByID{ 371 ID: *spec.ByID, 372 Type: apis.ID, 373 }) 374 case spec.OpenAI != nil: 375 err = si.FromOpenaiInjection(apis.OpenaiInjection{ 376 APIKey: spec.OpenAI.APIKey, 377 BaseURL: spec.OpenAI.BaseURL, 378 IfHeaders: spec.OpenAI.IfHeaders, 379 IfQueries: spec.OpenAI.IfQueries, 380 Type: apis.Openai, 381 }) 382 case spec.Anthropic != nil: 383 err = si.FromAnthropicInjection(apis.AnthropicInjection{ 384 APIKey: spec.Anthropic.APIKey, 385 BaseURL: spec.Anthropic.BaseURL, 386 IfHeaders: spec.Anthropic.IfHeaders, 387 IfQueries: spec.Anthropic.IfQueries, 388 Type: apis.Anthropic, 389 }) 390 case spec.Gemini != nil: 391 err = si.FromGeminiInjection(apis.GeminiInjection{ 392 APIKey: spec.Gemini.APIKey, 393 BaseURL: spec.Gemini.BaseURL, 394 IfHeaders: spec.Gemini.IfHeaders, 395 IfQueries: spec.Gemini.IfQueries, 396 Type: apis.Gemini, 397 }) 398 case spec.Qiniu != nil: 399 err = si.FromQiniuInjection(apis.QiniuInjection{ 400 APIKey: spec.Qiniu.APIKey, 401 BaseURL: spec.Qiniu.BaseURL, 402 IfHeaders: spec.Qiniu.IfHeaders, 403 IfQueries: spec.Qiniu.IfQueries, 404 Type: apis.Qiniu, 405 }) 406 case spec.Github != nil: 407 err = si.FromGithubInjection(apis.GithubInjection{ 408 BaseURL: spec.Github.BaseURL, 409 IfHeaders: spec.Github.IfHeaders, 410 IfQueries: spec.Github.IfQueries, 411 Token: spec.Github.Token, 412 Type: apis.Github, 413 }) 414 case spec.HTTP != nil: 415 err = si.FromHTTPInjection(apis.HTTPInjection{ 416 BaseURL: spec.HTTP.BaseURL, 417 Headers: spec.HTTP.Headers, 418 IfHeaders: spec.HTTP.IfHeaders, 419 IfQueries: spec.HTTP.IfQueries, 420 Type: apis.HTTP, 421 }) 422 } 423 return si, err 424 } 425 426 // --------------------------------------------------------------------------- 427 // 转换函数 — apis → SDK 428 // --------------------------------------------------------------------------- 429 430 func injectionSpecFromAPI(inj apis.Injection) (InjectionSpec, error) { 431 disc, err := inj.Discriminator() 432 if err != nil { 433 return InjectionSpec{}, err 434 } 435 switch disc { 436 case string(apis.Openai): 437 v, err := inj.AsOpenaiInjection() 438 if err != nil { 439 return InjectionSpec{}, err 440 } 441 return InjectionSpec{OpenAI: &OpenAIInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 442 case string(apis.Anthropic): 443 v, err := inj.AsAnthropicInjection() 444 if err != nil { 445 return InjectionSpec{}, err 446 } 447 return InjectionSpec{Anthropic: &AnthropicInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 448 case string(apis.Gemini): 449 v, err := inj.AsGeminiInjection() 450 if err != nil { 451 return InjectionSpec{}, err 452 } 453 return InjectionSpec{Gemini: &GeminiInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 454 case string(apis.Qiniu): 455 v, err := inj.AsQiniuInjection() 456 if err != nil { 457 return InjectionSpec{}, err 458 } 459 return InjectionSpec{Qiniu: &QiniuInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 460 case string(apis.Github): 461 v, err := inj.AsGithubInjection() 462 if err != nil { 463 return InjectionSpec{}, err 464 } 465 return InjectionSpec{Github: &GithubInjection{BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries, Token: v.Token}}, nil 466 case string(apis.HTTP): 467 v, err := inj.AsHTTPInjection() 468 if err != nil { 469 return InjectionSpec{}, err 470 } 471 return InjectionSpec{HTTP: &HTTPInjection{BaseURL: v.BaseURL, Headers: v.Headers, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 472 default: 473 return InjectionSpec{}, fmt.Errorf("unknown injection type: %s", disc) 474 } 475 } 476 477 func maskedSandboxInjectionFromAPI(inj apis.SandboxInjection) (MaskedSandboxInjection, error) { 478 disc, err := inj.Discriminator() 479 if err != nil { 480 return MaskedSandboxInjection{}, err 481 } 482 switch disc { 483 case string(apis.ID): 484 v, err := inj.AsInjectionByID() 485 if err != nil { 486 return MaskedSandboxInjection{}, err 487 } 488 return MaskedSandboxInjection{ByID: &v.ID}, nil 489 case string(apis.Openai): 490 v, err := inj.AsOpenaiInjection() 491 if err != nil { 492 return MaskedSandboxInjection{}, err 493 } 494 return MaskedSandboxInjection{OpenAI: &OpenAIInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 495 case string(apis.Anthropic): 496 v, err := inj.AsAnthropicInjection() 497 if err != nil { 498 return MaskedSandboxInjection{}, err 499 } 500 return MaskedSandboxInjection{Anthropic: &AnthropicInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 501 case string(apis.Gemini): 502 v, err := inj.AsGeminiInjection() 503 if err != nil { 504 return MaskedSandboxInjection{}, err 505 } 506 return MaskedSandboxInjection{Gemini: &GeminiInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 507 case string(apis.Qiniu): 508 v, err := inj.AsQiniuInjection() 509 if err != nil { 510 return MaskedSandboxInjection{}, err 511 } 512 return MaskedSandboxInjection{Qiniu: &QiniuInjection{APIKey: v.APIKey, BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 513 case string(apis.Github): 514 v, err := inj.AsGithubInjection() 515 if err != nil { 516 return MaskedSandboxInjection{}, err 517 } 518 return MaskedSandboxInjection{Github: &GithubInjection{BaseURL: v.BaseURL, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries, Token: v.Token}}, nil 519 case string(apis.HTTP): 520 v, err := inj.AsHTTPInjection() 521 if err != nil { 522 return MaskedSandboxInjection{}, err 523 } 524 return MaskedSandboxInjection{HTTP: &HTTPInjection{BaseURL: v.BaseURL, Headers: v.Headers, IfHeaders: v.IfHeaders, IfQueries: v.IfQueries}}, nil 525 default: 526 return MaskedSandboxInjection{}, fmt.Errorf("unknown sandbox injection type: %s", disc) 527 } 528 } 529 530 func maskedSandboxInjectionsFromAPI(injections []apis.SandboxInjection) ([]MaskedSandboxInjection, error) { 531 if injections == nil { 532 return nil, nil 533 } 534 result := make([]MaskedSandboxInjection, len(injections)) 535 for i, injection := range injections { 536 spec, err := maskedSandboxInjectionFromAPI(injection) 537 if err != nil { 538 return nil, err 539 } 540 result[i] = spec 541 } 542 return result, nil 543 } 544 545 func injectionRuleFromAPI(a apis.InjectionRule) (InjectionRule, error) { 546 spec, err := injectionSpecFromAPI(a.Injection) 547 if err != nil { 548 return InjectionRule{}, err 549 } 550 return InjectionRule{ 551 RuleID: a.RuleID, 552 Name: a.Name, 553 Injection: spec, 554 CreatedAt: a.CreatedAt, 555 UpdatedAt: a.UpdatedAt, 556 }, nil 557 } 558 559 func injectionRulesFromAPI(a []apis.InjectionRule) ([]InjectionRule, error) { 560 if a == nil { 561 return nil, nil 562 } 563 result := make([]InjectionRule, len(a)) 564 for i, r := range a { 565 rule, err := injectionRuleFromAPI(r) 566 if err != nil { 567 return nil, err 568 } 569 result[i] = rule 570 } 571 return result, nil 572 }