Skip to content

Conversation

@AmazingcatAndrew
Copy link
Contributor

Summary

Implementation of #33884

Design Decisions

1. Configuration Location

  • Choice: Place include_raw_response in model wrapper classes, NOT core message classes
  • Rationale:
    • This is a usage-time option, not a core messaging concern
    • Different providers can independently support it
    • Keeps core message design clean

2. Data Structure Flexibility

  • raw_response Type: dict[str, Any] | list[dict[str, Any]] | None
  • Rationale:
    • Single response → dict (simpler API)
    • Multiple responses → list[dict] (clear aggregation)
    • None → unset/disabled

3. Stream Processing Strategy

  • Trigger: Call add_ai_message_chunks() only when chunk_position="last" received
  • Rationale: Ensures complete stream before aggregation

4. Backward Compatibility

  • All new parameters have defaults
  • Existing code runs unchanged
  • raw_response completely optional

Key Changes

1. Core Message System (libs/core/langchain_core/messages/ai.py)

AIMessage Class

  • New Field: raw_response: dict[str, Any] | list[dict[str, Any]] | None = None
  • Constructor Support: Updated __init__() to accept and store raw_response parameter
  • Serialization: Modified dict() method to include non-None raw_response values
  • Documentation: Added parameter documentation

AIMessageChunk Class

  • New Field: raw_response: dict[str, Any] | None = None
  • Purpose: Store individual chunk's raw response during streaming

New Function: add_ai_message_chunks()

Aggregates multiple AIMessageChunk objects into a single final AIMessage

Key Features:

  • Intelligent raw_response merging:
    • Single response → use directly (dict)
    • Multiple responses → preserve as list
    • None values → filtered out
  • Triggered when chunk_position="last" marker received
  • Handles all other chunk fields (content, tools, metadata)

Example Usage:

from langchain_core.messages.ai import add_ai_message_chunks

# Aggregate chunks during streaming
final_message = add_ai_message_chunks(chunk1, chunk2, chunk3)
# If chunks have raw_response, they're merged intelligently
print(final_message.raw_response)  # dict or list[dict] depending on count

2. Message Utilities (libs/core/langchain_core/messages/utils.py)

message_chunk_to_message() Function

  • Added Logic: Transfer raw_response from chunk to final message
  • Condition: Only transfer if value is not None
  • Code:
    if hasattr(chunk, "raw_response") and chunk.raw_response is not None:
        data["raw_response"] = chunk.raw_response

3. Public API Exports (libs/core/langchain_core/messages/__init__.py)

all List

  • Added: "add_ai_message_chunks"

_dynamic_imports Dictionary

  • Added mapping: "add_ai_message_chunks": "ai"

4. OpenAI Integration (libs/partners/openai/langchain_openai/chat_models/base.py)

BaseChatOpenAI Class

New Configuration Field (line ~686):

include_raw_response: bool = False
  • Controls whether to capture raw OpenAI API responses
  • Default: False (maintains backward compatibility)
  • Type: Pydantic model field with documentation

Implementation Locations:

  1. _convert_chunk_to_generation_chunk() (~line 1071)

    • Captures streaming chunk's raw response
    • Code: if self.include_raw_response: message_chunk.raw_response = chunk
  2. _stream() (~line 1274)

    • Handles sync streaming
    • Transfers final chunk to generation message
    • Code: if self.include_raw_response and chunk_position == "last": ...
  3. _create_chat_result() (~line 1440)

    • Handles non-streaming calls
    • Attaches response_dict to message
    • Code: if self.include_raw_response and isinstance(message, AIMessage): ...
  4. _astream() (~line 1529)

    • Handles async streaming
    • Same logic as sync stream

Scope

Currently, only the OpenAI model class has been adapted.
If this PR is accepted, similar adaptations will be implemented for other partner integrations (Anthropic, Google, Bedrock, etc.) in follow-up PRs.

Testing

  • All new and existing tests pass locally.
  • Added isolated unit tests under tests/unit_tests/core/ and tests/unit_tests/partners/openai/.
  • No external API calls are made; all model interactions are fully mocked.

@github-actions github-actions bot added integration Related to a provider partner package integration core Related to the package `langchain-core` openai labels Nov 11, 2025
@AmazingcatAndrew AmazingcatAndrew changed the title feature/Capture raw provider response feat(core)/Capture raw provider response Nov 11, 2025
@AmazingcatAndrew AmazingcatAndrew changed the title feat(core)/Capture raw provider response feature: Capture raw provider response Nov 11, 2025
@AmazingcatAndrew AmazingcatAndrew changed the title feature: Capture raw provider response feat(core): Capture raw provider response Nov 11, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Nov 11, 2025

CodSpeed Performance Report

Merging #33922 will not alter performance

Comparing AmazingcatAndrew:feat/capture-raw-provider-response (394e21a) with master (3dfea96)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

✅ 19 untouched
⏩ 15 skipped1

Footnotes

  1. 15 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@AmazingcatAndrew
Copy link
Contributor Author

I will handle the failing checks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Related to the package `langchain-core` feature integration Related to a provider partner package integration openai

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant