open-swe/agent/chat.py

176 lines
6.7 KiB
Python
Raw Normal View History

feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
"""Chat graph.
A read-only "chat with this PR" agent for the review UI. Unlike the main agent
and reviewer, it has **no sandbox**: it answers questions about a single pull
request using the diff, the published review findings, and read-only access to
the repository over the GitHub API.
PR context (diff, findings, overview) is seeded as virtual files under ``/pr/``
into the ``files`` state channel by the dashboard chat proxy
(``agent/dashboard/review_chat_api.py``); the built-in ``read_file``/``grep``
tools operate over those. Repo coordinates and the reviewer thread id arrive in
``configurable``; a repo-scoped GitHub App token is resolved here so the
GitHub-backed tools never receive a user credential.
"""
# ruff: noqa: E402
from __future__ import annotations
import logging
import warnings
from langgraph.graph.state import RunnableConfig
from langgraph.pregel import Pregel
warnings.filterwarnings("ignore", module="langchain_core._api.deprecation")
warnings.filterwarnings("ignore", message=".*Pydantic V1.*", category=UserWarning)
from deepagents import create_deep_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
from .dashboard.options import (
SUPPORTED_MODEL_IDS,
gate_fable_model,
model_supports_effort,
)
from .dashboard.team_settings import (
get_effective_gateway_enabled,
get_team_default_model,
get_team_fable_enabled,
)
feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
from .middleware import (
ExcludeToolsMiddleware,
feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) (#155) * feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) Ports four upstream commits that add opt-in LLM call routing through the LangSmith Gateway, preserving fork conventions (Bedrock/Fireworks model IDs, no-agent-attribution, bun toolchain). - #1671 (e9dc6e01): opt-in gateway routing — new gateway.py, team-settings toggle, admin UI section, wired into make_model for all graph entrypoints - #1673 (702ef908): dedicated LANGSMITH_GATEWAY_API_KEY precedence over platform LANGSMITH_API_KEY - #1674 (5f7c2f46): fix Fireworks gateway base URL to /fireworks (bare host, SDK appends /v1/chat/completions) + SanitizeFireworksMessagesMiddleware - #1678 (73b7d1c0): fix OpenAI Responses reasoning replay — SanitizeOpenAIResponsesMiddleware, store/include config for encrypted reasoning content, reasoning_effort coercion for Chat Completions fallback Refs #134 * fix: downgrade gateway not-routed log to debug, add Bedrock UI note, add sanitizer parity - Downgrade logger.warning to logger.debug in gateway_overrides for not-routed providers and missing API key (Bedrock is the default provider in this fork, so these are expected steady states) - Add Bedrock to the LLMGatewaySection route-toggle description so admins know it is not routed through the gateway - Add SanitizeOpenAIResponsesMiddleware to chat.py for parity with server.py and reviewer.py - Restore the Bedrock region comment in model.py that explains the AWS_REGION / AWS_DEFAULT_REGION precedence Refs #138 --------- Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
2026-07-09 14:44:15 -04:00
SanitizeFireworksMessagesMiddleware,
SanitizeOpenAIResponsesMiddleware,
feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
SanitizeThinkingBlocksMiddleware,
SanitizeToolInputsMiddleware,
ToolErrorMiddleware,
)
from .server import (
DEFAULT_LLM_MAX_TOKENS,
DEFAULT_RECURSION_LIMIT,
graph_loaded_for_execution,
)
from .tools import (
fetch_url,
list_review_findings,
read_repo_file,
search_repo_code,
web_search,
)
from .utils.github_app import get_github_app_installation_token
from .utils.model import DEFAULT_LLM_REASONING, make_model, provider_model_kwargs
from .utils.tracing import AGENT_TRACING_PROJECT, traced_graph_factory
logger = logging.getLogger(__name__)
CHAT_MODEL_CALL_LIMIT = 100
# Read-only: the chat agent never mutates files or runs shell commands. These are
# injected by deepagents' FilesystemMiddleware and stripped before the model sees
# them (there is no sandbox, so ``execute`` would error anyway).
_EXCLUDED_TOOLS = frozenset({"execute", "write_file", "edit_file"})
CHAT_PROMPT = """You are a code-review chat assistant. You help the author and reviewers \
understand one GitHub pull request: `{repo_owner}/{repo_name}` #{pr_number}.
You have NO sandbox and cannot run code, execute tests, commit, or open PRs. You \
reason from the PR's diff, the published review findings, and read-only access to \
the repository.
Context already loaded as virtual files (use `read_file`, `ls`, `grep`):
- `/pr/overview.md` — title, description, author, branches, head commit, change stats.
- `/pr/diff.patch` — the unified diff under review.
- `/pr/findings.md` — the reviewer's published findings, rendered for reading.
Tools:
- `read_repo_file(path, ref)` — read any repo file/dir at a commit (defaults to the \
PR head). Use it to inspect callers, definitions, and neighboring code beyond the diff.
- `search_repo_code(query)` — find a symbol or phrase across the repository.
- `list_review_findings(status_filter)` — the live findings (open/resolved/dismissed) \
with severity, confidence, and resolution notes.
- `web_search`, `fetch_url` — for external docs or standards.
Guidance:
- Be concrete and cite specific files and line numbers from the diff.
- Ground claims about the review in the actual findings; don't invent issues.
- When you propose a change, describe it precisely — you cannot apply it yourself.
- Keep answers focused and skimmable. Match the depth of the question.
"""
async def _resolve_chat_model(configurable: dict) -> tuple[str, str]:
model_id = configurable.get("chat_model_id")
effort = configurable.get("chat_effort")
if (
isinstance(model_id, str)
and model_id in SUPPORTED_MODEL_IDS
and isinstance(effort, str)
and model_supports_effort(model_id, effort)
):
return model_id, effort
# Team review-chat default, which itself inherits the Agent default if unset.
return await get_team_default_model("chat")
async def get_chat_agent(config: RunnableConfig) -> Pregel:
"""Get a read-only PR chat agent. No sandbox; PR context comes via config."""
thread_id = config["configurable"].get("thread_id")
config["recursion_limit"] = DEFAULT_RECURSION_LIMIT
if thread_id is None or not graph_loaded_for_execution(config):
return create_deep_agent(system_prompt="", tools=[]).with_config(config)
configurable = config["configurable"]
repo_owner = str(configurable.get("chat_repo_owner") or "")
repo_name = str(configurable.get("chat_repo_name") or "")
pr_number = configurable.get("chat_pr_number")
# Resolve a repo-scoped, read-only App token in-graph so a user credential is
# never passed through the run config. Tools read it from configurable.
token = await get_github_app_installation_token(repositories=[repo_name] if repo_name else None)
if isinstance(token, str) and token:
configurable["chat_github_token"] = token
model_id, effort = await _resolve_chat_model(configurable)
model_id, effort = gate_fable_model(
model_id, effort, fable_enabled=await get_team_fable_enabled()
)
feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) (#155) * feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) Ports four upstream commits that add opt-in LLM call routing through the LangSmith Gateway, preserving fork conventions (Bedrock/Fireworks model IDs, no-agent-attribution, bun toolchain). - #1671 (e9dc6e01): opt-in gateway routing — new gateway.py, team-settings toggle, admin UI section, wired into make_model for all graph entrypoints - #1673 (702ef908): dedicated LANGSMITH_GATEWAY_API_KEY precedence over platform LANGSMITH_API_KEY - #1674 (5f7c2f46): fix Fireworks gateway base URL to /fireworks (bare host, SDK appends /v1/chat/completions) + SanitizeFireworksMessagesMiddleware - #1678 (73b7d1c0): fix OpenAI Responses reasoning replay — SanitizeOpenAIResponsesMiddleware, store/include config for encrypted reasoning content, reasoning_effort coercion for Chat Completions fallback Refs #134 * fix: downgrade gateway not-routed log to debug, add Bedrock UI note, add sanitizer parity - Downgrade logger.warning to logger.debug in gateway_overrides for not-routed providers and missing API key (Bedrock is the default provider in this fork, so these are expected steady states) - Add Bedrock to the LLMGatewaySection route-toggle description so admins know it is not routed through the gateway - Add SanitizeOpenAIResponsesMiddleware to chat.py for parity with server.py and reviewer.py - Restore the Bedrock region comment in model.py that explains the AWS_REGION / AWS_DEFAULT_REGION precedence Refs #138 --------- Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
2026-07-09 14:44:15 -04:00
use_gateway = await get_effective_gateway_enabled()
feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
model_kwargs = provider_model_kwargs(
model_id,
effort,
max_tokens=DEFAULT_LLM_MAX_TOKENS,
openai_reasoning_default=DEFAULT_LLM_REASONING,
)
system_prompt = CHAT_PROMPT.format(
repo_owner=repo_owner or "<owner>",
repo_name=repo_name or "<repo>",
pr_number=pr_number if isinstance(pr_number, int) else "?",
)
return create_deep_agent(
feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) (#155) * feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) Ports four upstream commits that add opt-in LLM call routing through the LangSmith Gateway, preserving fork conventions (Bedrock/Fireworks model IDs, no-agent-attribution, bun toolchain). - #1671 (e9dc6e01): opt-in gateway routing — new gateway.py, team-settings toggle, admin UI section, wired into make_model for all graph entrypoints - #1673 (702ef908): dedicated LANGSMITH_GATEWAY_API_KEY precedence over platform LANGSMITH_API_KEY - #1674 (5f7c2f46): fix Fireworks gateway base URL to /fireworks (bare host, SDK appends /v1/chat/completions) + SanitizeFireworksMessagesMiddleware - #1678 (73b7d1c0): fix OpenAI Responses reasoning replay — SanitizeOpenAIResponsesMiddleware, store/include config for encrypted reasoning content, reasoning_effort coercion for Chat Completions fallback Refs #134 * fix: downgrade gateway not-routed log to debug, add Bedrock UI note, add sanitizer parity - Downgrade logger.warning to logger.debug in gateway_overrides for not-routed providers and missing API key (Bedrock is the default provider in this fork, so these are expected steady states) - Add Bedrock to the LLMGatewaySection route-toggle description so admins know it is not routed through the gateway - Add SanitizeOpenAIResponsesMiddleware to chat.py for parity with server.py and reviewer.py - Restore the Bedrock region comment in model.py that explains the AWS_REGION / AWS_DEFAULT_REGION precedence Refs #138 --------- Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
2026-07-09 14:44:15 -04:00
model=make_model(model_id, use_gateway=use_gateway, **model_kwargs),
feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
system_prompt=system_prompt,
tools=[
read_repo_file,
search_repo_code,
list_review_findings,
web_search,
fetch_url,
],
middleware=[
SanitizeToolInputsMiddleware(),
ModelCallLimitMiddleware(run_limit=CHAT_MODEL_CALL_LIMIT, exit_behavior="end"),
ToolErrorMiddleware(),
ExcludeToolsMiddleware(excluded=_EXCLUDED_TOOLS),
feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) (#155) * feat: port LangSmith LLM Gateway routing from upstream (#1671, #1673, #1674, #1678) Ports four upstream commits that add opt-in LLM call routing through the LangSmith Gateway, preserving fork conventions (Bedrock/Fireworks model IDs, no-agent-attribution, bun toolchain). - #1671 (e9dc6e01): opt-in gateway routing — new gateway.py, team-settings toggle, admin UI section, wired into make_model for all graph entrypoints - #1673 (702ef908): dedicated LANGSMITH_GATEWAY_API_KEY precedence over platform LANGSMITH_API_KEY - #1674 (5f7c2f46): fix Fireworks gateway base URL to /fireworks (bare host, SDK appends /v1/chat/completions) + SanitizeFireworksMessagesMiddleware - #1678 (73b7d1c0): fix OpenAI Responses reasoning replay — SanitizeOpenAIResponsesMiddleware, store/include config for encrypted reasoning content, reasoning_effort coercion for Chat Completions fallback Refs #134 * fix: downgrade gateway not-routed log to debug, add Bedrock UI note, add sanitizer parity - Downgrade logger.warning to logger.debug in gateway_overrides for not-routed providers and missing API key (Bedrock is the default provider in this fork, so these are expected steady states) - Add Bedrock to the LLMGatewaySection route-toggle description so admins know it is not routed through the gateway - Add SanitizeOpenAIResponsesMiddleware to chat.py for parity with server.py and reviewer.py - Restore the Bedrock region comment in model.py that explains the AWS_REGION / AWS_DEFAULT_REGION precedence Refs #138 --------- Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
2026-07-09 14:44:15 -04:00
SanitizeOpenAIResponsesMiddleware(),
SanitizeFireworksMessagesMiddleware(),
feat: chat with your PR on the review page (#1534) * feat: chat with your PR on the review page Add a sandbox-less `chat` graph that answers questions about a single PR from its diff, the published review findings, and read-only GitHub access. - agent/chat.py: deepagents graph, no sandbox (default StateBackend, file mutation + execute tools excluded). PR context is seeded as virtual files under /pr/; a repo-scoped App token is resolved in-graph. - tools: read_repo_file, search_repo_code, list_review_findings. - dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph stream/commands/state/history proxy pinned to the chat assistant, seeds diff/findings/overview on first run. Gated by repo access. - UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon). * feat: admin setting for review-chat default model Add a 'Open SWE Review Chat' default to team settings (default_chat_model / default_chat_reasoning_effort). get_team_default_model("chat") inherits the Agent default when unset; the chat graph resolves through it. Admin RolePicker gains an 'Agent default' inherit option that clears the override. * feat: multi-conversation review chat (tabs, new chat, history) Replace the single per-PR chat thread with multiple per-user conversations: - threads minted client-side; first message persists with a title derived from the prompt. - list + delete endpoints; chat panel gets a tab strip (history), new-chat (+), close (x), refresh, an intro greeting, and suggested prompts. - get_review_chat now returns availability only (ids are client-minted). * ui fixes * ui: review-chat history dropdown, full-width AI replies, resizable side panel * fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change --------- Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
SanitizeThinkingBlocksMiddleware(),
],
).with_config(config)
traced_chat_agent = traced_graph_factory(get_chat_agent, AGENT_TRACING_PROJECT)