Skip to content

Commit 6d9fcb7

Browse files
abrichrclaude
andauthored
feat: Weave (W&B) integration for LLM/agent tracing (#228)
Weave auto-patches OpenAI and Anthropic clients after weave.init(), giving automatic tracing of every VLM call with prompts, responses, costs, and latency in hierarchical trace trees. Integration points: - vlm_call() — @weave_op: all planner/grounder/evaluator calls traced - vlm_judge() — @weave_op: milestone evaluation traced - DemoExecutor.run() + _execute_step() — @weave_op: episode trace tree - PlannerGrounderAgent.act() + _call_planner() — @weave_op: agent decisions - WandbLogger.init() — calls weave_init() alongside wandb.init() When weave is not installed, all decorators are zero-cost passthrough. weave>=0.50.0 added to [wandb] optional extra. 76/76 tests pass. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8481198 commit 6d9fcb7

8 files changed

Lines changed: 128 additions & 1 deletion

File tree

openadapt_evals/agents/demo_executor.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@
3030
from openadapt_evals.adapters.base import BenchmarkAction, BenchmarkObservation
3131
from openadapt_evals.demo_library import Demo, DemoStep
3232

33+
try:
34+
from openadapt_evals.integrations.weave_integration import weave_op
35+
except ImportError:
36+
weave_op = lambda fn: fn # noqa: E731
37+
3338
logger = logging.getLogger(__name__)
3439

3540

@@ -72,6 +77,7 @@ def __init__(
7277
self._step_delay = step_delay
7378
self._recovery_budget = recovery_budget
7479

80+
@weave_op
7581
def run(
7682
self,
7783
env, # RLEnvironment
@@ -176,6 +182,7 @@ def run(
176182

177183
return score, screenshots
178184

185+
@weave_op
179186
def _execute_step(
180187
self,
181188
step: DemoStep,

openadapt_evals/agents/planner_grounder_agent.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@
6767
format_accessibility_tree,
6868
)
6969

70+
try:
71+
from openadapt_evals.integrations.weave_integration import weave_op
72+
except ImportError:
73+
weave_op = lambda fn: fn # noqa: E731
74+
7075
if TYPE_CHECKING:
7176
from openadapt_evals.training.planner_cache import PlannerCache
7277
from openadapt_evals.training.trajectory_logger import PlannerTrajectoryLogger
@@ -257,6 +262,7 @@ def __init__(
257262
grounder_provider,
258263
)
259264

265+
@weave_op
260266
def act(
261267
self,
262268
observation: BenchmarkObservation,
@@ -660,6 +666,7 @@ def _check_action_loop(self) -> str:
660666

661667
return ""
662668

669+
@weave_op
663670
def _call_planner(
664671
self,
665672
observation: BenchmarkObservation,

openadapt_evals/integrations/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This module provides integrations with:
44
- Weights & Biases (wandb) for experiment tracking and report generation
55
- W&B callback functions for the standalone GRPO trainer
6+
- Weave (W&B) for LLM/agent execution tracing
67
"""
78

89
from openadapt_evals.integrations.wandb_logger import WandbLogger
@@ -33,9 +34,18 @@
3334
generate_demo_report = None
3435
_REPORTS_AVAILABLE = False
3536

37+
# Weave tracing (optional — no-op when weave not installed)
38+
try:
39+
from openadapt_evals.integrations.weave_integration import weave_init, weave_op
40+
except ImportError:
41+
weave_init = None
42+
weave_op = None
43+
3644
__all__ = [
3745
"WandbLogger",
3846
"WandbReportGenerator",
47+
"weave_init",
48+
"weave_op",
3949
"wandb_model_loaded",
4050
"wandb_rollout_logger",
4151
"wandb_step_logger",

openadapt_evals/integrations/wandb_logger.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ def init(self) -> wandb.Run:
135135
benchmark = self.config.get("benchmark", "eval")
136136
self.name = f"{benchmark}_{model_id}_{timestamp}"
137137

138+
# Initialize Weave tracing alongside W&B — auto-patches
139+
# OpenAI/Anthropic clients for VLM call tracing.
140+
try:
141+
from openadapt_evals.integrations.weave_integration import weave_init
142+
weave_init(self.project)
143+
except Exception:
144+
pass
145+
138146
self._run = wandb.init(
139147
project=self.project,
140148
entity=self.entity,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Weave (W&B) integration for LLM/agent tracing.
2+
3+
Weave auto-patches OpenAI and Anthropic clients after ``weave.init()``,
4+
giving automatic tracing of every VLM call (planner, grounder, evaluator)
5+
with prompts, responses, costs, and latency in hierarchical trace trees.
6+
7+
Usage:
8+
from openadapt_evals.integrations.weave_integration import weave_init, weave_op
9+
10+
# Initialize once at startup (alongside wandb.init if used)
11+
weave_init("openadapt-evals")
12+
13+
# Decorate functions for explicit trace tree structure
14+
@weave_op
15+
def my_agent_step(screenshot, instruction):
16+
...
17+
18+
When weave is not installed, ``weave_init`` is a no-op and ``weave_op``
19+
passes through the function unchanged. No runtime cost.
20+
"""
21+
22+
from __future__ import annotations
23+
24+
import functools
25+
import logging
26+
from typing import Any, Callable, TypeVar
27+
28+
logger = logging.getLogger(__name__)
29+
30+
F = TypeVar("F", bound=Callable[..., Any])
31+
32+
_weave_initialized = False
33+
34+
35+
def weave_init(project: str = "openadapt-evals") -> bool:
36+
"""Initialize Weave tracing.
37+
38+
Call once at startup. Auto-patches OpenAI and Anthropic clients
39+
so all subsequent VLM calls are traced automatically.
40+
41+
Args:
42+
project: Weave project name (appears in W&B UI).
43+
44+
Returns:
45+
True if Weave initialized successfully, False otherwise.
46+
"""
47+
global _weave_initialized
48+
if _weave_initialized:
49+
return True
50+
51+
try:
52+
import weave
53+
weave.init(project)
54+
_weave_initialized = True
55+
logger.info("Weave tracing initialized (project=%s)", project)
56+
return True
57+
except ImportError:
58+
logger.debug("weave not installed — tracing disabled")
59+
return False
60+
except Exception as exc:
61+
logger.warning("Weave init failed: %s", exc)
62+
return False
63+
64+
65+
def weave_op(fn: F) -> F:
66+
"""Decorator that wraps a function with ``@weave.op`` if available.
67+
68+
When weave is installed, the decorated function appears in trace
69+
trees with its arguments, return value, and execution time.
70+
71+
When weave is not installed, this is a zero-cost passthrough.
72+
"""
73+
try:
74+
import weave
75+
return weave.op(fn) # type: ignore[return-value]
76+
except ImportError:
77+
return fn
78+
except Exception:
79+
return fn

openadapt_evals/vlm.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
_DEFAULT_TIMEOUT = 120 # seconds — prevents indefinite hangs on API calls
1717

1818

19+
try:
20+
from openadapt_evals.integrations.weave_integration import weave_op
21+
except ImportError:
22+
weave_op = lambda fn: fn # noqa: E731
23+
24+
25+
@weave_op
1926
def vlm_call(
2027
prompt: str,
2128
*,

openadapt_evals/vlm_evaluator.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
Respond with ONLY the JSON object."""
4343

4444

45+
try:
46+
from openadapt_evals.integrations.weave_integration import weave_op
47+
except ImportError:
48+
weave_op = lambda fn: fn # noqa: E731
49+
50+
51+
@weave_op
4552
def vlm_judge(
4653
screenshot: bytes,
4754
description: str,

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ viewer = [
8989
"flask-cors>=4.0.0",
9090
]
9191
wandb = [
92-
# Weights & Biases for experiment tracking
92+
# Weights & Biases for experiment tracking + Weave agent tracing
9393
"wandb>=0.16.0",
94+
# Weave: LLM/agent tracing with auto-patched OpenAI/Anthropic clients
95+
"weave>=0.50.0",
9496
]
9597
training = [
9698
# Perceptual hashing for planner cache (falls back to MD5 if not installed)

0 commit comments

Comments
 (0)