Skip to content

Conversation

@ultmaster
Copy link
Contributor

@ultmaster ultmaster commented Dec 14, 2025

The current Weave implementation bases on registering a handler in finish_call. This suffers from drawbacks that the recorded call is not complete, as Weave will internally creates a different CallSchema object and upload to server. In parallel, Weave also provides (implicitly) a way for customizing TraceServer. If properly substituting the TraceServer with a customized implementation, we would be able to get all the traces.

This PR also refactors the emitter interface. The current approach heavily depends on otel, which is not a must for Weave. By decoupling emitter from tracer implementation (which now divides into Otel and non-Otel), and create_span and operation_context are added to tracer interface, we now have a way to clearly separate the creation of spans (via SpanCoreFields) from the propagation of spans (to tracers).

Copilot AI review requested due to automatic review settings December 14, 2025 17:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR reimplements the Weave tracer integration and unifies the emitter interface across different tracer backends. Key changes include:

  • Introduces a common SpanRecordingContext protocol and SpanCoreFields type for tracer-agnostic span creation
  • Refactors emitters (operation, message, exception, object, annotation) to use the unified interface
  • Replaces the old Weave tracer implementation with a new callback-based architecture using an in-memory trace server

Reviewed changes

Copilot reviewed 36 out of 38 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
agentlightning/tracer/weave.py Complete rewrite of Weave tracer using callback pattern and unified span recording interface
agentlightning/tracer/dummy.py New dummy tracer for testing without backend dependencies
agentlightning/tracer/base.py Added active tracer management and new abstract methods for span creation
agentlightning/tracer/otel.py Updated to implement unified span creation interface
agentlightning/emitter/*.py Refactored all emitters to use get_active_tracer() and SpanRecordingContext
agentlightning/utils/otel.py Added sanitization, flattening, and exception formatting utilities
agentlightning/types/tracer.py Added SpanCoreFields, SpanRecordingContext, and StatusCode types
agentlightning/instrumentation/weave.py Implemented comprehensive InMemoryWeaveTraceServer for testing
tests/**/*.py Updated tests to use new recording tracer patterns and unified interfaces
pyproject.toml Updated weave dependency version constraint and added setuptools override

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +309
with pytest.raises(ValueError):
complicated(1, "req", 7, 8, 9, kwonly="x", kwdefault="y", tag="value")
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The test now expects a ValueError to be raised but doesn't verify the actual result or behavior of the function. The original test verified the output result against expected values. Consider asserting on the specific error message or adding a separate test for the success case to ensure the function still works correctly when valid inputs are provided.

Copilot uses AI. Check for mistakes.
try:
weave_client.flush()
# It's possible that the call end futures are from a dedicated Weave thread pool,
await asyncio.gather(*[asyncio.wrap_future(future) for future in self._complete_call_futures])
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The asyncio.gather call attempts to wrap futures that may already be from a different event loop or thread pool. If the futures in self._complete_call_futures are from a dedicated Weave thread pool (as mentioned in the comment on line 372), wrapping them with asyncio.wrap_future could fail or cause deadlocks. Consider checking if the futures are already asyncio futures before wrapping, or handling the case where they originate from a different executor.

Suggested change
await asyncio.gather(*[asyncio.wrap_future(future) for future in self._complete_call_futures])
await asyncio.gather(*[
future if isinstance(future, asyncio.Future) else asyncio.wrap_future(future)
for future in self._complete_call_futures
])

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about this. Let's test it out later.

Comment on lines 412 to 415
assert op.span().start_time < op.span().end_time # type: ignore
assert op.span().start_time < time.time() # type: ignore
assert op.span().end_time < time.time() # type: ignore
assert op.span().start_time < op.span().end_time # type: ignore
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The assertions on lines 412 and 415 are duplicates - both check that start_time is less than end_time. This appears to be a copy-paste error. Consider removing the duplicate assertion on line 415.

Copilot uses AI. Check for mistakes.


def test_operation_context_records_exceptions(monkeypatch: pytest.MonkeyPatch) -> None:
tracer = _install_recording_tracer(monkeypatch)
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

Variable tracer is not used.

Copilot uses AI. Check for mistakes.
assert status.status_code == StatusCode.ERROR
assert status.description == "boom"
assert use_span.exit_calls[-1][1].args == ("boom",) # type: ignore
recording = tracer.recordings[-1]
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

This statement is unreachable.

Copilot uses AI. Check for mistakes.
@ultmaster
Copy link
Contributor Author

/ci

@github-actions
Copy link

github-actions bot commented Dec 15, 2025

🚀 CI Watcher for correlation id-3652764795-mj6m877e triggered by comment 3652764795
🏃‍♀️ Tracking 3 workflow run(s):

✅ All runs completed.

@ultmaster
Copy link
Contributor Author

/ci

@ultmaster ultmaster requested a review from Copilot December 15, 2025 05:06
@github-actions
Copy link

github-actions bot commented Dec 15, 2025

🚀 CI Watcher for correlation id-3653007475-mj6oy8b8 triggered by comment 3653007475
🏃‍♀️ Tracking 4 workflow run(s):

✅ All runs completed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 45 out of 47 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

agentlightning/types/core.py:359

    def init_worker(self, worker_id: int, *args: Any, **kwargs: Any) -> None:

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

with agentops_tracer.operation_context("agentops-op", attributes={"foo": "bar"}) as ctx:
raise ValueError("agentops boom")

recorded_span = ctx.get_recorded_span() # type: ignore
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

This statement is unreachable.

Copilot uses AI. Check for mistakes.
with tracer.operation_context("dummy-error") as ctx:
raise RuntimeError("boom")

recorded_span = ctx.get_recorded_span() # type: ignore
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

This statement is unreachable.

Copilot uses AI. Check for mistakes.
ctx.record_attributes({"custom": "attr"})
raise RuntimeError("otel failure")

recorded_span = ctx.get_recorded_span() # type: ignore
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

This statement is unreachable.

Copilot uses AI. Check for mistakes.
with tracer.operation_context("weave-op", attributes={"foo": "bar"}) as ctx:
raise RuntimeError("failed")

recorded_span = ctx.get_recorded_span() # type: ignore
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

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

This statement is unreachable.

Copilot uses AI. Check for mistakes.
@ultmaster
Copy link
Contributor Author

/ci

@github-actions
Copy link

github-actions bot commented Dec 15, 2025

🚀 CI Watcher for correlation id-3653439454-mj6qsqzi triggered by comment 3653439454
🏃‍♀️ Tracking 6 workflow run(s):

✅ All runs completed.

@ultmaster ultmaster merged commit 087c7d3 into main Dec 15, 2025
55 of 57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants