Skip to content

refactor(web-scraper): add configuration for specific sites that require special handling - #884

Merged
qdaxb merged 3 commits into
wecode-ai:mainfrom
parabala:feature/document-from-web
Mar 31, 2026
Merged

qdaxb merged 3 commits into
wecode-ai:mainfrom
parabala:feature/document-from-web

Conversation

@parabala

@parabala parabala commented Mar 31, 2026 •

Copy link
Copy Markdown
Collaborator
  • Add Web scraper site-specific configuration for specific sites that require special handling, due to anti-bot detection, dynamic content loading, or navigation patterns

Summary by CodeRabbit

  • New Features

    • Site-specific scraper configuration to tailor crawling behavior per domain.
    • Runtime hostname-based matching to apply per-site settings.
  • Improvements

    • Better anti-bot handling via randomized user agents, managed browser usage, and safer Chromium launch options.
    • Cache-bypass option to force fresh fetches when needed.
  • Chores

    • Added an environment-configurable setting to supply site configuration.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a3f7ef8-21b9-4aa0-a0c7-14fbfb3c49ac

📥 Commits

Reviewing files that changed from the base of the PR and between c266499 and 4338224.

📒 Files selected for processing (1)
  • backend/app/services/web_scraper/scraper_config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/services/web_scraper/scraper_config.py

📝 Walkthrough

Walkthrough

Adds a new WEB_SCRAPER_SITE_CONFIG setting and loader, exposes get_site_config(), and applies per-site runtime scrape settings plus stronger browser options when building crawl run configurations.

Changes

Cohort / File(s) Summary
Environment / Settings
backend/.env.example, backend/app/core/config.py
Added WEB_SCRAPER_SITE_CONFIG to example env and to Settings (defaults to "{}").
Site config loader
backend/app/services/web_scraper/scraper_config.py
New module that reads/parses WEB_SCRAPER_SITE_CONFIG, normalizes keys (lowercase, sorted by length), handles JSON errors/warnings, and exposes cached get_site_config().
Scraper runtime
backend/app/services/web_scraper/scraper_service.py
_build_run_config now accepts url, forces cache_mode=CacheMode.BYPASS, matches hostname against site config and merges per-site settings; crawler BrowserConfig updated with user_agent_mode="random", use_managed_browser=True, and Chromium args --no-sandbox, --disable-dev-shm-usage. Logging updated.

Sequence Diagram

sequenceDiagram
    participant Env as Environment (WEB_SCRAPER_SITE_CONFIG)
    participant Config as scraper_config.get_site_config()
    participant Scraper as Scraper Service
    participant Crawler as Crawl Run Config / Browser

    Env->>Config: provide JSON string
    Config->>Config: parse, normalize, cache site_config
    Scraper->>Config: get_site_config() for target URL
    Config-->>Scraper: return matching site settings (or {})
    Scraper->>Crawler: build run config (merge site settings, set cache_mode=BYPASS, set browser args)
    Crawler-->>Scraper: final run configuration
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through keys and JSON bright,

I parse and sort by day and night.
I nudge the browser, sly and spry—
The crawl begins beneath my sky.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding configuration for specific sites in the web scraper that require special handling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/services/web_scraper/scraper_service.py (1)

358-364: ⚠️ Potential issue | 🔴 Critical

Duplicate dictionary key remove_overlay_elements causes conflicting behavior.

The key is defined twice: first as False (line 361) with a comment explaining it's "disabled to avoid navigation conflicts", then immediately overwritten to True (line 363). This contradicts the comment and the second assignment silently wins. Decide which value is intended and remove the duplicate.

🐛 Proposed fix (if True is intended)
         config_kwargs = {
             "wait_until": "domcontentloaded",  # Faster than networkidle, avoids timeout
             "page_timeout": WEB_SCRAPER_TIMEOUT,
-            "remove_overlay_elements": False,  # Disabled to avoid navigation conflicts
             "cache_mode": CacheMode.BYPASS,  # Force refresh, bypassing the local cache
             "remove_overlay_elements": True,  # Automatically remove mask layers
         }
🐛 Proposed fix (if False is intended)
         config_kwargs = {
             "wait_until": "domcontentloaded",  # Faster than networkidle, avoids timeout
             "page_timeout": WEB_SCRAPER_TIMEOUT,
             "remove_overlay_elements": False,  # Disabled to avoid navigation conflicts
             "cache_mode": CacheMode.BYPASS,  # Force refresh, bypassing the local cache
-            "remove_overlay_elements": True,  # Automatically remove mask layers
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_service.py` around lines 358 - 364,
The config_kwargs dict in scraper_service.py defines the duplicate key
remove_overlay_elements twice (first False, then True) causing the second value
to silently override the first; decide the intended behavior and remove the
unwanted entry so only one remove_overlay_elements is present in config_kwargs,
and update its comment to reflect the chosen behavior (e.g., keep
remove_overlay_elements: False with comment "disabled to avoid navigation
conflicts" or keep True with comment "automatically remove mask layers"); locate
the dict by the symbol config_kwargs and edit the duplicate
remove_overlay_elements entries accordingly.
🧹 Nitpick comments (2)
backend/app/services/web_scraper/scraper_config.py (1)

18-24: Add logging when JSON parsing fails and include return type hint.

Silent failure on invalid JSON makes debugging difficult. Per coding guidelines, type hints are required.

♻️ Proposed fix
+import logging
+
+logger = logging.getLogger(__name__)
+
-def _load_site_config():
+def _load_site_config() -> dict:
     """Load site configuration from environment variable."""
     config_str = os.environ.get("WEB_SCRAPER_SITE_CONFIG", "{}")
     try:
         return json.loads(config_str)
     except json.JSONDecodeError:
+        logger.warning(
+            "Invalid JSON in WEB_SCRAPER_SITE_CONFIG, using empty config"
+        )
         return {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_config.py` around lines 18 - 24, The
_load_site_config function should declare its return type and log JSON parse
failures; change the signature to include a type hint (e.g., def
_load_site_config() -> Dict[str, Any]:), ensure you have typing imports (Dict,
Any), and in the except json.JSONDecodeError as e block call the module logger
(or logging.getLogger(__name__)) to log a clear error like "Failed to parse
WEB_SCRAPER_SITE_CONFIG" with the exception details before returning {} so
failures are visible in logs.
backend/app/services/web_scraper/scraper_service.py (1)

366-372: Substring domain matching may apply config to unintended URLs.

Using domain in url will match "example.com" in URLs like https://notexample.com/ or https://example.com.malicious.site/. Consider matching against the parsed hostname instead.

♻️ Proposed fix using hostname matching
+from urllib.parse import urlparse
+
         # Apply site-specific configuration if URL matches known sites
         if url:
+            parsed_url = urlparse(url)
+            hostname = parsed_url.netloc.lower()
             for domain, site_config in WEB_SCRAPER_SITE_CONFIG.items():
-                if domain in url:
+                domain_lower = domain.lower()
+                if hostname == domain_lower or hostname.endswith("." + domain_lower):
                     logger.info(f"Applying site-specific config for {domain}: {url}")
                     config_kwargs.update(site_config)
                     break
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_service.py` around lines 366 - 372,
The current substring check ("domain in url") can misapply site configs; instead
parse the URL hostname (e.g., via urllib.parse.urlparse(url).hostname),
normalize to lowercase, strip any port, and then match the domain against the
hostname using either exact equality or subdomain matching (hostname == domain
or hostname.endswith("." + domain)); replace the "if domain in url" condition in
the loop that iterates WEB_SCRAPER_SITE_CONFIG and continues to call logger.info
and config_kwargs.update(site_config) when a match is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@backend/app/services/web_scraper/scraper_service.py`:
- Around line 358-364: The config_kwargs dict in scraper_service.py defines the
duplicate key remove_overlay_elements twice (first False, then True) causing the
second value to silently override the first; decide the intended behavior and
remove the unwanted entry so only one remove_overlay_elements is present in
config_kwargs, and update its comment to reflect the chosen behavior (e.g., keep
remove_overlay_elements: False with comment "disabled to avoid navigation
conflicts" or keep True with comment "automatically remove mask layers"); locate
the dict by the symbol config_kwargs and edit the duplicate
remove_overlay_elements entries accordingly.

---

Nitpick comments:
In `@backend/app/services/web_scraper/scraper_config.py`:
- Around line 18-24: The _load_site_config function should declare its return
type and log JSON parse failures; change the signature to include a type hint
(e.g., def _load_site_config() -> Dict[str, Any]:), ensure you have typing
imports (Dict, Any), and in the except json.JSONDecodeError as e block call the
module logger (or logging.getLogger(__name__)) to log a clear error like "Failed
to parse WEB_SCRAPER_SITE_CONFIG" with the exception details before returning {}
so failures are visible in logs.

In `@backend/app/services/web_scraper/scraper_service.py`:
- Around line 366-372: The current substring check ("domain in url") can
misapply site configs; instead parse the URL hostname (e.g., via
urllib.parse.urlparse(url).hostname), normalize to lowercase, strip any port,
and then match the domain against the hostname using either exact equality or
subdomain matching (hostname == domain or hostname.endswith("." + domain));
replace the "if domain in url" condition in the loop that iterates
WEB_SCRAPER_SITE_CONFIG and continues to call logger.info and
config_kwargs.update(site_config) when a match is found.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a7aefcd-f649-40c9-9935-299a1867b18a

📥 Commits

Reviewing files that changed from the base of the PR and between 348974a and a91911e.

📒 Files selected for processing (3)
  • backend/.env.example
  • backend/app/services/web_scraper/scraper_config.py
  • backend/app/services/web_scraper/scraper_service.py

- Add scraper_config.py for site-specific crawling configurations
@parabala
parabala force-pushed the feature/document-from-web branch from a91911e to e212a75 Compare March 31, 2026 02:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/app/services/web_scraper/scraper_service.py (1)

358-364: ⚠️ Potential issue | 🟠 Major

Remove the duplicated remove_overlay_elements entry.

Line 361 never takes effect because Line 363 overwrites the same key. That means this block always enables overlay removal, while the preceding comment says the opposite, and Ruff will flag the duplicate literal.

🧹 Proposed fix
         config_kwargs = {
             "wait_until": "domcontentloaded",  # Faster than networkidle, avoids timeout
             "page_timeout": WEB_SCRAPER_TIMEOUT,
-            "remove_overlay_elements": False,  # Disabled to avoid navigation conflicts
             "cache_mode": CacheMode.BYPASS,  # Force refresh, bypassing the local cache
-            "remove_overlay_elements": True,  # Automatically remove mask layers
+            "remove_overlay_elements": True,  # Default; override per-site when needed
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_service.py` around lines 358 - 364,
Duplicate key in config_kwargs causes remove_overlay_elements to be overwritten;
open scraper_service.py and edit the config_kwargs dict (used where
WEB_SCRAPER_TIMEOUT and CacheMode are referenced) to remove the duplicated
"remove_overlay_elements" entry and ensure the remaining setting matches the
intended behavior described by the comment (either keep False and delete the
later True, or vice versa), then adjust the comment so it accurately reflects
the final value.
🧹 Nitpick comments (1)
backend/app/services/web_scraper/scraper_service.py (1)

335-337: Trace the new per-site config decision path.

This code now changes crawler behavior based on url, matched site config, and proxy, but none of those choices will be visible in telemetry when a scrape fails. Please trace the touched methods and set span attributes for the target host, matched config key, and proxy mode.

As per coding guidelines, "Use @trace_async(span_name, tracer_name, extract_attributes) decorator to trace entire async functions in OpenTelemetry", "Use @trace_sync(span_name, tracer_name, extract_attributes) decorator to trace entire sync functions in OpenTelemetry", and "Use set_span_attribute(key, value) to set attribute on current OpenTelemetry span".

Also applies to: 339-378

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_service.py` around lines 335 - 337,
The scrape call pathway that builds run_config and calls crawler.arun should
record the target host, the matched per-site config key, and the proxy mode on
the active OpenTelemetry span: decorate the async function that contains the
shown block (the function that calls _build_run_config and await crawler.arun)
with `@trace_async`(span_name, tracer_name, extract_attributes) and/or add the
decorator to any sibling async functions in the 339-378 region; before calling
_build_run_config / crawler.arun use set_span_attribute("scrape.target_host",
url_host), set_span_attribute("scrape.matched_config_key",
matched_config_key_or_null) and set_span_attribute("scrape.proxy_mode",
proxy_url and "proxy" or "none") so those values appear in telemetry; ensure you
derive url_host and matched_config_key from the same logic that
_build_run_config uses so the span attributes match the actual runtime
decisions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/app/services/web_scraper/scraper_service.py`:
- Around line 366-372: The current loop checks domain substrings against the
full url which can produce false matches; instead parse the hostname from url
(e.g., via urllib.parse.urlparse(url).hostname), sort WEB_SCRAPER_SITE_CONFIG
keys by length descending to prefer longest matches, then perform exact equality
or suffix matching against that hostname, update config_kwargs with the matching
site_config, and change the logger.info to reference the hostname and chosen
domain (keep the existing break after a match). Ensure this uses the parsed
hostname variable wherever domain matching occurs and handles a None hostname
safely.

---

Outside diff comments:
In `@backend/app/services/web_scraper/scraper_service.py`:
- Around line 358-364: Duplicate key in config_kwargs causes
remove_overlay_elements to be overwritten; open scraper_service.py and edit the
config_kwargs dict (used where WEB_SCRAPER_TIMEOUT and CacheMode are referenced)
to remove the duplicated "remove_overlay_elements" entry and ensure the
remaining setting matches the intended behavior described by the comment (either
keep False and delete the later True, or vice versa), then adjust the comment so
it accurately reflects the final value.

---

Nitpick comments:
In `@backend/app/services/web_scraper/scraper_service.py`:
- Around line 335-337: The scrape call pathway that builds run_config and calls
crawler.arun should record the target host, the matched per-site config key, and
the proxy mode on the active OpenTelemetry span: decorate the async function
that contains the shown block (the function that calls _build_run_config and
await crawler.arun) with `@trace_async`(span_name, tracer_name,
extract_attributes) and/or add the decorator to any sibling async functions in
the 339-378 region; before calling _build_run_config / crawler.arun use
set_span_attribute("scrape.target_host", url_host),
set_span_attribute("scrape.matched_config_key", matched_config_key_or_null) and
set_span_attribute("scrape.proxy_mode", proxy_url and "proxy" or "none") so
those values appear in telemetry; ensure you derive url_host and
matched_config_key from the same logic that _build_run_config uses so the span
attributes match the actual runtime decisions.
🪄 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: 12fbb5a8-b6c8-48a6-b5e6-caf516c3aac1

📥 Commits

Reviewing files that changed from the base of the PR and between a91911e and e212a75.

📒 Files selected for processing (3)
  • backend/.env.example
  • backend/app/services/web_scraper/scraper_config.py
  • backend/app/services/web_scraper/scraper_service.py
✅ Files skipped from review due to trivial changes (2)
  • backend/.env.example
  • backend/app/services/web_scraper/scraper_config.py

… instead of os.environ

- Add WEB_SCRAPER_SITE_CONFIG to Settings class in config.py
- Refactor scraper_config.py to use settings.WEB_SCRAPER_SITE_CONFIG with proper logging
- Replace module-level config loading with cached get_site_config() function
- Update scraper_service.py to use new get_site_config() function
- Add detailed logging for site config matching and application
- Fix remove_overlay_elements duplicate key in CrawlerRunConfig

This ensures the configuration is properly loaded from .env file via Pydantic settings."

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
backend/app/services/web_scraper/scraper_config.py (1)

73-77: Narrow the exception handler to avoid catching unrelated exceptions.

Catching bare Exception can mask programming errors (e.g., TypeError, AttributeError) that should propagate. Since the try block primarily accesses settings and performs dict operations, consider catching only the expected exceptions.

Proposed fix
     except json.JSONDecodeError as e:
         logger.error(f"[WebScraperConfig] Failed to parse WEB_SCRAPER_SITE_CONFIG: {e}")
         return {}
-    except Exception as e:
-        logger.error(
-            f"[WebScraperConfig] Error loading WEB_SCRAPER_SITE_CONFIG from settings: {e}"
-        )
-        return {}
+    except (TypeError, KeyError, AttributeError) as e:
+        logger.error(
+            f"[WebScraperConfig] Error loading WEB_SCRAPER_SITE_CONFIG from settings: {e}"
+        )
+        return {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/app/services/web_scraper/scraper_config.py` around lines 73 - 77,
Replace the broad "except Exception as e" around the WEB_SCRAPER_SITE_CONFIG
loading with a narrow exception tuple that reflects expected failures when
reading settings and manipulating dicts (e.g., except (AttributeError, KeyError,
TypeError, ValueError) as e), so that programming errors still propagate; keep
the existing logger.error call but ensure it logs the captured exception
variable (e) for context when these expected exceptions occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/app/services/web_scraper/scraper_config.py`:
- Around line 36-38: The current info-level log in WebScraperConfig that prints
the raw config_value risks exposing secrets; change it to not log the config
contents at INFO level—remove or replace the logger.info(f"[WebScraperConfig]
Loading WEB_SCRAPER_SITE_CONFIG from settings {config_value}") call with a
non-sensitive message (e.g., a simple "loaded WEB_SCRAPER_SITE_CONFIG" at INFO),
and if needed for debugging use logger.debug to log only non-sensitive
identifiers such as domain keys (e.g., via list(config.keys())) while keeping
any secret fields omitted.
- Around line 23-77: The functions _load_site_config and get_site_config lack
return type annotations; add explicit types (e.g., -> Dict[str, Any] or ->
Mapping[str, Any]) to both function signatures and ensure typing imports (Dict,
Any or Mapping) are present at top of the module; update the docstring/returns
only if needed to match the chosen type and keep behavior unchanged (normalize
config returns an empty dict on error so Dict[str, Any] is appropriate).

---

Nitpick comments:
In `@backend/app/services/web_scraper/scraper_config.py`:
- Around line 73-77: Replace the broad "except Exception as e" around the
WEB_SCRAPER_SITE_CONFIG loading with a narrow exception tuple that reflects
expected failures when reading settings and manipulating dicts (e.g., except
(AttributeError, KeyError, TypeError, ValueError) as e), so that programming
errors still propagate; keep the existing logger.error call but ensure it logs
the captured exception variable (e) for context when these expected exceptions
occur.
🪄 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: 366ad16d-c1c9-403b-b536-6d16ae2b7b7a

📥 Commits

Reviewing files that changed from the base of the PR and between e212a75 and c266499.

📒 Files selected for processing (3)
  • backend/app/core/config.py
  • backend/app/services/web_scraper/scraper_config.py
  • backend/app/services/web_scraper/scraper_service.py
✅ Files skipped from review due to trivial changes (1)
  • backend/app/core/config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/services/web_scraper/scraper_service.py

- Remove sensitive config value from INFO level logging
- Add return type annotations to _load_site_config and get_site_config
- Replace broad Exception catch with specific exception types (AttributeError, KeyError, TypeError, ValueError)
- Import Dict and Any from typing module
@qdaxb
qdaxb merged commit 4bb9ff0 into wecode-ai:main Mar 31, 2026
20 checks passed
FicoHu pushed a commit to FicoHu/Wegent that referenced this pull request Apr 1, 2026
…ire special handling (wecode-ai#884)

* refactor(web-scraper): extract site-specific config to scraper_config.py

- Add scraper_config.py for site-specific crawling configurations

* fix(web_scraper): load WEB_SCRAPER_SITE_CONFIG from Pydantic settings instead of os.environ

- Add WEB_SCRAPER_SITE_CONFIG to Settings class in config.py
- Refactor scraper_config.py to use settings.WEB_SCRAPER_SITE_CONFIG with proper logging
- Replace module-level config loading with cached get_site_config() function
- Update scraper_service.py to use new get_site_config() function
- Add detailed logging for site config matching and application
- Fix remove_overlay_elements duplicate key in CrawlerRunConfig

This ensures the configuration is properly loaded from .env file via Pydantic settings."

* refactor(backend): improve scraper_config security and type safety

- Remove sensitive config value from INFO level logging
- Add return type annotations to _load_site_config and get_site_config
- Replace broad Exception catch with specific exception types (AttributeError, KeyError, TypeError, ValueError)
- Import Dict and Any from typing module
qdaxb added a commit that referenced this pull request Apr 1, 2026
* feat: add workspace archive and recovery for Pod deletion

Implement Pod recovery feature that allows users to continue conversations
after executor Pods are deleted:

- Add ArchiveInfo schema to store archive metadata in Task.status.archive
- Add workspace_archive service for MinIO presigned URL generation
- Add archive/restore endpoints in executor and executor_manager
- Add ExecutorRecoveryService for Pod recreation with workspace restoration
- Add skip_git_clone support to prevent git clone conflicts during restore
- Add Celery Beat scheduled task for expired archive cleanup (30-day retention)
- Integrate archive workflow in executor_job before Pod deletion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(backend): add missing minio dependency

Add minio package to backend dependencies for workspace archive storage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(admin): add created_at field to device monitor list (#846)

- Add created_at field to AdminDeviceInfo schema in backend
- Add created_at to AdminDeviceInfo TypeScript interface
- Display creation time in device list UI
- Add i18n translations for created_at column (zh-CN and en)

Co-authored-by: yunpeng7 <yunpeng7@staff.weibo.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* perf: use async heartbeat (#849)

* fix(chat_shell): prevent orphaned tool call IDs in group chat history (#852)

* fix(chat_shell): prevent tool call group splitting in group chat history truncation

_truncate_history blindly sliced the flat message list, which could
split assistant+tool_calls and their tool response messages across the
truncation boundary. This produced orphaned function_call_output items
without matching function_call entries in the OpenAI Responses API
format, causing 'No tool call found for function call output' errors.

Add _group_messages() to treat assistant+tool_calls+tool responses as
atomic units, and modify _truncate_history() to truncate by groups
instead of individual messages.

* fix(chat_shell): ensure tool call group integrity across all compression paths

Extract group_tool_call_messages() into shared messages/utils.py and
apply atomic group handling to:
- HistoryTruncationStrategy.compress(): adjust first/last boundaries
  to avoid splitting tool call groups, add post-removal orphan cleanup
- _force_compression_to_target(): Step 2 removes middle groups instead
  of individual messages, Step 5 keeps first/last groups
- Refactor loader.py to import shared utility instead of inline helper

All 95 related tests pass. Comprehensive audit confirms no other code
paths in chat_shell break tool call group integrity.

* test(chat_shell): add tool-call group truncation regressions

Add regression coverage for preserving assistant tool calls with their matching tool results across group-chat history truncation, history compression boundaries, and forced compression fallback paths.

* fix: 在里面智能体的创建人和机器人的创建人不是一个人时会报错 (#853)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 在里面智能体的创建人和机器人的创建人不是一个人时会报错

* fix: 迁移其他的sql到kindReader

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fail fast on invalid tool call IDs across chat protocols (#854)

* fix: 订阅任务执行间隔调整 & trace http接口的匹配规则调整 (#855)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: telemetry的匹配规则调整

* fix: 订阅任务执行间隔调整

* fix: 调整逻辑

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: sanitize PyInstaller LD_LIBRARY_PATH for child processes (#859)

PyInstaller's bootloader prepends its extraction directory (/tmp/_MEIxxxxxx)
to LD_LIBRARY_PATH, causing child processes (Claude Code, shell commands)
to load wrong shared libraries (e.g., libtinfo.so.6).

Extract `sanitize_ld_library_path()` into platform_compat and apply it in
both local_mode_strategy (Claude SDK subprocess) and extension_handler
(extension script subprocess) to restore the original library path.

See: https://pyinstaller.org/en/stable/common-issues-and-pitfalls.html

Co-authored-by: sifang <sifang@staff.weibo.com>

* refactor(rag): move internal retrieve routing into backend (#847)

* refactor(rag): move internal retrieve routing into backend

* refactor(rag): move retrieve persistence into backend

* refactor(rag): move kb head persistence into backend

* fix(rag): tighten backend retrieve routing

* refactor(rag): simplify token estimation fallback

* refactor(rag): batch route token estimation

* fix(rag): token estimator decimal

* fix(rag): harden kb read persistence

* frontend: show 'indexing' status for documents pending indexing (#858)

* fix(frontend): show 'indexing' status for documents pending indexing

When a document is uploaded, is_active is false while indexing is in progress.
Previously, this showed 'index unavailable' which was misleading.

Now documents with status='enabled' but is_active=false will show 'indexing'
instead of 'index unavailable', matching the existing translation key.

Changes:
- Updated compact mode status indicator (line 247)
- Updated normal mode status badge (line 461)

Fixes: knowledge base document shows 'index unavailable' during indexing

* fix(i18n): update Developer role description in Chinese locale

* fix: 修复订阅器中脏数据导致的无法展示的问题 (#860)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复订阅器中脏数据导致的无法展示的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* feat(backend): add knowledge base RAG support to /v1/responses API (#844)

* feat(backend): add knowledge base RAG support to /v1/responses API

Implement knowledge base Q&A functionality for the OpenAI-compatible
/v1/responses endpoint with the following changes:

Schema Changes:
- Add knowledge_base_names field to WegentTool schema
- Support namespace#name format for KB specification

New Services:
- KnowledgeBaseNameResolver: resolves KB display names to IDs with
  permission checking via TaskKnowledgeBaseService
- KnowledgeBaseContextCreator: creates SubtaskContext records for
  API-specified KBs to integrate with existing RAG pipeline

Integration:
- Extend parse_wegent_tools() to handle knowledge_base tool type
- Add parse_knowledge_base_name() for namespace#name parsing
- Modify build_execution_request() to accept knowledge_base_names
- Add _create_kb_contexts_from_api_request() for KB context injection
- Update openapi_responses.py endpoints to pass KB names parameter

Testing:
- Add comprehensive unit tests for KB name resolution
- Add tests for KB context creation
- Add tests for helper functions with KB support
- All 32 new tests passing

The implementation reuses the existing RAG flow through
prepare_contexts_for_chat() and KnowledgeBaseTool, ensuring
consistency with the web UI behavior.

* refactor(kb_resolver): optimize database query complexity from O(n) to O(m)

- Add batch_get_knowledge_base_by_names() to TaskKnowledgeBaseService for efficient batch queries
- Extract _check_kb_access_permission() for unified permission checking logic
- Update kb_resolver to use service methods instead of direct queries
- Reduce DB queries from O(n) to O(m) where m is unique namespace count
- Update tests to mock service methods instead of internal functions

All 15 tests pass.

* refactor(backend): optimize KB permission check in kb_resolver

Simplify permission checking by using get_all_knowledge_bases_grouped()
which already returns all KBs accessible to the user.

Changes:
- Replace _batch_query_kbs() and _check_kb_access_permission() with
  _get_accessible_kb_lookup() that builds a lookup dict from grouped results
- Remove unused resolve_single() method and batch_get_knowledge_base_by_names()
- Update tests to mock KnowledgeService.get_all_knowledge_bases_grouped()

This reduces database queries and simplifies the permission check logic.

* feat(backend): integrate knowledge base context creation into unified AI trigger

- Add knowledge_base_names parameter to build_execution_request for OpenAPI v1/responses
- Create _create_kb_contexts_from_api_request helper to process KB names from API requests
- Integrate KB context processing into unified trigger flow
- Add KnowledgeBaseContextCreator class for creating SubtaskContext records
- Add comprehensive unit tests for KnowledgeBaseContextCreator

This enables RAG functionality for OpenAPI v1/responses endpoint by creating
SubtaskContext records that are processed by the existing context pipeline.

* fix(chat): propagate HTTPException from KnowledgeBaseNameResolver in unified trigger

- Add HTTPException import from fastapi
- Catch and re-raise HTTPException explicitly in _create_kb_contexts_from_api_request
- Keep generic exception handling for non-HTTP exceptions to preserve best-effort behavior
- This ensures 403/404 errors from KB resolution are properly propagated to callers
  instead of being swallowed and logged as warnings

* fix(chat_shell): use model_type for provider detection instead of model_id (#861)

- Extract shared detect_provider() into models/providers.py with
  PROVIDER_ALIASES as single source of truth
- model_type (env.model) is authoritative; raise ValueError on unknown
- Remove _PROVIDER_ID_PATTERNS model_id prefix fallback (same model_id
  can serve multiple protocols)
- Unify truncation notice logic for all providers (no system-role notice
  in middle of messages, preventing Anthropic non-consecutive system
  message errors)
- Pass model_type from model_config through MessageCompressor to
  TokenCounter for accurate provider detection
- Remove duplicate provider constants from factory.py and token_counter.py

Co-authored-by: wenbo17 <wenbo17@staff.weibo.com>

* ix: claude的子代理模式不可用修复 (#862)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: claude的子代理模式不可用修复

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* feat(inbox): implement work queue and message forwarding system (#851)

* feat(inbox): implement work queue and message forwarding system

Add a comprehensive message forwarding and work queue system that enables
users to forward conversation messages to other users. Recipients can
manage received messages through work queues and optionally configure
AI agents for automatic processing.

Backend changes:
- Add QueueMessage and RecentContact database models
- Add WorkQueue CRD schema stored in kinds table
- Implement work queue services for CRUD operations
- Implement message forwarding service with queue selection
- Add WebSocket events for real-time notifications
- Add API endpoints for work queue management
- Add ForbiddenException to core exceptions

Frontend changes:
- Add inbox page with queue sidebar and message list
- Implement ForwardMessageDialog for recipient selection
- Add queue edit/create dialog with auto-processing config
- Add message detail dialog with content preview
- Add i18n translations (zh-CN and en)
- Register inbox route and navigation

* fix: resolve lint errors in inbox feature

- Remove unused imports (RefreshCw, Settings, Menu, Button, Input, Clock)
- Format Python files with Black

* fix: resolve TypeScript errors in inbox feature

- Add 'flow' and 'inbox' to TopNavigation activePage type
- Replace Badge variant 'outline' with 'info' (outline not supported)

* fix: address CodeRabbit review comments for work queue feature

- Fix status parameter shadowing fastapi status module in work_queue.py
- Fix NamespaceMember.namespace to use group_name column reference
- Replace func.json_set with Python-level update for PostgreSQL compatibility
- Fix refreshQueues dependency loop using functional state update
- Remove Chinese text from inbox page comment
- Add NaN validation for schedule interval input
- Replace clickable Badge with accessible button for recent contacts

---------

Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>

* Wegent/work queue and message forwarding (#863)

* feat(inbox): implement work queue and message forwarding system

Add a comprehensive message forwarding and work queue system that enables
users to forward conversation messages to other users. Recipients can
manage received messages through work queues and optionally configure
AI agents for automatic processing.

Backend changes:
- Add QueueMessage and RecentContact database models
- Add WorkQueue CRD schema stored in kinds table
- Implement work queue services for CRUD operations
- Implement message forwarding service with queue selection
- Add WebSocket events for real-time notifications
- Add API endpoints for work queue management
- Add ForbiddenException to core exceptions

Frontend changes:
- Add inbox page with queue sidebar and message list
- Implement ForwardMessageDialog for recipient selection
- Add queue edit/create dialog with auto-processing config
- Add message detail dialog with content preview
- Add i18n translations (zh-CN and en)
- Register inbox route and navigation

* fix: resolve lint errors in inbox feature

- Remove unused imports (RefreshCw, Settings, Menu, Button, Input, Clock)
- Format Python files with Black

* fix: resolve TypeScript errors in inbox feature

- Add 'flow' and 'inbox' to TopNavigation activePage type
- Replace Badge variant 'outline' with 'info' (outline not supported)

* fix: address CodeRabbit review comments for work queue feature

- Fix status parameter shadowing fastapi status module in work_queue.py
- Fix NamespaceMember.namespace to use group_name column reference
- Replace func.json_set with Python-level update for PostgreSQL compatibility
- Fix refreshQueues dependency loop using functional state update
- Remove Chinese text from inbox page comment
- Add NaN validation for schedule interval input
- Replace clickable Badge with accessible button for recent contacts

* feat: update work queue

---------

Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>

* fix: fix column default value

* fix: fix forward message click

* feat: remove unused area

* feat: add inbox default message

* fix: 修复脏数据导致的订阅任务无法执行的问题 (#865)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复脏数据导致的订阅任务无法执行的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix(browser): harden browser-tool linux recovery (#867)

Co-authored-by: sifang <sifang@staff.weibo.com>

* fix: 修复无法使用自定义镜像以及自定义镜像无法校验的问题 (#866)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复无法使用自定义镜像

* fix: 修复无法使用自定义镜像以及自定义镜像无法校验的问题

* fix: 修复可能的bug

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fix welcome timezone (#868)

* docs: update inbox docs (#869)

* fix: 修复单测 (#870)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复单测

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fix page error (#871)

* fix: data-roleid="api-key-create-button" not in html attr (#873)

* fix: 修复停止按钮偶发变成发送的问题 & 修复失败的消息会丢失的问题 (#876)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复停止按钮偶发变成发送的问题

* fix: 修复失败的消息会丢失的问题

* fix: 修复类型报错

* fix: 修复前端复制按钮内容错误的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: 修复“skill not found” (#877)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 技能错乱

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* chore(executor): upgrade claude-agent-sdk from 0.1.31 to 0.1.51 (#880)

Co-authored-by: sifang <sifang@staff.weibo.com>

* feat(frontend): add HTML attachment preview and share link generation (#872)

- Add HtmlPreview component for previewing HTML files with iframe and source view modes
- Update FilePreview to support HTML file type detection and rendering
- Add createAttachmentShareLink API for generating public share links
- Add share button to FilePreviewDialog and FilePreviewPage (visible for owners only)
- Add Web Page type label and icon for HTML attachments in AttachmentCard
- Fix preview page height to fill viewport using h-screen and h-full
- Update AttachmentPreview to support HTML file preview button

Fixes share link browsing and download for table-type attachments

* fix(knowledge): harden document indexing state (#875)

* fix(knowledge): harden document indexing state

* fix(knowledge): harden indexing state transitions

* fix(knowledge): tighten reindex review followups

* fix(rag): KNOWLEDGE_INDEX_LOCK_MAX_RETRIES=1

* fix(knowledge): store index status without db enum

* fix(alembic): correct migration revision metadata

* docs: update contributors list (zh) (#879)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* docs: update contributors list (#878)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* feat: 支持skill认证 (#882)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: add skill http identity design and plan

* feat(skill): add http identity token flow

* feat: 修改样式

* feat: 修改样式

* feat: 移除计划

* feat: 调整为带有认证的接口

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>
# Conflicts:
#	executor_manager/services/sandbox/manager.py

* Feature/knowledge document read (#888)

* docs(backend): add knowledge document read spec and plan

* feat(backend): add orchestrated knowledge document reads

* fix(backend): tighten knowledge document read handling

* refactor(web-scraper): add configuration for specific sites that require special handling (#884)

* refactor(web-scraper): extract site-specific config to scraper_config.py

- Add scraper_config.py for site-specific crawling configurations

* fix(web_scraper): load WEB_SCRAPER_SITE_CONFIG from Pydantic settings instead of os.environ

- Add WEB_SCRAPER_SITE_CONFIG to Settings class in config.py
- Refactor scraper_config.py to use settings.WEB_SCRAPER_SITE_CONFIG with proper logging
- Replace module-level config loading with cached get_site_config() function
- Update scraper_service.py to use new get_site_config() function
- Add detailed logging for site config matching and application
- Fix remove_overlay_elements duplicate key in CrawlerRunConfig

This ensures the configuration is properly loaded from .env file via Pydantic settings."

* refactor(backend): improve scraper_config security and type safety

- Remove sensitive config value from INFO level logging
- Add return type annotations to _load_site_config and get_site_config
- Replace broad Exception catch with specific exception types (AttributeError, KeyError, TypeError, ValueError)
- Import Dict and Any from typing module

* refactor(backend): replace auto-cleanup with archive expiration check

Remove automatic MinIO archive cleanup (Celery Beat task) and instead
block task execution when workspace archive has expired (>30 days).
Users will receive a clear error message and must create a new task.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: 添加恢复行为测试和脚本路径修正

* fix: 使用 timezone.utc 替换 UTC 以确保时区正确

* test(backend): fix workspace recovery test patches

---------

Co-authored-by: yunpeng7 <yunpeng7@staff.weibo.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: axb <qindi@staff.weibo.com>
Co-authored-by: moqimoqidea <39821951+moqimoqidea@users.noreply.github.com>
Co-authored-by: MicroLee <yudexiaozhou@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>
Co-authored-by: icycrystal4 <icycrystal4@gmail.com>
Co-authored-by: sifang <sifang@staff.weibo.com>
Co-authored-by: Yanhe <yanhe1@staff.weibo.com>
Co-authored-by: parabala <chenyong1@staff.weibo.com>
Co-authored-by: wenbo17 <wenbo17@staff.weibo.com>
Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>
Co-authored-by: 李枨煊 <chengxuan@staff.weibo.com>
Co-authored-by: johnny0120 <jiangyang7@staff.weibo.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
qdaxb added a commit that referenced this pull request Apr 3, 2026
* feat: add workspace archive and recovery for Pod deletion

Implement Pod recovery feature that allows users to continue conversations
after executor Pods are deleted:

- Add ArchiveInfo schema to store archive metadata in Task.status.archive
- Add workspace_archive service for MinIO presigned URL generation
- Add archive/restore endpoints in executor and executor_manager
- Add ExecutorRecoveryService for Pod recreation with workspace restoration
- Add skip_git_clone support to prevent git clone conflicts during restore
- Add Celery Beat scheduled task for expired archive cleanup (30-day retention)
- Integrate archive workflow in executor_job before Pod deletion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(backend): add missing minio dependency

Add minio package to backend dependencies for workspace archive storage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(admin): add created_at field to device monitor list (#846)

- Add created_at field to AdminDeviceInfo schema in backend
- Add created_at to AdminDeviceInfo TypeScript interface
- Display creation time in device list UI
- Add i18n translations for created_at column (zh-CN and en)

Co-authored-by: yunpeng7 <yunpeng7@staff.weibo.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* perf: use async heartbeat (#849)

* fix(chat_shell): prevent orphaned tool call IDs in group chat history (#852)

* fix(chat_shell): prevent tool call group splitting in group chat history truncation

_truncate_history blindly sliced the flat message list, which could
split assistant+tool_calls and their tool response messages across the
truncation boundary. This produced orphaned function_call_output items
without matching function_call entries in the OpenAI Responses API
format, causing 'No tool call found for function call output' errors.

Add _group_messages() to treat assistant+tool_calls+tool responses as
atomic units, and modify _truncate_history() to truncate by groups
instead of individual messages.

* fix(chat_shell): ensure tool call group integrity across all compression paths

Extract group_tool_call_messages() into shared messages/utils.py and
apply atomic group handling to:
- HistoryTruncationStrategy.compress(): adjust first/last boundaries
  to avoid splitting tool call groups, add post-removal orphan cleanup
- _force_compression_to_target(): Step 2 removes middle groups instead
  of individual messages, Step 5 keeps first/last groups
- Refactor loader.py to import shared utility instead of inline helper

All 95 related tests pass. Comprehensive audit confirms no other code
paths in chat_shell break tool call group integrity.

* test(chat_shell): add tool-call group truncation regressions

Add regression coverage for preserving assistant tool calls with their matching tool results across group-chat history truncation, history compression boundaries, and forced compression fallback paths.

* fix: 在里面智能体的创建人和机器人的创建人不是一个人时会报错 (#853)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 在里面智能体的创建人和机器人的创建人不是一个人时会报错

* fix: 迁移其他的sql到kindReader

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fail fast on invalid tool call IDs across chat protocols (#854)

* fix: 订阅任务执行间隔调整 & trace http接口的匹配规则调整 (#855)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: telemetry的匹配规则调整

* fix: 订阅任务执行间隔调整

* fix: 调整逻辑

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: sanitize PyInstaller LD_LIBRARY_PATH for child processes (#859)

PyInstaller's bootloader prepends its extraction directory (/tmp/_MEIxxxxxx)
to LD_LIBRARY_PATH, causing child processes (Claude Code, shell commands)
to load wrong shared libraries (e.g., libtinfo.so.6).

Extract `sanitize_ld_library_path()` into platform_compat and apply it in
both local_mode_strategy (Claude SDK subprocess) and extension_handler
(extension script subprocess) to restore the original library path.

See: https://pyinstaller.org/en/stable/common-issues-and-pitfalls.html

Co-authored-by: sifang <sifang@staff.weibo.com>

* refactor(rag): move internal retrieve routing into backend (#847)

* refactor(rag): move internal retrieve routing into backend

* refactor(rag): move retrieve persistence into backend

* refactor(rag): move kb head persistence into backend

* fix(rag): tighten backend retrieve routing

* refactor(rag): simplify token estimation fallback

* refactor(rag): batch route token estimation

* fix(rag): token estimator decimal

* fix(rag): harden kb read persistence

* frontend: show 'indexing' status for documents pending indexing (#858)

* fix(frontend): show 'indexing' status for documents pending indexing

When a document is uploaded, is_active is false while indexing is in progress.
Previously, this showed 'index unavailable' which was misleading.

Now documents with status='enabled' but is_active=false will show 'indexing'
instead of 'index unavailable', matching the existing translation key.

Changes:
- Updated compact mode status indicator (line 247)
- Updated normal mode status badge (line 461)

Fixes: knowledge base document shows 'index unavailable' during indexing

* fix(i18n): update Developer role description in Chinese locale

* fix: 修复订阅器中脏数据导致的无法展示的问题 (#860)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复订阅器中脏数据导致的无法展示的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* feat(backend): add knowledge base RAG support to /v1/responses API (#844)

* feat(backend): add knowledge base RAG support to /v1/responses API

Implement knowledge base Q&A functionality for the OpenAI-compatible
/v1/responses endpoint with the following changes:

Schema Changes:
- Add knowledge_base_names field to WegentTool schema
- Support namespace#name format for KB specification

New Services:
- KnowledgeBaseNameResolver: resolves KB display names to IDs with
  permission checking via TaskKnowledgeBaseService
- KnowledgeBaseContextCreator: creates SubtaskContext records for
  API-specified KBs to integrate with existing RAG pipeline

Integration:
- Extend parse_wegent_tools() to handle knowledge_base tool type
- Add parse_knowledge_base_name() for namespace#name parsing
- Modify build_execution_request() to accept knowledge_base_names
- Add _create_kb_contexts_from_api_request() for KB context injection
- Update openapi_responses.py endpoints to pass KB names parameter

Testing:
- Add comprehensive unit tests for KB name resolution
- Add tests for KB context creation
- Add tests for helper functions with KB support
- All 32 new tests passing

The implementation reuses the existing RAG flow through
prepare_contexts_for_chat() and KnowledgeBaseTool, ensuring
consistency with the web UI behavior.

* refactor(kb_resolver): optimize database query complexity from O(n) to O(m)

- Add batch_get_knowledge_base_by_names() to TaskKnowledgeBaseService for efficient batch queries
- Extract _check_kb_access_permission() for unified permission checking logic
- Update kb_resolver to use service methods instead of direct queries
- Reduce DB queries from O(n) to O(m) where m is unique namespace count
- Update tests to mock service methods instead of internal functions

All 15 tests pass.

* refactor(backend): optimize KB permission check in kb_resolver

Simplify permission checking by using get_all_knowledge_bases_grouped()
which already returns all KBs accessible to the user.

Changes:
- Replace _batch_query_kbs() and _check_kb_access_permission() with
  _get_accessible_kb_lookup() that builds a lookup dict from grouped results
- Remove unused resolve_single() method and batch_get_knowledge_base_by_names()
- Update tests to mock KnowledgeService.get_all_knowledge_bases_grouped()

This reduces database queries and simplifies the permission check logic.

* feat(backend): integrate knowledge base context creation into unified AI trigger

- Add knowledge_base_names parameter to build_execution_request for OpenAPI v1/responses
- Create _create_kb_contexts_from_api_request helper to process KB names from API requests
- Integrate KB context processing into unified trigger flow
- Add KnowledgeBaseContextCreator class for creating SubtaskContext records
- Add comprehensive unit tests for KnowledgeBaseContextCreator

This enables RAG functionality for OpenAPI v1/responses endpoint by creating
SubtaskContext records that are processed by the existing context pipeline.

* fix(chat): propagate HTTPException from KnowledgeBaseNameResolver in unified trigger

- Add HTTPException import from fastapi
- Catch and re-raise HTTPException explicitly in _create_kb_contexts_from_api_request
- Keep generic exception handling for non-HTTP exceptions to preserve best-effort behavior
- This ensures 403/404 errors from KB resolution are properly propagated to callers
  instead of being swallowed and logged as warnings

* fix(chat_shell): use model_type for provider detection instead of model_id (#861)

- Extract shared detect_provider() into models/providers.py with
  PROVIDER_ALIASES as single source of truth
- model_type (env.model) is authoritative; raise ValueError on unknown
- Remove _PROVIDER_ID_PATTERNS model_id prefix fallback (same model_id
  can serve multiple protocols)
- Unify truncation notice logic for all providers (no system-role notice
  in middle of messages, preventing Anthropic non-consecutive system
  message errors)
- Pass model_type from model_config through MessageCompressor to
  TokenCounter for accurate provider detection
- Remove duplicate provider constants from factory.py and token_counter.py

Co-authored-by: wenbo17 <wenbo17@staff.weibo.com>

* ix: claude的子代理模式不可用修复 (#862)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: claude的子代理模式不可用修复

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* feat(inbox): implement work queue and message forwarding system (#851)

* feat(inbox): implement work queue and message forwarding system

Add a comprehensive message forwarding and work queue system that enables
users to forward conversation messages to other users. Recipients can
manage received messages through work queues and optionally configure
AI agents for automatic processing.

Backend changes:
- Add QueueMessage and RecentContact database models
- Add WorkQueue CRD schema stored in kinds table
- Implement work queue services for CRUD operations
- Implement message forwarding service with queue selection
- Add WebSocket events for real-time notifications
- Add API endpoints for work queue management
- Add ForbiddenException to core exceptions

Frontend changes:
- Add inbox page with queue sidebar and message list
- Implement ForwardMessageDialog for recipient selection
- Add queue edit/create dialog with auto-processing config
- Add message detail dialog with content preview
- Add i18n translations (zh-CN and en)
- Register inbox route and navigation

* fix: resolve lint errors in inbox feature

- Remove unused imports (RefreshCw, Settings, Menu, Button, Input, Clock)
- Format Python files with Black

* fix: resolve TypeScript errors in inbox feature

- Add 'flow' and 'inbox' to TopNavigation activePage type
- Replace Badge variant 'outline' with 'info' (outline not supported)

* fix: address CodeRabbit review comments for work queue feature

- Fix status parameter shadowing fastapi status module in work_queue.py
- Fix NamespaceMember.namespace to use group_name column reference
- Replace func.json_set with Python-level update for PostgreSQL compatibility
- Fix refreshQueues dependency loop using functional state update
- Remove Chinese text from inbox page comment
- Add NaN validation for schedule interval input
- Replace clickable Badge with accessible button for recent contacts

---------

Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>

* Wegent/work queue and message forwarding (#863)

* feat(inbox): implement work queue and message forwarding system

Add a comprehensive message forwarding and work queue system that enables
users to forward conversation messages to other users. Recipients can
manage received messages through work queues and optionally configure
AI agents for automatic processing.

Backend changes:
- Add QueueMessage and RecentContact database models
- Add WorkQueue CRD schema stored in kinds table
- Implement work queue services for CRUD operations
- Implement message forwarding service with queue selection
- Add WebSocket events for real-time notifications
- Add API endpoints for work queue management
- Add ForbiddenException to core exceptions

Frontend changes:
- Add inbox page with queue sidebar and message list
- Implement ForwardMessageDialog for recipient selection
- Add queue edit/create dialog with auto-processing config
- Add message detail dialog with content preview
- Add i18n translations (zh-CN and en)
- Register inbox route and navigation

* fix: resolve lint errors in inbox feature

- Remove unused imports (RefreshCw, Settings, Menu, Button, Input, Clock)
- Format Python files with Black

* fix: resolve TypeScript errors in inbox feature

- Add 'flow' and 'inbox' to TopNavigation activePage type
- Replace Badge variant 'outline' with 'info' (outline not supported)

* fix: address CodeRabbit review comments for work queue feature

- Fix status parameter shadowing fastapi status module in work_queue.py
- Fix NamespaceMember.namespace to use group_name column reference
- Replace func.json_set with Python-level update for PostgreSQL compatibility
- Fix refreshQueues dependency loop using functional state update
- Remove Chinese text from inbox page comment
- Add NaN validation for schedule interval input
- Replace clickable Badge with accessible button for recent contacts

* feat: update work queue

---------

Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>

* fix: fix column default value

* fix: fix forward message click

* feat: remove unused area

* feat: add inbox default message

* fix: 修复脏数据导致的订阅任务无法执行的问题 (#865)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复脏数据导致的订阅任务无法执行的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix(browser): harden browser-tool linux recovery (#867)

Co-authored-by: sifang <sifang@staff.weibo.com>

* fix: 修复无法使用自定义镜像以及自定义镜像无法校验的问题 (#866)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复无法使用自定义镜像

* fix: 修复无法使用自定义镜像以及自定义镜像无法校验的问题

* fix: 修复可能的bug

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fix welcome timezone (#868)

* docs: update inbox docs (#869)

* fix: 修复单测 (#870)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复单测

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: fix page error (#871)

* fix: data-roleid="api-key-create-button" not in html attr (#873)

* fix: 修复停止按钮偶发变成发送的问题 & 修复失败的消息会丢失的问题 (#876)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 修复停止按钮偶发变成发送的问题

* fix: 修复失败的消息会丢失的问题

* fix: 修复类型报错

* fix: 修复前端复制按钮内容错误的问题

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* fix: 修复“skill not found” (#877)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* fix: 技能错乱

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>

* chore(executor): upgrade claude-agent-sdk from 0.1.31 to 0.1.51 (#880)

Co-authored-by: sifang <sifang@staff.weibo.com>

* feat(frontend): add HTML attachment preview and share link generation (#872)

- Add HtmlPreview component for previewing HTML files with iframe and source view modes
- Update FilePreview to support HTML file type detection and rendering
- Add createAttachmentShareLink API for generating public share links
- Add share button to FilePreviewDialog and FilePreviewPage (visible for owners only)
- Add Web Page type label and icon for HTML attachments in AttachmentCard
- Fix preview page height to fill viewport using h-screen and h-full
- Update AttachmentPreview to support HTML file preview button

Fixes share link browsing and download for table-type attachments

* fix(knowledge): harden document indexing state (#875)

* fix(knowledge): harden document indexing state

* fix(knowledge): harden indexing state transitions

* fix(knowledge): tighten reindex review followups

* fix(rag): KNOWLEDGE_INDEX_LOCK_MAX_RETRIES=1

* fix(knowledge): store index status without db enum

* fix(alembic): correct migration revision metadata

* docs: update contributors list (zh) (#879)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* docs: update contributors list (#878)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* feat: 支持skill认证 (#882)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: update contributors list

* docs: update contributors list (zh)

* docs: add skill http identity design and plan

* feat(skill): add http identity token flow

* feat: 修改样式

* feat: 修改样式

* feat: 移除计划

* feat: 调整为带有认证的接口

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>
# Conflicts:
#	executor_manager/services/sandbox/manager.py

* Feature/knowledge document read (#888)

* docs(backend): add knowledge document read spec and plan

* feat(backend): add orchestrated knowledge document reads

* fix(backend): tighten knowledge document read handling

* refactor(web-scraper): add configuration for specific sites that require special handling (#884)

* refactor(web-scraper): extract site-specific config to scraper_config.py

- Add scraper_config.py for site-specific crawling configurations

* fix(web_scraper): load WEB_SCRAPER_SITE_CONFIG from Pydantic settings instead of os.environ

- Add WEB_SCRAPER_SITE_CONFIG to Settings class in config.py
- Refactor scraper_config.py to use settings.WEB_SCRAPER_SITE_CONFIG with proper logging
- Replace module-level config loading with cached get_site_config() function
- Update scraper_service.py to use new get_site_config() function
- Add detailed logging for site config matching and application
- Fix remove_overlay_elements duplicate key in CrawlerRunConfig

This ensures the configuration is properly loaded from .env file via Pydantic settings."

* refactor(backend): improve scraper_config security and type safety

- Remove sensitive config value from INFO level logging
- Add return type annotations to _load_site_config and get_site_config
- Replace broad Exception catch with specific exception types (AttributeError, KeyError, TypeError, ValueError)
- Import Dict and Any from typing module

* refactor(backend): replace auto-cleanup with archive expiration check

Remove automatic MinIO archive cleanup (Celery Beat task) and instead
block task execution when workspace archive has expired (>30 days).
Users will receive a clear error message and must create a new task.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: 添加恢复行为测试和脚本路径修正

* fix: 使用 timezone.utc 替换 UTC 以确保时区正确

* test(backend): fix workspace recovery test patches

* feat: 添加工作区归档配置测试与默认值

* feat: 添加工作区归档时区配置及测试

---------

Co-authored-by: yunpeng7 <yunpeng7@staff.weibo.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: axb <qindi@staff.weibo.com>
Co-authored-by: moqimoqidea <39821951+moqimoqidea@users.noreply.github.com>
Co-authored-by: MicroLee <yudexiaozhou@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: hongyu9 <hongyu9@staff.weibo.com>
Co-authored-by: icycrystal4 <icycrystal4@gmail.com>
Co-authored-by: sifang <sifang@staff.weibo.com>
Co-authored-by: Yanhe <yanhe1@staff.weibo.com>
Co-authored-by: parabala <chenyong1@staff.weibo.com>
Co-authored-by: wenbo17 <wenbo17@staff.weibo.com>
Co-authored-by: qdaxb <4157870+qdaxb@users.noreply.github.com>
Co-authored-by: 李枨煊 <chengxuan@staff.weibo.com>
Co-authored-by: johnny0120 <jiangyang7@staff.weibo.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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