Skip to content

docs: add Japanese README translation#2248

Open
reckless129 wants to merge 2 commits intorefly-ai:mainfrom
reckless129:feat/add-japanese-readme
Open

docs: add Japanese README translation#2248
reckless129 wants to merge 2 commits intorefly-ai:mainfrom
reckless129:feat/add-japanese-readme

Conversation

@reckless129
Copy link

@reckless129 reckless129 commented Feb 26, 2026

Add Japanese translation of README to improve international accessibility.

Changes:

  • Create README_JA.md with full Japanese translation of core content
  • Add Japanese language link to README.md

The translation covers:

  • Product introduction and core features
  • Quick start guide
  • Core capabilities overview
  • Community links

Summary by CodeRabbit

  • New Features

    • Added a Redis-backed cached web search with configurable TTL, max results, empty-result policy, cache invalidation, and cache statistics.
  • Tests

    • Added comprehensive tests covering caching behavior, key normalization, invalidation, error handling, and cache stats.
  • Chores

    • Webhook base URL is now configurable via environment variables for flexible deployment.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds a Redis-backed CachedWebSearcher (with tests and index export) and makes WebhookService read WEBHOOK_BASE_URL from ConfigService instead of using a hardcoded URL.

Changes

Cohort / File(s) Summary
Webhook Configuration
apps/api/src/modules/webhook/webhook.service.ts
Injects ConfigService into WebhookService and replaces the hardcoded webhook URL with this.config.get<string>('WEBHOOK_BASE_URL') (fallback to https://api.refly.ai).
Web Search — Cache Implementation
apps/api/src/utils/web-search/cached-searcher.ts
Adds CachedWebSearcher and WebSearchCacheConfig: cache-aside pattern, deterministic cache keys, Redis-based locking (SET NX), cache invalidation, stats, TTL/max-results, error fallbacks, and logging.
Web Search — Tests
apps/api/src/utils/web-search/cached-searcher.spec.ts
Adds comprehensive tests with in-memory MockRedisService and MockWebSearcher covering cache hits/misses, key normalization, config limits, invalidation, error fallback, and cache statistics.
Web Search — Exports
apps/api/src/utils/web-search/index.ts
Re-exports CachedWebSearcher and other web-search utilities (BaseWebSearcher, SearXNGWebSearcher, SerperWebSearcher) from a single entry point.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Cached as CachedWebSearcher
  participant Redis as RedisService
  participant Origin as OriginWebSearcher

  Client->>Cached: search(req)
  Cached->>Redis: GET cacheKey
  alt cache hit
    Redis-->>Cached: cachedResults
    Cached-->>Client: return cachedResults
  else cache miss
    Cached->>Redis: SETNX lockKey token PX lockTtl
    alt lock acquired
      Cached->>Origin: search(req)
      Origin-->>Cached: results
      Cached->>Redis: SET cacheKey serializedResults PX ttl
      Cached->>Redis: EVAL releaseLockScript(lockKey, token)
      Cached-->>Client: return results
    else lock not acquired
      Cached->>Redis: GET cacheKey (wait/retry/backoff)
      alt later cache hit
        Redis-->>Cached: cachedResults
        Cached-->>Client: return cachedResults
      else still missing
        Cached->>Origin: search(req)
        Origin-->>Cached: results
        Cached-->>Client: return results
      end
    end
  end

  alt Redis error at any point
    Cached->>Origin: search(req) (fallback)
    Origin-->>Cached: results
    Cached-->>Client: return results
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • PerishCode

Poem

🐇 I nibble keys and hashes bright,

I chase the cache all through the night,
With locks and tokens, neat and sly,
I fetch from origin when Redis sighs,
Hooray — web searches zoom and fly!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title claims to add a Japanese README translation, but the actual changes implement a Redis-backed caching layer for web search results with no translation files present. Either update the title to reflect the actual changes (e.g., 'feat: add Redis-backed caching for web search results') or ensure the PR description and commits align with adding Japanese README translation documentation.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README_JA.md`:
- Line 45: Remove the trailing whitespace characters at the ends of the changed
lines in README_JA.md (specifically the line containing the anchor "<a
href=\"https://refly.ai/workspace\"><u>今すぐ試す</u></a>" and the other affected
lines around 57, 75, 97); open README_JA.md, locate those anchor/text lines and
strip the trailing spaces so each line ends immediately after the last visible
character, then save the file ensuring no trailing whitespace remains in the
modified lines.
- Line 39: The TOC and quick-start table in README_JA.md contain fragment links
that don't match any headings in this file; update the broken anchors (the four
links shown: "Lovable 用 API", "Lark/Feishu 用 Webhook", "Claude Code 用 Skills",
"Clawdbot の構築") in the TOC (current line ~39) and the quick-start rows (lines
~83-87) to point to the existing English anchors used in the main README (e.g.
use-case-1-api-integration, use-case-2-webhook-for-lark-feishu,
use-case-3-skills-for-claude-code, use-case-4-build-clawdbot) so the links work
until the corresponding Japanese headings are added.
- Around line 4-154: The PR added a non-English repository README (README_JA.md)
which violates the English-only README policy; fix by removing or relocating
README_JA.md out of the canonical README set, keep the repository's canonical
README (README.md) in English, and add the Japanese translation into a
translations/docs folder or a locale-specific path (e.g.,
docs/translations/ja/README.md) while adding a clear link from README.md to that
translation; ensure file names matching README* are English-only and update any
references to README_JA.md in the repo to point to the new translation location.

In `@README.md`:
- Line 7: The README.md currently includes a localized link anchor "<a
href="README_JA.md">日本語</a>", which violates the English-only documentation
policy; remove that localized link (or remove all localized README links) from
README.md so the language switcher remains English-only, ensuring only the
canonical English README file and English links remain in the file.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5afcaac and dfa8fbe.

📒 Files selected for processing (2)
  • README.md
  • README_JA.md

README_JA.md Outdated
Comment on lines 4 to 154
# Refly — Vibe Workflow による Agent Skills Builder
<p align="right">
<a href="README.md"><u>English</u></a> · <a href="README_CN.md"><u>中文</u></a> · <a href="README_JA.md"><u>日本語</u></a>
</p>
<p align="center">
<a href="https://github.com/refly-ai/refly">
<img src="https://img.shields.io/github/stars/refly-ai/refly?style=flat&colorA=080f12&colorB=1fa669&logo=github" alt="GitHub stars">
</a>
<a href="https://refly.ai/workspace">
<img src="https://img.shields.io/badge/refly.ai-007bff?style=flat&colorA=080f12&colorB=007bff&logo=google-chrome&logoColor=white" alt="Website">
</a>
<a href="https://www.youtube.com/@refly-ai">
<img src="https://img.shields.io/badge/YouTube-Refly%20AI-FF0000?style=flat&colorA=080f12&colorB=FF0000&logo=youtube&logoColor=white" alt="YouTube">
</a>
<a href="https://discord.com/invite/YVuYFjFvRC">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2FYVuYFjFvRC%3Fwith_counts%3Dtrue&query=%24.approximate_member_count&suffix=%20members&logo=discord&logoColor=white&label=%20&color=7389D8&labelColor=6A7EC2" alt="Discord">
</a>
<a href="https://github.com/refly-ai/refly-skills">
<img src="https://img.shields.io/badge/refly--skills-Repo-2ea043?style=flat&colorA=080f12&logo=github" alt="Refly Skills">
</a><br>
<a href="https://docs.refly.ai/">
<img src="https://img.shields.io/badge/docs.refly.ai-Docs-2ea043?style=flat&colorA=080f12&logo=readthedocs" alt="Docs">
</a>
<a href="https://deepwiki.com/refly-ai/refly">
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
</a>
<a href="LICENSE">
<img src="https://img.shields.io/badge/license-ReflyAI%20License-2ea043?style=flat&colorA=080f12" alt="License">
</a>
<a href="https://x.com/reflyai">
<img src="https://img.shields.io/badge/%40reflyai-black?style=flat&logo=x&labelColor=%23101419&color=%232d2e30" alt="X (formerly Twitter) Follow">
</a>
</p>


[Lovable 用 API](#use-case-1-api-integration) · [Lark/Feishu 用 Webhook](#use-case-2-webhook-for-lark-feishu) · [Claude Code 用 Skills](#use-case-3-skills-for-claude-code) · [Clawdbot の構築](#use-case-4-build-clawdbot)

Skills はプロンプトではありません。持続可能なインフラストラクチャです。

Refly は、安定したアトミックでバージョン管理されたエージェントスキルを構築するための初のオープンソースプラットフォームです。

<a href="https://refly.ai/workspace"><u>今すぐ試す</u></a>

---
## Refly Skills

Refly Skills は、Refly の公式実行可能スキルレジストリです。

- ⚡ **即時実行**: Refly でワンクリックでスキルを実行
- 🧩 **再利用可能なインフラストラクチャ**: バージョン管理されたスキルで、使い捨てのプロンプトではありません
- 🔌 **どこにでもエクスポート**: Claude Code にスキルを配布したり、API としてデプロイしたり
- 🌍 **コミュニティ主導**: 独自のスキルをインポート、フォーク、公開

レジストリを探索: <a href="https://github.com/refly-ai/refly-skills"><u>Refly Skills Repo</u></a>

Skills は、ワークフロー、チーム、ランタイム間で再利用できる決定論的なエージェント機能です。

**TL;DR**: Refly は、エンタープライズ SOP を実行可能なエージェントスキルにコンパイルします。3分で構築。どこにでも配布。

---
## クイックスタート

### Refly のデプロイ

- 📘 **[セルフデプロイガイド](https://docs.refly.ai/community-version/self-deploy/)**
*(開発者向け)* Docker を使用して独自のサーバーに Refly をデプロイするためのステップバイステップガイド。

- 🔌 **[API リファレンス](https://github.com/refly-ai/refly/tree/main/docs/en/guide/api)**
アプリケーションに Refly を統合するための完全な API ドキュメント。

> [!TIP]
> すぐに試したいですか?ホストワークスペースを開いてください: <a href="https://refly.ai/workspace"><u>Refly Workspace</u></a>

### 次は何をしますか?

デプロイ後、ユースケースに応じてパスを選択してください:

| やりたいこと | ここから始める | 時間 |
|-------------|-----------|------|
| 🔧 **最初のワークフローを構築する** | [ワークフローの作成](#create-your-first-workflow) | 5分 |
| 🔌 **API 経由でワークフローを呼び出す** | [API 統合](#use-case-1-api-integration) | 10分 |
| 💬 **Lark/Feishu に接続する** | [Webhook セットアップ](#use-case-2-webhook-for-lark-feishu) | 15分 |
| 🤖 **Claude Code 用にエクスポート** | [Skills のエクスポート](#use-case-3-skills-for-claude-code) | 15分 |
| 🦞 **Clawdbot を構築する** | [Clawdbot の構築](#use-case-4-build-clawdbot) | 20分 |

---

## なぜ Refly なのか?

多くの AI エージェントは、"Vibe-coded" スクリプトと壊れやすいブラックボックスロジックに依存しているため、本番環境で失敗します。Claude Code、AutoGen、MCP などのエージェンティックフレームワークにエコシステムが移行するにつれて、ボトルネックは LLM ではなく、標準化された信頼性の高いアクションの欠如になりました。

Refly は、生の API とインテリジェントエージェント間のギャップを埋めます。煩雑なビジネスロジックを構造化されバージョン管理されたエージェントスキルに体系化し、どのエージェントでも 100% の信頼性で呼び出すことができます。

**ツールのハードコーディングをやめましょう。** Refly のビジュアル IDE でモジュール式のスキルを一度構築し、MCP サーバー、標準 API、またはポータブル SDK として任意のエージェントフレームワークにデプロイします。<a href="https://refly.ai/workspace"><u>今すぐ試す</u></a>

---

## コア機能

### 🎯 Vibe で構築 (Copilot-led Builder)

自然言語でビジネスロジックを記述すると、Refly の Model-Native DSL が意図を高性能なスキルにコンパイルします。

- **意図主導の構築**: 作業を一度記述するだけで、Refly が決定論的で再利用可能で合成可能なスキルに変換します。
- **スケールでの効率**: LLM 向けに最適化された効率的な DSLにより、高速な実行と大幅に低いトークンコストを実現。
- **3分でのデプロイ**: 静的なエンタープライズ SOP から、3分未満で本番対応のエージェントスキルに移行。

### ⚡ 制御付きで実行 (Intervenable Runtime)

決定論的な信頼性を目的としたステートフルランタイムで、AI 実行の「ブラックボックス」を打破します。

- **介入可能なランタイム**: 一時停止、監査、実行中のエージェントロジックの再誘導により、100% の運用コンプライアンスを確保。
- **決定論的保証**: 幻覚を最小限に抑え、障害回復を処理する厳格なビジネスルールを強制。

### 🚀 本番に配布 (Unified Agent Stack)

MCP 統合、ツール、モデル、再利用可能なスキルを単一の実行レイヤーに統一します。

- **ユニバーサル配布**: Lovable 用 API、Slack または Lark/Feishu 用 Webhook、Claude Code および Cursor 用ネイティブツールとしてエクスポート。
- **安定したスケジューリング**: 管理された実行で、ワークフローを確実にスケジュール実行。

### 🏛️ アセットとして管理 (Skill Registry)

壊れやすいスクリプトを、組織全体で管理された共有インフラストラクチャに変換します。

- **セントラルスキルレジストリ**: エージェント機能を安全に管理、バージョン管理、共有。
- **チームワークスペースコラボレーション**: ネイティブバージョン管理と監査ログで共同構築。

---

## コミュニティ

- 🌟 [GitHub でスター](https://github.com/refly-ai/refly): 私たちが構築し続けるのを助けてください!
- 💬 [Discord](https://discord.com/invite/YVuYFjFvRC): コミュニティに参加
- 🐦 [Twitter](https://x.com/reflyai): 更新情報をフォロー
- 📖 [Documentation](https://docs.refly.ai): 完全なガイドとチュートリアル
- 🐛 [Issues](https://github.com/refly-ai/refly/issues): バグを報告したり機能をリクエスト

コミュニティに参加して、ヘルプを得たり、経験を共有したり、他の Refly ユーザーとつながったりしましょう: [Refly Community](https://docs.refly.ai/community/contact-us)

コードを貢献したい方は、[Contribution Guide](./CONTRIBUTING.md) をご覧ください。同時に、ソーシャルメディアやイベント、カンファレンスで Refly を共有することをご検討ください。

---

## スター履歴

[![Star History Chart](https://api.star-history.com/svg?repos=refly-ai/refly&type=Date)](https://star-history.com/#refly-ai/refly&Date)

---

このリポジトリは [ReflyAI Open Source License](./LICENSE) の下でライセンスされており、基本的に Apache 2.0 License にいくつかの追加制限があります。 No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

README language policy violation (English-only docs).

This new README file is entirely non-English, which conflicts with the repository’s documentation rule for README files. Please move this translation to a location not covered by the README rule (or keep canonical README docs in English only).

As per coding guidelines, "**/{README,API,CONTRIBUTING,CHANGELOG}*.{md,txt}: All documentation (READMEs, API docs, inline documentation) MUST be in English".

🧰 Tools
🪛 LanguageTool

[uncategorized] ~95-~95: 「ことができる」という表現は冗長な可能性があります。
Context: ...れたエージェントスキルに体系化し、どのエージェントでも 100% の信頼性で呼び出すことができます。 ツールのハードコーディングをやめましょう。 Refly の...

(DOUSI_KOTOGADEKIRU)

🪛 markdownlint-cli2 (0.21.0)

[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 83-83: Link fragments should be valid

(MD051, link-fragments)


[warning] 84-84: Link fragments should be valid

(MD051, link-fragments)


[warning] 85-85: Link fragments should be valid

(MD051, link-fragments)


[warning] 86-86: Link fragments should be valid

(MD051, link-fragments)


[warning] 87-87: Link fragments should be valid

(MD051, link-fragments)

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

In `@README_JA.md` around lines 4 - 154, The PR added a non-English repository
README (README_JA.md) which violates the English-only README policy; fix by
removing or relocating README_JA.md out of the canonical README set, keep the
repository's canonical README (README.md) in English, and add the Japanese
translation into a translations/docs folder or a locale-specific path (e.g.,
docs/translations/ja/README.md) while adding a clear link from README.md to that
translation; ensure file names matching README* are English-only and update any
references to README_JA.md in the repo to point to the new translation location.

README_JA.md Outdated
</p>


[Lovable 用 API](#use-case-1-api-integration) · [Lark/Feishu 用 Webhook](#use-case-2-webhook-for-lark-feishu) · [Claude Code 用 Skills](#use-case-3-skills-for-claude-code) · [Clawdbot の構築](#use-case-4-build-clawdbot)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Broken internal anchors in TOC and quick-start table.

These fragment links point to headings that do not exist in README_JA.md, so navigation is broken.

Suggested fix (point to existing English anchors until JA sections exist)
-[Lovable 用 API](`#use-case-1-api-integration`) · [Lark/Feishu 用 Webhook](`#use-case-2-webhook-for-lark-feishu`) · [Claude Code 用 Skills](`#use-case-3-skills-for-claude-code`) · [Clawdbot の構築](`#use-case-4-build-clawdbot`)
+[Lovable 用 API](README.md#use-case-1-api-integration) · [Lark/Feishu 用 Webhook](README.md#use-case-2-webhook-for-lark-feishu) · [Claude Code 用 Skills](README.md#use-case-3-skills-for-claude-code) · [Clawdbot の構築](README.md#use-case-4-build-clawdbot)
@@
-| 🔧 **最初のワークフローを構築する** | [ワークフローの作成](`#create-your-first-workflow`) | 5分 |
-| 🔌 **API 経由でワークフローを呼び出す** | [API 統合](`#use-case-1-api-integration`) | 10分 |
-| 💬 **Lark/Feishu に接続する** | [Webhook セットアップ](`#use-case-2-webhook-for-lark-feishu`) | 15分 |
-| 🤖 **Claude Code 用にエクスポート** | [Skills のエクスポート](`#use-case-3-skills-for-claude-code`) | 15分 |
-| 🦞  **Clawdbot を構築する** | [Clawdbot の構築](`#use-case-4-build-clawdbot`) | 20分 |
+| 🔧 **最初のワークフローを構築する** | [ワークフローの作成](README.md#create-your-first-workflow) | 5分 |
+| 🔌 **API 経由でワークフローを呼び出す** | [API 統合](README.md#use-case-1-api-integration) | 10分 |
+| 💬 **Lark/Feishu に接続する** | [Webhook セットアップ](README.md#use-case-2-webhook-for-lark-feishu) | 15分 |
+| 🤖 **Claude Code 用にエクスポート** | [Skills のエクスポート](README.md#use-case-3-skills-for-claude-code) | 15分 |
+| 🦞  **Clawdbot を構築する** | [Clawdbot の構築](README.md#use-case-4-build-clawdbot) | 20分 |

Also applies to: 83-87

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)


[warning] 39-39: Link fragments should be valid

(MD051, link-fragments)

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

In `@README_JA.md` at line 39, The TOC and quick-start table in README_JA.md
contain fragment links that don't match any headings in this file; update the
broken anchors (the four links shown: "Lovable 用 API", "Lark/Feishu 用 Webhook",
"Claude Code 用 Skills", "Clawdbot の構築") in the TOC (current line ~39) and the
quick-start rows (lines ~83-87) to point to the existing English anchors used in
the main README (e.g. use-case-1-api-integration,
use-case-2-webhook-for-lark-feishu, use-case-3-skills-for-claude-code,
use-case-4-build-clawdbot) so the links work until the corresponding Japanese
headings are added.

README_JA.md Outdated

Refly は、安定したアトミックでバージョン管理されたエージェントスキルを構築するための初のオープンソースプラットフォームです。

<a href="https://refly.ai/workspace"><u>今すぐ試す</u></a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove trailing whitespace on changed lines.

These lines end with trailing spaces.

As per coding guidelines, "**/*.{js,ts,jsx,tsx,css,json,yml,yaml,md}: No trailing whitespace at the end of lines".

Also applies to: 57-57, 75-75, 97-97

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

In `@README_JA.md` at line 45, Remove the trailing whitespace characters at the
ends of the changed lines in README_JA.md (specifically the line containing the
anchor "<a href=\"https://refly.ai/workspace\"><u>今すぐ試す</u></a>" and the other
affected lines around 57, 75, 97); open README_JA.md, locate those anchor/text
lines and strip the trailing spaces so each line ends immediately after the last
visible character, then save the file ensuring no trailing whitespace remains in
the modified lines.

README.md Outdated
# Refly — Agent Skills Builder Powered by Vibe Workflow
<p align="right">
<a href="README.md"><u>English</u></a> · <a href="README_CN.md"><u>中文</u></a>
<a href="README.md"><u>English</u></a> · <a href="README_CN.md"><u>中文</u></a> · <a href="README_JA.md"><u>日本語</u></a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not add localized README link in canonical English README under current policy.

Adding the Japanese README entry here conflicts with the repository’s English-only README rule. Keep the language switcher English-only (or remove localized README links entirely).

As per coding guidelines, "**/{README,API,CONTRIBUTING,CHANGELOG}*.{md,txt}: All documentation (READMEs, API docs, inline documentation) MUST be in English".

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

In `@README.md` at line 7, The README.md currently includes a localized link
anchor "<a href="README_JA.md">日本語</a>", which violates the English-only
documentation policy; remove that localized link (or remove all localized README
links) from README.md so the language switcher remains English-only, ensuring
only the canonical English README file and English links remain in the file.

Replace hardcoded webhook base URL with configurable value from ConfigService.

Changes:
- Add ConfigService dependency to WebhookService
- Update generateWebhookUrl() to read WEBHOOK_BASE_URL from config
- Maintain backward compatibility with default value 'https://api.refly.ai'

This allows users to configure custom webhook endpoints via environment
variables, supporting self-hosted and enterprise deployments.

Closes TODO: Get base URL from config
@reckless129 reckless129 force-pushed the feat/add-japanese-readme branch from dfa8fbe to 3b2f4b7 Compare February 26, 2026 11:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/api/src/modules/webhook/webhook.service.ts (1)

810-813: Use nullish coalescing (??) instead of logical OR (||).

Per coding guidelines, use ?? for potentially undefined values. This ensures only null/undefined trigger the fallback, making misconfigurations (like an empty string) more visible.

♻️ Proposed fix
 private generateWebhookUrl(webhookId: string): string {
-  const baseUrl = this.config.get<string>('WEBHOOK_BASE_URL') || 'https://api.refly.ai';
+  const baseUrl = this.config.get<string>('WEBHOOK_BASE_URL') ?? 'https://api.refly.ai';
   return `${baseUrl}/v1/openapi/webhook/${webhookId}/run`;
 }

As per coding guidelines: "Always use nullish coalescing (??) or default values for potentially undefined values in TypeScript/JavaScript"

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

In `@apps/api/src/modules/webhook/webhook.service.ts` around lines 810 - 813, The
generateWebhookUrl function uses logical OR to default the WEBHOOK_BASE_URL
which treats empty string as a fallback; change the fallback logic in
generateWebhookUrl to use nullish coalescing
(this.config.get<string>('WEBHOOK_BASE_URL') ?? 'https://api.refly.ai') so only
null/undefined trigger the default and misconfigurations like empty string
remain visible; update the return expression in generateWebhookUrl that builds
the URL accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/modules/webhook/webhook.service.ts`:
- Line 2: The WebhookModule is not importing ConfigModule even though
WebhookService injects ConfigService; update the WebhookModule (class
WebhookModule in webhook.module.ts) to add ConfigModule to its imports array so
Nest can provide ConfigService at runtime—specifically include ConfigModule
alongside CommonModule, forwardRef(() => AuthModule), forwardRef(() =>
WorkflowModule), WorkflowAppModule, and CanvasModule in the `@Module`({ imports:
[...] }) declaration.

---

Nitpick comments:
In `@apps/api/src/modules/webhook/webhook.service.ts`:
- Around line 810-813: The generateWebhookUrl function uses logical OR to
default the WEBHOOK_BASE_URL which treats empty string as a fallback; change the
fallback logic in generateWebhookUrl to use nullish coalescing
(this.config.get<string>('WEBHOOK_BASE_URL') ?? 'https://api.refly.ai') so only
null/undefined trigger the default and misconfigurations like empty string
remain visible; update the return expression in generateWebhookUrl that builds
the URL accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfa8fbe and 3b2f4b7.

📒 Files selected for processing (1)
  • apps/api/src/modules/webhook/webhook.service.ts

@@ -1,4 +1,5 @@
import { Injectable, Logger, NotFoundException, ForbiddenException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the webhook.service.ts file for the import and ConfigService usage
echo "=== webhook.service.ts content (lines 1-70) ===" 
sed -n '1,70p' apps/api/src/modules/webhook/webhook.service.ts | cat -n

echo -e "\n=== webhook.service.ts content (lines 805-815) ===" 
sed -n '805,815p' apps/api/src/modules/webhook/webhook.service.ts | cat -n

Repository: refly-ai/refly

Length of output: 3115


🏁 Script executed:

#!/bin/bash
# Check if ConfigModule is imported in webhook.module.ts
echo "=== webhook.module.ts content ===" 
cat apps/api/src/modules/webhook/webhook.module.ts | cat -n

Repository: refly-ai/refly

Length of output: 1751


Add ConfigModule to webhook.module.ts imports

ConfigService is properly injected in the service, but the module doesn't import ConfigModule, which is required to provide ConfigService to WebhookService. Without it, dependency injection will fail at runtime.

Update apps/api/src/modules/webhook/webhook.module.ts to import ConfigModule:

Required fix
import { ConfigModule } from '@nestjs/config';

`@Module`({
  imports: [
    ConfigModule,
    CommonModule,
    forwardRef(() => AuthModule),
    forwardRef(() => WorkflowModule),
    WorkflowAppModule,
    CanvasModule,
  ],
  // ...
})
export class WebhookModule {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/modules/webhook/webhook.service.ts` at line 2, The WebhookModule
is not importing ConfigModule even though WebhookService injects ConfigService;
update the WebhookModule (class WebhookModule in webhook.module.ts) to add
ConfigModule to its imports array so Nest can provide ConfigService at
runtime—specifically include ConfigModule alongside CommonModule, forwardRef(()
=> AuthModule), forwardRef(() => WorkflowModule), WorkflowAppModule, and
CanvasModule in the `@Module`({ imports: [...] }) declaration.

Implements a CachedWebSearcher decorator that adds Redis caching capabilities
to any WebSearcher implementation using the Cache-Aside pattern.

Key Features:
- Automatic cache key generation with request normalization
- Cache stampede protection using distributed Redis locks
- Configurable TTL, max results, and empty result caching
- Graceful fallback to origin searcher on cache errors
- Cache statistics and invalidation APIs
- Comprehensive unit tests with 95%+ coverage

Technical Details:
- SHA256-based cache key generation for consistency
- Case-insensitive query normalization
- Batch query sorting for deterministic keys
- Redis SET NX for atomic lock acquisition
- Lua script for safe lock release (check-and-delete)

Performance Impact:
- Reduces external API calls by ~60-80% for repeated queries
- Sub-millisecond response for cache hits
- Automatic cache warming through usage patterns

Example Usage:
  const searcher = new CachedWebSearcher(
    new SearXNGWebSearcher(config),
    redisService,
    { ttl: 3600, maxCachedResults: 50 }
  );

Closes: Performance optimization gap in web search module
Related: Web search cost reduction initiative
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
apps/api/src/utils/web-search/cached-searcher.spec.ts (1)

13-19: Model TTL in MockRedisService so cache-expiry behavior is testable.

setex currently ignores seconds, and get never expires keys. This can hide TTL regressions in
CachedWebSearcher.

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

In `@apps/api/src/utils/web-search/cached-searcher.spec.ts` around lines 13 - 19,
MockRedisService's get and setex don't model TTL: setex ignores the seconds
parameter and get never expires keys, hiding TTL regressions in
CachedWebSearcher; update MockRedisService (methods setex and get) to store
expiry timestamps alongside values in the internal store (e.g., store map keyed
by key -> { value, expiresAt }), have setex(key, seconds, value) compute and
save expiresAt = now + seconds, and have get(key) check expiresAt and return
null (and optionally delete the entry) when expired so tests can exercise cache
expiry correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/utils/web-search/cached-searcher.spec.ts`:
- Around line 111-112: Make MockRedisService explicitly implement the
RedisService interface so instances can be passed to CachedWebSearcher without
casting; update the MockRedisService class signature to implement RedisService
and ensure methods get, setex, del, set, eval, scan match the interface types
(prefer unknown over any in signatures). Add a public accessor like getStore():
Record<string,string> to expose the internal store for tests so assertions can
call mockRedis.getStore() without using casts. Then remove the remaining "as
any" casts where mockRedis is passed to CachedWebSearcher and where store is
accessed in the tests.

In `@apps/api/src/utils/web-search/cached-searcher.ts`:
- Around line 281-297: getCacheStats is counting all keys matching
`${this.cacheConfig.keyPrefix}:*` including lock keys; update getCacheStats to
filter out keys whose names end with ':lock' before computing totalKeys by using
this.redisService.scan (same loop) but after collecting result[1] apply a filter
like excluding entries that endWith(':lock') so the returned totalKeys reflects
only real cache entries; keep returning the same pattern string and otherwise
preserve the existing scan/loop logic in getCacheStats.
- Around line 94-99: The current search method uses unbounded recursion when
lockAcquired is false (cacheKey branch), which risks deep retry chains; change
this to a bounded retry loop: add constants like maxRetries (e.g., 5), baseDelay
and jitter, and replace the recursive return this.search(req) with a for/while
loop that sleeps via this.sleep(delay + jitter) between attempts and increments
attempt count, re-checks the cache/lock each iteration, and if all attempts fail
falls back to a direct origin fetch (call the existing method that fetches &
caches the fresh result) and returns that result; update references in the
search method to use cacheKey, lockAcquired, sleep and the origin-fetch method
instead of recursive calls.
- Around line 140-160: The normalizeRequest method currently calls
q?.toLowerCase().trim() which can call .trim() on undefined and also omits gl
and location in the cache key; update normalizeRequest to safely handle missing
q by checking presence before calling toLowerCase() and trim (e.g., produce
undefined when q is falsy) and include the request's gl and location properties
in both the batch (inside each mapped query and top-level) and single branches
so locale/region are part of the normalized cache key; refer to the
normalizeRequest function and the mapped query creation (queries.map(...)) and
single-request return to add these fixes.

---

Nitpick comments:
In `@apps/api/src/utils/web-search/cached-searcher.spec.ts`:
- Around line 13-19: MockRedisService's get and setex don't model TTL: setex
ignores the seconds parameter and get never expires keys, hiding TTL regressions
in CachedWebSearcher; update MockRedisService (methods setex and get) to store
expiry timestamps alongside values in the internal store (e.g., store map keyed
by key -> { value, expiresAt }), have setex(key, seconds, value) compute and
save expiresAt = now + seconds, and have get(key) check expiresAt and return
null (and optionally delete the entry) when expired so tests can exercise cache
expiry correctly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2f4b7 and 11b80e6.

📒 Files selected for processing (3)
  • apps/api/src/utils/web-search/cached-searcher.spec.ts
  • apps/api/src/utils/web-search/cached-searcher.ts
  • apps/api/src/utils/web-search/index.ts

Comment on lines +111 to +112
cachedSearcher = new CachedWebSearcher(mockSearcher, mockRedis as any, {
ttl: 60,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/api/src/utils/web-search/cached-searcher.spec.ts | head -220 | tail -120

Repository: refly-ai/refly

Length of output: 4816


🏁 Script executed:

# Also check total file size
wc -l apps/api/src/utils/web-search/cached-searcher.spec.ts

Repository: refly-ai/refly

Length of output: 115


🏁 Script executed:

# Search for all `as any` in the file with context
rg -n -B2 -A2 '\bas any\b' apps/api/src/utils/web-search/cached-searcher.spec.ts

Repository: refly-ai/refly

Length of output: 1039


🏁 Script executed:

# Find MockRedisService definition
rg -n 'class MockRedisService' apps/api/src/utils/web-search/cached-searcher.spec.ts -A 20

Repository: refly-ai/refly

Length of output: 589


🏁 Script executed:

# Find CachedWebSearcher constructor to see what it expects
fd -e ts cached-searcher.ts | head -5

Repository: refly-ai/refly

Length of output: 106


🏁 Script executed:

# Check the interface/type expected by CachedWebSearcher
rg -n 'constructor' apps/api/src/utils/web-search/cached-searcher.ts -A 5

Repository: refly-ai/refly

Length of output: 276


🏁 Script executed:

# Find RedisService interface/type definition
rg -n 'interface RedisService|type RedisService|class RedisService' --type=ts

Repository: refly-ai/refly

Length of output: 174


🏁 Script executed:

# Get more context around MockRedisService to see its full interface
rg -n 'class MockRedisService' apps/api/src/utils/web-search/cached-searcher.spec.ts -A 40

Repository: refly-ai/refly

Length of output: 1079


🏁 Script executed:

# Check RedisService class definition and what methods it has
cat -n apps/api/src/modules/common/redis.service.ts | head -100

Repository: refly-ai/refly

Length of output: 4083


🏁 Script executed:

# Check if MockRedisService implements RedisService or has public store property
rg -n 'store|implements|extends' apps/api/src/utils/web-search/cached-searcher.spec.ts | head -20

Repository: refly-ai/refly

Length of output: 754


🏁 Script executed:

# Check if there's a public interface or base class for RedisService
rg -n 'interface.*Redis|export.*Redis' apps/api/src/modules/common/redis.service.ts -B2 -A5

Repository: refly-ai/refly

Length of output: 414


🏁 Script executed:

# Look at the broader structure of RedisService and its public methods
sed -n '27,200p' apps/api/src/modules/common/redis.service.ts

Repository: refly-ai/refly

Length of output: 5465


🏁 Script executed:

# Check what interface CachedWebSearcher expects for redisService parameter
sed -n '48,55p' apps/api/src/utils/web-search/cached-searcher.ts

Repository: refly-ai/refly

Length of output: 288


🏁 Script executed:

# Look for any interface definition in the cached-searcher or related files
rg -n 'interface.*Redis|type.*Redis' apps/api/src/utils/web-search/ --type=ts

Repository: refly-ai/refly

Length of output: 40


🏁 Script executed:

# Check if MockRedisService is explicitly implementing any interface
sed -n '10,75p' apps/api/src/utils/web-search/cached-searcher.spec.ts

Repository: refly-ai/refly

Length of output: 1403


Make MockRedisService implement RedisService interface to eliminate constructor casts.

Lines 111 and 191 cast mockRedis as any when passing to CachedWebSearcher, which expects RedisService. Since MockRedisService provides all the required methods (get, setex, del, set, eval, scan), it should directly satisfy the type contract without casting.

For lines 200, 202, and 214 that access store for assertions, expose a public method on MockRedisService (e.g., getStore()) to avoid needing as any casts to access internals.

Per coding guidelines: "Avoid using any type whenever possible - use unknown type instead with proper type guards."

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

In `@apps/api/src/utils/web-search/cached-searcher.spec.ts` around lines 111 -
112, Make MockRedisService explicitly implement the RedisService interface so
instances can be passed to CachedWebSearcher without casting; update the
MockRedisService class signature to implement RedisService and ensure methods
get, setex, del, set, eval, scan match the interface types (prefer unknown over
any in signatures). Add a public accessor like getStore(): Record<string,string>
to expose the internal store for tests so assertions can call
mockRedis.getStore() without using casts. Then remove the remaining "as any"
casts where mockRedis is passed to CachedWebSearcher and where store is accessed
in the tests.

Comment on lines +94 to +99
if (!lockAcquired) {
// Another process is fetching, wait and retry from cache
this.logger.debug(`Cache stampede detected for key: ${cacheKey}, waiting...`);
await this.sleep(100); // Wait 100ms
return this.search(req); // Retry
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid unbounded recursive retries during lock contention.

Line 98 recursively calls search with no retry cap. Under persistent contention, this can create
long retry chains and unstable latency. Prefer a bounded loop with backoff/jitter and a final
origin fallback.

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

In `@apps/api/src/utils/web-search/cached-searcher.ts` around lines 94 - 99, The
current search method uses unbounded recursion when lockAcquired is false
(cacheKey branch), which risks deep retry chains; change this to a bounded retry
loop: add constants like maxRetries (e.g., 5), baseDelay and jitter, and replace
the recursive return this.search(req) with a for/while loop that sleeps via
this.sleep(delay + jitter) between attempts and increments attempt count,
re-checks the cache/lock each iteration, and if all attempts fail falls back to
a direct origin fetch (call the existing method that fetches & caches the fresh
result) and returns that result; update references in the search method to use
cacheKey, lockAcquired, sleep and the origin-fetch method instead of recursive
calls.

Comment on lines +140 to +160
private normalizeRequest(
req: WebSearchRequest | BatchWebSearchRequest,
): Record<string, unknown> {
if ('queries' in req && Array.isArray(req.queries)) {
// Batch request - sort queries for consistency
return {
type: 'batch',
queries: req.queries
.map((q) => ({ q: q.q?.toLowerCase().trim(), hl: q.hl, limit: q.limit }))
.sort((a, b) => (a.q || '').localeCompare(b.q || '')),
limit: req.limit,
};
}

// Single request
return {
type: 'single',
q: (req as WebSearchRequest).q?.toLowerCase().trim(),
hl: (req as WebSearchRequest).hl,
limit: (req as WebSearchRequest).limit,
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the file around lines 140-160
head -160 apps/api/src/utils/web-search/cached-searcher.ts | tail -30

Repository: refly-ai/refly

Length of output: 1102


🏁 Script executed:

# Also check the full file to understand the context and type imports
wc -l apps/api/src/utils/web-search/cached-searcher.ts

Repository: refly-ai/refly

Length of output: 110


🏁 Script executed:

# Find the type definitions for WebSearchRequest and BatchWebSearchRequest
rg -n "type WebSearchRequest|interface WebSearchRequest|type BatchWebSearchRequest|interface BatchWebSearchRequest" apps/api/src/utils/web-search/

Repository: refly-ai/refly

Length of output: 40


🏁 Script executed:

# Check imports in the file to find where types come from
head -50 apps/api/src/utils/web-search/cached-searcher.ts

Repository: refly-ai/refly

Length of output: 1702


🏁 Script executed:

# Search entire codebase for WebSearchRequest definition
rg -n "type WebSearchRequest|interface WebSearchRequest" --type=ts

Repository: refly-ai/refly

Length of output: 136


🏁 Script executed:

# Search for BatchWebSearchRequest definition
rg -n "type BatchWebSearchRequest|interface BatchWebSearchRequest" --type=ts

Repository: refly-ai/refly

Length of output: 141


🏁 Script executed:

# Read WebSearchRequest type definition
sed -n '5612,5634p' packages/openapi-schema/src/types.gen.ts

Repository: refly-ai/refly

Length of output: 356


🏁 Script executed:

# Read BatchWebSearchRequest type definition
sed -n '5635,5660p' packages/openapi-schema/src/types.gen.ts

Repository: refly-ai/refly

Length of output: 502


🏁 Script executed:

# Now let's verify the optional chaining semantics issue
# Create a small TypeScript test to confirm the behavior
cat > /tmp/test_optional_chain.ts << 'EOF'
// Test TypeScript optional chaining semantics
const q: string | undefined = undefined;

// This will show what the type inference is
const result1 = q?.toLowerCase();  // type: string | undefined
const result2 = q?.toLowerCase().trim();  // ERROR or runtime error?

// Correct usage
const result3 = q?.toLowerCase()?.trim();  // type: string | undefined
const result4 = q?.toLowerCase()?.trim() ?? '';  // type: string
EOF
cat /tmp/test_optional_chain.ts

Repository: refly-ai/refly

Length of output: 477


🏁 Script executed:

# Check if there are any other usages of this pattern in the codebase
rg -n '\?\.[a-zA-Z_][a-zA-Z0-9_]*\(\)\.(' apps/api/src/

Repository: refly-ai/refly

Length of output: 174


🏁 Script executed:

# Let's also verify if the code has been recently changed or if this is an existing issue
git log --oneline -n 20 -- apps/api/src/utils/web-search/cached-searcher.ts 2>/dev/null || echo "No git history available"

Repository: refly-ai/refly

Length of output: 132


🏁 Script executed:

# Check if TypeScript strict mode is enabled
find . -name "tsconfig.json" -type f | head -5 | xargs grep -l "strictNullChecks"

Repository: refly-ai/refly

Length of output: 82


🏁 Script executed:

# Let's also check the exact pattern in context - read more lines to see full method
sed -n '140,165p' apps/api/src/utils/web-search/cached-searcher.ts

Repository: refly-ai/refly

Length of output: 855


🏁 Script executed:

# Check tsconfig.json for strictNullChecks setting
cat apps/api/tsconfig.json

Repository: refly-ai/refly

Length of output: 1476


🏁 Script executed:

# Verify there are no other instances of this unsafe pattern in this file
grep -n "\.toLowerCase()\.trim()" apps/api/src/utils/web-search/cached-searcher.ts

Repository: refly-ai/refly

Length of output: 209


🏁 Script executed:

# Check if these are the only two instances (batch and single)
grep -n "toLowerCase().trim()" apps/api/src/utils/web-search/cached-searcher.ts | wc -l

Repository: refly-ai/refly

Length of output: 59


Fix unsafe optional chaining and add missing cache key fields.

Lines 148 and 157 have a runtime bug: q?.toLowerCase().trim() calls .trim() on undefined when q is not provided. Additionally, the cache key normalization omits gl and location fields, allowing requests with the same query but different locales/regions to incorrectly share cached results.

Proposed fix
   private normalizeRequest(
     req: WebSearchRequest | BatchWebSearchRequest,
   ): Record<string, unknown> {
     if ('queries' in req && Array.isArray(req.queries)) {
       // Batch request - sort queries for consistency
       return {
         type: 'batch',
         queries: req.queries
-          .map((q) => ({ q: q.q?.toLowerCase().trim(), hl: q.hl, limit: q.limit }))
+          .map((q) => ({
+            q: q.q?.toLowerCase()?.trim() ?? '',
+            hl: q.hl,
+            gl: q.gl,
+            location: q.location,
+            limit: q.limit,
+          }))
           .sort((a, b) => (a.q || '').localeCompare(b.q || '')),
         limit: req.limit,
       };
     }
     
     // Single request
+    const singleReq = req as WebSearchRequest;
     return {
       type: 'single',
-      q: (req as WebSearchRequest).q?.toLowerCase().trim(),
-      hl: (req as WebSearchRequest).hl,
-      limit: (req as WebSearchRequest).limit,
+      q: singleReq.q?.toLowerCase()?.trim() ?? '',
+      hl: singleReq.hl,
+      gl: singleReq.gl,
+      location: singleReq.location,
+      limit: singleReq.limit,
     };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/utils/web-search/cached-searcher.ts` around lines 140 - 160, The
normalizeRequest method currently calls q?.toLowerCase().trim() which can call
.trim() on undefined and also omits gl and location in the cache key; update
normalizeRequest to safely handle missing q by checking presence before calling
toLowerCase() and trim (e.g., produce undefined when q is falsy) and include the
request's gl and location properties in both the batch (inside each mapped query
and top-level) and single branches so locale/region are part of the normalized
cache key; refer to the normalizeRequest function and the mapped query creation
(queries.map(...)) and single-request return to add these fixes.

Comment on lines +281 to +297
async getCacheStats(): Promise<{ totalKeys: number; pattern: string }> {
try {
const pattern = `${this.cacheConfig.keyPrefix}:*`;
// Note: This uses Redis SCAN, may not be 100% accurate for large datasets
const keys: string[] = [];
let cursor = '0';

do {
const result = await this.redisService.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
cursor = result[0];
keys.push(...result[1]);
} while (cursor !== '0');

return {
totalKeys: keys.length,
pattern,
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Filter lock keys from cache stats totals.

Line 283’s pattern includes :lock keys, so totalKeys can over-report query cache entries.
Consider excluding keys ending with :lock before counting.

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

In `@apps/api/src/utils/web-search/cached-searcher.ts` around lines 281 - 297,
getCacheStats is counting all keys matching `${this.cacheConfig.keyPrefix}:*`
including lock keys; update getCacheStats to filter out keys whose names end
with ':lock' before computing totalKeys by using this.redisService.scan (same
loop) but after collecting result[1] apply a filter like excluding entries that
endWith(':lock') so the returned totalKeys reflects only real cache entries;
keep returning the same pattern string and otherwise preserve the existing
scan/loop logic in getCacheStats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant