|
| 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 |
0 commit comments