diff --git a/CLAUDE.md b/CLAUDE.md index f70c7bd3..11942dc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,9 +27,9 @@ make format # ruff format + ruff check --fix | Graph | Entrypoint | Purpose | |---|---|---| -| `agent` | `agent.server:get_agent` | Main coding agent (Slack/Linear/GitHub-triggered). | -| `reviewer` | `agent.reviewer:get_reviewer_agent` | Read-only PR reviewer. Findings model + `publish_review`. | -| `analyzer` | `agent.analyzer:get_analyzer` | Learns per-repo reviewer style from historical PRs and this reviewer's own finding outcomes. | +| `agent` | `agent.server:traced_agent` (wraps `get_agent`) | Main coding agent (Slack/Linear/GitHub-triggered). | +| `reviewer` | `agent.reviewer:traced_reviewer_agent` (wraps `get_reviewer_agent`) | Read-only PR reviewer. Findings model + `publish_review`. | +| `analyzer` | `agent.analyzer:traced_analyzer` (wraps `get_analyzer`) | Learns per-repo reviewer style from historical PRs and this reviewer's own finding outcomes. | The FastAPI app is `agent.webapp:app`. diff --git a/INSTALLATION.md b/INSTALLATION.md index 7cc25ecc..c76287b3 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -632,9 +632,9 @@ The `langgraph.json` at the project root defines the three graphs and the HTTP a ```json { "graphs": { - "agent": "agent.server:get_agent", - "reviewer": "agent.reviewer:get_reviewer_agent", - "analyzer": "agent.analyzer:get_analyzer" + "agent": "agent.server:traced_agent", + "reviewer": "agent.reviewer:traced_reviewer_agent", + "analyzer": "agent.analyzer:traced_analyzer" }, "http": { "app": "agent.webapp:app" diff --git a/agent/analyzer.py b/agent/analyzer.py index d89d264f..0f8cc1da 100644 --- a/agent/analyzer.py +++ b/agent/analyzer.py @@ -46,6 +46,7 @@ from .utils.github_app import get_github_app_installation_token from .utils.model import DEFAULT_LLM_REASONING, make_model, provider_model_kwargs from .utils.sandbox_paths import aresolve_sandbox_work_dir from .utils.sandbox_state import unwrap_sandbox_backend +from .utils.tracing import REVIEW_TRACING_PROJECT, traced_graph_factory logger = logging.getLogger(__name__) @@ -148,3 +149,6 @@ async def get_analyzer(config: RunnableConfig) -> Pregel: ToolErrorMiddleware(), ], ).with_config(config) + + +traced_analyzer = traced_graph_factory(get_analyzer, REVIEW_TRACING_PROJECT) diff --git a/agent/reviewer.py b/agent/reviewer.py index 41784165..dfec50be 100644 --- a/agent/reviewer.py +++ b/agent/reviewer.py @@ -74,6 +74,7 @@ from .utils.github_token import cache_github_token_for_thread from .utils.model import DEFAULT_LLM_REASONING, make_model, provider_model_kwargs from .utils.repo_prep import materialize_trusted_skills, prepare_review_repo from .utils.sandbox_paths import aresolve_sandbox_work_dir +from .utils.tracing import REVIEW_TRACING_PROJECT, traced_graph_factory REVIEWER_PROMPT_TEMPLATE = """You are a specialized code reviewer agent. Your job is to review one GitHub PR and publish a single review. @@ -1052,3 +1053,6 @@ async def get_reviewer_agent(config: RunnableConfig) -> Pregel: settle_review_check_on_exit, ], ).with_config(config) + + +traced_reviewer_agent = traced_graph_factory(get_reviewer_agent, REVIEW_TRACING_PROJECT) diff --git a/agent/server.py b/agent/server.py index 94481921..afc44071 100644 --- a/agent/server.py +++ b/agent/server.py @@ -95,6 +95,7 @@ from .utils.model import ( ) from .utils.sandbox import create_sandbox from .utils.sandbox_paths import aresolve_sandbox_work_dir +from .utils.tracing import AGENT_TRACING_PROJECT, traced_graph_factory client = get_client() @@ -726,3 +727,6 @@ async def get_agent(config: RunnableConfig) -> Pregel: SanitizeThinkingBlocksMiddleware(), ], ).with_config(config) + + +traced_agent = traced_graph_factory(get_agent, AGENT_TRACING_PROJECT) diff --git a/agent/utils/tracing.py b/agent/utils/tracing.py new file mode 100644 index 00000000..24beac91 --- /dev/null +++ b/agent/utils/tracing.py @@ -0,0 +1,24 @@ +"""Per-graph LangSmith tracing-project routing for langgraph.json entrypoints.""" + +import contextlib +from collections.abc import AsyncIterator, Awaitable, Callable + +import langsmith as ls +from langgraph.graph.state import RunnableConfig +from langgraph.pregel import Pregel + +AGENT_TRACING_PROJECT = "open-swe-agent" +REVIEW_TRACING_PROJECT = "open-swe-review" + + +def traced_graph_factory( + factory: Callable[[RunnableConfig], Awaitable[Pregel]], + project_name: str, +) -> Callable[[RunnableConfig], contextlib.AbstractAsyncContextManager[Pregel]]: + @contextlib.asynccontextmanager + async def entrypoint(config: RunnableConfig) -> AsyncIterator[Pregel]: + graph = await factory(config) + with ls.tracing_context(project_name=project_name): + yield graph + + return entrypoint diff --git a/langgraph.json b/langgraph.json index 7e5f491e..5a08e676 100644 --- a/langgraph.json +++ b/langgraph.json @@ -3,9 +3,9 @@ "python_version": "3.12", "api_version": "0.10.0rc3", "graphs": { - "agent": "agent.server:get_agent", - "reviewer": "agent.reviewer:get_reviewer_agent", - "analyzer": "agent.analyzer:get_analyzer", + "agent": "agent.server:traced_agent", + "reviewer": "agent.reviewer:traced_reviewer_agent", + "analyzer": "agent.analyzer:traced_analyzer", "scheduler": "agent.scheduler:get_scheduler" }, "dependencies": [