Skip to content

feat: 向量语义搜索 — Cloudflare Vectorize 集成#228

Merged
AmintaCCCP merged 16 commits into
mainfrom
feature/vector-search
Jun 24, 2026
Merged

feat: 向量语义搜索 — Cloudflare Vectorize 集成#228
AmintaCCCP merged 16 commits into
mainfrom
feature/vector-search

Conversation

@AmintaCCCP

@AmintaCCCP AmintaCCCP commented Jun 24, 2026

Copy link
Copy Markdown
Owner

概述

引入基于 Cloudflare Vectorize 的向量语义搜索作为可选增强功能。用户可以用自然语言搜索星标仓库(如"能帮我管理 docker 容器的工具"),系统会理解语义意图而非仅做关键词匹配。

架构

前端 (Embedding 生成)  →  Cloudflare Worker (Vectorize 代理)  →  Vectorize (HNSW 向量数据库)
  • Embedding:前端调用用户配置的 API(支持 6 种渠道)
  • 存储/检索:Cloudflare Worker 作为纯代理,不持有任何 AI Key
  • 回退:向量搜索失败时自动回退到现有关键词搜索

新增文件

文件 说明
src/services/vectorSearchService.ts EmbeddingClient + VectorSearchService + indexAllRepos
src/components/settings/VectorSearchSettings.tsx 设置 UI(Embedding 配置 + Worker 配置 + 状态 + 索引管理 + 部署指南)
src/utils/repoUtils.ts isRepoCustomized 工具函数(消除 3 处重复)
cloudflare-worker/src/index.ts Worker TypeScript 源码
cloudflare-worker/worker.js Worker JS 版本(可直接粘贴到 Cloudflare Dashboard)
cloudflare-worker/wrangler.toml Worker 部署配置
cloudflare-worker/README.md 部署指南(CLI + Web UI 两种方式)

修改文件

文件 改动
src/types/index.ts 新增 EmbeddingConfig, VectorSearchConfig 等类型
src/store/useAppStore.ts 新增 embedding/vectorSearch state + actions + persist/migrate
src/components/SearchBar.tsx 插入向量搜索分支 + 提取 isRepoCustomized 消除重复
src/components/SettingsPanel.tsx 新增"向量搜索"tab
src/services/autoSync.ts 同步 embedding + vectorSearch 配置
src/services/backendAdapter.ts 新增 fetch/sync 方法
server/src/db/schema.ts 新增 embedding_configs, vector_search_configs 表
server/src/routes/configs.ts 新增路由 + 重构为 registerEncryptedConfigRoutes 工厂

Embedding 渠道支持

渠道 apiType 默认维度
OpenAI openai 1536
OpenAI 兼容端点 openai-compatible 1536
硅基流动 siliconflow 1024
Gemini gemini 768
Cohere cohere 1024
Ollama (本地) ollama 768

部署方式

支持两种 Cloudflare Worker 部署方式:

  1. Web UI:在 Cloudflare Dashboard 中创建 Worker,粘贴 worker.js
  2. CLI:使用 wrangler deploy

审计修复

  • ✅ Embedding bulk sync 添加 ALL_CONFIGS_SKIPPED 安全守卫
  • ✅ Embedding bulk sync 添加空 key 跳过逻辑
  • syncVectorSearchConfig 改用 fetchWithRetry(3 次重试)
  • handleRebuildIndex 使用表单状态而非 store 快照
  • indexAllRepos 添加向量数量校验
  • ✅ SearchBar 向量搜索 O(n×m) → O(n+m) Map 查找
  • ✅ 配置路由重构为工厂函数,消除 ~200 行重复代码
  • isRepoCustomized 提取为工具函数,消除 3 处重复

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a Vector Search settings tab for configuring embeddings and connecting to a Cloudflare Vectorize worker (connection tests, save, deploy guidance, rebuild with progress/abort, and status).
    • Enabled semantic vector search with similarity ranking and thresholding, reranking, and automatic fallback to existing search when vector results aren’t available.
    • Introduced backend-managed embedding configs and Vector Search worker configuration with encrypted credential storage.
    • Added an authenticated, CORS-enabled Vectorize Worker API (upsert/query/delete/cleanup/status).
  • Bug Fixes
    • Improved “customized/edited” detection and search flow to keep filters and result ordering consistent, including preventing text search from overwriting vector-ranked results.

- Add EmbeddingClient supporting 6 providers: OpenAI, OpenAI-compatible,
  SiliconFlow, Gemini, Cohere, Ollama
- Add VectorSearchService for Cloudflare Worker proxy communication
- Add VectorSearchSettings UI with embedding config, worker config,
  status display, index management, and deploy guide
- Add Cloudflare Worker (TS + JS) as pure Vectorize proxy
- Add embedding_configs and vector_search_configs DB tables
- Add CRUD routes for embedding and vector search configs
- Add autoSync integration for new config types
- Integrate vector search into SearchBar with auto-fallback
- Refactor config routes into shared factory (registerEncryptedConfigRoutes)
- Extract isRepoCustomized helper to eliminate 3x duplication

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

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds vector-search state, backend persistence and routes, a Cloudflare Worker proxy, client vector services, a settings tab, auto-sync wiring, and SearchBar vector-search integration.

Changes

Semantic Vector Search

Layer / File(s) Summary
Contracts and state
src/types/index.ts, src/store/useAppStore.ts, server/src/db/schema.ts
Adds embedding/vector-search types, extends app state and persistence, and creates tables for the new config records.
Backend config routes
server/src/routes/configs.ts
Adds encrypted config route helpers plus embedding and vector-search GET/PUT/bulk endpoints for the backend API.
Cloudflare Worker proxy
cloudflare-worker/src/index.ts, cloudflare-worker/worker.js, cloudflare-worker/wrangler.toml, cloudflare-worker/package.json, cloudflare-worker/tsconfig.json, cloudflare-worker/README.md
Adds the authenticated Vectorize worker, cleanup/status endpoints, and deployment/configuration docs and metadata.
Vector services and sync
src/services/vectorSearchService.ts, src/services/backendAdapter.ts, src/services/autoSync.ts
Adds embedding generation, worker API clients, batch indexing, backend adapter methods, and auto-sync propagation for embedding and vector-search config changes.
Vector search settings UI
src/components/settings/VectorSearchSettings.tsx, src/components/settings/index.ts, src/components/SettingsPanel.tsx
Adds the VectorSearchSettings tab, its UI, and the panel wiring needed to render it.
Repo edit detection and vector search
src/utils/repoUtils.ts, src/components/SearchBar.tsx
Adds a shared repository customization helper and uses it in SearchBar alongside the new vector-search query path.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐇 I hop by vectors, swift and bright,
Through settings tabs and worker light.
A token, query, cleanup, and more,
Now all the burrow’s paths explore.
Hooray — my carrots found their score!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Cloudflare Vectorize-powered vector semantic search.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/vector-search

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a vector semantic search feature powered by Cloudflare Vectorize, including a Cloudflare Worker proxy, database schema updates, backend API routes, and frontend settings and search integration. The code review feedback highlights a critical security vulnerability where empty authentication tokens could bypass verification in the Worker, and suggests high-value improvements to optimize embedding quality for Cohere and Gemini by distinguishing between document indexing and query tasks. It also recommends adding default API URL placeholders for these providers in the settings UI.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread cloudflare-worker/src/index.ts Outdated
Comment on lines +48 to +51
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-critical critical

如果 env.AUTH_TOKEN 未配置或为空字符串,当客户端发送空 Token(例如 Authorization: Bearer )或未发送 Authorization 头部时,token !== env.AUTH_TOKEN 的判断可能会被绕过(例如 "" !== ""false)。这会导致未授权的用户可以直接访问您的向量数据库。建议增加对 env.AUTH_TOKEN 是否为空的校验。

Suggested change
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!env.AUTH_TOKEN || token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}

Comment thread cloudflare-worker/worker.js Outdated
Comment on lines +33 to +36
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-critical critical

src/index.ts,如果 env.AUTH_TOKEN 未配置或为空字符串,当客户端发送空 Token 时,安全校验可能会被绕过。建议增加对 env.AUTH_TOKEN 是否为空的校验。

Suggested change
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!env.AUTH_TOKEN || token !== env.AUTH_TOKEN) {
return jsonResponse({ success: false, error: 'Unauthorized' }, 401);
}

Comment thread src/services/vectorSearchService.ts Outdated
Comment on lines +20 to +34
async embed(texts: string[]): Promise<number[][]> {
switch (this.config.apiType) {
case 'openai':
case 'openai-compatible':
case 'siliconflow':
return this.embedOpenAICompatible(texts);
case 'ollama':
return this.embedOllama(texts);
case 'gemini':
return this.embedGemini(texts);
case 'cohere':
return this.embedCohere(texts);
default:
throw new Error(`Unsupported embedding API type: ${this.config.apiType}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

对于 Cohere 和 Gemini 等 Embedding 模型,索引文档和查询问题时推荐使用不同的任务类型(Task Type / Input Type)。例如,Cohere 推荐在索引时使用 search_document,在查询时使用 search_query;Gemini 推荐使用 RETRIEVAL_DOCUMENTRETRIEVAL_QUERY。如果统一使用 search_document 进行查询,会显著降低搜索的相关性和准确度。建议在 embed 方法中增加 purpose 参数以区分用途。

Suggested change
async embed(texts: string[]): Promise<number[][]> {
switch (this.config.apiType) {
case 'openai':
case 'openai-compatible':
case 'siliconflow':
return this.embedOpenAICompatible(texts);
case 'ollama':
return this.embedOllama(texts);
case 'gemini':
return this.embedGemini(texts);
case 'cohere':
return this.embedCohere(texts);
default:
throw new Error(`Unsupported embedding API type: ${this.config.apiType}`);
}
async embed(texts: string[], purpose: 'document' | 'query' = 'document'): Promise<number[][]> {
switch (this.config.apiType) {
case 'openai':
case 'openai-compatible':
case 'siliconflow':
return this.embedOpenAICompatible(texts);
case 'ollama':
return this.embedOllama(texts);
case 'gemini':
return this.embedGemini(texts, purpose);
case 'cohere':
return this.embedCohere(texts, purpose);
default:
throw new Error("Unsupported embedding API type: " + this.config.apiType);
}
}

Comment thread src/services/vectorSearchService.ts Outdated
Comment on lines +144 to +158
private async embedCohere(texts: string[]): Promise<number[][]> {
const url = `${this.config.baseUrl.replace(/\/+$/, '')}/v1/embed`;

const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify({
model: this.config.model,
texts,
input_type: 'search_document',
}),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

配合 embed 方法的 purpose 参数,为 Cohere 的 embed 请求指定对应的 input_typesearch_querysearch_document),以避免查询时使用 search_document 导致搜索质量下降。

Suggested change
private async embedCohere(texts: string[]): Promise<number[][]> {
const url = `${this.config.baseUrl.replace(/\/+$/, '')}/v1/embed`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify({
model: this.config.model,
texts,
input_type: 'search_document',
}),
});
private async embedCohere(texts: string[], purpose: 'document' | 'query'): Promise<number[][]> {
const url = `${this.config.baseUrl.replace(/\/+$/, '')}/v1/embed`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.config.apiKey}`,
},
body: JSON.stringify({
model: this.config.model,
texts,
input_type: purpose === 'query' ? 'search_query' : 'search_document',
}),
});

Comment thread src/components/SearchBar.tsx Outdated
Comment on lines +514 to +515
// 1. 前端调用 Embedding API 生成查询向量
const queryVectors = await embeddingClient.embed([searchQuery]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

调用 embeddingClient.embed 时,传入 'query' 参数,以便底层模型(如 Cohere/Gemini)使用针对查询优化的任务类型,从而大幅提升语义搜索的召回质量。

Suggested change
// 1. 前端调用 Embedding API 生成查询向量
const queryVectors = await embeddingClient.embed([searchQuery]);
// 1. 前端调用 Embedding API 生成查询向量
const queryVectors = await embeddingClient.embed([searchQuery], 'query');

Comment thread src/services/vectorSearchService.ts Outdated
Comment on lines +116 to +129
private async embedGemini(texts: string[]): Promise<number[][]> {
const baseUrl = this.config.baseUrl.replace(/\/+$/, '');
const url = `${baseUrl}/v1beta/models/${this.config.model}:batchEmbedContents?key=${this.config.apiKey}`;

const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
requests: texts.map((text) => ({
model: `models/${this.config.model}`,
content: { parts: [{ text }] },
})),
}),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

配合 embed 方法的 purpose 参数,为 Gemini 的 batchEmbedContents 请求指定对应的 taskTypeRETRIEVAL_QUERYRETRIEVAL_DOCUMENT),以提升语义搜索的准确度。

Suggested change
private async embedGemini(texts: string[]): Promise<number[][]> {
const baseUrl = this.config.baseUrl.replace(/\/+$/, '');
const url = `${baseUrl}/v1beta/models/${this.config.model}:batchEmbedContents?key=${this.config.apiKey}`;
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
requests: texts.map((text) => ({
model: `models/${this.config.model}`,
content: { parts: [{ text }] },
})),
}),
});
private async embedGemini(texts: string[], purpose: 'document' | 'query'): Promise<number[][]> {
const baseUrl = this.config.baseUrl.replace(/\/+$/, '');
const url = `${baseUrl}/v1beta/models/${this.config.model}:batchEmbedContents?key=${this.config.apiKey}`;
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
requests: texts.map((text) => ({
model: `models/${this.config.model}`,
content: { parts: [{ text }] },
taskType: purpose === 'query' ? 'RETRIEVAL_QUERY' : 'RETRIEVAL_DOCUMENT',
})),
}),
});

Comment on lines +325 to +333
placeholder={
formApiType === 'openai'
? 'https://api.openai.com'
: formApiType === 'siliconflow'
? 'https://api.siliconflow.cn'
: formApiType === 'ollama'
? 'http://localhost:11434'
: 'https://api.example.com/v1/embeddings'
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

在模型来源选择 Gemini 或 Cohere 时,输入框缺少对应的官方默认 API 地址占位符。建议补充 Gemini (https://generativelanguage.googleapis.com) 和 Cohere (https://api.cohere.com) 的默认地址,以提升用户配置体验。

            placeholder={
              formApiType === 'openai'
                ? 'https://api.openai.com'
                : formApiType === 'siliconflow'
                ? 'https://api.siliconflow.cn'
                : formApiType === 'ollama'
                ? 'http://localhost:11434'
                : formApiType === 'gemini'
                ? 'https://generativelanguage.googleapis.com'
                : formApiType === 'cohere'
                ? 'https://api.cohere.com'
                : 'https://api.example.com/v1/embeddings'
            }

@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: 15

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

Inline comments:
In `@cloudflare-worker/README.md`:
- Around line 54-56: The fenced URL example in the README is missing a language
tag, which triggers markdownlint. Update the fenced block containing the Worker
URL example to use a text code fence, keeping the example content unchanged, and
make sure the fix is applied in the README section with the Worker URL
placeholder.

In `@cloudflare-worker/src/index.ts`:
- Around line 47-51: The auth check in the request handler allows a bypass when
AUTH_TOKEN is empty or unset, so update the logic around the existing token
validation in index.ts to reject requests if env.AUTH_TOKEN is missing before
comparing anything. Use a constant-time comparison in the same auth path instead
of direct string equality, and ensure the Authorization parsing keeps an
explicit fallback so an empty bearer token cannot authenticate when the secret
is not configured.

In `@cloudflare-worker/wrangler.toml`:
- Around line 5-6: Remove the plaintext AUTH_TOKEN entry from the [vars] section
in wrangler.toml so it is only managed as a secret. Update the Cloudflare worker
config to stop defining AUTH_TOKEN there, and keep using wrangler secret put
AUTH_TOKEN for the actual value to avoid the name collision and empty-token
deploy path.

In `@server/src/routes/configs.ts`:
- Around line 106-115: The bulk config sync logic is incorrectly skipping every
config with no secret, which breaks keyless embedding providers such as Ollama.
Update the shared config factory and bulk route handling around the
encryptedKey/skip check so keyless embedding configs are allowed through, either
by adding a requiresSecret option or by special-casing providers used by
EmbeddingClient.embedOllama(). Make sure the same fix is applied in both
occurrences of the skip logic so configs without apiKey are not dropped.
- Around line 620-625: The create flow in the configs route is returning the
wrong identifier because the INSERT into embedding_configs does not persist the
TEXT primary key id and instead uses lastInsertRowid. Update the create logic in
the config handler around the INSERT and response so it generates and stores an
explicit id value for embedding_configs.id, includes that id in the INSERT
columns/values, and returns that same stored text id in the JSON response
instead of lastInsertRowid.

In `@src/components/SearchBar.tsx`:
- Around line 535-536: The SearchBar filter effect is overwriting vector search
results because updating searchFilters.query retriggers the basic text search
path and replaces the results set by setSearchResults. Adjust the SearchBar flow
so semantic/vector results are preserved when searchFilters.query changes, or
split the effect logic so the recomputation in the effect at the
searchFilters/query path does not run after vector searches. Use the
setSearchResults, setSearchFilters, and the effect that depends on
searchFilters.query to locate and update the behavior.
- Around line 522-535: The vector search path in SearchBar is losing similarity
order because applyFilters re-sorts the repos by UI settings after the
score-based ordering is created. In the scoredRepos flow, keep the vector
ranking from scoreMap by re-sorting the filtered results using the same scores
after applyFilters, then pass that reordered list to setSearchResults so the
rendered results preserve similarity order.

In `@src/components/settings/VectorSearchSettings.tsx`:
- Around line 202-215: The rebuild flow in VectorSearchSettings.tsx only calls
indexAllRepos(), which upserts current repositories but never removes vectors
for repos that are no longer present or indexable. Update the rebuild path
around the indexAllRepos call and subsequent setVectorSearchConfig handling so
the Vectorize index is first reconciled against the current repositories list by
deleting stale entries before or during indexing. Use the existing indexAllRepos
/ setIndexResult / setVectorSearchConfig flow to ensure the final indexed count
reflects only current repos.
- Around line 188-195: The embedding test flow is still using the saved config
instead of the current unsaved form values, which can make rebuild/reindex use
stale credentials after a successful test. Update handleTestEmbedding in
VectorSearchSettings so EmbeddingClient is constructed from the same form state
used for VectorSearchService (for example, the current form URL/token and
selected embedding config/model), not from activeConfig. Keep the embedding
client instantiation aligned with the existing form-driven logic so the tested
settings are the ones actually used afterward.

In `@src/components/SettingsPanel.tsx`:
- Line 34: The SettingsPanel tab type now includes vectorSearch, but the runtime
allowlist still blocks it, so update the tab validation used by the
gsm:pending-settings-tab and gsm:navigate-to-settings-tab handlers. Modify the
VALID_TABS definition in SettingsPanel to include vectorSearch, and make sure
any tab-checking logic in the same component uses that updated allowlist so the
new panel can be reached at runtime.

In `@src/services/autoSync.ts`:
- Around line 229-231: The vector-search sync path in autoSync should not
overwrite a valid local token when `/api/configs/vector-search` returns an empty
authToken or authTokenStatus is decrypt_failed. Update the
`changed.vectorSearch` handling in `src/services/autoSync.ts` to merge the
incoming `vectorSearchResult.value` with the existing state from
`state.setVectorSearchConfig`, preserving the current `authToken` in the same
way the AI/WebDAV/embedding sync paths do. Use the `vectorSearchResult` response
fields to detect decrypt failures before applying the config.

In `@src/services/vectorSearchService.ts`:
- Around line 71-75: The current request path in VectorSearchService and related
embedding/provider calls ignores the AbortSignal, so cancellation only happens
between batches. Thread the signal from indexAllRepos through
EmbeddingClient.embed(...), VectorSearchService.request(...), and every provider
fetch so in-flight requests can be aborted promptly. Update the fetch call sites
in VectorSearchService and the referenced provider/worker methods to accept and
forward the signal consistently.
- Around line 153-157: The embedding request in vectorSearchService is hardcoded
to use search_document, but the same path is also used for user search queries.
Update the VectorSearchService embedding flow so the caller passes the embedding
purpose into the request builder, and switch query-side calls to use
search_query while keeping stored-content embeddings on search_document. Use the
existing vectorSearchService method(s) that build the Cohere request body to
thread this purpose through cleanly.

In `@src/store/useAppStore.ts`:
- Around line 1323-1328: `deleteEmbeddingConfig` and `setEmbeddingConfigs` in
`useAppStore` only update `embeddingConfigs`, leaving
`vectorSearchConfig.embeddingConfigId` pointing to a missing config and breaking
the vector path used by `SearchBar`. Update these store actions to reconcile
`vectorSearchConfig` whenever an embedding config is removed or the full list is
replaced: clear or reset the stale `embeddingConfigId`, and disable vector
search if the selected config no longer exists. Keep the logic localized around
`deleteEmbeddingConfig`, `setEmbeddingConfigs`, and the `vectorSearchConfig`
state so the UI can’t stay enabled with an invalid provider reference.

In `@src/types/index.ts`:
- Around line 227-233: `VectorSearchConfig` currently mixes persisted config
with transient runtime state by including `status`, which can leak UI-only
telemetry into synced backend data. Move `status` out of the shared config
contract in `src/types/index.ts` and create a separate runtime/store type for
it, then update `useAppStore` and `backendAdapter` serialization to persist or
sync only the config fields (`enabled`, `workerUrl`, `authToken`,
`embeddingConfigId`) while explicitly excluding `status`.
🪄 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: 02af5f95-118c-4bd7-9b40-01902c81c1f0

📥 Commits

Reviewing files that changed from the base of the PR and between 9bffb06 and 8bcd079.

⛔ Files ignored due to path filters (1)
  • cloudflare-worker/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • cloudflare-worker/README.md
  • cloudflare-worker/package.json
  • cloudflare-worker/src/index.ts
  • cloudflare-worker/tsconfig.json
  • cloudflare-worker/worker.js
  • cloudflare-worker/wrangler.toml
  • server/src/db/schema.ts
  • server/src/routes/configs.ts
  • src/components/SearchBar.tsx
  • src/components/SettingsPanel.tsx
  • src/components/settings/VectorSearchSettings.tsx
  • src/components/settings/index.ts
  • src/services/autoSync.ts
  • src/services/backendAdapter.ts
  • src/services/vectorSearchService.ts
  • src/store/useAppStore.ts
  • src/types/index.ts
  • src/utils/repoUtils.ts

Comment thread cloudflare-worker/README.md
Comment thread cloudflare-worker/src/index.ts
Comment thread cloudflare-worker/wrangler.toml Outdated
Comment thread server/src/routes/configs.ts Outdated
Comment thread server/src/routes/configs.ts Outdated
Comment thread src/services/autoSync.ts
Comment thread src/services/vectorSearchService.ts
Comment thread src/services/vectorSearchService.ts
Comment thread src/store/useAppStore.ts Outdated
Comment thread src/types/index.ts
AmintaCCCP and others added 2 commits June 24, 2026 19:23
Security:
- Worker: reject requests when AUTH_TOKEN is empty/unset
- Remove plaintext AUTH_TOKEN from wrangler.toml [vars]

Functional:
- Add purpose parameter for Cohere/Gemini (search_document vs search_query)
- Pass 'query' purpose in SearchBar vector search
- Preserve vector similarity ranking after applyFilters
- Generate proper UUID for embedding config POST (TEXT PK)
- Allow keyless embedding configs in factory (Ollama)
- Clear vectorSearchConfig when embedding config is deleted
- Preserve authToken on decrypt failure in autoSync
- Use form state for EmbeddingClient in handleRebuildIndex
- Add vectorSearch to VALID_TABS runtime allowlist
- Add Gemini/Cohere URL placeholders in settings UI
- Add language tag to README code fence

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix filter effect overwriting vector results (skipNextTextSearchRef)
- Add /cleanup endpoint to Worker for stale vector removal
- Thread AbortSignal through all fetch calls (embed + worker requests)
- Split VectorSearchStatus out of VectorSearchConfig (runtime-only)
- Cleanup stale vectors before rebuild in handleRebuildIndex

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

@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

♻️ Duplicate comments (1)
cloudflare-worker/worker.js (1)

85-103: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Same /cleanup defects as the TypeScript source.

This is the JS mirror of cloudflare-worker/src/index.ts; it has the identical topK-exceeds-Vectorize-max and hardcoded-1536-dimension issues. Apply the same fix here so both builds stay in sync.

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

In `@cloudflare-worker/worker.js` around lines 85 - 103, The /cleanup logic in
worker.js mirrors the TypeScript implementation and still has the same Vectorize
issues: it hardcodes a 1536-length zero vector and uses sampleSize directly as
topK, which can exceed Vectorize’s maximum. Update the cleanup flow in the same
block that builds zeroVector, calls env.VECTORIZE.describe(), and runs
env.VECTORIZE.query() so the vector dimension comes from the index metadata and
topK is capped to Vectorize’s allowed limit, keeping this JS mirror aligned with
src/index.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cloudflare-worker/src/index.ts`:
- Around line 100-119: The /cleanup flow in the vector cleanup handler is using
a hardcoded 1536-dimension zero vector and an oversized query window, which
breaks on indexes with different dimensions or when sampleSize exceeds Vectorize
limits. Update the cleanup logic in the handler that calls
env.VECTORIZE.describe() and env.VECTORIZE.query() to build the zero vector from
info.dimensions instead of a fixed size, and cap topK to 100 (or less) rather
than sampleSize. Keep the existing staleIds filtering and deleteByIds flow
unchanged.

---

Duplicate comments:
In `@cloudflare-worker/worker.js`:
- Around line 85-103: The /cleanup logic in worker.js mirrors the TypeScript
implementation and still has the same Vectorize issues: it hardcodes a
1536-length zero vector and uses sampleSize directly as topK, which can exceed
Vectorize’s maximum. Update the cleanup flow in the same block that builds
zeroVector, calls env.VECTORIZE.describe(), and runs env.VECTORIZE.query() so
the vector dimension comes from the index metadata and topK is capped to
Vectorize’s allowed limit, keeping this JS mirror aligned with src/index.ts.
🪄 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: 2e290d75-77b5-4fa9-89b6-bf0541077c15

📥 Commits

Reviewing files that changed from the base of the PR and between 8425b3c and 6d95b6c.

📒 Files selected for processing (7)
  • cloudflare-worker/src/index.ts
  • cloudflare-worker/worker.js
  • src/components/SearchBar.tsx
  • src/components/settings/VectorSearchSettings.tsx
  • src/services/vectorSearchService.ts
  • src/store/useAppStore.ts
  • src/types/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/types/index.ts
  • src/services/vectorSearchService.ts
  • src/store/useAppStore.ts
  • src/components/SearchBar.tsx
  • src/components/settings/VectorSearchSettings.tsx

Comment thread cloudflare-worker/src/index.ts
Search quality:
- Fetch README content during indexing (first 2000 chars)
- buildEmbeddingText now includes README for richer semantic context
- indexAllRepos accepts optional readmeFetcher callback

Settings UI:
- Save buttons show green 'Saved' feedback for 2 seconds
- Auto-detect dimensions focuses the input to show the value

Deploy guide:
- Remove Cloudflare Dashboard deployment method (no Vectorize UI)
- Add update/redeploy instructions
- Add prominent warning about model change requiring index rebuild

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

@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

🧹 Nitpick comments (1)
src/services/vectorSearchService.ts (1)

368-381: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Fetch READMEs per batch instead of preloading every repo.

The current prefetch phase performs all README network calls before the first embedding batch, so large libraries can look stuck and consume API quota even if later batches fail or are aborted. Keep README fetching scoped to the current batch.

Proposed refactor
-  // 预先批量获取 README 内容(如果提供了 fetcher)
-  const readmeCache = new Map<string, string>();
-  if (readmeFetcher) {
-    for (const repo of indexable) {
-      if (signal?.aborted) throw new Error('Aborted');
-      try {
-        const [owner, name] = repo.full_name.split('/');
-        const readme = await readmeFetcher(owner, name, signal);
-        if (readme) readmeCache.set(repo.full_name, readme);
-      } catch {
-        // README 获取失败不影响索引
-      }
-    }
-  }
-
   for (let i = 0; i < indexable.length; i += batchSize) {
     if (signal?.aborted) {
       throw new Error('Aborted');
     }
 
     const batch = indexable.slice(i, i + batchSize);
+    const readmeCache = new Map<string, string>();
+    if (readmeFetcher) {
+      for (const repo of batch) {
+        if (signal?.aborted) throw new Error('Aborted');
+        try {
+          const [owner, name] = repo.full_name.split('/');
+          const readme = await readmeFetcher(owner, name, signal);
+          if (readme) readmeCache.set(repo.full_name, readme);
+        } catch (err) {
+          if (signal?.aborted || (err instanceof Error && err.message === 'Aborted')) {
+            throw new Error('Aborted');
+          }
+          // README 获取失败不影响索引
+        }
+      }
+    }
     const texts = batch.map(repo => buildEmbeddingText(repo, readmeCache.get(repo.full_name)));

Also applies to: 388-389

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

In `@src/services/vectorSearchService.ts` around lines 368 - 381, The README
prefetch in vectorSearchService is doing all network fetches up front instead of
per embedding batch, which can stall large runs and waste quota; move the
readmeFetcher usage into the current batch processing flow so each batch fetches
only its own READMEs. Update the logic around the readmeCache handling in
vectorSearchService to scope caching/fetching to the batch loop, and make sure
abort checks still short-circuit batch work cleanly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/vectorSearchService.ts`:
- Line 361: Validate the caller-provided batchSize in vectorSearchService before
it is used by the indexing loop that increments i by batchSize; reject or
normalize zero, negative, or non-finite values in the code path that reads
options in the relevant indexing function, so the loop stride always makes
forward progress. Update both places where batchSize is consumed to use the
validated value, and keep the behavior centered around the existing options
destructuring and the i += batchSize loop.

---

Nitpick comments:
In `@src/services/vectorSearchService.ts`:
- Around line 368-381: The README prefetch in vectorSearchService is doing all
network fetches up front instead of per embedding batch, which can stall large
runs and waste quota; move the readmeFetcher usage into the current batch
processing flow so each batch fetches only its own READMEs. Update the logic
around the readmeCache handling in vectorSearchService to scope caching/fetching
to the batch loop, and make sure abort checks still short-circuit batch work
cleanly.
🪄 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: 70b91d87-1e92-47a5-898a-b967ca4cb701

📥 Commits

Reviewing files that changed from the base of the PR and between 6d95b6c and 877eab5.

📒 Files selected for processing (3)
  • cloudflare-worker/README.md
  • src/components/settings/VectorSearchSettings.tsx
  • src/services/vectorSearchService.ts
✅ Files skipped from review due to trivial changes (1)
  • cloudflare-worker/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/settings/VectorSearchSettings.tsx

Comment thread src/services/vectorSearchService.ts Outdated
Search quality:
- Increase README content from 2000 to 6000 chars
- Strip decorative badges/images/HTML from README before embedding
- Add AI reranking step after vector search (uses existing AI config)

Worker /cleanup fix:
- Cap topK to 100 (Vectorize limit)
- Use info.dimensions for zero vector instead of hardcoded 1536

Validation:
- Add batchSize validation in indexAllRepos (must be positive integer)

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

@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)
cloudflare-worker/src/index.ts (1)

105-113: 🗄️ Data Integrity & Integration | 🟠 Major

Use paginated vector listing for /cleanup (cloudflare-worker/src/index.ts:107-115). env.VECTORIZE.query(..., { topK: 100 }) only returns the 100 nearest matches, so stale vectors outside that window can survive cleanup runs. Enumerate IDs with list-vectors pagination or pass explicit delete candidates instead.

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

In `@cloudflare-worker/src/index.ts` around lines 105 - 113, The `/cleanup` logic
in `index.ts` currently uses `env.VECTORIZE.query` with a fixed `topK: 100`,
which can miss stale vectors beyond the returned window. Update the cleanup flow
around the `zeroVector`, `existing.matches`, and `staleIds` handling to
enumerate vector IDs with paginated listing or otherwise gather explicit delete
candidates before deleting. Keep the `keepSet` filtering, but ensure all stale
vectors are discovered across pages rather than relying on a single query
result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cloudflare-worker/src/index.ts`:
- Around line 105-113: The `/cleanup` logic in `index.ts` currently uses
`env.VECTORIZE.query` with a fixed `topK: 100`, which can miss stale vectors
beyond the returned window. Update the cleanup flow around the `zeroVector`,
`existing.matches`, and `staleIds` handling to enumerate vector IDs with
paginated listing or otherwise gather explicit delete candidates before
deleting. Keep the `keepSet` filtering, but ensure all stale vectors are
discovered across pages rather than relying on a single query result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c733483f-01f3-4856-ae08-3a7f448c8d30

📥 Commits

Reviewing files that changed from the base of the PR and between 877eab5 and f6677f2.

📒 Files selected for processing (4)
  • cloudflare-worker/src/index.ts
  • cloudflare-worker/worker.js
  • src/components/SearchBar.tsx
  • src/services/vectorSearchService.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/SearchBar.tsx
  • src/services/vectorSearchService.ts

AmintaCCCP and others added 7 commits June 24, 2026 20:37
The previous /cleanup used query(topK: 100) which only returns the 100
nearest matches, missing stale vectors beyond that window. Now uses
listVectors with cursor-based pagination to enumerate ALL vector IDs
and delete those not in the keepIds list.

Co-Authored-By: Claude <noreply@anthropic.com>
- Worker test success now updates store vectorSearchStatus
- Add delete index section with copy-to-clipboard CLI commands
- README.md reference is now a clickable GitHub link
- Renumber deploy guide section to ⑥

Co-Authored-By: Claude <noreply@anthropic.com>
- indexAllRepos now reports phase (readme/embedding/uploading) + progress
- Progress bar shows phase emoji and label (📖/🧠/☁️)
- Indexing state moved from component local state to Zustand store
- State persists across page navigation (切页不丢失)

Co-Authored-By: Claude <noreply@anthropic.com>
1. Remove redundant AI reranking for vector search results (speed up)
2. Add search phase display below AI search button
3. Fix sort changing loses vector results (use vectorScoreMapRef)
4. Clear vector state when disabling vector search
5. Auto-index new repos after star sync when vector search enabled
6. Add 'Incremental Index' button (upsert only, no cleanup)

Co-Authored-By: Claude <noreply@anthropic.com>
Restored the LLM-based reranking step that was incorrectly removed.
Now shows 'AI 校验排序...' progress during reranking so users know
what's happening. Falls back to vector order if reranking fails.

Co-Authored-By: Claude <noreply@anthropic.com>
Primary buttons: bg-purple-500 → bg-brand-indigo
Hover: bg-purple-600 → bg-brand-hover
Toggle: bg-purple-500 → bg-brand-indigo
Rounded: rounded-md → rounded-lg

Co-Authored-By: Claude <noreply@anthropic.com>
- Add indexMode ('description' | 'readme') to VectorSearchConfig
- Add readmeMaxChars (default 6000) for configurable README truncation
- Description mode: fast, lower precision (metadata only)
- README mode: high precision, slower (fetches and indexes README)
- Index mode selector with visual cards in settings UI
- README max chars input (500-20000, step 1000)
- Consolidated save button in index management section
- All index operations respect the selected mode

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

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@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: 18

🧹 Nitpick comments (1)
cloudflare-worker/worker.js (1)

1-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Two parallel worker implementations will drift.

worker.js duplicates src/index.ts (the past auth/cleanup fixes had to be applied twice, and these files can silently diverge). Consider shipping a single source of truth — e.g. generate worker.js from the TS build, or document one as canonical and the other as a generated artifact.

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

In `@cloudflare-worker/worker.js` around lines 1 - 122, The Worker logic is
duplicated between the standalone worker implementation and the TypeScript
entrypoint, which will cause the two versions to drift over time. Make one
source of truth for the request handling in fetch and the VECTORIZE operations
(upsert, query, delete, cleanup, status), then have the other file be generated
from it or reduced to a build artifact/entry wrapper. Update the build or repo
convention so future changes only need to be made in the canonical
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cloudflare-worker/package.json`:
- Line 12: Update the wrangler dependency in the package.json manifest to a
V2-compatible minimum version by changing the current wrangler range from the
existing broad 3.0.x allowance to a 3.71.0+ range; this ensures CI and lockfile
resolution cannot pick an incompatible release. Use the wrangler dependency
entry in the package manifest as the target for the version bump.

In `@cloudflare-worker/src/index.ts`:
- Line 106: The `staleIds` declaration should use `const` instead of `let`
because the array reference is never reassigned, only mutated with `push`.
Update the `staleIds` variable in `index.ts` to `const` and keep the existing
push-based usage unchanged.
- Around line 75-78: The Vectorize full-metadata query in the query path uses
topK directly, but returnMetadata: true only supports up to 50 results. Update
the query logic around env.VECTORIZE.query in the index.ts flow to clamp topK to
a maximum of 50 before invoking query(), while keeping the existing metadata
option unchanged.
- Around line 95-119: The cleanup handler in the POST /cleanup branch of
index.ts is calling env.VECTORIZE.listVectors, which is not supported by the
Workers Vectorize binding surface and will fail at runtime. Update this path to
use a supported listing approach via the Vectorize REST/CLI-backed flow, then
keep the existing staleIds collection and deleteByIds call unchanged once IDs
are obtained. Use the request handling logic around jsonResponse, describe, and
deleteByIds as the anchor points when refactoring.

In `@cloudflare-worker/worker.js`:
- Line 91: Change the staleIds declaration in the worker logic to use const
instead of let, matching the pattern used in src/index.ts. Locate the variable
initialization for staleIds and update it to an immutable binding since the
array reference is not reassigned; keep the surrounding stale-id handling
unchanged.

In `@server/src/db/schema.ts`:
- Around line 124-134: The vector search config table is missing persistence for
the README indexing settings used by VectorSearchConfig. Update the
vector_search_configs schema and any related migration/create-table logic in
schema.ts to store indexMode and readmeMaxChars alongside the existing fields,
and ensure the backend config save/load paths use those columns so auto-sync
does not drop the values.

In `@server/src/routes/configs.ts`:
- Around line 160-168: The secret-handling logic in the configs route is
treating an explicit empty string the same as an omitted or masked value, so
fields like apiKey/password/authToken cannot be cleared. Update the branch
around the existing secret lookup and the updateSql/updateParams flow so only
omitted values or masked values reuse the stored secret; if the request
explicitly sends an empty string, persist a cleared value instead, and for
fields marked by requiresSecret return 422 rather than silently keeping the old
secret. Apply the same fix in both affected secret update paths.
- Around line 124-145: The bulk sync flow in the configs route currently commits
partial writes even when some configs are skipped, because the deletion and
inserts inside bulkSync only abort on ALL_CONFIGS_SKIPPED. Update the bulk sync
logic in the route handler around bulkSync/res.json so any non-empty
syncResult.skipped causes the whole transaction to roll back before commit,
rather than returning success with partial replacement. Use the existing
bulkSync, syncResult.skipped, and logger.errorFromError flow to locate the fix
and ensure the response is only sent after a fully successful sync.

In `@src/components/SearchBar.tsx`:
- Around line 169-170: The vector score cache in SearchBar is not tied to the
query that produced it, so stale vector hits can be reused after filters or
subsequent searches change. Update the vector search flow around SearchBar,
vectorScoreMapRef, and the query-handling logic so the stored scores are
associated with the current searchFilters.query and cleared or ignored when that
query changes. Make sure the code paths that read and write vectorScoreMapRef
only apply scores when they match the active query, including the affected
search/update logic near the referenced sections.
- Around line 555-572: The final sorting in SearchBar’s reranking flow is
overriding the order returned by AIService.searchRepositoriesWithReranking(), so
successful AI reranks never affect the displayed results. Update the rerank
branch in SearchBar to preserve the reranked array order when reranking
succeeds, and only fall back to the existing vector-score sort when reranking is
unavailable or throws. Keep applyFilters from reordering the AI result, and use
the rerankService/searchRepositoriesWithReranking path as the source of truth
for the final list.
- Around line 871-887: The auto-index path in SearchBar currently triggers on
newRepoCount > 0 but passes mergedRepositories to indexAllRepos, which causes
the whole set to be reprocessed instead of only the new additions. Update this
flow to build and pass only the newly added repository list to indexAllRepos,
keeping the existing vectorSearchConfig, EmbeddingClient, VectorSearchService,
and readmeFetcher wiring unchanged.

In `@src/components/settings/VectorSearchSettings.tsx`:
- Around line 261-265: The vector search status is recording the wrong dimension
value after a rebuild: the indexing run uses the edited form value, but
setVectorSearchStatus in VectorSearchSettings still წერს
activeConfig.dimensions. Update the status payload to report the dimensions
actually used for the current indexing run, using the same formDimensions value
passed into the embedding client/build path so the UI reflects the newly rebuilt
index correctly.
- Around line 285-291: `isConfigComplete` in `VectorSearchSettings` is currently
driven by unsaved form values, so the rebuild actions can appear enabled even
when `runIndexAll()` would no-op because there is no active embedding config.
Update the enablement logic for the indexing/rebuild controls to depend on the
persisted config state used by `runIndexAll()` (or explicitly require
`activeConfig`/saved config existence), and keep the form-based checks only for
validating edits rather than enabling indexing.
- Around line 233-239: Fail the rebuild when vector cleanup throws instead of
swallowing the error in VectorSearchSettings’ rebuild flow. In the cleanup block
that calls vectorService.cleanup, pass the abort signal through to cleanup so it
can be cancelled, and remove the local catch/console.warn so the exception
bubbles to the outer rebuild catch. This keeps the rebuild from continuing with
stale vectors when cleanup fails.
- Around line 140-150: The memoized handleSaveWorkerConfig callback is missing
dependencies for formIndexMode and formReadmeMaxChars, so it can capture stale
values when saving the vector search worker config. Update the useCallback
dependency list in VectorSearchSettings so it includes both fields along with
the existing formWorkerUrl, formAuthToken, activeEmbeddingConfig, and
setVectorSearchConfig references. This will ensure the save action always
persists the current index mode and readme limit values.

In `@src/services/backendAdapter.ts`:
- Around line 631-649: The sync/pull flow for vector-search config is treating
VectorSearchConfig as complete, but indexMode and readmeMaxChars are not
preserved by the backend contract. Update syncVectorSearchConfig and
fetchVectorSearchConfig in backendAdapter to stop round-tripping a partial
object, and either extend the backend configs route/schema to persist and return
those fields or move them into the settings payload that autoSync consumes. Make
sure the VectorSearchConfig shape and the backend /configs/vector-search
contract stay aligned so these README indexing settings are not clobbered.

In `@src/services/vectorSearchService.ts`:
- Around line 341-344: The README cleanup in vectorSearchService’s markdown
stripping currently removes plain image syntax before linked badges, so linked
badges like [![...](...)](...) can leak into the cleaned text. Update the
replacement order in the readmeContent normalization logic so the linked-badge
pattern is stripped first, then the plain image pattern, while keeping the rest
of the cleaning chain in place.

In `@src/store/useAppStore.ts`:
- Around line 640-651: Normalize the persisted vector search config so it cannot
reference a missing embedding provider. In both normalizePersistedState and
setVectorSearchConfig, validate vectorSearchConfig.embeddingConfigId against the
current embeddingConfigs, and if the id is absent, clear embeddingConfigId and
disable vector search instead of keeping the stale selection. Make sure the
hydration path and the runtime setter use the same guard so setEmbeddingConfigs
cleanup cannot be overwritten by an invalid persisted vector config.

---

Nitpick comments:
In `@cloudflare-worker/worker.js`:
- Around line 1-122: The Worker logic is duplicated between the standalone
worker implementation and the TypeScript entrypoint, which will cause the two
versions to drift over time. Make one source of truth for the request handling
in fetch and the VECTORIZE operations (upsert, query, delete, cleanup, status),
then have the other file be generated from it or reduced to a build
artifact/entry wrapper. Update the build or repo convention so future changes
only need to be made in the canonical implementation.
🪄 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: 79361008-fc6f-46e5-899d-a711710315a5

📥 Commits

Reviewing files that changed from the base of the PR and between 9bffb06 and 99dc3e2.

⛔ Files ignored due to path filters (1)
  • cloudflare-worker/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • cloudflare-worker/README.md
  • cloudflare-worker/package.json
  • cloudflare-worker/src/index.ts
  • cloudflare-worker/tsconfig.json
  • cloudflare-worker/worker.js
  • cloudflare-worker/wrangler.toml
  • server/src/db/schema.ts
  • server/src/routes/configs.ts
  • src/components/SearchBar.tsx
  • src/components/SettingsPanel.tsx
  • src/components/settings/VectorSearchSettings.tsx
  • src/components/settings/index.ts
  • src/services/autoSync.ts
  • src/services/backendAdapter.ts
  • src/services/vectorSearchService.ts
  • src/store/useAppStore.ts
  • src/types/index.ts
  • src/utils/repoUtils.ts

Comment thread cloudflare-worker/package.json Outdated
Comment thread cloudflare-worker/src/index.ts
Comment thread cloudflare-worker/src/index.ts
Comment thread cloudflare-worker/src/index.ts Outdated
Comment thread cloudflare-worker/worker.js Outdated
Comment thread src/components/settings/VectorSearchSettings.tsx
Comment thread src/components/settings/VectorSearchSettings.tsx
Comment thread src/services/backendAdapter.ts
Comment thread src/services/vectorSearchService.ts
Comment thread src/store/useAppStore.ts Outdated
Comment on lines +640 to +651
vectorSearchConfig: safePersisted.vectorSearchConfig && typeof safePersisted.vectorSearchConfig === 'object'
? {
enabled: !!safePersisted.vectorSearchConfig.enabled,
workerUrl: typeof safePersisted.vectorSearchConfig.workerUrl === 'string' ? safePersisted.vectorSearchConfig.workerUrl : '',
authToken: typeof safePersisted.vectorSearchConfig.authToken === 'string' ? safePersisted.vectorSearchConfig.authToken : '',
embeddingConfigId: typeof safePersisted.vectorSearchConfig.embeddingConfigId === 'string' ? safePersisted.vectorSearchConfig.embeddingConfigId : '',
indexMode: safePersisted.vectorSearchConfig.indexMode === 'description' ? 'description' : 'readme',
readmeMaxChars: typeof safePersisted.vectorSearchConfig.readmeMaxChars === 'number' && safePersisted.vectorSearchConfig.readmeMaxChars > 0
? safePersisted.vectorSearchConfig.readmeMaxChars
: 6000,
}
: { enabled: false, workerUrl: '', authToken: '', embeddingConfigId: '', indexMode: 'readme' as const, readmeMaxChars: 6000 },

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Normalize vector config against available embedding configs.

normalizePersistedState and setVectorSearchConfig accept any embeddingConfigId. Since backend sync pulls embedding configs and vector config independently, a stale vector config can overwrite the cleanup done by setEmbeddingConfigs, leaving vector search enabled with no usable provider. Clear the id and disable vector search when the merged id is not present in embeddingConfigs.

Suggested localized guard
-      setVectorSearchConfig: (config) => set((state) => ({
-        vectorSearchConfig: { ...state.vectorSearchConfig, ...config }
-      })),
+      setVectorSearchConfig: (config) => set((state) => {
+        const vectorSearchConfig = { ...state.vectorSearchConfig, ...config };
+        const hasEmbeddingConfig =
+          !vectorSearchConfig.embeddingConfigId ||
+          state.embeddingConfigs.some(({ id }) => id === vectorSearchConfig.embeddingConfigId);
+
+        return {
+          vectorSearchConfig: hasEmbeddingConfig
+            ? vectorSearchConfig
+            : { ...vectorSearchConfig, embeddingConfigId: '', enabled: false },
+        };
+      }),

Apply the same validity check in hydration normalization.

Also applies to: 1352-1354

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

In `@src/store/useAppStore.ts` around lines 640 - 651, Normalize the persisted
vector search config so it cannot reference a missing embedding provider. In
both normalizePersistedState and setVectorSearchConfig, validate
vectorSearchConfig.embeddingConfigId against the current embeddingConfigs, and
if the id is absent, clear embeddingConfigId and disable vector search instead
of keeping the stale selection. Make sure the hydration path and the runtime
setter use the same guard so setEmbeddingConfigs cleanup cannot be overwritten
by an invalid persisted vector config.

AmintaCCCP and others added 4 commits June 24, 2026 22:05
- Add index_mode and readme_max_chars columns to vector_search_configs
- Update GET/PUT routes to handle new fields
- Fix returnMetadata: true → 'all' for Vectorize V2
- Replace listVectors (doesn't exist in binding) with query-based cleanup
- Change let staleIds → const staleIds in both TS and JS worker
- Update wrangler version to ^3.71.0 for Vectorize V2

Co-Authored-By: Claude <noreply@anthropic.com>
- Bulk sync: rollback when ANY config skipped (not just all)
- Distinguish omitted secrets from explicit empty string clears
- Bind vector scores to the query that produced them
- Preserve AI rerank order (don't re-sort by vector score after rerank)
- Add formIndexMode/formReadmeMaxChars to handleSaveWorkerConfig deps
- Fail rebuild when cleanup fails (don't silently continue)
- Report formDimensions in status (not stale activeConfig.dimensions)
- Disable indexing buttons until embedding config exists
- Remove linked badges before plain images in README cleanup

Co-Authored-By: Claude <noreply@anthropic.com>
Bug fixes:
- Make cleanup non-blocking so rebuild continues even if cleanup fails
- Clear vectorScoreMapRef when vector search is disabled
- Check vsEnabled in effect before reusing cached vector scores
- Show error message in indexing result (red for all-failed)

Audit fixes:
- r3467643178: Single update route also distinguishes empty vs omitted
- r3467643227: Auto-index only new repos (not entire library)
- r3467643354: normalizePersistedState validates embeddingConfigId exists
- r3467643081: returnMetadata already 'all' (confirmed)

Co-Authored-By: Claude <noreply@anthropic.com>
…mpty secrets

- Clamp topK to 50 in both TS and JS workers (Vectorize caps at 50 with returnMetadata:'all')
- Fix worker.js returnMetadata: true → 'all' (was using deprecated format)
- Vector search config PUT: authToken='' now clears token, omitted/masked reuses existing

Audit fixes: r3467643081, r3467643178

Co-Authored-By: Claude <noreply@anthropic.com>
@AmintaCCCP AmintaCCCP merged commit 927a533 into main Jun 24, 2026
5 checks passed
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