Skip to content

fix(sagemaker): pass abortSignal to the transport, not just to the loop - #1566

Open
murdore wants to merge 1 commit into
releasefrom
fix/sagemaker-transport-abort
Open

fix(sagemaker): pass abortSignal to the transport, not just to the loop#1566
murdore wants to merge 1 commit into
releasefrom
fix/sagemaker-transport-abort

Conversation

@murdore

@murdore murdore commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The same defect as Bedrock's (#1565), in a different shape. streaming.ts polls options.abortSignal between iterations, which stops the consumer reading but leaves the HTTP request running. The calls that actually needed the signal are the two client.send() invocations in sagemaker/client.ts — neither took an options argument.

Why this wasn't a two-line change

Bedrock's executeStep already receives a live AbortSignal. SageMaker had none in scope: invokeEndpoint takes an InvokeEndpointParams with no room for one. So this adds an optional abortSignal field and threads it from the two real call sites in language-model.tsdoGenerate and the streaming path. The six detection.ts calls are capability probes and are left alone.

Two details worth review:

  • abortSignal is camelCase against PascalCase neighbours, deliberately. Every other field mirrors an AWS InvokeEndpointCommandInput member. This one is a transport option handed to client.send() as @smithy/types HttpHandlerOptions and never travels in the command payload — spelling it differently keeps that boundary visible.
  • readAbortSignal narrows with instanceof. doGenerate/doStream type their options as Record<string, unknown>, so the value arrives as unknown even though LanguageModelV2CallOptions declares abortSignal?: AbortSignal. instanceof is a real runtime check rather than an assertion, per rule 14.

executeWithRetry needed no change: an AbortError matches none of RETRYABLE_ERROR_CONDITIONS' error names or message keywords, so an abort surfaces instead of re-issuing a request the caller has already abandoned.

Demonstrated

Local stand-in servers, abort fired at 1000ms. Both runs assert the request reached the server before reading anything into the result.

command client outcome server saw close
InvokeEndpoint without still waiting at the 8002ms cap never
InvokeEndpoint with AbortError at 1002ms 1002ms
InvokeEndpointWithResponseStream without still waiting at the 8002ms cap never
InvokeEndpointWithResponseStream with AbortError at 1002ms 1002ms

This corrects a note the Bedrock fix left behind

That work recorded a warning that any SageMaker probe would need HTTP/2 or report a false negative. It does not. SageMaker Runtime speaks HTTP/1.1 and fails against an h2c listener with Parse Error: Expected HTTP/, RTSP/ or ICE/. Bedrock is the HTTP/2 one.

The probe ran both protocols and let the reached-the-server guard decide which was real. That habit — rather than any fixed assumption about a client's transport — is what caught it, and is the same guard that stopped three earlier Bedrock probes' false negative from repeating here.

Verification

sagemaker-streaming 5/5 · providers-mocked 70 passed · 0 failed (of 70) · provider-structure 3/3 · tsc --noEmit --strict 0 errors against a stashed baseline of the same tree · eslint + prettier clean.

No behaviour change when nothing aborts: the field is optional and readAbortSignal returns undefined when the SDK supplies no signal, which is exactly what client.send() received before.

Copilot AI lite review requested due to automatic review settings August 26, 2026 20:57
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 5 minutes.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: afc88038-8807-4988-9a2f-d87c55ad601a

📥 Commits

Reviewing files that changed from the base of the PR and between 04c3fd0 and e81bcbd.

📒 Files selected for processing (70)
  • docs/api/NeuroLink-API-Reference/namespaces/BedrockTypes/type-aliases/BedrockClient.md
  • docs/api/NeuroLink-API-Reference/namespaces/BedrockTypes/type-aliases/InvokeModelCommand.md
  • docs/api/NeuroLink-API-Reference/namespaces/MistralTypes/type-aliases/MistralClient.md
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Counter.md
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Histogram.md
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Meter.md
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Span.md
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Tracer.md
  • docs/api/type-aliases/BatchInferenceConfig.md
  • docs/api/type-aliases/CollectedChunkResult.md
  • docs/api/type-aliases/CostEstimate.md
  • docs/api/type-aliases/DetectionTestConfig.md
  • docs/api/type-aliases/DiagnosticReport.md
  • docs/api/type-aliases/DiagnosticResult.md
  • docs/api/type-aliases/EndpointHealth.md
  • docs/api/type-aliases/EndpointMetrics.md
  • docs/api/type-aliases/GeminiMultimodalInput.md
  • docs/api/type-aliases/GoogleLiveAudioQueueItem.md
  • docs/api/type-aliases/InvokeEndpointParams.md
  • docs/api/type-aliases/InvokeEndpointResponse.md
  • docs/api/type-aliases/LanguageModelObject.md
  • docs/api/type-aliases/ModelDeploymentConfig.md
  • docs/api/type-aliases/ModelDetectionResult.md
  • docs/api/type-aliases/NativeFunctionCall.md
  • docs/api/type-aliases/NativeFunctionDeclaration.md
  • docs/api/type-aliases/NativeFunctionResponse.md
  • docs/api/type-aliases/NativeToolDeclarationsResult.md
  • docs/api/type-aliases/NativeToolsConfig.md
  • docs/api/type-aliases/NeuroLinkInstance.md
  • docs/api/type-aliases/OpenRouterModelInfo.md
  • docs/api/type-aliases/OpenRouterModelsResponse.md
  • docs/api/type-aliases/OpenRouterProviderCache.md
  • docs/api/type-aliases/ParallelDetectionConfig.md
  • docs/api/type-aliases/ProviderConstructor.md
  • docs/api/type-aliases/ProviderDescriptor.md
  • docs/api/type-aliases/ProviderHealthCheckOptions.md
  • docs/api/type-aliases/ProviderHealthStatusOptions.md
  • docs/api/type-aliases/ProviderRegistration.md
  • docs/api/type-aliases/SageMakerAsLanguageModel.md
  • docs/api/type-aliases/SageMakerErrorCode.md
  • docs/api/type-aliases/SageMakerErrorInfo.md
  • docs/api/type-aliases/SageMakerGenerateResult.md
  • docs/api/type-aliases/SageMakerGenerationOptions.md
  • docs/api/type-aliases/SageMakerGenerationResponse.md
  • docs/api/type-aliases/SageMakerOpenAIToolCall.md
  • docs/api/type-aliases/SageMakerStreamChunk.md
  • docs/api/type-aliases/SageMakerStreamingToolCall.md
  • docs/api/type-aliases/SageMakerStreamingToolResult.md
  • docs/api/type-aliases/SageMakerStructuredOutput.md
  • docs/api/type-aliases/SageMakerToolCall.md
  • docs/api/type-aliases/SageMakerToolResult.md
  • docs/api/type-aliases/StepFinishEvent.md
  • docs/api/type-aliases/ToolWithLegacyParams.md
  • docs/api/type-aliases/VertexAnthropicCacheControl.md
  • docs/api/type-aliases/VertexAnthropicCacheInput.md
  • docs/api/type-aliases/VertexAnthropicCacheOutput.md
  • docs/api/type-aliases/VertexAnthropicContentBlock.md
  • docs/api/type-aliases/VertexAnthropicMessage.md
  • docs/api/type-aliases/VertexAnthropicSystemBlock.md
  • docs/api/type-aliases/VertexAnthropicTool.md
  • docs/api/type-aliases/VertexGenaiFunctionDeclaration.md
  • docs/api/type-aliases/VertexNativeLoopPart.md
  • docs/api/type-aliases/VertexNativePart.md
  • docs/api/type-aliases/VertexRegularSegment.md
  • docs/api/type-aliases/VertexSegment.md
  • docs/api/type-aliases/VertexToolStep.md
  • docs/api/type-aliases/VertexUsageCounter.md
  • src/lib/providers/sagemaker/client.ts
  • src/lib/providers/sagemaker/language-model.ts
  • src/lib/types/providers.ts

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.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: e81bcbdcfe118ab327d50c2a798bdb289d8b637d
  • Message: fix(sagemaker): pass abortSignal to the transport, not just to the loop
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The same defect as Bedrock's, in a different shape. streaming.ts polls
options.abortSignal between iterations, which stops the consumer reading
but leaves the HTTP request running; the calls that actually needed the
signal are the two client.send() invocations in sagemaker/client.ts, and
neither took an options argument.

Unlike Bedrock, no signal was in scope to pass. invokeEndpoint takes an
InvokeEndpointParams that had no room for one, so this adds an optional
abortSignal field and threads it from the two real call sites in
language-model.ts: doGenerate and the streaming path. The six detection.ts
calls are capability probes and are left alone.

abortSignal is spelled in camelCase against PascalCase neighbours on
purpose. Every other field mirrors an AWS InvokeEndpointCommandInput
member; this one is a transport option handed to client.send() as
@smithy/types HttpHandlerOptions and never travels in the command payload.

doGenerate and doStream type their options as Record<string, unknown>, so
the value arrives as unknown even though LanguageModelV2CallOptions
declares abortSignal?: AbortSignal. readAbortSignal narrows with
instanceof — a real runtime check rather than an assertion, per rule 14.

executeWithRetry needed no change: an AbortError matches none of
RETRYABLE_ERROR_CONDITIONS' error names or message keywords, so an abort
surfaces instead of re-issuing a request the caller already abandoned.

Demonstrated against local stand-in servers, abort fired at 1000ms, with
both runs asserting the request reached the server before anything was read
into the result:

  InvokeEndpoint                    without: still waiting at the 8002ms cap,
                                             server never saw the socket close
                                    with:    AbortError at 1002ms, close at 1002ms
  InvokeEndpointWithResponseStream  without: same, never closed
                                    with:    AbortError at 1002ms, close at 1002ms

This also corrects a note left behind by the Bedrock fix, which warned that
a SageMaker probe would need HTTP/2 or report a false negative. It does
not: SageMaker Runtime speaks HTTP/1.1 and fails on an h2c listener with
"Parse Error: Expected HTTP/, RTSP/ or ICE/". Bedrock is the HTTP/2 one.
The probe ran both protocols and let the reached-the-server guard decide,
which is the habit worth keeping — not a fixed assumption about either.

docs/api is regenerated because the new field is on an exported type, and
the docs-currency gate fails on any difference. 67 files move: one gains
the abortSignal section, and the other 66 shift their "Defined in:" line
numbers by ten, because every type they document also lives in
types/providers.ts below the insertion point. Every changed line outside
the new section is such a shift, checked rather than assumed.

Verified: sagemaker-streaming 5/5, providers-mocked 70/70,
provider-structure 3/3, tsc --noEmit --strict clean against a baseline of
the same tree, eslint and prettier clean, and docs:api now regenerates with
no drift.
@murdore
murdore force-pushed the fix/sagemaker-transport-abort branch from c9e0362 to e81bcbd Compare August 26, 2026 21:06
@github-actions

Copy link
Copy Markdown
Contributor

Documentation Validation Results

🚀 Documentation validation passed!

Check Status Result
Frontmatter Validation Passed
TypeScript Check Passed
Build Passed
Link Validation Passed

📦 Build artifact uploaded successfully. Ready for deployment preview.

Commit: 79f5596a037875f4a16ef70dc954fd5704dc76ac | Workflow: View logs

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