Skip to content

feat: Add UMO Group aggregation support and routing#153

Draft
lekoOwO wants to merge 3 commits intoSXP-Simon:mainfrom
lekoOwO:feature/umo-group
Draft

feat: Add UMO Group aggregation support and routing#153
lekoOwO wants to merge 3 commits intoSXP-Simon:mainfrom
lekoOwO:feature/umo-group

Conversation

@lekoOwO
Copy link
Copy Markdown
Contributor

@lekoOwO lekoOwO commented Apr 2, 2026

Summary by Sourcery

Add support for aggregating analyses across multiple UMO sources into virtual UMO groups and integrate them into existing daily and incremental reporting flows.

New Features:

  • Introduce daily analysis execution for multiple UMO sources aggregated under a virtual UMO group ID.
  • Add incremental analysis execution for aggregated UMO sources with per-source watermarks and group-level batch storage.
  • Support scheduled reporting and incremental analysis for UMO groups alongside individual groups, including report dispatch to multiple output destinations.
  • Extend report dispatching to generate reports under a logical report group ID distinct from the physical output group.

Enhancements:

  • Refactor the analysis pipeline into a reusable helper to share logic between single-group and UMO group analyses.
  • Relax adapter requirements in final incremental reporting to allow adapter-less UMO group reports when appropriate.
  • Extend configuration management with UMO group definitions, normalization, validation, and lookup helpers, and integrate them with existing whitelist/blacklist logic.
  • Update scheduler logging and task accounting to reflect combined processing of normal groups and UMO groups.

Documentation:

  • Document UMO group aggregation concepts, configuration, and constraints in the README, including how they interact with existing scheduling and whitelist/blacklist settings.

Co-authored-by: lekoOwO <20151124+lekoOwO@users.noreply.github.com>
Co-authored-by: openai-code-agent[bot] <242516109+Codex@users.noreply.github.com>
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 2, 2026

Reviewer's Guide

Implements UMO Group aggregation support for both daily and incremental analyses, adds scheduler wiring to treat UMO groups as first‑class analysis targets with their own source/output UMO configuration and list‑filtering, updates config and reporting to understand UMO group IDs and aggregated report context, and refactors common analysis logic into reusable helpers.

Sequence diagram for UMO Group daily aggregation and reporting

sequenceDiagram
    actor Admin
    participant AutoScheduler
    participant AnalysisApplicationService as AnalysisService
    participant BotManager
    participant Adapter
    participant MessageCleanerService as Cleaner
    participant LLMAnalyzer
    participant HistoryManager
    participant ReportDispatcher as Dispatcher

    Admin->>AutoScheduler: configure UMO_groups, schedules

    AutoScheduler->>AutoScheduler: _get_umo_group_targets(mode_filter=None)
    AutoScheduler->>AutoScheduler: _perform_umo_group_analysis_with_timeout(group_id, sources, outputs, mode=traditional)
    AutoScheduler->>AutoScheduler: _perform_umo_group_analysis(group_id, sources, outputs, mode)

    AutoScheduler->>AnalysisService: execute_daily_analysis_for_sources(group_id, source_umos)
    activate AnalysisService

    loop for each source_umo in source_umos
        AnalysisService->>ConfigManager: parse_umo_string(source_umo)
        ConfigManager-->>AnalysisService: platform_id, session_id
        AnalysisService->>BotManager: get_adapter(platform_id)
        alt adapter exists
            BotManager-->>AnalysisService: adapter
            AnalysisService->>Adapter: fetch_messages(group_id=session_id, days, max_count)
            Adapter-->>AnalysisService: raw_messages
        else adapter missing
            BotManager-->>AnalysisService: None
            AnalysisService->>AnalysisService: skip this source_umo
        end
    end

    AnalysisService->>MessageCleanerService: clean_messages(all_raw_messages, bot_self_ids, filter_commands=True)
    MessageCleanerService-->>AnalysisService: unified_messages

    AnalysisService->>AnalysisService: _analyze_messages(unified_messages, group_id, platform_id=first_platform, adapter=None, umo_override=group_id)
    activate AnalysisService
        AnalysisService->>StatisticsService: calculate_group_statistics(unified_messages)
        StatisticsService-->>AnalysisService: statistics

        AnalysisService->>ConfigManager: get_bot_self_ids()
        ConfigManager-->>AnalysisService: bot_self_ids
        AnalysisService->>AnalysisDomainService: analyze_user_activity(unified_messages, bot_self_ids)
        AnalysisDomainService-->>AnalysisService: user_activity

        AnalysisService->>ConfigManager: get_max_user_titles()
        ConfigManager-->>AnalysisService: limit
        AnalysisService->>AnalysisDomainService: get_top_users(user_activity, limit)
        AnalysisDomainService-->>AnalysisService: top_users

        AnalysisService->>ConfigManager: get_topic_analysis_enabled(), get_user_title_analysis_enabled(), get_golden_quote_analysis_enabled(), get_chat_quality_analysis_enabled()
        ConfigManager-->>AnalysisService: flags

        AnalysisService->>StatisticsService: _convert_to_legacy_dict(unified_messages)
        StatisticsService-->>AnalysisService: legacy_messages

        alt any LLM flag enabled
            AnalysisService->>LLMAnalyzer: analyze_all_concurrent(legacy_messages, user_activity, umo=group_id, top_users, flags)
            LLMAnalyzer-->>AnalysisService: topics, user_titles, golden_quotes, token_usage, chat_quality_review
        else
            AnalysisService->>AnalysisService: skip LLM analysis
        end

        AnalysisService->>HistoryManager: save_analysis(group_id, analysis_result)
        HistoryManager-->>AnalysisService: saved

        AnalysisService-->>AutoScheduler: {success=True, analysis_result, messages_count, adapter=None, platform_id=first_platform}
    deactivate AnalysisService
    deactivate AnalysisService

    AutoScheduler->>ReportDispatcher: dispatch(dest.group_id, analysis_result, dest.platform_id, report_group_id=group_id) for each dest in outputs
    activate Dispatcher
        alt output_format=image
            Dispatcher->>ReportGenerator: generate_image_report(analysis_result, report_group_id, html_render_func, avatar_url_getter)
            ReportGenerator-->>Dispatcher: image_url, html_content
            Dispatcher->>Adapter: send_image(group_id, image_url or file)
        else output_format=pdf or html or text
            Dispatcher->>ReportGenerator: generate_pdf_report or generate_html_report(with report_group_id)
            ReportGenerator-->>Dispatcher: path
            Dispatcher->>Adapter: send_file_or_text(group_id, path or text)
        end
    deactivate Dispatcher

    AutoScheduler-->>Admin: UMO Group aggregated report delivered
Loading

Sequence diagram for UMO Group incremental aggregation and final report

sequenceDiagram
    actor Admin
    participant AutoScheduler
    participant AnalysisApplicationService as AnalysisService
    participant BotManager
    participant IncrementalStore
    participant MessageCleanerService as Cleaner
    participant LLMAnalyzer
    participant ReportDispatcher as Dispatcher

    Admin->>AutoScheduler: configure UMO_groups, incremental lists

    AutoScheduler->>AutoScheduler: _get_umo_group_targets(mode_filter=incremental)

    AutoScheduler->>AutoScheduler: _perform_incremental_analysis_for_umo_group_with_timeout(group_id, source_umos)
    AutoScheduler->>AutoScheduler: _perform_incremental_analysis_for_umo_group(group_id, source_umos)

    AutoScheduler->>AnalysisService: execute_incremental_analysis_for_sources(group_id, source_umos)
    activate AnalysisService

    loop for each source_umo in source_umos
        AnalysisService->>ConfigManager: parse_umo_string(source_umo)
        ConfigManager-->>AnalysisService: platform_id, session_id
        AnalysisService->>BotManager: get_adapter(platform_id)
        BotManager-->>AnalysisService: adapter or None

        alt adapter exists
            AnalysisService->>AnalysisService: _build_source_progress_key(group_id, source_umo)
            AnalysisService->>IncrementalStore: get_last_analyzed_timestamp(progress_key)
            IncrementalStore-->>AnalysisService: last_ts

            AnalysisService->>Adapter: fetch_messages(group_id=session_id, days, max_count, since_ts=last_ts)
            Adapter-->>AnalysisService: raw_messages

            AnalysisService->>MessageCleanerService: clean_messages(raw_messages, bot_self_ids, filter_commands=True)
            MessageCleanerService-->>AnalysisService: unified_messages

            AnalysisService->>AnalysisService: filter messages with timestamp > last_ts
            AnalysisService->>AnalysisService: append to combined_messages, update source_watermarks
        else
            AnalysisService->>AnalysisService: skip this source_umo
        end
    end

    AnalysisService->>StatisticsService: calculate_group_statistics(combined_messages)
    StatisticsService-->>AnalysisService: statistics

    AnalysisService->>AnalysisDomainService: analyze_user_activity(combined_messages, bot_self_ids)
    AnalysisDomainService-->>AnalysisService: user_activity

    AnalysisService->>AnalysisService: _compute_hourly_counts(combined_messages)
    AnalysisService-->>AnalysisService: hourly_msg_counts, hourly_char_counts

    AnalysisService->>ConfigManager: get_incremental_topics_per_batch(), get_incremental_quotes_per_batch(), LLM flags
    ConfigManager-->>AnalysisService: topics_per_batch, quotes_per_batch, flags

    AnalysisService->>StatisticsService: _convert_to_legacy_dict(combined_messages)
    StatisticsService-->>AnalysisService: legacy_messages

    alt any LLM flag enabled
        AnalysisService->>LLMAnalyzer: analyze_incremental_concurrent(legacy_messages, umo=group_id_with_platform_hint, topics_per_batch, quotes_per_batch, flags)
        LLMAnalyzer-->>AnalysisService: topics, golden_quotes, token_usage, chat_quality_review
    else
        AnalysisService->>AnalysisService: skip LLM analysis
    end

    AnalysisService->>AnalysisService: build IncrementalBatch(group_id, stats, user_stats, emoji_stats, topics, quotes, token_usage, chat_quality_review)
    AnalysisService->>IncrementalStore: save_batch(batch)
    IncrementalStore-->>AnalysisService: saved

    loop for each source watermark
        AnalysisService->>IncrementalStore: update_last_analyzed_timestamp(progress_key, safe_ts)
        IncrementalStore-->>AnalysisService: updated
    end

    AnalysisService-->>AutoScheduler: {success=True, batch_summary, messages_count, group_id, platform_id_hint}
    deactivate AnalysisService

    alt incremental_report_immediately enabled
        AutoScheduler->>AutoScheduler: _perform_umo_group_analysis_with_timeout(group_id, sources, outputs, mode=incremental)
        AutoScheduler->>AnalysisService: execute_incremental_final_report(group_id, platform_id=None, require_adapter=False)
        AnalysisService-->>AutoScheduler: {success, analysis_result, messages_count}

        AutoScheduler->>ReportDispatcher: dispatch(dest.group_id, analysis_result, dest.platform_id, report_group_id=group_id) for each dest
        Dispatcher-->>AutoScheduler: dispatched
    else
        AutoScheduler->>Admin: incremental batches accumulated only
    end
Loading

Class diagram for UMO Group config, scheduling, and analysis

classDiagram
    class ConfigManager {
        +UMO_GROUP_PREFIX
        +UMO_GROUP_ID_INVALID_PATTERN
        +is_group_allowed(group_id_or_umo str) bool
        +is_umo_group_id(value str) bool
        +normalize_umo_group_id(group_id str) str
        +parse_umo_string(umo str) (str, str)
        +get_umo_groups() list
        +get_umo_group_map() dict
        +get_umo_group(group_id str) dict
        +get_umo_group_sources(group_id str) list~str~
        +get_umo_group_outputs(group_id str) list~str~
        +find_umo_groups_by_source(source_umo str) list~str~
        +get_scheduled_group_list_mode() str
        +get_scheduled_group_list() list
        +get_incremental_group_list_mode() str
        +get_incremental_group_list() list
        +get_analysis_days() int
        +get_max_messages() int
        +get_incremental_safe_limit() int
        +get_incremental_min_messages() int
        +get_topic_analysis_enabled() bool
        +get_user_title_analysis_enabled() bool
        +get_golden_quote_analysis_enabled() bool
        +get_chat_quality_analysis_enabled() bool
        +get_max_user_titles() int
        +get_incremental_topics_per_batch() int
        +get_incremental_quotes_per_batch() int
        +get_output_format() str
        +get_bot_self_ids() list~str~
    }

    class AnalysisApplicationService {
        +execute_daily_analysis(platform_id str, group_id str) dict
        +execute_daily_analysis_for_sources(group_id str, source_umos list~str~) dict
        +_analyze_messages(unified_messages list~UnifiedMessage~, group_id str, platform_id str, adapter Any, umo_override str) dict
        +execute_incremental_analysis(platform_id str, group_id str) dict
        +execute_incremental_analysis_for_sources(group_id str, source_umos list~str~) dict
        +execute_incremental_final_report(group_id str, platform_id str, require_adapter bool) dict
        +_build_source_progress_key(group_id str, source_umo str) str
        -statistics_service
        -analysis_domain_service
        -config_manager
        -llm_analyzer
        -history_manager
        -incremental_store
        -bot_manager
        -llm_semaphore
    }

    class AutoScheduler {
        +_run_scheduled_report()
        +_run_incremental_analysis()
        +_get_scheduled_targets(mode_filter str) list
        +_get_umo_group_targets(mode_filter str) list~dict~
        +_normalize_group_entry(value str, defined_ids set~str~) str
        +_is_group_selected(group_id str, mode str, configured_ids set~str~) bool
        +_prepare_umo_group_sources(source_umos list~str~) list~str~
        +_normalize_output_destinations(outputs list~str~) list~dict~
        +_perform_auto_analysis_for_group(group_id str, target_platform_id str)
        +_perform_auto_analysis_for_group_with_timeout(group_id str, target_platform_id str)
        +_perform_umo_group_analysis(group_id str, source_umos list~str~, output_targets list~dict~, mode str)
        +_perform_umo_group_analysis_with_timeout(group_id str, source_umos list~str~, output_targets list~dict~, mode str)
        +_perform_incremental_analysis_for_group(group_id str, platform_id str)
        +_perform_incremental_analysis_for_group_with_timeout(group_id str, platform_id str)
        +_perform_incremental_analysis_for_umo_group(group_id str, source_umos list~str~)
        +_perform_incremental_analysis_for_umo_group_with_timeout(group_id str, source_umos list~str~)
        -config_manager
        -analysis_service
        -report_dispatcher
        -bot_manager
        -_terminating bool
    }

    class ReportDispatcher {
        +dispatch(group_id str, analysis_result dict, platform_id str, report_group_id str)
        +_dispatch_image(group_id str, analysis_result dict, platform_id str, report_group_id str) bool
        +_dispatch_pdf(group_id str, analysis_result dict, platform_id str, report_group_id str) bool
        +_dispatch_html(group_id str, analysis_result dict, platform_id str, report_group_id str) bool
        -config_manager
        -report_generator
        -_html_render_func
    }

    class BotManager {
        +get_adapter(platform_id str) Adapter
        +is_ready_for_auto_analysis() bool
    }

    class Adapter {
        +platform_id str
        +fetch_messages(group_id str, days int, max_count int, since_ts int) list~dict~
        +send_image(group_id str, image Any) bool
        +send_file_or_text(group_id str, payload Any) bool
    }

    class IncrementalStore {
        +get_last_analyzed_timestamp(progress_key str) int
        +update_last_analyzed_timestamp(progress_key str, ts int)
        +save_batch(batch IncrementalBatch)
    }

    class IncrementalBatch {
        +group_id str
        +timestamp float
        +messages_count int
        +characters_count int
        +hourly_msg_counts dict
        +hourly_char_counts dict
        +user_stats dict
        +emoji_stats dict
        +topics list~dict~
        +golden_quotes list~dict~
        +token_usage dict
        +chat_quality_review dict
        +last_message_timestamp int
        +participant_ids list~str~
        +get_summary() dict
    }

    ConfigManager <.. AutoScheduler : uses
    ConfigManager <.. AnalysisApplicationService : uses
    ConfigManager <.. ReportDispatcher : uses

    AutoScheduler --> AnalysisApplicationService : orchestrates
    AutoScheduler --> ReportDispatcher : dispatches_reports
    AutoScheduler --> BotManager : checks_adapters

    AnalysisApplicationService --> BotManager : fetch_messages
    AnalysisApplicationService --> IncrementalStore : read_write_progress

    ReportDispatcher --> Adapter : send_outputs

    IncrementalStore <.. AnalysisApplicationService
    IncrementalBatch <.. AnalysisApplicationService

    BotManager o-- Adapter
Loading

File-Level Changes

Change Details Files
Refactor daily analysis pipeline into a reusable helper and add UMO Group multi-source daily analysis entrypoint.
  • Make adapter acquisition for daily analysis tolerant of missing platform_id
  • Extract shared analysis steps (statistics, user analysis, LLM analysis, persistence, and return payload) into _analyze_messages
  • Introduce execute_daily_analysis_for_sources to fetch and clean messages from multiple source UMOs, aggregate them, validate thresholds, and call the shared pipeline with an optional umo_override origin
src/application/services/analysis_application_service.py
Add incremental UMO Group aggregation flow, including per-source watermarks, batch persistence, and timeout wrappers in the scheduler.
  • Implement execute_incremental_analysis_for_sources to pull incremental messages from multiple source UMOs with individual progress keys, clean/merge/filter them, run statistics and LLM incremental analysis, build IncrementalBatch objects, save batches, and update per-source watermarks safely
  • Add helpers _build_source_progress_key and new _umo_group scheduler methods to run incremental aggregation for UMO groups with timeouts and proper logging/trace context
  • Wire UMO group incremental analysis into the existing incremental orchestration, including immediate-report mode for groups and UMO groups
src/application/services/analysis_application_service.py
src/infrastructure/scheduler/auto_scheduler.py
Extend scheduler to treat UMO groups as scheduled/incremental targets with explicit source and output UMO configuration and filtering.
  • Add helpers to normalize and select UMO group IDs based on configured whitelist/blacklist modes
  • Filter and validate UMO group sources and outputs (adapter existence, base is_group_allowed check) and produce normalized destination descriptors
  • Integrate UMO group targets into scheduled report and incremental analysis loops, sharing concurrency limits, staggering, and result accounting with traditional group targets
src/infrastructure/scheduler/auto_scheduler.py
Extend configuration to define and resolve UMO groups, parse UMO strings, and integrate UMO groups into existing group allow-listing semantics.
  • Introduce UMO group ID normalization, validation, and detection, including a restricted character set and common prefix
  • Add parse_umo_string helper to split platform and session IDs from a UMO string
  • Expose getters for UMO group list/map, individual group, sources, outputs, and a reverse lookup find_umo_groups_by_source, and hook UMO groups into is_group_allowed matching logic
src/infrastructure/config/config_manager.py
Update report dispatcher to support UMO group report context separate from the physical output group ID.
  • Extend dispatch and underlying _dispatch_image/_dispatch_pdf/_dispatch_html to accept a report_group_id used when generating reports while still sending to the output group
  • Propagate report_group_id from scheduler when dispatching UMO group reports so aggregated reports carry the virtual group identity in their content
src/infrastructure/reporting/dispatcher.py
Document UMO Group aggregation configuration and behavior in the README.
  • Describe UMO 分组 configuration structure (id, source_umos, output_umos) and how to reference groups via umoGroup: in various lists
  • Clarify how sources can belong to multiple groups, how IDs are validated, and how incremental mode behaves for UMO groups
README.md
_conf_schema.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • In execute_incremental_analysis_for_sources, avoid the inline import time and reuse the existing time_mod import for consistency and to prevent redundant imports.
  • The scheduler’s UMO Group helper methods (_get_umo_group_targets, _normalize_output_destinations, etc.) pass around loosely-typed dict/object structures; consider introducing a small dataclass or TypedDict for targets to make the code easier to follow and less error-prone when accessing fields like group_id, mode, sources, and outputs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `execute_incremental_analysis_for_sources`, avoid the inline `import time` and reuse the existing `time_mod` import for consistency and to prevent redundant imports.
- The scheduler’s UMO Group helper methods (`_get_umo_group_targets`, `_normalize_output_destinations`, etc.) pass around loosely-typed `dict`/`object` structures; consider introducing a small dataclass or TypedDict for targets to make the code easier to follow and less error-prone when accessing fields like `group_id`, `mode`, `sources`, and `outputs`.

## Individual Comments

### Comment 1
<location path="src/application/services/analysis_application_service.py" line_range="868-877" />
<code_context>
+                participant_ids=participant_ids,
+            )
+
+            await self.incremental_store.save_batch(batch)
+
+            import time
+
+            safe_now = int(time.time()) + 60
+            for progress_key, ts in source_watermarks.items():
+                safe_ts = min(ts, safe_now)
</code_context>
<issue_to_address>
**suggestion:** Avoid mixing `time_mod` and a local `import time` in the same function for clarity and consistency.

This function first uses `time_mod.time()` and later does a local `import time` and calls `time.time()`. Please use a single approach (e.g., only `time_mod` or a module‑level `import time`) so the time source stays consistent and easier to mock or trace.

```suggestion
            await self.incremental_store.save_batch(batch)

            safe_now = int(time_mod.time()) + 60
            for progress_key, ts in source_watermarks.items():
                safe_ts = min(ts, safe_now)
                await self.incremental_store.update_last_analyzed_timestamp(
                    progress_key, safe_ts
                )
```
</issue_to_address>

### Comment 2
<location path="src/infrastructure/config/config_manager.py" line_range="68-73" />
<code_context>
         glist = [str(g) for g in self.get_group_list()]
         target = str(group_id_or_umo)

+        target_umo_groups = set(self.find_umo_groups_by_source(target))
+
         target_simple_id = target.split(":")[-1] if ":" in target else target
</code_context>
<issue_to_address>
**suggestion (performance):** New UMO-group matching path in `is_group_allowed` may be relatively expensive due to repeated scans of all UMO groups.

`target_umo_groups = set(self.find_umo_groups_by_source(target))` causes every `is_group_allowed` call to traverse all configured UMO groups (via `get_umo_group_map`), even when the input contains no UMO-group IDs. Since `is_group_allowed` is used in scheduling paths like `_prepare_umo_group_sources`, this may introduce noticeable overhead at scale. Please either skip this UMO-group lookup when the group list has no UMO-group IDs, or cache the mapping used by `find_umo_groups_by_source` so it isn’t rebuilt on each call.

```suggestion
        glist = [str(g) for g in self.get_group_list()]
        target = str(group_id_or_umo)

        # Only resolve UMO groups by source when there are UMO-group IDs in the group list.
        # This avoids repeatedly scanning all configured UMO groups in common non-UMO paths.
        if any(self.is_umo_group_id(item) for item in glist):
            target_umo_groups = set(self.find_umo_groups_by_source(target))
        else:
            target_umo_groups = set()

        target_simple_id = target.split(":")[-1] if ":" in target else target
```
</issue_to_address>

### Comment 3
<location path="src/infrastructure/reporting/dispatcher.py" line_range="43-48" />
<code_context>
         """
         trace_id = TraceContext.get()
         output_format = self.config_manager.get_output_format()
+        target_group_id = report_group_id or group_id
         logger.info(
             f"[{trace_id}] 正在分发群 {group_id} 的报告 (格式: {output_format})"
         )
</code_context>
<issue_to_address>
**suggestion:** Log message still references the physical target group, not the logical report group ID.

`target_group_id` is now used for content generation, but the log still prints `group_id`, which is the delivery destination. For UMO groups this can be confusing, since the report may originate from a different logical group. Please log both the physical `group_id` and the logical `report_group_id` (when present) so the analysis origin and delivery destination are clearly distinguishable when debugging.

```suggestion
        trace_id = TraceContext.get()
        output_format = self.config_manager.get_output_format()
        target_group_id = report_group_id or group_id
        if report_group_id:
            logger.info(
                f"[{trace_id}] 正在分发报告 (逻辑群: {report_group_id}, 发送目标群: {group_id}, 格式: {output_format})"
            )
        else:
            logger.info(
                f"[{trace_id}] 正在分发群 {group_id} 的报告 (格式: {output_format})"
            )
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/application/services/analysis_application_service.py
Comment thread src/infrastructure/config/config_manager.py
Comment thread src/infrastructure/reporting/dispatcher.py Outdated
@lekoOwO lekoOwO marked this pull request as draft April 2, 2026 17:42
@lekoOwO
Copy link
Copy Markdown
Contributor Author

lekoOwO commented Apr 2, 2026

還沒測試,先轉成 Draft

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.

2 participants