Open Closed

AI Management: a workspace with an empty Embedder Provider ('') still adds the search_documents RAG tool to every chat call #10901


User avatar
0
AyazNaseer created

Version: ABP 10.3.0, Volo.AIManagement 10.3.0, Blazor Server, EF Core / SQL Server

Description A workspace without any embedder or vector store still gets the RAG tool on every chat request. As a result, the model often calls search_documents, the tool answers that no embedding provider is configured, and the whole prompt (including attachments) is sent to the model a second time.

Steps to reproduce

Create a workspace in the AI Management UI (OpenAI chat provider) and leave Embedder Provider and Vector Store Provider empty. Save it. In the database, EmbedderProvider and VectorStoreProvider are stored as '' (empty string), not NULL. Send a chat request through this workspace, for example via IChatClientResolver or IAIChatCompletionIntegrationService.

Expected

No RAG tool and no extra system instruction, because the workspace has no embedder.

Actual

AIManagementChatClient.PrepareOptionsAsync adds a search_documents tool (tool_choice = auto) and a system instruction to always call search_documents first. The model calls the tool, which replies that the embedding provider is not configured, and the request is sent again.

In our measurement, a request of about 186K prompt tokens was billed as about 372K. This doubles cost and rate-limit (HTTP 429) pressure.

Cause (version 10.3.0)

Two checks disagree on what "configured" means:

WorkspaceConfigurationStore: HasEmbedderConfiguration = EmbedderProvider != null, so '' counts as configured. DocumentSearchService: uses string.IsNullOrWhiteSpace(...), so '' counts as not configured. The Workspaces page saves empty fields as '', so a workspace edited in the UI can never be stored as NULL.

Suggested fix

Use !string.IsNullOrWhiteSpace(EmbedderProvider) in WorkspaceConfigurationStore, and/or normalise empty provider fields to NULL when saving a workspace.

Workaround Set the columns to NULL directly in the database. Editing the workspace in the UI afterwards brings the problem back.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

1 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    This is a bug. The workspace configuration treats an empty EmbedderProvider as configured, and the Workspaces page saves empty fields as ''.

    For now, you can add this class to your Application project (it runs in the HttpApi.Host in your tiered solution). It saves empty providers as NULL when a workspace is created, updated or duplicated:

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Volo.Abp.DependencyInjection;
    using Volo.Abp.Domain.Repositories;
    using Volo.AIManagement.ApplicationWorkspaceProviders;
    using Volo.AIManagement.McpServers;
    using Volo.AIManagement.VectorStores;
    using Volo.AIManagement.WorkspaceDataSources;
    using Volo.AIManagement.Workspaces;
    
    namespace MyCompanyName.MyProjectName;
    
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IWorkspaceAppService), typeof(WorkspaceAppService))]
    public class MyWorkspaceAppService : WorkspaceAppService
    {
        public MyWorkspaceAppService(
            IWorkspaceRepository repository,
            IRepository<ApplicationAIProvider, Guid> applicationWorkspaceProviderRepository,
            IMcpServerRepository mcpServerRepository,
            ApplicationWorkspaceManager applicationWorkspaceManager,
            VectorStoreInitializer vectorStoreInitializer,
            IEnumerable<IVectorStoreFactory> vectorStoreFactories,
            IWorkspaceDataSourceAppService workspaceDataSourceAppService)
            : base(
                repository,
                applicationWorkspaceProviderRepository,
                mcpServerRepository,
                applicationWorkspaceManager,
                vectorStoreInitializer,
                vectorStoreFactories,
                workspaceDataSourceAppService)
        {
        }
    
        public override Task<WorkspaceDetailDto> CreateAsync(WorkspaceCreateInput input)
        {
            input.EmbedderProvider = NullIfWhiteSpace(input.EmbedderProvider);
            input.VectorStoreProvider = NullIfWhiteSpace(input.VectorStoreProvider);
            return base.CreateAsync(input);
        }
    
        public override async Task<WorkspaceDetailDto> UpdateAsync(Guid id, WorkspaceUpdateInput input)
        {
            input.EmbedderProvider = NullIfWhiteSpace(input.EmbedderProvider);
            input.VectorStoreProvider = NullIfWhiteSpace(input.VectorStoreProvider);
    
            // Normalize the stored values first, so '' -> NULL is not treated as an embedder change
            var workspace = await Repository.GetAsync(id);
            var embedderProvider = NullIfWhiteSpace(workspace.EmbedderProvider);
            var vectorStoreProvider = NullIfWhiteSpace(workspace.VectorStoreProvider);
            if (workspace.EmbedderProvider != embedderProvider || workspace.VectorStoreProvider != vectorStoreProvider)
            {
                workspace.EmbedderProvider = embedderProvider;
                workspace.VectorStoreProvider = vectorStoreProvider;
                await Repository.UpdateAsync(workspace, autoSave: true);
            }
    
            return await base.UpdateAsync(id, input);
        }
    
        public override async Task<WorkspaceDetailDto> DuplicateAsync(Guid id, WorkspaceDuplicateInput input)
        {
            var result = await base.DuplicateAsync(id, input);
            await CurrentUnitOfWork!.SaveChangesAsync();
    
            var workspace = await Repository.GetAsync(result.Id);
            workspace.EmbedderProvider = NullIfWhiteSpace(workspace.EmbedderProvider);
            workspace.VectorStoreProvider = NullIfWhiteSpace(workspace.VectorStoreProvider);
            await Repository.UpdateAsync(workspace);
    
            return ObjectMapper.Map<Workspace, WorkspaceDetailDto>(workspace);
        }
    
        private static string? NullIfWhiteSpace(string? value)
        {
            return string.IsNullOrWhiteSpace(value) ? null : value;
        }
    }
    

    After you deploy it:

    1. Open each workspace that has no embedder in the UI and save it once. This changes the stored '' values to NULL.
    2. Remove the cached workspace configurations, including the entries cached for tenants. Run this against the Redis server your applications use (add -h, -p, -n and -a to both redis-cli calls as needed):
    redis-cli --scan --pattern '*Volo.AIManagement.Factory.ChatClientCreationConfiguration*' | while IFS= read -r key; do redis-cli del "$key"; done
    

    If you use the default in-memory cache instead of Redis, skip this step. The restart in the next step clears it.

    1. Restart your applications (HttpApi.Host and Blazor), so no running chat client keeps the old configuration.

    After that, the search_documents tool is no longer added for these workspaces, and later edits in the UI keep the values as NULL.

    We'll also fix this in the AI Management module.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.