-
Notifications
You must be signed in to change notification settings - Fork 749
FEAT: Score partial content from content-filtered responses #1689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
668512d
e6fae92
3beff4a
9a4505a
fc6c7e7
c49debc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -454,6 +454,34 @@ def _check_content_filter(self, response: Any) -> bool: | |
| return _is_content_filter_error(response_dict) | ||
| return False | ||
|
|
||
| def _extract_partial_content(self, response: Any) -> Optional[str]: | ||
| """ | ||
| Extract partial content from a Response API response that was content-filtered. | ||
|
|
||
| The Response API may include partial text in ``response.output`` message sections | ||
| even when the response has a content filter error. | ||
|
|
||
| Args: | ||
| response: A Response object from the OpenAI SDK. | ||
|
|
||
| Returns: | ||
| The partial text content, or None if no content was generated. | ||
| """ | ||
| try: | ||
| if not hasattr(response, "output") or not response.output: | ||
| return None | ||
| parts: list[str] = [] | ||
| for section in response.output: | ||
| if getattr(section, "type", None) == MessagePieceType.MESSAGE: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a lot of Am I missing something here? |
||
| content = getattr(section, "content", None) | ||
| if content and len(content) > 0: | ||
| text = getattr(content[0], "text", None) | ||
| if text: | ||
| parts.append(text) | ||
| return "\n".join(parts) if parts else None | ||
| except (AttributeError, IndexError, TypeError): | ||
| return None | ||
|
|
||
| def _validate_response(self, response: Any, request: MessagePiece) -> Optional[Message]: | ||
| """ | ||
| Validate a Response API response for errors. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,6 +63,14 @@ async def _score_async(self, message: Message, *, objective: Optional[str] = Non | |
| # Build the full conversation text | ||
| conversation_text = "" | ||
|
|
||
| # Check if the caller requested scoring of blocked content by inspecting whether | ||
| # the incoming message was substituted by score_async._apply_blocked_content_substitution. | ||
| # A substituted piece has partial_content in metadata but response_error="none". | ||
| incoming_piece = message.message_pieces[0] | ||
| use_partial_content = ( | ||
| "partial_content" in incoming_piece.prompt_metadata and incoming_piece.response_error == "none" | ||
| ) | ||
|
|
||
| # Goes through each message in the conversation and appends user/assistant messages only | ||
| # Explicitly excludes system, tool, developer messages from being scored/included in conversation history | ||
| # they are allowed in validation but not included in the scored conversation text | ||
|
|
@@ -71,7 +79,13 @@ async def _score_async(self, message: Message, *, objective: Optional[str] = Non | |
| # Only include user and assistant messages in the conversation text | ||
| if piece.api_role in ["user", "assistant", "tool"]: | ||
| role_display = "Assistant (simulated)" if piece.is_simulated else piece.api_role.capitalize() | ||
| conversation_text += f"{role_display}: {piece.converted_value}\n" | ||
| # For blocked pieces with partial content, use the partial content | ||
| # instead of the error JSON when score_blocked_content is enabled | ||
| if use_partial_content and piece.is_blocked() and "partial_content" in piece.prompt_metadata: | ||
| text = str(piece.prompt_metadata["partial_content"]) | ||
| else: | ||
| text = piece.converted_value | ||
| conversation_text += f"{role_display}: {text}\n" | ||
|
Comment on lines
+84
to
+88
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should perhaps tell the scorer that this is partial content because the full response was blocked? Isn't that relevant information? |
||
|
|
||
| # Create a new message with the concatenated conversation text | ||
| # Preserve the original message piece metadata | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this were an attribute on the scorer, it wouldn't change this