Skip to content

Centralize agent logging behind scoped callbacks and remove artifact_root tramp data#23935

Open
luisorofino wants to merge 1 commit into
loa/run-tasksfrom
loa/no-artifact-root
Open

Centralize agent logging behind scoped callbacks and remove artifact_root tramp data#23935
luisorofino wants to merge 1 commit into
loa/run-tasksfrom
loa/no-artifact-root

Conversation

@luisorofino
Copy link
Copy Markdown
Contributor

@luisorofino luisorofino commented Jun 5, 2026

What does this PR do?

Reworks how the ddev/ai agent framework does run logging, replacing path-threading with a single, identity-aware, callback-driven logger. No user-facing behavior changes — this is an internal architecture refactor of the agent runtime.

Highlights:

  • New AgentScope (agent/scope.py) — a small (owner_id, AgentRole) value carrying explicit agent identity, where AgentRole is one of PHASE, SUBAGENT, or GOAL_REVIEWER. This identity now travels with each event instead of being reconstructed from a path.
  • Scoped callbacks (callbacks/callbacks.py) — agent-tier callbacks now carry an AgentScope. Added on_agent_start, renamed on_completeon_agent_finish and on_erroron_agent_error, and added Callbacks.with_set(...) for composition.
  • Run-wide AgentLogger (runtime/agent_log.py) — one long-lived observer, created once by the orchestrator, that demultiplexes the callback stream into one append-only JSONL file per agent keyed by AgentScope. It is the only thing that knows the log root.
  • ReActProcessFactory (react/factory.py) — binds the runtime builder and the run-wide Callbacks once and exposes create(scope, agent_config, system_prompt). It injects itself into the runtime it builds so a nested spawn_subagent can create properly-scoped children. Replaces the loose runtime_builder + callbacks primitives that callers previously had to re-assemble.
  • ReActProcess (react/process.py) — now requires a scope, fires on_agent_start, stamps every callback with its scope, and gained run_once(...) for single-turn calls so phases no longer hand-fire callbacks for memory steps.
  • Simplified spawn_subagent — no more manual path/log orchestration; it just derives a child AgentScope and asks the injected factory for a scoped child process.
  • Deleted artifact_root everywhere and removed the old per-agent tools/agents/agent_logger.py.

Motivation

The previous design had a few related smells:

  1. artifact_root tramp data. A filesystem path was threaded through the orchestrator → resources → phases → agent build → tool registry → spawn_subagent, purely so each layer could reconstruct a per-agent logging directory. Most layers didn't use the path; they just passed it deeper. Classic tramp data.
  2. Manual, per-agent logging. Each agent/subagent built its own logger and path, and phases/tools fired log writes by hand. Logging concerns leaked into business logic — spawn_subagent orchestrated directories and log lines, and phases manually logged memory steps.
  3. Identity-blind callbacks. Callbacks didn't carry which agent an event belonged to, so a single run-wide observer couldn't attribute events. That's what forced per-agent loggers and the path threading in the first place.
  4. Loose wiring. Layers received runtime_builder and callbacks as separate primitives and had to re-derive how to construct child processes, duplicating setup.

This PR pushes identity into the event stream (AgentScope) and centralizes logging behind callbacks, so the path lives in exactly one place (the orchestrator) and everything downstream is identity-aware and path-free.

Notes / follow-ups

The code is intentionally not perfect yet — this is a step, not the destination.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Add qa/required if this PR needs QA validation, or qa/skip-qa if it does not. Exactly one of the two is required.
  • If you need to backport this PR to another branch, you can add the backport/<branch-name> label to the PR and it will automatically open a backport PR once this one is merged

@dd-octo-sts dd-octo-sts Bot added the ddev label Jun 5, 2026
@datadog-datadog-prod-us1-2
Copy link
Copy Markdown

datadog-datadog-prod-us1-2 Bot commented Jun 5, 2026

Pipelines  Tests  Code Coverage

Fix all issues with BitsAI

⚠️ Warnings

🚦 7 Pipeline jobs failed

PR All | test / j06ca546 / SNMP   View in Datadog   GitHub Actions

PR All | test / j3a8e004 (py3.13-linux-FreeTDS-2019-ha) / SQL Server on Linux-py3.13-linux-FreeTDS-2019-ha   View in Datadog   GitHub Actions

PR All | test / j46da136 / JBoss_WildFly   View in Datadog   GitHub Actions

View all 7 failed jobs.

🧪 20 Tests failed in 1 job

PR All | run   GitHub Actions

test_bulk_table from test_check.py   View in Datadog (Fix with Cursor)
HTTPSConnectionPool(host=&#39;ddintegrations.blob.core.windows.net&#39;, port=443): Max retries exceeded with url: /snmp/cisco-3850.snmprec (Caused by NameResolutionError(&#34;HTTPSConnection(host=&#39;ddintegrations.blob.core.windows.net&#39;, port=443): Failed to resolve &#39;ddintegrations.blob.core.windows.net&#39; ([Errno -2] Name or service not known)&#34;))
test_cast_metrics from test_check.py   View in Datadog (Fix with Cursor)
HTTPSConnectionPool(host=&#39;ddintegrations.blob.core.windows.net&#39;, port=443): Max retries exceeded with url: /snmp/cisco-3850.snmprec (Caused by NameResolutionError(&#34;HTTPSConnection(host=&#39;ddintegrations.blob.core.windows.net&#39;, port=443): Failed to resolve &#39;ddintegrations.blob.core.windows.net&#39; ([Errno -2] Name or service not known)&#34;))

View all 20 test failures

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 97.80%
Overall Coverage: 88.02% (-0.00%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 480793d | Docs | Datadog PR Page | Give us feedback!

@luisorofino luisorofino changed the title Delete artifact_root, create AgentLogger for runtime, create ReActPro… Centralize agent logging behind scoped callbacks and remove artifact_root tramp data Jun 5, 2026
@luisorofino luisorofino added the qa/skip-qa Automatically skip this PR for the next QA label Jun 5, 2026
@luisorofino luisorofino force-pushed the loa/no-artifact-root branch from 14218e4 to 480793d Compare June 5, 2026 15:28
@luisorofino luisorofino marked this pull request as ready for review June 5, 2026 15:29
@luisorofino luisorofino requested a review from a team as a code owner June 5, 2026 15:29
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented Jun 5, 2026

Validation Report

All 21 validations passed.

Show details
Validation Description Status
agent-reqs Verify check versions match the Agent requirements file
ci Validate CI configuration and code coverage settings
codeowners Validate every integration has a CODEOWNERS entry
config Validate default configuration files against spec.yaml
dep Verify dependency pins are consistent and Agent-compatible
http Validate integrations use the HTTP wrapper correctly
imports Validate check imports do not use deprecated modules
integration-style Validate check code style conventions
jmx-metrics Validate JMX metrics definition files and config
labeler Validate PR labeler config matches integration directories
legacy-signature Validate no integration uses the legacy Agent check signature
license-headers Validate Python files have proper license headers
licenses Validate third-party license attribution list
metadata Validate metadata.csv metric definitions
models Validate configuration data models match spec.yaml
openmetrics Validate OpenMetrics integrations disable the metric limit
package Validate Python package metadata and naming
qa-label Validate the pull request declares whether it needs QA for the next Agent release
readmes Validate README files have required sections
saved-views Validate saved view JSON file structure and fields
version Validate version consistency between package and changelog

View full run

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 480793d456

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

"""Run a single agent turn with no tools available, firing scoped callbacks.
The caller is responsible for token accounting."""
await self._callbacks.fire_before_agent_send(self._scope, prompt, 1)
response = await self._agent.send(prompt, allowed_tools=[])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Report run_once send failures to scoped callbacks

When the single-turn path is used for the phase memory step, an agent.send() failure here propagates without firing fire_agent_error, unlike start(). In that scenario the run-wide AgentLogger never writes the error/failed-finish records for the phase, so the centralized agent log shows the last successful ReAct loop but omits the failing memory LLM call that aborted the phase; wrap this send in the same BaseException notification pattern used by start().

Useful? React with 👍 / 👎.

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

Labels

ddev qa/skip-qa Automatically skip this PR for the next QA team/agent-integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant