mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-08-30 16:53:21 +08:00
fix(web-search): normalize default tenant web search config at runtime
This commit is contained in:
@@ -152,20 +152,19 @@ func (t *WebSearchTool) Execute(ctx context.Context, args json.RawMessage) (*typ
|
||||
}
|
||||
|
||||
// Get tenant info from context (same approach as search.go)
|
||||
tenant := ctx.Value(types.TenantInfoContextKey).(*types.Tenant)
|
||||
if tenant == nil || tenant.WebSearchConfig == nil {
|
||||
logger.Errorf(ctx, "[Tool][WebSearch] Web search not configured for tenant %d", tenantID)
|
||||
return &types.ToolResult{
|
||||
Success: false,
|
||||
Error: "web search is not configured for this tenant",
|
||||
}, fmt.Errorf("web search is not configured for tenant %d", tenantID)
|
||||
var tenant *types.Tenant
|
||||
if tenantValue := ctx.Value(types.TenantInfoContextKey); tenantValue != nil {
|
||||
tenant, _ = tenantValue.(*types.Tenant)
|
||||
}
|
||||
|
||||
// Resolve provider ID: tool-level (set from agent config, which already resolved default)
|
||||
resolvedProviderID := t.providerID
|
||||
|
||||
// Create a copy of web search config with maxResults from agent config
|
||||
searchConfig := *tenant.WebSearchConfig
|
||||
// Create a copy of the effective web search config with maxResults from agent config.
|
||||
searchConfig := types.EffectiveWebSearchConfig(nil)
|
||||
if tenant != nil {
|
||||
searchConfig = types.EffectiveWebSearchConfig(tenant.WebSearchConfig)
|
||||
}
|
||||
searchConfig.MaxResults = t.maxResults
|
||||
|
||||
// Perform web search
|
||||
@@ -175,7 +174,7 @@ func (t *WebSearchTool) Execute(ctx context.Context, args json.RawMessage) (*typ
|
||||
resolvedProviderID,
|
||||
searchConfig.MaxResults,
|
||||
)
|
||||
webResults, err := t.webSearchService.Search(ctx, resolvedProviderID, &searchConfig, query)
|
||||
webResults, err := t.webSearchService.Search(ctx, resolvedProviderID, searchConfig, query)
|
||||
if err != nil {
|
||||
logger.Errorf(ctx, "[Tool][WebSearch] Web search failed: %v", err)
|
||||
return &types.ToolResult{
|
||||
@@ -187,8 +186,8 @@ func (t *WebSearchTool) Execute(ctx context.Context, args json.RawMessage) (*typ
|
||||
logger.Infof(ctx, "[Tool][WebSearch] Web search returned %d results", len(webResults))
|
||||
|
||||
// Apply RAG compression if configured
|
||||
if len(webResults) > 0 && tenant.WebSearchConfig.CompressionMethod != "none" &&
|
||||
tenant.WebSearchConfig.CompressionMethod != "" {
|
||||
if len(webResults) > 0 && searchConfig.CompressionMethod != "none" &&
|
||||
searchConfig.CompressionMethod != "" {
|
||||
// Load session-scoped temp KB state from Redis using WebSearchStateRepository
|
||||
tempKBID, seen, ids := t.webSearchStateService.GetWebSearchTempKBState(ctx, t.sessionID)
|
||||
|
||||
@@ -197,7 +196,7 @@ func (t *WebSearchTool) Execute(ctx context.Context, args json.RawMessage) (*typ
|
||||
|
||||
logger.Infof(ctx, "[Tool][WebSearch] Applying RAG compression")
|
||||
compressed, kbID, newSeen, newIDs, err := t.webSearchService.CompressWithRAG(
|
||||
ctx, t.sessionID, tempKBID, questions, webResults, tenant.WebSearchConfig,
|
||||
ctx, t.sessionID, tempKBID, questions, webResults, searchConfig,
|
||||
t.knowledgeBaseService, t.knowledgeService, seen, ids,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -624,22 +624,18 @@ func (p *PluginSearch) searchWebIfEnabled(ctx context.Context, chatManage *types
|
||||
tenant, _ := types.TenantInfoFromContext(ctx)
|
||||
providerID := chatManage.WebSearchProviderID
|
||||
|
||||
var webConfig *types.WebSearchConfig
|
||||
if tenant != nil && tenant.WebSearchConfig != nil {
|
||||
// Clone tenant config so we can safely override MaxResults
|
||||
cfg := *tenant.WebSearchConfig
|
||||
webConfig = &cfg
|
||||
} else if providerID != "" {
|
||||
webConfig = &types.WebSearchConfig{
|
||||
MaxResults: 10,
|
||||
}
|
||||
} else {
|
||||
if providerID == "" {
|
||||
pipelineWarn(ctx, "Search", "web_config_missing", map[string]interface{}{
|
||||
"tenant_id": chatManage.TenantID,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
webConfig := types.EffectiveWebSearchConfig(nil)
|
||||
if tenant != nil {
|
||||
webConfig = types.EffectiveWebSearchConfig(tenant.WebSearchConfig)
|
||||
}
|
||||
|
||||
// Apply agent-level web search overrides
|
||||
if chatManage.WebSearchMaxResults > 0 {
|
||||
webConfig.MaxResults = chatManage.WebSearchMaxResults
|
||||
|
||||
@@ -962,8 +962,8 @@ func (s *sessionService) resolveWebSearchMaxResults(ctx context.Context, req *ty
|
||||
return req.CustomAgent.Config.WebSearchMaxResults
|
||||
}
|
||||
tenantInfo, _ := types.TenantInfoFromContext(ctx)
|
||||
if tenantInfo != nil && tenantInfo.WebSearchConfig != nil && tenantInfo.WebSearchConfig.MaxResults > 0 {
|
||||
return tenantInfo.WebSearchConfig.MaxResults
|
||||
if tenantInfo != nil {
|
||||
return types.EffectiveWebSearchConfig(tenantInfo.WebSearchConfig).MaxResults
|
||||
}
|
||||
return 10
|
||||
return types.DefaultWebSearchMaxResults
|
||||
}
|
||||
|
||||
@@ -750,6 +750,8 @@ func (h *TenantHandler) updateTenantWebSearchConfigInternal(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
cfg = *types.EffectiveWebSearchConfig(&cfg)
|
||||
|
||||
// Validate configuration
|
||||
if cfg.MaxResults < 1 || cfg.MaxResults > 50 {
|
||||
c.Error(errors.NewBadRequestError("max_results must be between 1 and 50"))
|
||||
@@ -777,7 +779,7 @@ func (h *TenantHandler) updateTenantWebSearchConfigInternal(c *gin.Context) {
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": updatedTenant.WebSearchConfig,
|
||||
"data": types.EffectiveWebSearchConfig(updatedTenant.WebSearchConfig),
|
||||
"message": "Web search configuration updated successfully",
|
||||
})
|
||||
}
|
||||
@@ -807,7 +809,7 @@ func (h *TenantHandler) GetTenantWebSearchConfig(c *gin.Context) {
|
||||
logger.Infof(ctx, "Tenant web search config retrieved successfully, Tenant ID: %d", tenant.ID)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": tenant.WebSearchConfig,
|
||||
"data": types.EffectiveWebSearchConfig(tenant.WebSearchConfig),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,41 @@ type WebSearchConfig struct {
|
||||
ProxyURL string `json:"proxy_url,omitempty"` // Optional per-request proxy override; normally empty — use WebSearchProviderEntity.Parameters.proxy_url. Merged at call time when set.
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultWebSearchMaxResults = 10
|
||||
DefaultWebSearchCompressionMethod = "none"
|
||||
)
|
||||
|
||||
// DefaultWebSearchConfig returns the shared default tenant-level web search configuration.
|
||||
func DefaultWebSearchConfig() *WebSearchConfig {
|
||||
return &WebSearchConfig{
|
||||
MaxResults: DefaultWebSearchMaxResults,
|
||||
IncludeDate: false,
|
||||
CompressionMethod: DefaultWebSearchCompressionMethod,
|
||||
Blacklist: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// EffectiveWebSearchConfig normalizes a possibly empty config to the effective runtime config.
|
||||
func EffectiveWebSearchConfig(cfg *WebSearchConfig) *WebSearchConfig {
|
||||
if cfg == nil {
|
||||
return DefaultWebSearchConfig()
|
||||
}
|
||||
|
||||
normalized := *cfg
|
||||
if normalized.MaxResults <= 0 {
|
||||
normalized.MaxResults = DefaultWebSearchMaxResults
|
||||
}
|
||||
if normalized.CompressionMethod == "" {
|
||||
normalized.CompressionMethod = DefaultWebSearchCompressionMethod
|
||||
}
|
||||
if normalized.Blacklist == nil {
|
||||
normalized.Blacklist = []string{}
|
||||
}
|
||||
|
||||
return &normalized
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface for WebSearchConfig
|
||||
func (c WebSearchConfig) Value() (driver.Value, error) {
|
||||
return json.Marshal(c)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestEffectiveWebSearchConfigUsesDefaultsForNilConfig(t *testing.T) {
|
||||
cfg := EffectiveWebSearchConfig(nil)
|
||||
|
||||
if cfg.MaxResults != DefaultWebSearchMaxResults {
|
||||
t.Fatalf("MaxResults = %d, want %d", cfg.MaxResults, DefaultWebSearchMaxResults)
|
||||
}
|
||||
if cfg.CompressionMethod != DefaultWebSearchCompressionMethod {
|
||||
t.Fatalf("CompressionMethod = %q, want %q", cfg.CompressionMethod, DefaultWebSearchCompressionMethod)
|
||||
}
|
||||
if cfg.Blacklist == nil {
|
||||
t.Fatal("Blacklist = nil, want empty slice")
|
||||
}
|
||||
if len(cfg.Blacklist) != 0 {
|
||||
t.Fatalf("Blacklist length = %d, want 0", len(cfg.Blacklist))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEffectiveWebSearchConfigNormalizesZeroValuesWithoutMutatingSource(t *testing.T) {
|
||||
source := &WebSearchConfig{
|
||||
IncludeDate: true,
|
||||
}
|
||||
|
||||
cfg := EffectiveWebSearchConfig(source)
|
||||
|
||||
if cfg == source {
|
||||
t.Fatal("EffectiveWebSearchConfig returned original pointer, want normalized copy")
|
||||
}
|
||||
if cfg.MaxResults != DefaultWebSearchMaxResults {
|
||||
t.Fatalf("MaxResults = %d, want %d", cfg.MaxResults, DefaultWebSearchMaxResults)
|
||||
}
|
||||
if cfg.CompressionMethod != DefaultWebSearchCompressionMethod {
|
||||
t.Fatalf("CompressionMethod = %q, want %q", cfg.CompressionMethod, DefaultWebSearchCompressionMethod)
|
||||
}
|
||||
if cfg.Blacklist == nil {
|
||||
t.Fatal("Blacklist = nil, want empty slice")
|
||||
}
|
||||
if !cfg.IncludeDate {
|
||||
t.Fatal("IncludeDate = false, want true")
|
||||
}
|
||||
|
||||
if source.MaxResults != 0 {
|
||||
t.Fatalf("source MaxResults mutated to %d, want 0", source.MaxResults)
|
||||
}
|
||||
if source.CompressionMethod != "" {
|
||||
t.Fatalf("source CompressionMethod mutated to %q, want empty string", source.CompressionMethod)
|
||||
}
|
||||
if source.Blacklist != nil {
|
||||
t.Fatalf("source Blacklist mutated to non-nil value: %#v", source.Blacklist)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user