Skip to content

Add LLM invisibility for group users#363

Merged
ModerRAS merged 4 commits into
masterfrom
codex/llm-invisibility
Jun 1, 2026
Merged

Add LLM invisibility for group users#363
ModerRAS merged 4 commits into
masterfrom
codex/llm-invisibility

Conversation

@ModerRAS

@ModerRAS ModerRAS commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • add per-group IsLlmInvisible flag and self-service commands for users to enable/disable/check LLM invisibility
  • filter invisible users out of provider chat history, agent task history, LLM search/history tool results, and agent chat batch inputs
  • skip Alt generation for invisible users in both realtime photo handling and RefreshService batch Alt refresh

Tests

  • dotnet build TelegramSearchBot.sln --configuration Release --no-restore
  • dotnet test TelegramSearchBot.sln --no-restore --no-build --configuration Release

Summary by CodeRabbit

  • New Features

    • Users can now mark themselves as invisible to LLM features within specific groups.
    • Added ability to toggle invisibility status and check current visibility state.
    • AI processing now respects invisibility settings—invisible users' messages are excluded from LLM context and analysis across all AI services.
  • Tests

    • Added test coverage for invisibility filtering and visibility state management.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ModerRAS, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 11 minutes and 21 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e58e96f-90b7-4227-ae4b-6b87b425ff01

📥 Commits

Reviewing files that changed from the base of the PR and between 72ec777 and 3e3eb77.

📒 Files selected for processing (12)
  • TelegramSearchBot.Database/Migrations/20260601113631_AddLlmInvisibilityLookupIndex.Designer.cs
  • TelegramSearchBot.Database/Migrations/20260601113631_AddLlmInvisibilityLookupIndex.cs
  • TelegramSearchBot.Database/Migrations/DataDbContextModelSnapshot.cs
  • TelegramSearchBot.Database/Model/DataDbContext.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs
  • TelegramSearchBot.Test/Controller/Manage/LlmVisibilityControllerTests.cs
  • TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs
  • TelegramSearchBot/Controller/Manage/LlmVisibilityController.cs
  • TelegramSearchBot/Service/BotAPI/TelegramCommandRegistryService.cs
  • TelegramSearchBot/Service/Manage/RefreshService.cs
  • TelegramSearchBot/Service/Tools/SearchToolService.cs
📝 Walkthrough

Walkthrough

This PR introduces a per-user/group "LLM invisibility" feature allowing users to suppress LLM processing and filtering. A new IsLlmInvisible boolean is added to UserWithGroup via migration; LlmVisibilityService manages visibility state and filters messages. Controllers check invisibility before LLM execution; a new management controller handles user commands. LLM providers and queue services filter history; search and refresh services respect invisibility rules.

Changes

LLM Invisibility System

Layer / File(s) Summary
Database model and migration for IsLlmInvisible property
TelegramSearchBot.Database/Model/Data/UserWithGroup.cs, TelegramSearchBot.Database/Migrations/20260601111353_*
Added IsLlmInvisible boolean column to UsersWithGroup table via EF Core migration; entity and model snapshot updated.
LlmVisibilityService core implementation and tests
TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs, TelegramSearchBot.LLM.Test/Service/AI/LLM/LlmVisibilityServiceTests.cs
LlmVisibilityService checks per-group user invisibility, retrieves invisible user IDs, filters message collections, and persists state changes; xUnit tests verify state updates and message filtering.
Entry-point controller guards for invisibility
TelegramSearchBot/Controller/AI/LLM/AgentChatModeController.cs, AltPhotoController.cs, GeneralLLMController.cs
Injected LlmVisibilityService into three controllers; each performs early-return guard checking IsUserInvisibleAsync before proceeding with LLM-related operations.
LlmVisibilityController for user-facing invisibility commands
TelegramSearchBot/Controller/Manage/LlmVisibilityController.cs
New IOnUpdate controller handling enable/disable/status commands for user invisibility toggling, replying with confirmation messages.
LLM service providers with visibility-aware history filtering
TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs, GeminiService.cs, OpenAIService.cs, OpenAIResponsesService.cs
Updated all LLM providers to accept optional LlmVisibilityService dependency; GetChatHistory and equivalent methods now filter messages via FilterVisibleMessagesAsync and conditionally suppress input from invisible users.
Message queue and batch dispatch with visibility filtering
TelegramSearchBot/Service/AI/LLM/LLMTaskQueueService.cs, AgentChatBatchDispatchService.cs
LLMTaskQueueService filters history during task loading; AgentChatBatchDispatchService filters buffered messages before dispatch, canceling batch if no visible messages remain.
Search and ALT processing with invisibility filtering
TelegramSearchBot/Service/Tools/SearchToolService.cs, TelegramSearchBot/Service/Manage/RefreshService.cs
SearchToolService filters keyword and history query results to exclude invisible users; RefreshService skips ALT analysis for invisible users; adaptive Lucene fetch-size helper preserves pagination correctness.
Integration test for task queue filtering
TelegramSearchBot.Test/Service/AI/LLM/LLMTaskQueueServiceTests.cs
New test validates that LLMTaskQueueService with LlmVisibilityService correctly excludes invisible users' messages and extensions from serialized agent execution tasks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ModerRAS/TelegramSearchBot#361: Modifies the same AgentChatBatchDispatchService and LLMTaskQueueService code paths that this PR enhances with visibility filtering.
  • ModerRAS/TelegramSearchBot#265: Introduces the LLMTaskQueueService agent queue pipeline that this PR extends with visibility-aware history filtering.
  • ModerRAS/TelegramSearchBot#350: Refactors LLM provider constructor wiring via IBotIdentityProvider in the same AnthropicService/GeminiService/OpenAIService classes where this PR adds LlmVisibilityService dependencies.

🐰 A rabbit hops through the garden with glee,
No longer shall some users in the LLM's eye be,
With "invisible" toggles, they vanish from sight,
While visible pals chat with models so bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an LLM invisibility feature for group users, which is the core objective reflected throughout the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/llm-invisibility

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

PR Check Report

Summary

Test Results

Platform Status Details
Ubuntu Passed Tests passed, artifacts uploaded
Windows Passed Tests passed, artifacts uploaded

Code Quality

  • Code formatting check
  • Security vulnerability scan
  • Dependency analysis
  • Code coverage collection

Test Artifacts

  • Test results artifacts count: 2
  • Code coverage uploaded to Codecov

Links


This report is auto-generated by GitHub Actions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs (2)

64-77: 💤 Low value

Remove redundant Update() call on tracked entity.

Line 76 calls Update() on userWithGroup retrieved via FirstOrDefaultAsync, which returns a tracked entity. EF Core automatically detects changes to tracked entities, so the Update() call is redundant (though harmless). Simply modifying the property on Line 75 is sufficient.

♻️ Proposed refactor
             } else {
                 userWithGroup.IsLlmInvisible = isInvisible;
-                _dbContext.UsersWithGroup.Update(userWithGroup);
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs` around lines 64
- 77, The code is calling _dbContext.UsersWithGroup.Update(userWithGroup) after
retrieving userWithGroup via FirstOrDefaultAsync (a tracked entity); remove the
redundant Update() call and rely on EF Core change tracking by simply setting
userWithGroup.IsLlmInvisible = isInvisible (keep the AddAsync branch as-is for
new entities) and ensure SaveChangesAsync is called later to persist the change.

33-42: ⚖️ Poor tradeoff

Consider adding a composite index for invisibility queries.

GetInvisibleUserIdsAsync filters by GroupId and IsLlmInvisible. The current unique index on (UserId, GroupId) (line 741-742 in the snapshot) doesn't cover queries filtering by (GroupId, IsLlmInvisible). If invisibility checks become frequent or groups grow large, a composite index on (GroupId, IsLlmInvisible) would improve query performance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs` around lines 33
- 42, GetInvisibleUserIdsAsync performs queries on UsersWithGroup filtering by
GroupId and IsLlmInvisible; add a composite index on (GroupId, IsLlmInvisible)
to speed these lookups and avoid full scans for large groups. Update the EF Core
model/DbContext configuration (where the UsersWithGroup entity is configured) to
include HasIndex(e => new { e.GroupId, e.IsLlmInvisible }) (make it non-unique)
so the database will create the composite index; then add/emit a migration and
apply it to ensure the index exists in the database. Ensure the index name is
descriptive and include the same property names (GroupId, IsLlmInvisible) so it
directly supports GetInvisibleUserIdsAsync.
TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs (1)

248-253: ⚡ Quick win

Notice is sent on every mention/reply from an invisible user.

Each time an invisible user mentions or replies to the bot, a public message is posted to the group. This is noisy and repeatedly advertises the user's invisibility state to the whole chat. Consider rate-limiting this notice (the codebase already has a SendRateLimitedWarningAsync Redis-TTL pattern in AgentChatModeController) or suppressing it after the first hit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs` around lines 248
- 253, The current invisibility notice is sent every time an invisible user
triggers LLM checks in GeneralLLMController (the block using
_llmVisibilityService.IsUserInvisibleAsync and SendMessageService.SendMessage),
which is noisy; change it to rate-limit or suppress repeated notices by reusing
the existing rate-limit pattern (e.g.,
AgentChatModeController.SendRateLimitedWarningAsync or a Redis-TTL key per
chat+user). Specifically, before calling SendMessageService.SendMessage,
check/create a TTL key (unique by telegramMessage.Chat.Id + fromUserId +
"llm_invisible") and only send the notice if the key is missing, then set the
TTL (e.g., minutes) so subsequent hits skip the send; if your codebase exposes
SendRateLimitedWarningAsync, call that helper instead of direct
SendMessageService.SendMessage to enforce the same behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs`:
- Around line 126-133: The cached ChatSession in _chatSessions[chatId] can
retain messages for users who later become invisible; modify ExecAsync (the code
that retrieves/reuses _chatSessions[ChatId]) to detect visibility changes and
evict/rebuild the session: before reusing a cached ChatSession, call
_llmVisibilityService.IsUserInvisibleAsync for the participants or run
FilterVisibleMessagesAsync against the session's stored messages and if any
messages would be filtered out, remove _chatSessions[chatId] (or create a fresh
ChatSession) so the filtered history is not reused; ensure you reference
_chatSessions, ExecAsync, ChatId, _llmVisibilityService.IsUserInvisibleAsync and
FilterVisibleMessagesAsync when implementing the eviction logic.

In `@TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs`:
- Line 13: The LlmVisibilityService is registered as
[Injectable(ServiceLifetime.Transient)] but depends on DataDbContext (a scoped
DbContext), so change the registration to use ServiceLifetime.Scoped to match
the DbContext lifetime; update the attribute on the LlmVisibilityService class
from ServiceLifetime.Transient to ServiceLifetime.Scoped so the service and its
DataDbContext dependency share the same request scope.

In `@TelegramSearchBot/Controller/Manage/LlmVisibilityController.cs`:
- Around line 31-34: The code extracts message text into the local variable
command and then compares it to exact strings; update the normalization where
command is set so slash commands with a bot suffix match: take the first token
of (message.Text ?? message.Caption ?? string.Empty).Trim() (i.e., up to the
first space) and, if that token contains '@', truncate everything from the '@'
onward, then use that normalized command for the existing comparisons (e.g.,
against "/llm_invisible_on", "/llm_invisible_off", "/llm_invisible_status") so
commands like "/llm_invisible_on@YourBot" will match.

In `@TelegramSearchBot/Service/Manage/RefreshService.cs`:
- Around line 330-333: The loop in RefreshService.cs skips invisible messages
via _llmVisibilityService.IsUserInvisibleAsync(chatId, message.FromUserId) and
uses continue, but it doesn't update the processed counter, causing stalled
progress; update the code so the processed-count variable (e.g.,
processed/processedCount/processedFiles used in this method) is incremented for
invisible items before the continue, or better, move the increment into a common
tail/finally block that runs for every iteration; apply the same fix to the
other early-continue path referenced around lines 347-350 so all skipped items
are counted as processed.

In `@TelegramSearchBot/Service/Tools/SearchToolService.cs`:
- Around line 29-37: Make LlmVisibilityService mandatory by removing the default
null from the SearchToolService constructor parameter (llmVisibilityService =
null → LlmVisibilityService llmVisibilityService), add an ArgumentNullException
check in the constructor to enforce non-null, and remove the empty-set fallback
logic that currently silently disables filtering (the code block around lines
325-328 that returns an empty set when _llmVisibilityService is null). Ensure
all usages of _llmVisibilityService assume it is present and rely on it to
perform LLM visibility filtering rather than skipping or returning an empty
visibility set.
- Around line 337-350: The code currently returns visibleDtos.Count as the
total, which underreports when you only fetched a window; update the return to
compute a correct totalVisibleFound: call _luceneManager.Search and build
visibleDtos as now, then when deciding the return value use this rule — if you
fetched the entire result set (searchResult.Item2.Count >= searchResult.Item1)
set totalVisibleFound = visibleDtos.Count; otherwise set totalVisibleFound =
searchResult.Item1 - invisibleHitsInFetchedSet (where invisibleHitsInFetchedSet
= searchResult.Item2.Count(m => invisibleUserIds.Contains(m.FromUserId))) so the
total reflects Lucene's total hits minus known invisible hits instead of the
current window size; keep the rest of the paging logic and return
(totalVisibleFound, visibleDtos.Skip(skip).Take(take).ToList()).

---

Nitpick comments:
In `@TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs`:
- Around line 64-77: The code is calling
_dbContext.UsersWithGroup.Update(userWithGroup) after retrieving userWithGroup
via FirstOrDefaultAsync (a tracked entity); remove the redundant Update() call
and rely on EF Core change tracking by simply setting
userWithGroup.IsLlmInvisible = isInvisible (keep the AddAsync branch as-is for
new entities) and ensure SaveChangesAsync is called later to persist the change.
- Around line 33-42: GetInvisibleUserIdsAsync performs queries on UsersWithGroup
filtering by GroupId and IsLlmInvisible; add a composite index on (GroupId,
IsLlmInvisible) to speed these lookups and avoid full scans for large groups.
Update the EF Core model/DbContext configuration (where the UsersWithGroup
entity is configured) to include HasIndex(e => new { e.GroupId, e.IsLlmInvisible
}) (make it non-unique) so the database will create the composite index; then
add/emit a migration and apply it to ensure the index exists in the database.
Ensure the index name is descriptive and include the same property names
(GroupId, IsLlmInvisible) so it directly supports GetInvisibleUserIdsAsync.

In `@TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs`:
- Around line 248-253: The current invisibility notice is sent every time an
invisible user triggers LLM checks in GeneralLLMController (the block using
_llmVisibilityService.IsUserInvisibleAsync and SendMessageService.SendMessage),
which is noisy; change it to rate-limit or suppress repeated notices by reusing
the existing rate-limit pattern (e.g.,
AgentChatModeController.SendRateLimitedWarningAsync or a Redis-TTL key per
chat+user). Specifically, before calling SendMessageService.SendMessage,
check/create a TTL key (unique by telegramMessage.Chat.Id + fromUserId +
"llm_invisible") and only send the notice if the key is missing, then set the
TTL (e.g., minutes) so subsequent hits skip the send; if your codebase exposes
SendRateLimitedWarningAsync, call that helper instead of direct
SendMessageService.SendMessage to enforce the same behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 409d4d76-c8a2-418c-9697-4a029691e25f

📥 Commits

Reviewing files that changed from the base of the PR and between 4bcb77f and 72ec777.

📒 Files selected for processing (19)
  • TelegramSearchBot.Database/Migrations/20260601111353_AddLlmInvisibilityToUserWithGroup.Designer.cs
  • TelegramSearchBot.Database/Migrations/20260601111353_AddLlmInvisibilityToUserWithGroup.cs
  • TelegramSearchBot.Database/Migrations/DataDbContextModelSnapshot.cs
  • TelegramSearchBot.Database/Model/Data/UserWithGroup.cs
  • TelegramSearchBot.LLM.Test/Service/AI/LLM/LlmVisibilityServiceTests.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/OpenAIResponsesService.cs
  • TelegramSearchBot.LLM/Service/AI/LLM/OpenAIService.cs
  • TelegramSearchBot.Test/Service/AI/LLM/LLMTaskQueueServiceTests.cs
  • TelegramSearchBot/Controller/AI/LLM/AgentChatModeController.cs
  • TelegramSearchBot/Controller/AI/LLM/AltPhotoController.cs
  • TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs
  • TelegramSearchBot/Controller/Manage/LlmVisibilityController.cs
  • TelegramSearchBot/Service/AI/LLM/AgentChatBatchDispatchService.cs
  • TelegramSearchBot/Service/AI/LLM/LLMTaskQueueService.cs
  • TelegramSearchBot/Service/Manage/RefreshService.cs
  • TelegramSearchBot/Service/Tools/SearchToolService.cs

Comment thread TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs
Comment thread TelegramSearchBot.LLM/Service/AI/LLM/LlmVisibilityService.cs Outdated
Comment thread TelegramSearchBot/Controller/Manage/LlmVisibilityController.cs Outdated
Comment thread TelegramSearchBot/Service/Manage/RefreshService.cs
Comment thread TelegramSearchBot/Service/Tools/SearchToolService.cs Outdated
Comment thread TelegramSearchBot/Service/Tools/SearchToolService.cs Outdated
@ModerRAS ModerRAS merged commit f7b20dc into master Jun 1, 2026
5 checks passed
@ModerRAS ModerRAS deleted the codex/llm-invisibility branch June 1, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant