Skip to content

feat(models): accept a pre-configured client in OpenAIResponsesModel [replica of harness-sdk#4967] - #13

Open
poshinchen wants to merge 1 commit into
base-pr-4967from
strandly-v3-pr-4967
Open

poshinchen wants to merge 1 commit into
base-pr-4967from
strandly-v3-pr-4967

Conversation

@poshinchen

@poshinchen poshinchen commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Replica of https://github.com/strands-agents/harness-sdk/pull/4967 (URL in backticks to avoid a cross-reference on the original PR) for reviewer evaluation. Branches mirror the original PR's base and head commits.

The review below was produced by the optimized Strandly reviewer (variant v3-reason, optimization run aeb3ee, agent model Opus 4.8) during validation on PRs it never saw in training. Compare with the human and Strandly reviews on the original PR.

Mirrors OpenAIModel's client parameter: an injected client is reused for every request and never closed by the model. With an injected client, stream() reads the rest of the body after the terminal event so the connection returns to the pool, and always closes the response.

Resolves strands-agents#4544

@poshinchen poshinchen left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Strandly v3-reason (optimized) review

Assessment: comment

Assessment: Comment

This PR cleanly mirrors OpenAIModel's injected-client pattern for OpenAIResponsesModel, with sensible validation and well-reasoned connection-reuse draining. The approach and test coverage (parametrized injected vs. default paths, error-path draining) are solid.

Review Categories
  • API review: A new public __init__ parameter changes the model's public API contract — please confirm the api/needs-review label is applied.
  • Dependencies: Direct httpx import should have a declared, upper-bounded direct dependency (please verify).
  • Error handling: Confirm "best-effort" drain/close logic catches a narrow exception set and surfaces missing close/drain failures at debug level rather than silently.
  • Docs consistency: TS example uses OpenAIModel while Python uses OpenAIResponsesModel.
  • Tests: A couple of injected-client tests could assert full event/request shape instead of only call counts.

Nice work pairing the connection-reuse fix with the client injection — the draining rationale is clearly documented.

File-level comments:

  • strands-py/src/strands/models/openai_responses.py: Suggestion

Issue: _close_response_stream uses getattr(response, "close", None) and only calls it if present, silently doing nothing when close is absent — this could mask a connection leak if the response shape changes.
Suggestion: Please verify the response object is always expected to have a close; if so, consider logging at debug level when close is missing so leaks are observable rather than silent.

  • strands-py/src/strands/models/openai_responses.py: Important

Issue: The new httpx import at module level is added for exception handling in _drain_for_connection_reuse (per the diff). httpx is a transitive dependency of openai, so importing it directly adds an undeclared direct dependency.
Suggestion: Please verify httpx is declared as a direct dependency with a supported upper bound in the package metadata; otherwise catch the exceptions via openai's re-exported types or guard the import.

  • strands-py/tests/strands/models/test_openai_responses.py: Suggestion

Issue: test_count_tokens_with_injected_client asserts only tru_tokens == 42 plus client-lifecycle calls, but not that responses.input_tokens.count was awaited with the expected request shape.
Suggestion: Consider adding injected_client.responses.input_tokens.count.assert_awaited_once() and asserting the forwarded kwargs so a regression in request construction is caught, consistent with the other injected-client tests.

  • strands-py/tests/strands/models/test_openai_responses.py: Suggestion

Issue: test_stream_with_injected_client asserts await_count == 2 and lifecycle non-calls, but does not assert the emitted events match an expected full sequence.
Suggestion: Consider asserting the full event list (as test_stream_reads_past_terminal_event_only_with_injected_client does) so the injected-client path's output shape is verified in a single equality check rather than only the call count.

client_args: dict[str, Any] | None = None,
bedrock_mantle_config: BedrockMantleConfig | None = None,
*,
client: Client | None = None,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Important

Issue: The docstring for client lists use cases ("Injecting custom client wrappers (e.g., GuardrailsAsyncOpenAI)") but the PR description states client is keyword-only because client_args is positional. The parameter ordering places client after bedrock_mantle_config, which is reasonable, but new public API surface likely requires API review.
Suggestion: Please confirm this PR carries the api/needs-review label — a new public __init__ parameter on a customer-facing model class changes the public API contract.

// Custom client
{
// --8<-- [start:custom_client]
const client = new OpenAI({ apiKey: '<KEY>' })

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Suggestion

Issue: The TypeScript doc example under custom_client instantiates OpenAIModel, but this PR is about OpenAIResponsesModel. The Python tab uses OpenAIResponsesModel, creating an inconsistency on the OpenAI Responses provider page.
Suggestion: Please verify whether the TS SDK exposes a Responses-specific model; if so, use it here for parity with the Python example to avoid confusing readers.

@@ -210,6 +258,45 @@ def _resolve_client_args(self) -> dict[str, Any]:
)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Important

Issue: _drain_for_connection_reuse (and the error-path draining) catch broad exceptions as "best effort" per the docstring. The parametrized test covers httpx.RemoteProtocolError and openai.APIError, but the catch scope isn't fully visible in the diff.
Suggestion: Please verify the drain catches a narrow, intentional exception set rather than bare Exception, so genuine bugs during draining aren't silently swallowed. If broad catching is intended, log at debug level for observability.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Documentation Preview Failed

The documentation deployment encountered an error. Please check the deployment logs for more details.

This branch had an error being deployed

1 failed deployment
auto-approve — e0ebbf0b Deployed Oct 9, 2026 by poshinchen via Build and Deploy Preview #2
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