AI网关新增TTS/重排/审核端点,xAI工具扩展,swagger修缺
CI / test (push) Successful in 32s
Release / release (push) Successful in 1m4s

- 新端点 /ai/v1/audio/speech(xai.grok-tts)、/rerank(cohere.rerank-v4)、/moderations(OCI Guardrails)
- Responses 放行 code_interpreter 与远程 mcp 工具,web_search/x_search 解除仅非流式限制
- 模型能力映射扩展:TEXT_RERANK→RERANK、TEXT_TO_AUDIO→TTS
- AI 网关文档独立 docs/ai-gateway.md,字段兼容矩阵只列支持项;README 精简引用
- swagger 修缺:135 处响应注解具体化,RawMessage/联合类型统一渲染 AnyJSON,overrides 迁至 docs/.swaggo
- CHANGELOG 0.4.0,版本段不再记日期;DASH_VERSION v0.4.0
This commit is contained in:
2026-07-13 20:17:06 +08:00
parent 9309ad1ffc
commit 0a86b5a291
50 changed files with 10699 additions and 1371 deletions
+6
View File
@@ -106,6 +106,12 @@ type Client interface {
GenAiCompatResponses(ctx context.Context, cred Credentials, region string, body []byte) ([]byte, error)
// GenAiCompatResponsesStream 流式直通 /actions/v1/responses,建立成功返回 SSE body。
GenAiCompatResponsesStream(ctx context.Context, cred Credentials, region string, body []byte) (io.ReadCloser, error)
// GenAiCompatSpeech 直通 OpenAI Audio Speech 请求体到 /openai/v1/audio/speech,返回音频与 Content-Type。
GenAiCompatSpeech(ctx context.Context, cred Credentials, region string, body []byte) ([]byte, string, error)
// GenAiRerank 文档重排,返回按相关度排序的下标与得分。
GenAiRerank(ctx context.Context, cred Credentials, region, modelOcid, query string, documents []string, topN *int) ([]RerankRank, error)
// GenAiApplyGuardrails 对单条文本执行内容审核 / PII / 提示注入检测。
GenAiApplyGuardrails(ctx context.Context, cred Credentials, region, text string) (*GuardrailsOutcome, error)
// 控制台连接:创建(VNC/串口连接串)、列出、删除。
CreateConsoleConnection(ctx context.Context, cred Credentials, region, instanceID, sshPublicKey string) (ConsoleConnection, error)
ListConsoleConnections(ctx context.Context, cred Credentials, region, instanceID string) ([]ConsoleConnection, error)
+5
View File
@@ -131,6 +131,11 @@ func modelCapability(m generativeai.ModelSummary) string {
return "CHAT"
case generativeai.ModelCapabilityTextEmbeddings:
capability = "EMBEDDING"
case generativeai.ModelCapabilityTextRerank:
capability = "RERANK"
case generativeai.ModelCapabilityEnum("TEXT_TO_AUDIO"):
// SDK v65.120 尚无该枚举常量,按原始字符串匹配(xai.grok-tts)
capability = "TTS"
}
}
return capability
+127
View File
@@ -0,0 +1,127 @@
package oci
import (
"context"
"fmt"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/generativeaiinference"
)
// RerankRank 是重排结果的一项:index 指向入参 documents 下标。
type RerankRank struct {
Index int
Score float64
}
// GuardrailCategory 是内容审核分类得分(OVERALL / BLOCKLIST)。
type GuardrailCategory struct {
Name string
Score float64
}
// GuardrailPiiHit 是一处 PII 命中(片段原文与位置)。
type GuardrailPiiHit struct {
Text string
Label string
Score float64
Offset int
Length int
}
// GuardrailsOutcome 汇总 ApplyGuardrails 三能力结果。
type GuardrailsOutcome struct {
Categories []GuardrailCategory
Pii []GuardrailPiiHit
PromptInjectionScore *float64
}
// GenAiRerank 实现 Client:文档重排(on-demand serving),返回按相关度排序的下标与得分。
func (c *RealClient) GenAiRerank(ctx context.Context, cred Credentials, region, modelOcid, query string, documents []string, topN *int) ([]RerankRank, error) {
ic, err := c.genAiInferenceClient(cred, region)
if err != nil {
return nil, err
}
resp, err := ic.RerankText(ctx, generativeaiinference.RerankTextRequest{
RerankTextDetails: generativeaiinference.RerankTextDetails{
CompartmentId: &cred.TenancyOCID,
ServingMode: generativeaiinference.OnDemandServingMode{ModelId: &modelOcid},
Input: &query,
Documents: documents,
TopN: topN,
},
})
if err != nil {
return nil, fmt.Errorf("genai rerank: %w", err)
}
ranks := make([]RerankRank, 0, len(resp.DocumentRanks))
for _, r := range resp.DocumentRanks {
if r.Index == nil || r.RelevanceScore == nil {
continue
}
ranks = append(ranks, RerankRank{Index: *r.Index, Score: *r.RelevanceScore})
}
return ranks, nil
}
// GenAiApplyGuardrails 实现 Client:对单条文本执行内容审核 / PII / 提示注入三检测。
func (c *RealClient) GenAiApplyGuardrails(ctx context.Context, cred Credentials, region, text string) (*GuardrailsOutcome, error) {
ic, err := c.genAiInferenceClient(cred, region)
if err != nil {
return nil, err
}
resp, err := ic.ApplyGuardrails(ctx, generativeaiinference.ApplyGuardrailsRequest{
ApplyGuardrailsDetails: generativeaiinference.ApplyGuardrailsDetails{
CompartmentId: &cred.TenancyOCID,
Input: generativeaiinference.GuardrailsTextInput{Content: common.String(text)},
GuardrailConfigs: &generativeaiinference.GuardrailConfigs{
ContentModerationConfig: &generativeaiinference.ContentModerationConfiguration{Categories: []string{"OVERALL"}},
PersonallyIdentifiableInformationConfig: &generativeaiinference.PersonallyIdentifiableInformationConfiguration{Types: []string{}},
PromptInjectionConfig: &generativeaiinference.PromptInjectionConfiguration{},
},
},
})
if err != nil {
return nil, fmt.Errorf("genai guardrails: %w", err)
}
return guardrailsOutcome(resp.Results), nil
}
// guardrailsOutcome 把 SDK 结果换算为 IR;空段置空切片,得分缺失跳过。
func guardrailsOutcome(r *generativeaiinference.GuardrailsResults) *GuardrailsOutcome {
out := &GuardrailsOutcome{}
if r == nil {
return out
}
if r.ContentModeration != nil {
for _, cat := range r.ContentModeration.Categories {
if cat.Name == nil || cat.Score == nil {
continue
}
out.Categories = append(out.Categories, GuardrailCategory{Name: *cat.Name, Score: *cat.Score})
}
}
for _, hit := range r.PersonallyIdentifiableInformation {
h := GuardrailPiiHit{}
if hit.Text != nil {
h.Text = *hit.Text
}
if hit.Label != nil {
h.Label = *hit.Label
}
if hit.Score != nil {
h.Score = *hit.Score
}
if hit.Offset != nil {
h.Offset = *hit.Offset
}
if hit.Length != nil {
h.Length = *hit.Length
}
out.Pii = append(out.Pii, h)
}
if r.PromptInjection != nil && r.PromptInjection.Score != nil {
out.PromptInjectionScore = r.PromptInjection.Score
}
return out
}
+3 -3
View File
@@ -14,9 +14,9 @@ import (
const compatResponsesLimit = int64(8 << 20)
// GenAiCompatResponses 实现 Client:把 OpenAI Responses 请求体直通到 OCI
// `/20231130/actions/v1/responses`(IAM 签名)。该端点实测可执行 xAI 服务端工具
// (web_search / x_search),但不在 Oracle 文档化工具白名单内,行为可能随服务
// 版本、模型或区域变化;调用方须自行校验并改写请求体(store/stream)。
// `/20231130/actions/v1/responses`(IAM 签名)。xAI 服务端工具(web_search /
// x_search / code_interpreter)与 mcp 已被 Oracle 文档正式支持,工具参数与限制
// 遵循 xAI 规格;调用方须自行校验并改写请求体(store/stream)。
func (c *RealClient) GenAiCompatResponses(ctx context.Context, cred Credentials, region string, body []byte) ([]byte, error) {
ic, err := c.genAiInferenceClient(cred, region)
if err != nil {
+44
View File
@@ -0,0 +1,44 @@
package oci
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"github.com/oracle/oci-go-sdk/v65/common"
)
// compatSpeechLimit 限制 TTS 音频响应体大小(长文本 mp3 给足余量)。
const compatSpeechLimit = int64(64 << 20)
// GenAiCompatSpeech 实现 Client:把 OpenAI Audio Speech 请求体直通到 OCI
// 兼容面 `/openai/v1/audio/speech`(IAM 签名,BasePath 置空——该面与
// /20231130/actions 面并存,实测仅前者承载 TTS)。返回音频字节与 Content-Type。
func (c *RealClient) GenAiCompatSpeech(ctx context.Context, cred Credentials, region string, body []byte) ([]byte, string, error) {
ic, err := c.genAiInferenceClient(cred, region)
if err != nil {
return nil, "", err
}
client := ic.BaseClient
common.UpdateEndpointTemplateForOptions(&client)
common.SetMissingTemplateParams(&client)
client.BasePath = ""
request, err := http.NewRequestWithContext(ctx, http.MethodPost, "/openai/v1/audio/speech", bytes.NewReader(body))
if err != nil {
return nil, "", fmt.Errorf("build compat speech request: %w", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("opc-compartment-id", cred.TenancyOCID)
response, err := client.Call(ctx, request)
if err != nil {
return nil, "", err
}
defer response.Body.Close()
payload, err := io.ReadAll(io.LimitReader(response.Body, compatSpeechLimit))
if err != nil {
return nil, "", fmt.Errorf("read compat speech body: %w", err)
}
return payload, response.Header.Get("Content-Type"), nil
}