AI网关新增TTS/重排/审核端点,xAI工具扩展,swagger修缺
- 新端点 /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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user