Skip to content

Conversation

@thepatrickchin
Copy link
Contributor

@thepatrickchin thepatrickchin commented Oct 21, 2025

Description

This PR ensures the input message and output message are displayed in the root observability trace in Weave when workflows are executed using either nat run or nat serve with NAT-UI.

With nat run:

image

With nat serve

/chat

image

/generate

image

/chat/stream:

For streaming endpoints, a preview of the first few tokens is collected for display in Weave

image

/generate/stream

image

Websocket Schemas

chat

image

chat_stream

image

generate

image

generate_stream

image

Closes #1041

By Submitting this PR I confirm:

  • I am familiar with the Contributing Guidelines.
  • We require that all contributors "sign-off" on their commits. This certifies that the contribution is your original work, or you have rights to submit it under the same license, or a compatible license.
    • Any contribution which contains commits that are not Signed-Off will not be accepted.
  • When the PR is ready for review, new or existing tests cover these changes.
  • When the PR is ready for review, the documentation is up to date with these changes.

Summary by CodeRabbit

  • New Features

    • Workflow start/end events now include structured input/output data.
    • Streaming workflows include an output preview (up to 50 items) on completion.
  • Improvements

    • Better extraction of inputs from websocket-style messages.
    • Enhanced parsing to surface message/content details alongside raw outputs.
    • Added automatic truncation utility for consistent, readable output previews.

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

Enriches workflow START/END events with StreamEventData (input/output/preview), restores Weave exporter handling for websocket-style inputs and varied response formats, and adds a truncate_string utility for concise previews.

Changes

Cohort / File(s) Summary
Weave exporter message extraction
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
Added typing imports and truncate_string usage; _create_weave_call now extracts websocket-style messages into input_message; added _extract_output_message to derive output_message/output_preview from choices, streaming/delta, or value outputs; _finish_weave_call populates message/content fields alongside raw output.
Runner workflow event data enrichment
src/nat/runtime/runner.py
WORKFLOW_START and WORKFLOW_END events now include data: StreamEventData in both result() and result_stream() flows; result_stream() collects up to 50 streaming items into output_preview for END events; intermediate step payloads now carry data (changed IntermediateStepPayload shape).
String utilities
src/nat/utils/string_utils.py
New function `truncate_string(text: str

Sequence Diagram(s)

sequenceDiagram
    participant Runner as Runner
    participant WeaveExporter as Weave Exporter
    participant Weave as Weave Backend

    rect rgb(248,249,255)
    Note over Runner: Workflow start
    Runner->>Runner: Emit WORKFLOW_START with data: StreamEventData(input=...)
    Runner->>WeaveExporter: _create_weave_call(payload with StreamEventData)
    end

    rect rgb(255,250,240)
    Note over WeaveExporter: Input extraction
    WeaveExporter->>WeaveExporter: Detect websocket `messages` or fallback input
    WeaveExporter->>Weave: Register call including `input_message`
    end

    rect rgb(248,255,245)
    Note over Runner: Workflow end / streaming
    Runner->>Runner: Collect streaming outputs (preview up to 50)
    Runner->>Runner: Emit WORKFLOW_END with data: StreamEventData(output=..., preview=...)
    Runner->>WeaveExporter: _finish_weave_call(payload with StreamEventData)
    end

    rect rgb(255,250,240)
    Note over WeaveExporter: Output extraction
    WeaveExporter->>WeaveExporter: _extract_output_message handles choices/delta/value
    WeaveExporter->>Weave: Update call with `output`, `output_message`, `output_preview`
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Include input and output messages in weave observability traces" is concise at 63 characters (well within the 72-character limit), uses clear imperative mood with the verb "Include," and accurately describes the main objective of the changeset. The title directly reflects the primary changes made to the weave exporter and workflow event handling to display input and output messages in observability traces.
Linked Issues Check ✅ Passed The pull request directly addresses the requirements from issue #1041 by restoring input and output display in Weave root traces. The changes implement the necessary infrastructure through three coordinated modifications: extraction methods in WeaveExporter to derive input and output messages from various response formats, addition of a data field to workflow events (WORKFLOW_START and WORKFLOW_END) to carry input/output information, and a truncate_string utility to manage output preview lengths. These changes collectively resolve the regression that prevented input and output from appearing on the root trace.
Out of Scope Changes Check ✅ Passed All code changes in the pull request are directly related to the scope of issue #1041. The modifications to weave_exporter.py implement the core feature of extracting input and output messages, the changes to runner.py provide the necessary infrastructure to carry this data through workflow events, and the addition of truncate_string in string_utils.py is a supporting utility function for managing output display. No unrelated or tangential changes were introduced beyond the requirements of displaying input and output in weave observability traces.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb8d1c9 and 0e80471.

📒 Files selected for processing (1)
  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@copy-pr-bot
Copy link

copy-pr-bot bot commented Oct 21, 2025

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@thepatrickchin thepatrickchin marked this pull request as ready for review October 21, 2025 10:45
@thepatrickchin thepatrickchin requested a review from a team as a code owner October 21, 2025 10:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (3)

152-166: Harden input_message extraction for dict-shaped messages and avoid attribute-only access.

Current logic assumes attribute access and may miss dict-based schemas. Add dict handling and safer extraction.

-                # Extract message content if input has messages attribute
-                # When websocket mode is enabled, the message is located at messages[0].content[0].text,
-                messages = getattr(step.payload.data.input, 'messages', [])
-                if messages:
-                    content = messages[0].content
-                    if isinstance(content, list) and content:
-                        inputs["input_message"] = getattr(content[0], 'text', content[0])
-                    else:
-                        inputs["input_message"] = content
+                # Extract message content for websocket/NAT-UI schemas.
+                raw_input = step.payload.data.input
+                # Support both object-style and dict-style inputs
+                messages = getattr(raw_input, "messages", None)
+                if messages is None and isinstance(raw_input, dict):
+                    messages = raw_input.get("messages", [])
+                if messages:
+                    first = messages[0]
+                    # content can be attribute, key, list, or plain string
+                    content = getattr(first, "content", first.get("content") if isinstance(first, dict) else first)
+                    if isinstance(content, list) and content:
+                        head = content[0]
+                        # prefer 'text' then 'content' then raw
+                        if isinstance(head, dict):
+                            inputs["input_message"] = head.get("text") or head.get("content") or str(head)
+                        else:
+                            inputs["input_message"] = getattr(head, "text", head)
+                    else:
+                        inputs["input_message"] = content

190-224: Make output extraction resilient: support dicts, strings, and empty/variant choices.

Handle common OpenAI-like dicts, list-of-dicts, and plain string outputs; guard indices and fallbacks.

-    def _extract_output_message(self, output_data: Any, outputs: dict[str, Any]) -> None:
+    def _extract_output_message(self, output_data: Any, outputs: dict[str, Any]) -> None:
         """
         Extract message content from various response formats and add to outputs dictionary.
 
         Args:
             output_data: The raw output data from the response
             outputs: Dictionary to populate with extracted message content
         """
-        # Handle direct "choices" attribute (non-streaming: output.choices[0].message.content)
-        choices = getattr(output_data, 'choices', None)
-        if choices:
-            outputs["output_message"] = choices[0].message.content
-            return
+        # Fast-path: plain string output
+        if isinstance(output_data, str):
+            outputs["output_message"] = output_data
+            return
+
+        # Normalize accessor helpers
+        def _getattr_or_key(obj: Any, name: str, default: Any = None) -> Any:
+            if isinstance(obj, dict):
+                return obj.get(name, default)
+            return getattr(obj, name, default)
+
+        # Handle direct "choices" (non-streaming)
+        choices = _getattr_or_key(output_data, "choices")
+        if choices:
+            first_choice = choices[0] if len(choices) > 0 else None
+            if first_choice:
+                message = _getattr_or_key(first_choice, "message")
+                if message:
+                    outputs["output_message"] = _getattr_or_key(message, "content")
+                    return
+                delta = _getattr_or_key(first_choice, "delta")
+                if delta:
+                    outputs["output_preview"] = _getattr_or_key(delta, "content")
+                    return
 
-        # Handle list-based output (streaming or websocket) – content may be in the following formats:
+        # Handle list-based output (streaming or websocket) - content may be in the following formats:
         # output[0].choices[0].message.content
         # output[0].choices[0].delta.content
         # output[0].value
         if not isinstance(output_data, list) or not output_data:
             return
 
-        choices = getattr(output_data[0], 'choices', None)
+        first = output_data[0]
+        choices = _getattr_or_key(first, "choices")
         if choices:
-            message = getattr(choices[0], 'message', None)
-            delta = getattr(choices[0], 'delta', None)
+            first_choice = choices[0] if len(choices) > 0 else None
+            message = _getattr_or_key(first_choice, "message") if first_choice else None
+            delta = _getattr_or_key(first_choice, "delta") if first_choice else None
 
             if message:
-                outputs["output_message"] = getattr(message, 'content', None)
+                outputs["output_message"] = _getattr_or_key(message, "content")
             elif delta:
-                outputs["output_preview"] = getattr(delta, 'content', None)
+                outputs["output_preview"] = _getattr_or_key(delta, "content")
         else:
-            value = getattr(output_data[0], 'value', None)
+            value = _getattr_or_key(first, "value")
             if value:
                 outputs["output_preview"] = value
+            else:
+                # As a last resort, if the element itself is a string-like content
+                if isinstance(first, str):
+                    outputs["output_message"] = first

152-166: Optional: add configurable redaction/truncation for PII and very large messages.

To reduce risk and payload size, consider redacting or truncating input/output message fields via an opt-in env var (e.g., NAT_OBSERVABILITY_REDACT_MESSAGES=1) and/or a max length (e.g., 2–4k chars).

I can draft a minimal redaction helper and wiring if you want.

Also applies to: 190-224

src/nat/runtime/runner.py (2)

254-261: Name a constant for preview size and annotate the list for clarity.

Improves readability and easy tuning without code changes.

-                # Collect preview of streaming results for the WORKFLOW_END event
-                output_preview = []
+                # Collect preview of streaming results for the WORKFLOW_END event
+                output_preview: list[typing.Any] = []
@@
-                    if len(output_preview) < 50:
+                    if len(output_preview) < OUTPUT_PREVIEW_LIMIT:
                         output_preview.append(m)

Add this constant near the top of the file (outside this hunk):

# How many streaming items to include in the workflow END preview
OUTPUT_PREVIEW_LIMIT = 50

173-175: Code changes are correct; consider extracting preview limit to a named constant with type annotation.

The START/END event payload changes are sound. Verification confirms all exporters (Weave, span exporter, property adapter) safely guard payload.data field access with null checks, so unknown fields pose no compatibility risk.

However, the original review's suggestions remain unimplemented:

  • Line 257: Extract hardcoded 50 to a named constant (e.g., MAX_STREAMING_OUTPUT_PREVIEW = 50)
  • Line 257: Add type annotation to output_preview (e.g., output_preview: list[Any])

These improve maintainability and code clarity.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17bb09b and 01644fa.

📒 Files selected for processing (2)
  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (4 hunks)
  • src/nat/runtime/runner.py (3 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/runtime/runner.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/runtime/runner.py
packages/*/src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Importable Python code inside packages must live under packages//src/

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/runtime/runner.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/runtime/runner.py
packages/**/*

⚙️ CodeRabbit configuration file

packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain a pyproject.toml file. - The pyproject.toml file should declare a dependency on nvidia-nat or another package with a name starting
with nvidia-nat-. This dependency should be declared using ~=<version>, and the version should be a two
digit version (ex: ~=1.0).

  • Not all packages contain Python code, if they do they should also contain their own set of tests, in a
    tests/ directory at the same level as the pyproject.toml file.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All importable Python code must live under src/ (or packages//src/)

Files:

  • src/nat/runtime/runner.py
src/nat/**/*

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Changes in src/nat should prioritize backward compatibility

Files:

  • src/nat/runtime/runner.py

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/runtime/runner.py
🧬 Code graph analysis (2)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (1)
src/nat/data_models/intermediate_step.py (1)
  • data (293-294)
src/nat/runtime/runner.py (3)
src/nat/data_models/intermediate_step.py (2)
  • data (293-294)
  • StreamEventData (67-77)
src/nat/builder/function.py (3)
  • astream (207-208)
  • astream (211-212)
  • astream (215-260)
tests/nat/runtime/test_runner_trace_ids.py (1)
  • astream (40-43)
🪛 Ruff (0.14.1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py

204-204: Comment contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF003)

🔇 Additional comments (3)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (2)

19-19: LGTM: import use is appropriate.

Importing Any is justified for mixed Weave payload shapes.


245-245: LGTM: populating derived output fields in Weave outputs.

Calling _extract_output_message here aligns the UI with root-trace visibility.

Please run your streaming and non-streaming smoke tests to confirm outputs["output_message"] or outputs["output_preview"] always exist for the common schemas you support.

src/nat/runtime/runner.py (1)

272-276: END event with StreamEventData(output=output_preview) aligns with exporter’s preview logic.

Good; the exporter derives output_message/output_preview from list-shaped outputs.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (1)

205-205: Fix ruff RUF003: replace EN DASH with hyphen.

This issue was flagged in a previous review but remains unresolved.

-        # Handle list-based output (streaming or websocket) – content may be in the following formats:
+        # Handle list-based output (streaming or websocket) - content may be in the following formats:
🧹 Nitpick comments (1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (1)

158-166: Consider using getattr for safer attribute access.

While the surrounding try-except block (line 154) will catch any AttributeError, explicitly using getattr would make the code more defensive and clearer.

                 # Extract message content if input has messages attribute
                 # When websocket mode is enabled, the message is located at messages[0].content[0].text,
                 messages = getattr(step.payload.data.input, 'messages', [])
                 if messages:
-                    content = messages[0].content
+                    content = getattr(messages[0], 'content', None)
-                    if isinstance(content, list) and content:
+                    if content and isinstance(content, list):
                         inputs["input_message"] = getattr(content[0], 'text', content[0])
-                    else:
+                    elif content:
                         inputs["input_message"] = content
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01644fa and 5768269.

📒 Files selected for processing (2)
  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (4 hunks)
  • src/nat/utils/string_utils.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/utils/string_utils.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/utils/string_utils.py
packages/*/src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Importable Python code inside packages must live under packages//src/

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/utils/string_utils.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
  • src/nat/utils/string_utils.py
packages/**/*

⚙️ CodeRabbit configuration file

packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain a pyproject.toml file. - The pyproject.toml file should declare a dependency on nvidia-nat or another package with a name starting
with nvidia-nat-. This dependency should be declared using ~=<version>, and the version should be a two
digit version (ex: ~=1.0).

  • Not all packages contain Python code, if they do they should also contain their own set of tests, in a
    tests/ directory at the same level as the pyproject.toml file.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All importable Python code must live under src/ (or packages//src/)

Files:

  • src/nat/utils/string_utils.py
src/nat/**/*

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Changes in src/nat should prioritize backward compatibility

Files:

  • src/nat/utils/string_utils.py

⚙️ CodeRabbit configuration file

This directory contains the core functionality of the toolkit. Changes should prioritize backward compatibility.

Files:

  • src/nat/utils/string_utils.py
🧬 Code graph analysis (1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (3)
src/nat/data_models/intermediate_step.py (2)
  • IntermediateStep (235-310)
  • data (293-294)
src/nat/utils/string_utils.py (1)
  • truncate_string (41-54)
src/nat/builder/context.py (1)
  • output (59-60)
🪛 Ruff (0.14.1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py

205-205: Comment contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF003)

🔇 Additional comments (4)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (4)

19-19: LGTM!

The new imports are properly used in the _extract_output_message method.

Also applies to: 26-26


246-246: LGTM!

The call to _extract_output_message is correctly placed within the try-except block and properly populates the outputs dictionary with extracted message content.


212-220: Add length check before accessing choices[0].

Similar to the earlier case, choices[0] is accessed without verifying the list is non-empty.

         choices = getattr(output_data[0], 'choices', None)
-        if choices:
+        if choices and len(choices) > 0:
             message = getattr(choices[0], 'message', None)
             delta = getattr(choices[0], 'delta', None)
 
             if message:
                 outputs["output_message"] = truncate_string(getattr(message, 'content', None))
             elif delta:
                 outputs["output_preview"] = truncate_string(getattr(delta, 'content', None))

Likely an incorrect or invalid review comment.


191-204: Add length check before accessing choices[0].

The code accesses choices[0] without verifying the list is non-empty, which could raise an IndexError.

     def _extract_output_message(self, output_data: Any, outputs: dict[str, Any]) -> None:
         """
         Extract message content from various response formats and add to outputs dictionary.
 
         Args:
             output_data: The raw output data from the response
             outputs: Dictionary to populate with extracted message content
         """
         # Handle direct "choices" attribute (non-streaming: output.choices[0].message.content)
         choices = getattr(output_data, 'choices', None)
-        if choices:
+        if choices and len(choices) > 0:
             outputs["output_message"] = truncate_string(choices[0].message.content)
             return

Likely an incorrect or invalid review comment.

@willkill07 willkill07 added improvement Improvement to existing functionality non-breaking Non-breaking change labels Oct 21, 2025
@willkill07 willkill07 changed the title Include input and output messages in observability traces Include input and output messages in weave observability traces Oct 21, 2025
@willkill07
Copy link
Member

/ok to test 5768269

Copy link
Member

@willkill07 willkill07 left a comment

Choose a reason for hiding this comment

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

Approving with additional documentation suggestion.

Thank you for your contribution!

@willkill07
Copy link
Member

willkill07 commented Oct 21, 2025

Something seems weird to me here:

  • input: /generate and /generate/stream with input_message
  • output: /generate/stream (all) and /generate (conversation)
Screenshot 2025-10-21 at 10 37 16 AM

From top-to-bottom:

  • /generate/stream with {"messages": [{"role": "user", "content": <input> }] }
  • /generate with {"messages": [{"role": "user", "content": <input> }] }
  • /chat/stream with {"messages": [{"role": "user", "content": <input> }] }
  • /chat with {"messages": [{"role": "user", "content": <input> }] }
  • /generate/stream with { "input_message": <input> }
  • /generate with { "input_message": <input> }

Note: i'm just doing raw curl requests

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (1)

200-248: Comprehensive output extraction with good documentation.

The method handles multiple output formats with clear documentation, addressing past review feedback about documenting supported formats. The early returns and safety checks are well-structured.

Optional: Consider consistent type handling for truncate_string.

Line 248 uses str(value) before calling truncate_string, but lines 218, 224, 236, and 242 pass values directly. While the current implementation works due to the surrounding try-except block, explicitly ensuring string types would improve consistency and type safety.

For example:

outputs["output_message"] = truncate_string(str(choices[0].message.content))

This is a minor refinement and can be deferred since exceptions are already caught at the call site.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5768269 and e78344b.

📒 Files selected for processing (1)
  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (4 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{py,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.{py,yaml,yml}: Configure response_seq as a list of strings; values cycle per call, and [] yields an empty string.
Configure delay_ms to inject per-call artificial latency in milliseconds for nat_test_llm.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/nat-test-llm.mdc)

**/*.py: Programmatic use: create TestLLMConfig(response_seq=[...], delay_ms=...), add with builder.add_llm("", cfg).
When retrieving the test LLM wrapper, use builder.get_llm(name, wrapper_type=LLMFrameworkEnum.) and call the framework’s method (e.g., ainvoke, achat, call).

**/*.py: In code comments/identifiers use NAT abbreviations as specified: nat for API namespace/CLI, nvidia-nat for package name, NAT for env var prefixes; do not use these abbreviations in documentation
Follow PEP 20 and PEP 8; run yapf with column_limit=120; use 4-space indentation; end files with a single trailing newline
Run ruff check --fix as linter (not formatter) using pyproject.toml config; fix warnings unless explicitly ignored
Respect naming: snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants
Treat pyright warnings as errors during development
Exception handling: use bare raise to re-raise; log with logger.error() when re-raising to avoid duplicate stack traces; use logger.exception() when catching without re-raising
Provide Google-style docstrings for every public module, class, function, and CLI command; first line concise and ending with a period; surround code entities with backticks
Validate and sanitize all user input, especially in web or CLI interfaces
Prefer httpx with SSL verification enabled by default and follow OWASP Top-10 recommendations
Use async/await for I/O-bound work; profile CPU-heavy paths with cProfile or mprof before optimizing; cache expensive computations with functools.lru_cache or external cache; leverage NumPy vectorized operations when beneficial

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
packages/*/src/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Importable Python code inside packages must live under packages//src/

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
{src/**/*.py,packages/*/src/**/*.py}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

All public APIs must have Python 3.11+ type hints on parameters and return values; prefer typing/collections.abc abstractions; use typing.Annotated when useful

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
**/*

⚙️ CodeRabbit configuration file

**/*: # Code Review Instructions

  • Ensure the code follows best practices and coding standards. - For Python code, follow
    PEP 20 and
    PEP 8 for style guidelines.
  • Check for security vulnerabilities and potential issues. - Python methods should use type hints for all parameters and return values.
    Example:
    def my_function(param1: int, param2: str) -> bool:
        pass
  • For Python exception handling, ensure proper stack trace preservation:
    • When re-raising exceptions: use bare raise statements to maintain the original stack trace,
      and use logger.error() (not logger.exception()) to avoid duplicate stack trace output.
    • When catching and logging exceptions without re-raising: always use logger.exception()
      to capture the full stack trace information.

Documentation Review Instructions - Verify that documentation and comments are clear and comprehensive. - Verify that the documentation doesn't contain any TODOs, FIXMEs or placeholder text like "lorem ipsum". - Verify that the documentation doesn't contain any offensive or outdated terms. - Verify that documentation and comments are free of spelling mistakes, ensure the documentation doesn't contain any

words listed in the ci/vale/styles/config/vocabularies/nat/reject.txt file, words that might appear to be
spelling mistakes but are listed in the ci/vale/styles/config/vocabularies/nat/accept.txt file are OK.

Misc. - All code (except .mdc files that contain Cursor rules) should be licensed under the Apache License 2.0,

and should contain an Apache License 2.0 header comment at the top of each file.

  • Confirm that copyright years are up-to date whenever a file is changed.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
packages/**/*

⚙️ CodeRabbit configuration file

packages/**/*: - This directory contains optional plugin packages for the toolkit, each should contain a pyproject.toml file. - The pyproject.toml file should declare a dependency on nvidia-nat or another package with a name starting
with nvidia-nat-. This dependency should be declared using ~=<version>, and the version should be a two
digit version (ex: ~=1.0).

  • Not all packages contain Python code, if they do they should also contain their own set of tests, in a
    tests/ directory at the same level as the pyproject.toml file.

Files:

  • packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py
🧬 Code graph analysis (1)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (3)
src/nat/data_models/intermediate_step.py (2)
  • IntermediateStep (235-310)
  • data (293-294)
src/nat/observability/exporter/span_exporter.py (1)
  • SpanExporter (47-308)
src/nat/utils/string_utils.py (1)
  • truncate_string (41-54)
🔇 Additional comments (3)
packages/nvidia_nat_weave/src/nat/plugins/weave/weave_exporter.py (3)

19-19: LGTM! Appropriate imports for the new functionality.

The Any type and truncate_string utility are correctly imported and used in the new message extraction methods.

Also applies to: 26-26


182-198: Well-structured input extraction.

The method correctly handles both direct content and websocket mode with appropriate safety checks for empty lists. The implementation addresses the past review feedback to refactor input extraction into a dedicated method.


157-157: LGTM! Proper integration of message extraction.

Both calls are appropriately placed within try-except blocks and enhance the trace data after capturing the raw input/output. This ensures the root traces display input and output messages as intended by the PR objectives.

Also applies to: 270-270

@thepatrickchin
Copy link
Contributor Author

Something seems weird to me here:

  • input: /generate and /generate/stream with input_message
  • output: /generate/stream (all) and /generate (conversation)
Screenshot 2025-10-21 at 10 37 16 AM From top-to-bottom:
  • /generate/stream with {"messages": [{"role": "user", "content": <input> }] }
  • /generate with {"messages": [{"role": "user", "content": <input> }] }
  • /chat/stream with {"messages": [{"role": "user", "content": <input> }] }
  • /chat with {"messages": [{"role": "user", "content": <input> }] }
  • /generate/stream with { "input_message": <input> }
  • /generate with { "input_message": <input> }

Note: i'm just doing raw curl requests

In some cases the input message already appears in the original dictionary without need for extraction so I left that as-is. I think users are unlikely to be switching between completion endpoints and websocket schema so normally the additional column wouldn't appear in the table. As for the outputs of the /generate endpoints, the content may have been shown under the value column, but I've just updated it now to extract the value to output_message (or output_preview for generate/stream). I'm not able to reproduce the issue seen with the /generate/stream output

@willkill07
Copy link
Member

/merge

@rapids-bot rapids-bot bot merged commit fe3f8d1 into NVIDIA:develop Oct 21, 2025
17 checks passed
@thepatrickchin thepatrickchin deleted the weave-bugfix branch November 4, 2025 04:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improvement to existing functionality non-breaking Non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression in Weave integration results in missing input and output display

2 participants