Skip to content

feat: Enhanced Phoenix sync with OpenInference schema support - #32

Merged
visahak merged 1 commit into
AgentToolkit:mainfrom
visahak:main
Jan 24, 2026
Merged

feat: Enhanced Phoenix sync with OpenInference schema support#32
visahak merged 1 commit into
AgentToolkit:mainfrom
visahak:main

Conversation

@visahak

@visahak visahak commented Jan 23, 2026

Copy link
Copy Markdown
Collaborator

Description

Summary

Enhanced the Phoenix sync module to support multiple OpenTelemetry/Phoenix schema formats, improving compatibility with different tracing implementations and Phoenix API versions.

Changes Made

Enhanced Message Extraction (_extract_messages_from_span)

  • Added support for OpenInference schema attributes (llm.input_messages, llm.output_messages)
  • Added fallback parsing for input.value and output.value attributes
  • Handles both flattened (message.role, message.content) and nested message formats
  • Maintains backward compatibility with GenAI semantic conventions
  • Supports tool_calls in messages

Improved Token Usage Tracking

  • Added fallback token count attributes:
    • llm.token_count.prompt (fallback for gen_ai.usage.prompt_tokens)
    • llm.token_count.completion (fallback for gen_ai.usage.completion_tokens)
    • llm.token_count.total (fallback for llm.usage.total_tokens)

Enhanced Span Filtering

  • Relaxed span name filtering (commented out strict litellm_request check)
  • Now accepts any span with GenAI or LLM message attributes
  • More flexible for different tracing implementations

Improved Null Safety

  • Changed span.get("attributes", {}) to span.get("attributes") or {} throughout
  • Better handling of None values

Trajectory Storage Update

  • Messages now serialized with json.dumps() before storage for consistency

Testing

  • ✅ All 50 existing tests pass
  • ✅ No breaking changes to existing functionality
  • ✅ Backward compatible with existing data

Benefits

  • Broader compatibility with Phoenix API versions
  • Support for multiple OpenTelemetry schema formats
  • More robust error handling
  • Better support for different LLM providers and tracing implementations

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced message extraction to support OpenInference format specifications and additional message parsing paths
    • Expanded span filtering to process more types of LLM interactions
  • Improvements

    • Improved robustness when handling varied message formats and span attributes
    • Better token usage derivation with fallback mechanisms for different telemetry sources

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR enhances message extraction in Phoenix sync to support OpenInference formats (llm.input_messages, llm.output_messages, input.value, output.value) with unified message structure, changes trajectory storage from list to JSON string, updates token usage mapping fallbacks, and improves span filtering and None handling safety.

Changes

Cohort / File(s) Summary
Response Format Safety Check
kaizen/llm/tips/tips.py
Refactors inline response_format existence check into two-step assignment and validation, improving safety when get_supported_openai_params() returns None or non-dict types
Phoenix Message Extraction & Trajectory Processing
kaizen/sync/phoenix_sync.py
Enhances _extract_messages_from_span to parse OpenInference message formats (llm.input_messages, llm.output_messages, input.value, output.value) into unified message structure with index/type/role/content/tool_calls; changes trajectory storage to JSON string via json.dumps(); updates token usage to use llm.token_count.\* fallbacks; broadens span filtering to accept GenAI prompt attributes or llm message indicators; improves None-safety for attribute access

Sequence Diagram(s)

sequenceDiagram
    participant Span as Phoenix Span
    participant Filter as Span Filter
    participant Extract as Message Extractor
    participant Format as OpenAI Formatter
    participant Store as Trajectory Storage

    Span->>Filter: Check span eligibility (name or GenAI/llm attributes)
    alt Span matches new broader criteria
        Filter->>Extract: Process span
        Extract->>Extract: Check llm.input_messages/output_messages
        alt OpenInference format found
            Extract->>Extract: Parse input.value/output.value
            Extract->>Extract: Build unified messages (index/type/role/content)
        else
            Extract->>Extract: Fallback to GenAI prompt/completion
        end
        Extract->>Format: Unified message structure
        Format->>Format: Convert to OpenAI format
        Format->>Store: Stringified trajectory (json.dumps)
    else
        Filter-->>Span: Skip span
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • vinodmut
  • illeatmyhat

Poem

🐰 Hops through spans with OpenInference delight,
Messages nested, now unified and tight!
From GenAI prompts to llm's lucid view,
Trajectories stringified—JSON anew!
Safety checks guard where None dares appear.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Enhanced Phoenix sync with OpenInference schema support' directly aligns with the main changes in the pull request, which focus on enhancing the Phoenix sync module with OpenInference schema support.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@visahak
visahak requested a review from vinodmut January 23, 2026 20:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kaizen/sync/phoenix_sync.py (1)

390-402: Trajectory content is stored as JSON string, but deserialization is inconsistent between backends.

The json.dumps(messages) storage change requires careful handling: Milvus backend automatically deserializes via parse_milvus_entity(), so consumers get a list back. However, Filesystem backend returns the raw JSON string without deserialization, creating inconsistent behavior. Code using Filesystem backend that expects entity.content to be a list will receive a JSON string instead. Either add deserialization to Filesystem backend's search_entities (similar to Milvus's parse_milvus_entity), or document this backend-specific behavior.

🤖 Fix all issues with AI agents
In `@kaizen/sync/phoenix_sync.py`:
- Around line 178-179: Replace the bare "except: pass" in phoenix_sync.py with a
specific exception handler and proper logging/handling: identify the surrounding
function/block containing that bare except (the except block shown in the diff),
change it to catch the expected exception types (e.g., ValueError, KeyError or
more generally "except Exception as e" if multiple types are possible), and
record the error (e.g., logger.error or logger.exception with the exception
object) or take corrective action instead of silently passing so failures are
observable and debuggable.
- Around line 135-136: Replace the bare "except: pass" in phoenix_sync.py with a
specific exception handler and logging: catch the likely parsing/processing
exceptions (e.g., ValueError, KeyError, json.JSONDecodeError or Exception if
uncertain) using "except <ExceptionType> as e" and emit a logged error (e.g.,
logger.error or logger.exception with contextual info about the record/item
being parsed) instead of silently passing so failures are visible while still
allowing the loop to continue; update the try/except block around the failing
parse/processing code accordingly.
🧹 Nitpick comments (3)
kaizen/sync/phoenix_sync.py (3)

144-161: Add type guard before accessing dict methods.

If input_msgs contains non-dict items (e.g., strings), calling msg.get() will raise AttributeError.

Proposed fix
             if isinstance(input_msgs, list):
                 for i, msg in enumerate(input_msgs):
+                     if not isinstance(msg, dict):
+                         continue
                      # OpenInference often uses message.role / message.content keys in flattened export

185-200: Same type guard needed for output messages.

Apply the same type check before calling msg.get().

Proposed fix
              if isinstance(output_msgs, list):
                 for i, msg in enumerate(output_msgs):
+                     if not isinstance(msg, dict):
+                         continue
                      role = msg.get("message.role") or msg.get("role")

470-472: Remove commented-out code instead of leaving it.

Dead code should be deleted rather than commented out. Version control preserves history if needed later.

Proposed fix
         for span in spans:
-            # Filter to LLM request spans - accept any span with prompt attributes
-            # if span.get("name") != "litellm_request":
-            #     continue
+            # Filter to LLM request spans - accept any span with prompt attributes

Comment on lines +135 to +136
except:
pass

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid bare except with silent pass.

Bare except catches all exceptions including KeyboardInterrupt and SystemExit. Silent pass makes debugging difficult when unexpected parsing failures occur.

Proposed fix
-                except:
-                    pass
+                except (json.JSONDecodeError, ValueError, TypeError) as e:
+                    logger.debug(f"Failed to parse input.value: {e}")
🧰 Tools
🪛 Ruff (0.14.13)

135-135: Do not use bare except

(E722)


135-136: try-except-pass detected, consider logging the exception

(S110)

🤖 Prompt for AI Agents
In `@kaizen/sync/phoenix_sync.py` around lines 135 - 136, Replace the bare
"except: pass" in phoenix_sync.py with a specific exception handler and logging:
catch the likely parsing/processing exceptions (e.g., ValueError, KeyError,
json.JSONDecodeError or Exception if uncertain) using "except <ExceptionType> as
e" and emit a logged error (e.g., logger.error or logger.exception with
contextual info about the record/item being parsed) instead of silently passing
so failures are visible while still allowing the loop to continue; update the
try/except block around the failing parse/processing code accordingly.

Comment on lines +178 to +179
except:
pass

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same issue: bare except with silent pass.

Apply the same fix as suggested for input parsing.

Proposed fix
-                except:
-                    pass
+                except (json.JSONDecodeError, ValueError, TypeError) as e:
+                    logger.debug(f"Failed to parse output.value: {e}")
🧰 Tools
🪛 Ruff (0.14.13)

178-178: Do not use bare except

(E722)


178-179: try-except-pass detected, consider logging the exception

(S110)

🤖 Prompt for AI Agents
In `@kaizen/sync/phoenix_sync.py` around lines 178 - 179, Replace the bare
"except: pass" in phoenix_sync.py with a specific exception handler and proper
logging/handling: identify the surrounding function/block containing that bare
except (the except block shown in the diff), change it to catch the expected
exception types (e.g., ValueError, KeyError or more generally "except Exception
as e" if multiple types are possible), and record the error (e.g., logger.error
or logger.exception with the exception object) or take corrective action instead
of silently passing so failures are observable and debuggable.

@visahak
visahak merged commit 384a1ef into AgentToolkit:main Jan 24, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants