feat: serverless-offline-bedrock + serverless-offline-transcribe (local AI emulators, MVP)#299
Open
silouone wants to merge 8 commits into
Open
feat: serverless-offline-bedrock + serverless-offline-transcribe (local AI emulators, MVP)#299silouone wants to merge 8 commits into
silouone wants to merge 8 commits into
Conversation
- dynamodb-streams-readable@5.0.0 - serverless-apigateway-access-logs@3.0.0 - serverless-offline-dynamodb-streams@8.0.0 - @coorpacademy/serverless-offline-eventbridge@1.0.0 - serverless-offline-kinesis@9.0.0 - serverless-offline-s3@8.0.0 - serverless-offline-sqs@9.0.0 - serverless-offline-ssm-provider@4.0.0 - serverless-offline-plugins-integration@5.0.0
Copy plugin skeleton (log.js, client-config.js) from serverless-offline-sqs, add bedrock package.json + OpenAI Converse adapter (pure request/response translation). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SPIKE 1 PROVEN: unmodified BedrockRuntimeClient, with only the injected
AWS_ENDPOINT_URL_BEDROCK_RUNTIME env var, dials our local server and parses
our Converse body (end-to-end AVA test). Key finding: Bedrock Runtime SDK uses
NodeHttp2Handler (h2c), so the emulator uses Node's http2 server, not @hapi/hapi
(dropped from deps) — the one evidence-forced deviation from the plan.
- src/index.js: thin plugin (hooks, _mergeOptions, endpoint injection, enabled guard)
- src/bedrock.js: h2c emulator (POST /model/{modelId}/converse, url-decode modelId)
- src/converse.js: orchestration + AC-A4 error shaping (never crashes offline)
- src/adapters/{openai,anthropic}.js: pure Converse<->backend translation
- src/backend-config.js: resolveBackend per-modelId deep-merge, fail-fast
- test/index.js: 32 AVA tests (adapters, resolveBackend, error/disabled paths, SPIKE 1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SPIKE 2 PROVEN with a live Whisper 'base' run: verified words carry a leading
space + glued trailing punctuation (' world,' -> 'world' + ','); the fixture
(test/fixtures/whisper-word-timestamps.json) is the real output, and
whisperJsonToTranscribe reproduces the AWS shape (STRING numerics, punctuation
items omit start/end times). Transcribe batch uses the SDK's HTTP/1.1 handler
(verified), so @hapi/hapi is correct here.
- src/index.js: thin plugin, AWS_ENDPOINT_URL_TRANSCRIBE injection (no generic var)
- src/transcribe.js: @hapi/hapi AWS-JSON-1.1 server, x-amz-target routing, async job pipeline
- src/job-registry.js: pure descriptor builders + Map holder
- src/s3-io.js: s3:// parse, OutputKey rules, path-style Minio URL, forcePathStyle S3 client
- src/whisper.js: execFile worker (no shell), fail-fast on missing binary (AC-C5)
- src/transcript.js: pure whisperJsonToTranscribe + splitWordPunctuation
- test/index.js: 26 AVA tests incl. real TranscribeClient Start->Get->FAILED round-trip
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…INT_URL - READMEs (AC-X3): prerequisites, config table, model/engine map, copy-paste example. - bedrock index.js: inject ONLY the service-specific AWS_ENDPOINT_URL_BEDROCK_RUNTIME, not the generic AWS_ENDPOINT_URL (which would misroute the app's other AWS clients — S3/SQS/DynamoDB — to the bedrock port in a multi-service offline stack). SPIKE 1 proved the service-specific var alone is sufficient. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
npm ci on CI fails EUSAGE (Missing: serverless-offline-bedrock@1.0.0 ...) because the lockfile predates the two new packages and their @aws-sdk deps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hemeral ports) Travis has no whisper binary and fixed ports 4019/4569 collide with live offline sessions on dev machines. The transcribe lifecycle test now skips cleanly when whisper is absent (its fail-fast behavior keeps its own unit test) and both integration tests bind OS-assigned free ports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r class, shutdown, packaging) Correctness: - transcribe/s3-io: stream S3 media to disk (pipeline) instead of buffering the whole object with transformToByteArray — a large-but-valid recording no longer OOMs the offline process. - bedrock+transcribe: honor an explicit `port: 0` (OS-assigned free port). A new resolvePort() replaces `Number(port) || DEFAULT_PORT`, which treated the valid 0 as unset and rebound the fixed port (collision/EADDRINUSE). - bedrock/converse: move toBackendRequest inside the guard so a malformed request (e.g. toolConfig entry without toolSpec) returns 400 ValidationException instead of escaping handleConverse's "never throws" contract as a wrong 500. - transcribe/whisper: add `--verbose False` and raise maxBuffer to 256MB so a long recording's stdout can't trip ERR_CHILD_PROCESS_STDIO_MAXBUFFER and false-FAIL a run that actually succeeded. - bedrock: stop() now tracks raw sockets (incl. stray HTTP/1.1 keep-alives) and honors its timeout arg, so shutdown can't hang the offline process. - bedrock/openai: collapse array content-parts to a string so a non-string OpenAI `content` never leaks into a malformed Converse `text` block. Cleanup: - transcribe/transcript: fold the word→item shaping into a single traversal that emits items and per-segment id-ranges together (was 3x per job with two hand-written id counters kept in lockstep). _processJob reuses the shaped items for the AC-C4 empty-speech guard instead of re-running the pass. - packaging: move the test/README-only @aws-sdk/client-bedrock-runtime and @aws-sdk/client-transcribe to devDependencies; delete the orphaned bedrock src/client-config.js (never required) and drop it from files[]. Tests: +5 (resolvePort x2, malformed-request 400, array content, keep-alive shutdown). 63 pass, eslint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two new plugins that emulate AWS AI data-plane services locally, so the our internal TLS batch pipeline (audio → Transcribe → Bedrock-translate) runs fully offline — the same role ElasticMQ plays for SQS. Implements spec
002-offline-bedrock-transcribeMVP (Phase A + Phase C; streaming B/D deferred).serverless-offline-bedrock— local Bedrock Runtime endpoint:POST /model/{modelId}/converse, pure OpenAI- and Anthropic-compatible backend adapters, per-modelId backend mapping, endpoint injection viaAWS_ENDPOINT_URL_BEDROCK_RUNTIME.serverless-offline-transcribe— local Transcribe (AWS JSON 1.1): Start/Get/List transcription jobs, in-memory job registry, S3/Minio media I/O, OpenAI-Whisper worker (execFile, no shell), AWS-shaped transcript JSON (string numerics, untimed punctuation items).Proven spikes
BedrockRuntimeClientwith only the injected service env var dials the local emulator — encoded as a permanent AVA integration test.whisper --word_timestamps Truerun (fixture checked in).Evidence-forced deviations from the plan (documented in code)
http2(h2c) server, not@hapi/hapi— the SDK's defaultNodeHttp2Handlerspeaks cleartext HTTP/2, which hapi cannot serve. One dep fewer.AWS_ENDPOINT_URL_*vars are injected; the plan's genericAWS_ENDPOINT_URLfallback would misroute the app's other AWS clients (S3/SQS/DDB).Test plan
npx eslint packages/serverless-offline-bedrock packages/serverless-offline-transcribe— cleannpx avaboth suites — 58 tests green (32 + 26), incl. both integration testsCOMPLETEDpath against live Minio (known gap — unit-covered)Notes for reviewers
1d67ee8; the diff shrinks to the 4 plugin commits once master is pushed afterlerna publish.whisperonPATH(else that single test fails fast per AC-C5) and bind ports 4019/4569 — flagged for a follow-up hermeticity tweak.🤖 Generated with Claude Code