feat: 分页获取分支 & 修复分支选择器宽度 & 修复 payload 解析失败 & 修复 Opus 4.8 兼容性#26
feat: 分页获取分支 & 修复分支选择器宽度 & 修复 payload 解析失败 & 修复 Opus 4.8 兼容性#26Bluestar-coder wants to merge 4 commits into
Conversation
- scanner.py: fetch all branches with pagination (was capped at 100) - branch-selector.tsx: use min/max-width instead of trigger-width for dropdown - pyproject.toml / requirements-lock.txt: dependency updates - docker-compose.override.yml: local dev config adjustments
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughGitHub branch listing now paginates through all pages, backend runtime fallback handling and finalizer behavior changed, LLM adapters special-case extended-thinking models, compose ports were remapped, the branch selector width changed, and code-security binding entries were enabled. ChangesGitHub Branch Pagination and SOCKS Proxy Support
Backend Runtime Fallback and Prompt Changes
LLM Adapter Thinking-Model Handling
Docker Compose Port Overrides
Branch Selector Popover Width
Skill Library Binding Updates
Estimated code review effort: 3 (Moderate) | ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@backend/app/services/scanner.py`:
- Around line 181-193: The pagination loop in scanner.py can run without a hard
stop, so add a defensive maximum page cap to the branch-fetching logic in the
branch scanning function that contains the current while True loop. Update the
loop condition or add an explicit break using a max page constant before calling
github_api, and keep the existing per_page/len(branches_data) termination checks
so pagination still stops normally when fewer items are returned.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 78f91323-9b60-4dba-a323-d529372d892c
📒 Files selected for processing (8)
backend/app/services/scanner.pybackend/pyproject.tomlbackend/requirements-lock.txtdocker-compose.override.ymlfrontend/src/components/ui/branch-selector.tsxskill_library/.runtime/installed_skills.jsonskill_library/agents/finding/bindings.jsonskill_library/code-security/bindings.json
| while True: | ||
| branches_url = f"https://api.github.com/repos/{owner}/{repo}/branches?per_page={per_page}&page={page}" | ||
| branches_data = await github_api(branches_url, token) | ||
|
|
||
| if not branches_data: | ||
| break | ||
|
|
||
| all_branches.extend([b["name"] for b in branches_data]) | ||
|
|
||
| if len(branches_data) < per_page: | ||
| break | ||
|
|
||
| page += 1 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Add a safety cap on the number of pages fetched.
The pagination loop has no upper bound on the number of pages. If a repository has an extremely large number of branches, or if the GitHub API returns exactly per_page items on every page due to a quirk, the loop will continue making API calls indefinitely or until the rate limit is exhausted. GitHub's API itself caps at 30 pages for most endpoints, but the code should enforce its own limit as a defensive measure.
🔒️ Proposed fix: add a max page limit
all_branches = []
page = 1
per_page = 100
+ max_pages = 30 # GitHub API caps at 30 pages (3000 branches)
- while True:
+ while page <= max_pages:
branches_url = f"https://api.github.com/repos/{owner}/{repo}/branches?per_page={per_page}&page={page}"
branches_data = await github_api(branches_url, token)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| while True: | |
| branches_url = f"https://api.github.com/repos/{owner}/{repo}/branches?per_page={per_page}&page={page}" | |
| branches_data = await github_api(branches_url, token) | |
| if not branches_data: | |
| break | |
| all_branches.extend([b["name"] for b in branches_data]) | |
| if len(branches_data) < per_page: | |
| break | |
| page += 1 | |
| all_branches = [] | |
| page = 1 | |
| per_page = 100 | |
| max_pages = 30 # GitHub API caps at 30 pages (3000 branches) | |
| while page <= max_pages: | |
| branches_url = f"https://api.github.com/repos/{owner}/{repo}/branches?per_page={per_page}&page={page}" | |
| branches_data = await github_api(branches_url, token) | |
| if not branches_data: | |
| break | |
| all_branches.extend([b["name"] for b in branches_data]) | |
| if len(branches_data) < per_page: | |
| break | |
| page += 1 |
🤖 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 `@backend/app/services/scanner.py` around lines 181 - 193, The pagination loop
in scanner.py can run without a hard stop, so add a defensive maximum page cap
to the branch-fetching logic in the branch scanning function that contains the
current while True loop. Update the loop condition or add an explicit break
using a max page constant before calling github_api, and keep the existing
per_page/len(branches_data) termination checks so pagination still stops
normally when fewer items are returned.
- agent_runtime/specs.py:为 build_triage_runtime_spec 添加 _triage_fallback_payload,LLM 未调用 FinalizeTriageBatch 时 不再抛 ValueError,改为返回标记 requires_retry=True 的不完整 payload - agent_tasks.py:continue_session_until_report_payload 调用补充 fallback_payload_builder=lambda _: None,报告生成失败时替换为 更清晰的 'did not return a report bundle' 错误 - finding_runtime/bridge.py:删除 _default_finalizer_prompts 中 第一个 return 后永远不可达的死代码块
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@backend/app/services/agent_runtime/specs.py`:
- Around line 57-64: The fallback payload in _triage_fallback_payload is
hardcoding runtime_completion_mode as the uppercase string, but downstream logic
expects the enum value format. Update this function to use
RuntimeCompletionMode.INCOMPLETE.value instead of the raw string so the payload
matches the lowercase completion mode used elsewhere and is recognized
consistently.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fc56f10b-aad4-4a91-9f88-557e1af1eb9f
📒 Files selected for processing (3)
backend/app/api/v1/endpoints/agent_tasks.pybackend/app/services/agent_runtime/specs.pybackend/app/services/finding_runtime/bridge.py
💤 Files with no reviewable changes (1)
- backend/app/services/finding_runtime/bridge.py
| def _triage_fallback_payload(snapshot: Any) -> dict[str, Any]: | ||
| return { | ||
| "findings": [], | ||
| "summary": "Triage batch did not complete - FinalizeTriageBatch was not called.", | ||
| "runtime_completion_mode": "INCOMPLETE", | ||
| "is_partial": True, | ||
| "requires_retry": True, | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify RuntimeCompletionMode enum value and is_partial usage in triage context
rg -nC3 'RuntimeCompletionMode' backend/app/services/agent_runtime/specs.py
rg -nC3 'is_partial' backend/app/services/ --type=python
rg -nC3 'is_final' backend/app/services/ --type=python
rg -nC3 'class RuntimeCompletionMode' backend/Repository: larlarua/AutoCVE
Length of output: 760
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## specs.py relevant slice\n'
nl -ba backend/app/services/agent_runtime/specs.py | sed -n '1,140p'
printf '\n## bridge.py relevant slice\n'
nl -ba backend/app/services/finding_runtime/bridge.py | sed -n '1,220p'
printf '\n## search for is_partial / is_final in backend/app/services\n'
rg -nC2 'is_partial|is_final' backend/app/services -g '*.py'
printf '\n## RuntimeCompletionMode definition and uses\n'
rg -nC2 'class RuntimeCompletionMode|RuntimeCompletionMode\.' backend/app/services -g '*.py'Repository: larlarua/AutoCVE
Length of output: 222
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## specs.py relevant slice\n'
sed -n '1,160p' backend/app/services/agent_runtime/specs.py
printf '\n## bridge.py relevant slice\n'
sed -n '1,260p' backend/app/services/finding_runtime/bridge.py
printf '\n## search for is_partial / is_final in backend/app/services\n'
grep -RInE --include='*.py' 'is_partial|is_final' backend/app/services || true
printf '\n## RuntimeCompletionMode definition and uses\n'
grep -RInE --include='*.py' 'class RuntimeCompletionMode|RuntimeCompletionMode\.' backend/app/services || trueRepository: larlarua/AutoCVE
Length of output: 19385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## RuntimeCompletionMode definition\n'
sed -n '1,120p' backend/app/services/finding_runtime/models.py
printf '\n## finding_runtime/bridge.py around fallback payload\n'
sed -n '940,990p' backend/app/services/finding_runtime/bridge.py
printf '\n## search for triage payload/schema and is_partial across repo\n'
grep -RInE --include='*.py' 'is_partial|triage.*payload|FinalizeTriageBatch|runtime_completion_mode' backend | sed -n '1,220p'
printf '\n## agent_runtime usage of fallback builder\n'
grep -RInE --include='*.py' 'fallback_payload_builder|_triage_fallback_payload|build_triage_runtime_spec' backend/app/services | sed -n '1,200p'Repository: larlarua/AutoCVE
Length of output: 17949
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## agent_tasks.py runtime_completion_mode handling\n'
sed -n '360,430p' backend/app/api/v1/endpoints/agent_tasks.py
printf '\n## any lowercase/uppercase incomplete comparisons in backend/app/services and backend/app/api\n'
grep -RInE --include='*.py' 'runtime_completion_mode[^\n]*incomplete|== "incomplete"|== "INCOMPLETE"|RuntimeCompletionMode\.INCOMPLETE' backend/app backend/tests | sed -n '1,240p'Repository: larlarua/AutoCVE
Length of output: 5575
Use RuntimeCompletionMode.INCOMPLETE.value here
runtime_completion_mode is consumed as lowercase "incomplete" elsewhere, so the raw "INCOMPLETE" string will not match downstream checks.
🤖 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 `@backend/app/services/agent_runtime/specs.py` around lines 57 - 64, The
fallback payload in _triage_fallback_payload is hardcoding
runtime_completion_mode as the uppercase string, but downstream logic expects
the enum value format. Update this function to use
RuntimeCompletionMode.INCOMPLETE.value instead of the raw string so the payload
matches the lowercase completion mode used elsewhere and is recognized
consistently.
- factory.py:Claude 模型支持列表新增 claude-opus-4-8 - anthropic_adapter.py: 新增 _is_thinking_model() 辅助函数(匹配 opus-4-8 / claude-3-7-sonnet) _build_payload 对 extended thinking 模型跳过 temperature 参数 _build_payload 对 extended thinking 模型自动移除末尾 assistant 消息(prefill 不支持) - litellm_adapter.py: 内联 extended thinking 模型检测,跳过 temperature 参数传递 修复 issue larlarua#22
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
backend/app/services/llm/adapters/anthropic_adapter.py (2)
207-218: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCache the
_is_thinking_modelresult to avoid redundant calls.
_is_thinking_model(self.config.model)is invoked twice with the same argument on lines 208 and 218. Compute once and reuse.♻️ Suggested refactor
def _build_payload(self, request: LLMRequest, *, stream: bool) -> dict[str, Any]: messages, system = self._normalize_messages([msg.to_dict() for msg in request.messages]) - # Extended thinking models do not support assistant prefill (trailing assistant message) - if messages and messages[-1]["role"] == "assistant" and _is_thinking_model(self.config.model): + thinking = _is_thinking_model(self.config.model) + # Extended thinking models do not support assistant prefill (trailing assistant message) + if thinking and messages and messages[-1]["role"] == "assistant": messages = messages[:-1] payload: dict[str, Any] = { "model": self.config.model, "messages": messages, "max_tokens": request.max_tokens if request.max_tokens is not None else self.config.max_tokens, "stream": stream, } temperature = request.temperature if request.temperature is not None else self.config.temperature # Extended thinking models do not accept the temperature parameter - if temperature is not None and not _is_thinking_model(self.config.model): + if temperature is not None and not thinking: payload["temperature"] = temperature🤖 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 `@backend/app/services/llm/adapters/anthropic_adapter.py` around lines 207 - 218, The `_is_thinking_model(self.config.model)` check is repeated with the same argument in the Anthropic adapter request path. In `anthropic_adapter.py`, compute the result once in the surrounding method before building the payload, store it in a local variable, and reuse that value for both the trailing assistant-message handling and the temperature gate so the logic stays identical while avoiding redundant calls.
32-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDRY: thinking-model detection logic is duplicated across adapters.
_is_thinking_model()is defined here inanthropic_adapter.pybut the same substring-matching logic is inlined inlitellm_adapter.pylines 227-230. If the set of thinking models changes, both must be updated in sync. Extract this into a shared utility (e.g., in a common module or base adapter) and import it in both adapters.Also applies to: 227-230
🤖 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 `@backend/app/services/llm/adapters/anthropic_adapter.py` around lines 32 - 37, The thinking-model detection logic is duplicated between anthropic_adapter.py and litellm_adapter.py, so extract the shared substring check into a common helper or base adapter and reuse it from both _is_thinking_model() and the litellm adapter’s inline model check. Keep the existing behavior for matching model names like opus-4-8 and claude-3-7-sonnet, but centralize the implementation so future model updates only need one change.
🤖 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 `@backend/app/services/llm/adapters/litellm_adapter.py`:
- Around line 227-230: The streaming path in stream_complete still
unconditionally sets temperature, which breaks thinking models that do not
accept it. Mirror the model guard already used in _send_request by checking
self.config.model before adding kwargs["temperature"] in stream_complete, and
ensure the same opus-4-8 / claude-3-7-sonnet exclusion applies to streaming
requests.
---
Nitpick comments:
In `@backend/app/services/llm/adapters/anthropic_adapter.py`:
- Around line 207-218: The `_is_thinking_model(self.config.model)` check is
repeated with the same argument in the Anthropic adapter request path. In
`anthropic_adapter.py`, compute the result once in the surrounding method before
building the payload, store it in a local variable, and reuse that value for
both the trailing assistant-message handling and the temperature gate so the
logic stays identical while avoiding redundant calls.
- Around line 32-37: The thinking-model detection logic is duplicated between
anthropic_adapter.py and litellm_adapter.py, so extract the shared substring
check into a common helper or base adapter and reuse it from both
_is_thinking_model() and the litellm adapter’s inline model check. Keep the
existing behavior for matching model names like opus-4-8 and claude-3-7-sonnet,
but centralize the implementation so future model updates only need one change.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 476e1207-1e9e-4441-a0e7-729f7b3bd957
📒 Files selected for processing (3)
backend/app/services/llm/adapters/anthropic_adapter.pybackend/app/services/llm/adapters/litellm_adapter.pybackend/app/services/llm/factory.py
✅ Files skipped from review due to trivial changes (1)
- backend/app/services/llm/factory.py
| # Extended thinking models (opus-4-8, claude-3-7-sonnet) do not accept the temperature parameter | ||
| _m = (self.config.model or "").lower() | ||
| if "opus-4-8" not in _m and "claude-3-7-sonnet" not in _m: | ||
| kwargs["temperature"] = request.temperature if request.temperature is not None else self.config.temperature |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
stream_complete still sends temperature for thinking models.
The guard added here in _send_request is missing from stream_complete (line 362), which unconditionally sets kwargs["temperature"]. If a thinking model like claude-opus-4-8 is used with streaming, the API will receive an unsupported temperature parameter and may reject the request. Apply the same guard to the streaming path.
🐛 Proposed fix for `stream_complete`
kwargs = {
"model": self._litellm_model,
"messages": messages,
- "temperature": request.temperature if request.temperature is not None else self.config.temperature,
"max_tokens": request.max_tokens if request.max_tokens is not None else self.config.max_tokens,
"stream": True,
}
+
+ # Extended thinking models do not accept the temperature parameter
+ _m = (self.config.model or "").lower()
+ if "opus-4-8" not in _m and "claude-3-7-sonnet" not in _m:
+ kwargs["temperature"] = request.temperature if request.temperature is not None else self.config.temperature🤖 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 `@backend/app/services/llm/adapters/litellm_adapter.py` around lines 227 - 230,
The streaming path in stream_complete still unconditionally sets temperature,
which breaks thinking models that do not accept it. Mirror the model guard
already used in _send_request by checking self.config.model before adding
kwargs["temperature"] in stream_complete, and ensure the same opus-4-8 /
claude-3-7-sonnet exclusion applies to streaming requests.
…tion _should_attempt_finalizer 之前对 INCOMPLETE 直接返回 False,导致 RUNTIME_FINALIZATION_PROMPT 这条最终兜底的 continuation 从未被执行: 即使模型已完成审计、因超出 nudge 次数而结束,系统也不会给模型最后 一次机会通过 FinalizeFinding 提交结构化结果。 修复方案: - 仅对 FINALIZE_TOOL(已成功提交)返回 False - INCOMPLETE 状态也允许进入 finalizer prompts 循环,先尝试 RUNTIME_FINALIZATION_PROMPT;若仍未提交则回退至 _default_fallback_payload - 同步移除对 RuntimeTerminalAction 的引用(相关检查已不再需要), 清理未使用导入
变更说明
✨ 功能改进
GitHub 分支列表支持分页获取(
backend/app/services/scanner.py)原实现仅请求一页(最多 100 条),对于分支数量超过 100 的大型仓库会导致分支列表不完整。现改为循环分页拉取,直到获取全部分支后返回,解决分支丢失问题。
🐛 Bug 修复
分支选择器下拉框宽度异常(
frontend/src/components/ui/branch-selector.tsx)原来使用
w-[--radix-popover-trigger-width]会导致下拉框宽度随触发按钮宽度变化而过窄,改为min-w-[280px] max-w-[400px],保证分支名称完整展示。运行时会话未返回可解析 payload 时任务崩溃
LLM 未调用终止工具(如
FinalizeTriageBatch)时,_ensure_payload()因缺少兜底逻辑直接抛出错误,导致任务失败:修复涉及三处:
backend/app/services/agent_runtime/specs.py:新增_triage_fallback_payload,build_triage_runtime_spec()的fallback_payload_builder从None改为该函数backend/app/api/v1/endpoints/agent_tasks.py:continue_session_until_report_payload调用补充fallback_payload_builder=lambda _: Nonebackend/app/services/finding_runtime/bridge.py:删除_default_finalizer_prompts()中永远不可达的死代码块Opus 4.8 不支持 temperature 参数及 assistant prefill(fix issue #22)
backend/app/services/llm/factory.py:Claude 模型支持列表新增claude-opus-4-8backend/app/services/llm/adapters/anthropic_adapter.py:新增_is_thinking_model(),对 extended thinking 模型(opus-4-8、claude-3-7-sonnet)自动跳过temperature参数并移除末尾 assistant 消息(prefill 不兼容)backend/app/services/llm/adapters/litellm_adapter.py:同类模型跳过temperature传递🔧 依赖更新
httpx升级为httpx[socks],支持通过 SOCKS 代理访问 GitHub API⚙️ 本地开发配置
docker-compose.override.yml改为高端口映射方案,避免与本机服务端口冲突测试说明
claude-opus-4-8模型时不再报 temperature 参数错误Summary by CodeRabbit