feat: Last9LogToSpanProcessor — bridge GenAI log events onto spans (v1.1.0)#11
Merged
prathamesh-sonpatki merged 2 commits intomainfrom Apr 19, 2026
Merged
Conversation
opentelemetry-instrumentation-openai-v2 (v2+) follows the new GenAI semantic
conventions and emits prompts, completions, and tool calls as OTel log events
rather than span attributes. The Last9 LLM dashboard reads span attributes and
events, so those payloads never reached the dashboard.
Last9LogToSpanProcessor listens for gen_ai.* log records and promotes their
payloads onto the active span as:
- flat attrs gen_ai.prompt / gen_ai.completion (JSON arrays)
- span events gen_ai.content.prompt / gen_ai.content.completion
- indexed gen_ai.prompt.{i}.* / gen_ai.completion.{i}.* (AgentOps/Traceloop)
Last9SpanProcessor now accepts log_processor= and frees per-span counter
state when its span ends.
Bumps version to 1.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers: - Flat + indexed attrs set on prompt/user/system/tool/assistant events - Completion event with tool_calls, finish_reason, index - Multi-prompt accumulation preserves order - Unrelated event names ignored - Non-dict body ignored - Truncation applied beyond max_content_length - cleanup_span releases per-span state on span end - Tool messages capture tool_call.id - Orphan log record (no active span) dropped Coverage: log_processor.py 21% -> 94%; total 78% -> 86%. Also applies black formatting to satisfy CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Last9LogToSpanProcessor, a newLogRecordProcessorthat promotesGenAI log events emitted by
opentelemetry-instrumentation-openai-v2ontothe active span as flat + indexed attributes + span events so the Last9
LLM dashboard renders prompts, completions, and tool calls.
Last9SpanProcessorgains an optionallog_processor=kwarg for per-spancounter cleanup.
Why
opentelemetry-instrumentation-openai-v2(v2+) follows the new OTel GenAIsemantic conventions — it emits message content, tool calls, and completions
as OTel log events, not as span attributes. The Last9 LLM dashboard reads
span attributes (
gen_ai.prompt,gen_ai.completion) and span events(
gen_ai.content.prompt,gen_ai.content.completion), so those payloadswere invisible until now.
This bridge closes the gap without monkey-patching the openai SDK. Works for
any framework that routes LLM calls through
openai-v2(AutoGen, LangChain,direct usage).
Test plan
uv run pytest tests/ -x -q— 117 passing.AssistantAgent+ tool call; chat spancarries
gen_ai.prompt,gen_ai.completion,gen_ai.content.promptevent, and
gen_ai.content.completionevent.Last9LogToSpanProcessoritself (follow-up — coveragecurrently 21%, functionally verified via integration demo).
Notes
Python 3.14 users must pin
wrapt<2becauseopentelemetry-instrumentation-openai-v22.3b0 callswrap_function_wrapper(module=..., name=..., wrapper=...)and wrapt 2.0renamed the first kwarg to
target=. Without the pin, instrumentation failssilently. README + CHANGELOG call this out.
Not in scope
execute_toolspan(tracked as Phase 2 — requires wrapping
autogen_core.tools.FunctionTool.run_json).🤖 Generated with Claude Code