Ctrl K
ComunidadGuías…

relay/channel/zhipu_4v/web_search.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package zhipu_4v

import (
	"github.com/QuantumNous/new-api/common"
	relaycommon "github.com/QuantumNous/new-api/relay/common"
	"github.com/QuantumNous/new-api/relaykit/dto"
	"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
	"github.com/QuantumNous/new-api/relaykit/types"

	"github.com/MoeForge/moego/ojson"
)

// zhipuSearchEngine is the documented default search_engine. It is also the
// tool-price key of the searches billed on this path.
const zhipuSearchEngine = "search_std"

// encodeWebSearch maps hosted web search onto the GLM Chat web_search tool
// (https://docs.bigmodel.cn/api-reference/模型-api/对话补全). Claude and
// Responses clients reach Zhipu natively, so only the Chat target is handled.
// search_result makes non-stream responses carry the top-level web_search
// array; search_intent:false skips intent detection when the client forced
// the search.
func encodeWebSearch(info *relaycommon.RelayInfo) convmeta.WebSearchEncoder {
	return func(call convmeta.WebSearchCall) (*convmeta.WebSearchEncoding, error) {
		if call.Chat == nil {
			return nil, nil
		}
		var diagnostics []types.ConversionDiagnostic
		webSearch := map[string]any{"enable": true, "search_engine": zhipuSearchEngine, "search_result": true}
		if call.Forced {
			webSearch["search_intent"] = false
			// Stream responses carry no search evidence; a forced search is
			// the only proof that one ran. A non-stream web_search array
			// confirms the same single call through zhipuChatToolUsage.
			info.RecordRequestInferredToolCall(zhipuSearchEngine)
		}
		// Only searches this encoder requested are known to use search_std.
		info.VendorToolUsage = zhipuChatToolUsage
		spec := call.Spec
		switch {
		case len(spec.AllowedDomains) == 1:
			webSearch["search_domain_filter"] = spec.AllowedDomains[0]
		case len(spec.AllowedDomains) > 1:
			diagnostics = append(diagnostics, call.SemanticLoss("unsupported_domain_filter", "GLM web_search accepts one search_domain_filter; the domain filter was dropped"))
		}
		if len(spec.BlockedDomains) > 0 {
			diagnostics = append(diagnostics, call.SemanticLoss("unsupported_domain_filter", "GLM web_search cannot exclude domains; blocked_domains was dropped"))
		}
		if spec.MaxUses != nil {
			diagnostics = append(diagnostics, call.SemanticLoss("unsupported_search_limit", "GLM web_search cannot preserve max_uses"))
		}
		if len(spec.AllowedCallers) > 0 || spec.ExternalWebAccess != nil {
			diagnostics = append(diagnostics, call.SemanticLoss("unsupported_search_controls", "GLM web_search cannot preserve caller or external-access constraints"))
		}
		switch spec.SearchContextSize {
		case "high":
			webSearch["content_size"] = "high"
		case "", "medium":
		default:
			diagnostics = append(diagnostics, call.PresentationLoss("unsupported_search_tuning", "GLM web_search content_size has no low setting"))
		}
		if spec.Location != nil || spec.ResponseInclusion != "" || len(spec.ReturnTokenBudget) > 0 {
			diagnostics = append(diagnostics, call.PresentationLoss("unsupported_search_tuning", "GLM web_search cannot preserve location or result tuning"))
		}
		native, err := common.Marshal(map[string]any{"type": "web_search", "web_search": webSearch})
		if err != nil {
			return nil, err
		}
		return &convmeta.WebSearchEncoding{
			Tool:        dto.ToolCallRequest{Type: "web_search", Native: native},
			Diagnostics: diagnostics,
		}, nil
	}
}

// zhipuChatToolUsage bills one search_std call when a non-stream GLM Chat
// response carries the top-level web_search result array. The array lists
// results, not searches, so its length is not a count.
func zhipuChatToolUsage(raw []byte) map[string]int {
	results := ojson.GetBytes(raw, "web_search")
	if !results.IsArray() || len(results.Array()) == 0 {
		return nil
	}
	return map[string]int{zhipuSearchEngine: 1}
}