Repository navigation
Add QwenPaw 2.x runtime support - #994
123liuziming wants to merge 7 commits into
Conversation
Assisted-by: GPT-6.1 Sol
Pull request dashboard statusWaiting on the author · refreshed 2026-11-03 06:34 UTC Resolve merge conflicts. Respond to 4 review items (e.g. link a commit, explain why not, ask a follow-up): Status above doesn't look right?
|
Assisted-by: GPT-6.1 Sol
Assisted-by: GPT-6.1 Sol
Assisted-by: GPT-6.1 Sol
Assisted-by: GPT-6.1 Sol
Retain hook context across stream reads, merge partial message snapshots, and use shared media conversion. Cover both runtime generations and keep fixture-specific assertions in unit tests. Assisted-by: GPT-6.1 Sol
There was a problem hiding this comment.
🟡 Changes recommended
Snapshot capture overwrites valid media content when text and media block indexes collide, causing finished spans and completion hooks to lose content.
1 open issue
What changed in this PR
Extends the existing QwenPaw instrumentation to support the 2.x Runtime.run entry point while retaining 1.x runner support.
Changes:
- Adds lazy runtime tracing, conversation propagation, and streamed message capture.
- Expands unit and conformance coverage across both runtime generations.
- Updates dependency ranges, examples, and documentation for 2.x support.
Paths below are relative to instrumentation/opentelemetry-instrumentation-genai-qwenpaw/, except those marked repository root.
| File | Description |
|---|---|
uv.lock (repository root) |
Updates the supported QwenPaw range. |
tox.ini (repository root) |
Adds version-specific test environments and shared dependency constraints. |
README.md (repository root) |
Updates the instrumentation compatibility table. |
tests/test_runtime.py |
Tests runtime tracing, lifecycle, context, and message capture. |
tests/test_message_mapping.py |
Uses structured user-message content. |
tests/test_instrumentor.py |
Adjusts dependency and instrumentation lifecycle tests. |
tests/test_instrumentation_scope.py |
Removes the legacy-runner fixture requirement. |
tests/test_conformance.py |
Registers error and rich-message scenarios. |
tests/requirements.v2.txt |
Adds the QwenPaw 2.0.0 test dependencies. |
tests/requirements.v1.txt |
Adds latest-1.x test dependencies. |
tests/requirements.oldest.txt |
Uses the unreleased workspace utility package. |
tests/requirements.latest.txt |
Moves latest-version testing to QwenPaw 2.x. |
tests/harness.py |
Adds runtime and request construction helpers. |
tests/conftest.py |
Selects fixtures by runtime generation. |
tests/conformance/runtime_messages.py |
Exercises rich messages through runtime envelopes. |
tests/conformance/runtime_error.py |
Exercises failed turns in both generations. |
tests/conformance/invoke_agent.py |
Supports successful turns in both generations. |
src/opentelemetry/instrumentation/genai/qwenpaw/runtime.py |
Implements lazy runtime tracing and envelope capture. |
src/opentelemetry/instrumentation/genai/qwenpaw/package.py |
Broadens declared QwenPaw compatibility. |
src/opentelemetry/instrumentation/genai/qwenpaw/__init__.py |
Selects and unwraps version-specific instrumentation. |
README.rst |
Documents runtime support, captured content, and test environments. |
pyproject.toml |
Broadens QwenPaw support and raises the utility dependency floor. |
examples/zero-code/requirements.txt |
Updates automatic-instrumentation examples to 2.x. |
examples/manual/requirements.txt |
Updates manual-instrumentation examples to 2.x. |
.changelog/994.added |
Records added 2.x support and retained 1.x compatibility. |
Review effort: Balanced
Distinguish text and media block indexes and preserve snapshot ordering without dropping unfinished content. Cover complete, early-close, and failed streams using real QwenPaw envelopes. Assisted-by: GPT-6.1 Sol
| arguments = ( | ||
| self._self_tool_arguments.get(message_id, "") + delta | ||
| ) | ||
| self._self_tool_arguments[message_id] = arguments | ||
| part = ToolCallRequestPart( | ||
| name=previous.name, | ||
| id=previous.id, | ||
| arguments=_json_value(arguments), | ||
| ) |
There was a problem hiding this comment.
This re-concatenates and re-parses (json.loads on partial JSON, which mostly raises) the whole argument string on every delta, so a long tool call is quadratic. Could we just append the delta here and parse once when the message snapshot arrives or in _on_stream_end/_on_stream_error?
| and isinstance(previous, (TextPart, ReasoningPart)) | ||
| and part.type == previous.type | ||
| ): | ||
| part = type(part)(content=previous.content + part.content) |
There was a problem hiding this comment.
Same for text: each token delta copies the full accumulated text (and rebuilds the OutputMessage), which is O(n^2) on long replies. Collecting deltas in a list per block and joining at finalization would keep the per-chunk cost constant.
| _RUNTIME_MODULE = "qwenpaw.runtime.runtime" | ||
|
|
||
|
|
||
| def _runtime_v2() -> bool: |
There was a problem hiding this comment.
nit: keying off distribution metadata breaks with skip_dep_check=True or a source install (PackageNotFoundError in _instrument/_uninstrument). Feature-detecting the entry point avoids that:
def _runtime_v2() -> bool:
return find_spec("qwenpaw.runtime.runtime") is not None| Version 2.x support requires ``opentelemetry-util-genai`` 1.4b0 or newer | ||
| (``1.4b0.dev`` from this workspace during development). QwenPaw 2.x requires Python >= 3.11, |
There was a problem hiding this comment.
nit: the workspace dev note will end up on PyPI.
| Version 2.x support requires ``opentelemetry-util-genai`` 1.4b0 or newer | |
| (``1.4b0.dev`` from this workspace during development). QwenPaw 2.x requires Python >= 3.11, | |
| Version 2.x support requires ``opentelemetry-util-genai`` 1.4b0 or newer. | |
| QwenPaw 2.x requires Python >= 3.11, |

Adds QwenPaw 2.x
Runtime.runsupport to the existingQwenPawInstrumentor, retaining the original 1.xAgentRunner.query_handlerinstrumentation. Captures streamed envelopes, propagates conversation IDs, and tests both runtime generations.Follow-up to #969.
Validation on current main: QwenPaw 1.1.0, 1.1.12.post3, 2.0.0, and 2.2.2.post1 pass all 10 Weaver conformance scenarios with no skips. Real DashScope
qwen-plusrequests on all four versions pass span/message/duration assertions and Weaver live-check with zero violations in Kubernetes. Pre-commit and README rendering pass.WIP: requires unreleased util-genai 1.4b0. The 1.1.0 live environment uses OpenAI SDK 2.x because its pinned AgentScope fails to parse streams with OpenAI SDK 3.x. The combined workspace retains QwenPaw 1.x for CrewAI dependency compatibility; isolated environments test both major versions.