@copilotkit/aimock 1.24.1 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +38 -0
- package/README.md +21 -11
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aws-event-stream.cjs +2 -1
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +3 -1
- package/dist/aws-event-stream.d.cts.map +1 -1
- package/dist/aws-event-stream.d.ts +3 -1
- package/dist/aws-event-stream.d.ts.map +1 -1
- package/dist/aws-event-stream.js +2 -1
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/bedrock-converse.cjs +8 -2
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +8 -2
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +8 -2
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +8 -2
- package/dist/bedrock.js.map +1 -1
- package/dist/cli.cjs +36 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +36 -1
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +206 -2
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +207 -4
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/elevenlabs-audio.cjs +173 -1
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +173 -2
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +1 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.js +1 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +2 -4
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.js +2 -4
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +2 -2
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +2 -2
- package/dist/fal.js.map +1 -1
- package/dist/fixture-loader.cjs +16 -3
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +16 -3
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +166 -0
- package/dist/gemini-embeddings.cjs.map +1 -0
- package/dist/gemini-embeddings.js +166 -0
- package/dist/gemini-embeddings.js.map +1 -0
- package/dist/gemini-interactions.cjs +10 -2
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +10 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +12 -2
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +12 -2
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +70 -33
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +9 -5
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +9 -5
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +68 -34
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +295 -13
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts +9 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts +9 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +294 -14
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/llmock.cjs +16 -1
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +2 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +2 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +16 -1
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +9 -2
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +9 -2
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +2 -0
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +2 -0
- package/dist/metrics.js.map +1 -1
- package/dist/ndjson-writer.cjs +2 -1
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +3 -2
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +3 -2
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +2 -1
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +197 -2
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +198 -4
- package/dist/ollama.js.map +1 -1
- package/dist/recorder.cjs +49 -5
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +49 -5
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +11 -2
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +11 -2
- package/dist/responses.js.map +1 -1
- package/dist/server.cjs +196 -49
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +201 -54
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +1 -1
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +1 -1
- package/dist/speech.js.map +1 -1
- package/dist/sse-writer.cjs +48 -10
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +12 -4
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +12 -4
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +48 -10
- package/dist/sse-writer.js.map +1 -1
- package/dist/transcription.cjs +9 -6
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts +2 -2
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts +2 -2
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +8 -7
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +45 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +45 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/video.cjs +1 -1
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +1 -1
- package/dist/video.js.map +1 -1
- package/dist/ws-gemini-live.cjs +14 -4
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +1 -0
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +1 -0
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +15 -5
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +21 -4
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +1 -0
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +1 -0
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +22 -5
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +8 -5
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts +1 -0
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts +1 -0
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +9 -6
- package/dist/ws-responses.js.map +1 -1
- package/package.json +2 -2
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"source": {
|
|
10
10
|
"source": "npm",
|
|
11
11
|
"package": "@copilotkit/aimock",
|
|
12
|
-
"version": "^1.
|
|
12
|
+
"version": "^1.26.0"
|
|
13
13
|
},
|
|
14
14
|
"description": "Fixture authoring skill for @copilotkit/aimock — LLM, multimedia (image/TTS/transcription/video), MCP, A2A, AG-UI, vector, embeddings, structured output, sequential responses, streaming physics, record/replay, agent loop patterns, and debugging"
|
|
15
15
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Timing-aware recording and replay** — proxy recording captures per-frame
|
|
8
|
+
arrival timestamps as `recordedTimings` on fixtures. Replay uses recorded
|
|
9
|
+
timings for approximate timing reproduction based on recorded TTFT and
|
|
10
|
+
inter-frame cadence instead of the synthetic model. Replay chunk count may
|
|
11
|
+
differ from recording chunk count — TTFT and average pace are preserved,
|
|
12
|
+
not per-token fidelity. `--replay-speed N` multiplier applies to all delay
|
|
13
|
+
sources (recorded timings, streaming profiles, global latency). Per-fixture
|
|
14
|
+
`replaySpeed` override. Covers SSE, NDJSON, Bedrock EventStream, and
|
|
15
|
+
WebSocket protocols.
|
|
16
|
+
- **Gemini `embedContent` endpoint** — `POST /v1beta/models/{model}:embedContent`
|
|
17
|
+
with deterministic fallback embeddings and fixture matching
|
|
18
|
+
- **`/v1/images/edit` and `/v1/images/variations` endpoints** — multipart
|
|
19
|
+
form-data, same response format as generations. Closes #221
|
|
20
|
+
- **`/v1/audio/translations` endpoint** — reuses transcription handler with
|
|
21
|
+
`endpoint: "translation"` and `task: "translate"` in verbose mode
|
|
22
|
+
- **Ollama `/api/embeddings` endpoint** — single-embedding response, supports
|
|
23
|
+
both `prompt` and `input` (string or array) fields
|
|
24
|
+
- **Cohere `/v2/embed` endpoint** — multi-text embedding with configurable
|
|
25
|
+
`embedding_types` (float, int8, etc.)
|
|
26
|
+
- **ElevenLabs `/v1/text-to-speech/{voice_id}` endpoint** — binary audio
|
|
27
|
+
response with voice routing and `onElevenLabsTTS` helper
|
|
28
|
+
- **Streaming usage chunks** — when `stream_options.include_usage` is set,
|
|
29
|
+
emits a final SSE chunk with token usage before `[DONE]`
|
|
30
|
+
- **Automatic token usage estimation** — responses without explicit fixture
|
|
31
|
+
`usage` overrides now return estimated token counts (~4 chars/token)
|
|
32
|
+
instead of zeros
|
|
33
|
+
- **Rate limiting headers on 429 responses** — `Retry-After`,
|
|
34
|
+
`x-ratelimit-limit-*`, `x-ratelimit-remaining-*`,
|
|
35
|
+
`x-ratelimit-reset-*` headers on all error fixtures with status 429.
|
|
36
|
+
Custom `retryAfter` override via fixture field
|
|
37
|
+
- **`onTranslation` convenience method** — register translation fixtures
|
|
38
|
+
with endpoint discrimination
|
|
39
|
+
- **`onElevenLabsTTS` convenience method** — register ElevenLabs TTS
|
|
40
|
+
fixtures
|
|
41
|
+
- **Configurable proxy timeouts** — `RecordConfig` now accepts `upstreamTimeoutMs` (default 30s) and `bodyTimeoutMs` (default 30s). The body-idle timeout is the Node socket inactivity timer that fires `req.destroy()` mid-stream; under concurrent load against reasoning models (e.g. Grok 4.3 + structured output), token-emission gaps can routinely exceed 30s during the thinking phase, causing record-mode runs to truncate SSE responses mid-stream with no `[DONE]` and no `finish_reason`. Lift to e.g. `bodyTimeoutMs: 180_000` to record cleanly under that workload.
|
|
42
|
+
|
|
5
43
|
## [1.24.1] - 2026-05-14
|
|
6
44
|
|
|
7
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# aimock [](https://github.com/CopilotKit/aimock/actions/workflows/test-unit.yml) [](https://github.com/CopilotKit/aimock/actions/workflows/test-drift.yml) [](https://www.npmjs.com/package/@copilotkit/aimock)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, audio generation, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.
|
|
4
|
+
Mock infrastructure for AI application testing — LLM APIs, image generation, image editing, text-to-speech, transcription, audio translation, audio generation, video generation, embeddings, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.
|
|
5
5
|
|
|
6
6
|
## Quick Start
|
|
7
7
|
|
|
@@ -34,23 +34,24 @@ await mock.stop();
|
|
|
34
34
|
|
|
35
35
|
aimock mocks everything your AI app talks to:
|
|
36
36
|
|
|
37
|
-
| Tool | What it mocks
|
|
38
|
-
| -------------- |
|
|
39
|
-
| **LLMock** | OpenAI (Chat/Responses/Realtime GA+Beta), Claude, Gemini (REST/Live/Interactions), Bedrock, Azure, Vertex AI, Ollama, Cohere | [Providers](https://aimock.copilotkit.dev/docs) |
|
|
40
|
-
| **MCPMock** | MCP tools, resources, prompts with session management
|
|
41
|
-
| **A2AMock** | Agent-to-agent protocol with SSE streaming
|
|
42
|
-
| **AGUIMock** | AG-UI agent-to-UI event streams for frontend testing
|
|
43
|
-
| **VectorMock** | Pinecone, Qdrant, ChromaDB compatible endpoints
|
|
44
|
-
| **Services** | Tavily search, Cohere rerank, OpenAI moderation
|
|
37
|
+
| Tool | What it mocks | Docs |
|
|
38
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
39
|
+
| **LLMock** | OpenAI (Chat/Responses/Realtime GA+Beta), Claude, Gemini (REST/Live/Interactions/Embeddings), Bedrock, Azure, Vertex AI, Ollama (chat/embeddings), Cohere (chat/embed), ElevenLabs TTS | [Providers](https://aimock.copilotkit.dev/docs) |
|
|
40
|
+
| **MCPMock** | MCP tools, resources, prompts with session management | [MCP](https://aimock.copilotkit.dev/mcp-mock) |
|
|
41
|
+
| **A2AMock** | Agent-to-agent protocol with SSE streaming | [A2A](https://aimock.copilotkit.dev/a2a-mock) |
|
|
42
|
+
| **AGUIMock** | AG-UI agent-to-UI event streams for frontend testing | [AG-UI](https://aimock.copilotkit.dev/agui-mock) |
|
|
43
|
+
| **VectorMock** | Pinecone, Qdrant, ChromaDB compatible endpoints | [Vector](https://aimock.copilotkit.dev/vector-mock) |
|
|
44
|
+
| **Services** | Tavily search, Cohere rerank, OpenAI moderation, ElevenLabs TTS | [Services](https://aimock.copilotkit.dev/services) |
|
|
45
45
|
|
|
46
46
|
Run them all on one port with `npx @copilotkit/aimock --config aimock.json`, or use the programmatic API to compose exactly what you need.
|
|
47
47
|
|
|
48
48
|
## Features
|
|
49
49
|
|
|
50
50
|
- **[Record & Replay](https://aimock.copilotkit.dev/record-replay)** — Proxy real APIs, save as fixtures, replay deterministically forever
|
|
51
|
+
- **Timing-aware recording and replay** — Recorded fixtures capture per-frame arrival timestamps; replay uses recorded timings for approximate timing reproduction based on recorded TTFT and inter-frame cadence (replay chunk count may differ from recording — TTFT and average pace are preserved, not per-token fidelity) with configurable `--replay-speed` multiplier
|
|
51
52
|
- **[Multi-turn Conversations](https://aimock.copilotkit.dev/multi-turn)** — Record and replay multi-turn traces with tool rounds; match distinct turns via `turnIndex`, `hasToolResult`, `toolCallId`, `sequenceIndex`, `systemMessage` (gate on host-supplied agent context), or custom predicates
|
|
52
|
-
- **[
|
|
53
|
-
- **Multimedia APIs** — [image generation](https://aimock.copilotkit.dev/images) (DALL-E, Imagen), [text-to-speech](https://aimock.copilotkit.dev/speech), [audio transcription](https://aimock.copilotkit.dev/transcription), [video generation](https://aimock.copilotkit.dev/video), [fal.ai](https://aimock.copilotkit.dev/fal-ai) (image / video / audio with queue lifecycle)
|
|
53
|
+
- **[14 LLM Providers](https://aimock.copilotkit.dev/docs)** — OpenAI Chat, OpenAI Responses, OpenAI Realtime (GA + Beta shim), Claude, Gemini (REST + embedContent), Gemini Live, Gemini Interactions, Azure, Bedrock, Vertex AI, Ollama (chat + embeddings), Cohere (chat + embed), ElevenLabs TTS — full streaming support
|
|
54
|
+
- **Multimedia APIs** — [image generation](https://aimock.copilotkit.dev/images) (DALL-E, Imagen), [image editing](https://aimock.copilotkit.dev/images) (/v1/images/edit), [text-to-speech](https://aimock.copilotkit.dev/speech) (OpenAI + ElevenLabs), [audio transcription](https://aimock.copilotkit.dev/transcription), [audio translation](https://aimock.copilotkit.dev/transcription) (/v1/audio/translations), [video generation](https://aimock.copilotkit.dev/video), [fal.ai](https://aimock.copilotkit.dev/fal-ai) (image / video / audio with queue lifecycle)
|
|
54
55
|
- **[MCP](https://aimock.copilotkit.dev/mcp-mock) / [A2A](https://aimock.copilotkit.dev/a2a-mock) / [AG-UI](https://aimock.copilotkit.dev/agui-mock) / [Vector](https://aimock.copilotkit.dev/vector-mock)** — Mock every protocol your AI agents use
|
|
55
56
|
- **[Chaos Testing](https://aimock.copilotkit.dev/chaos-testing)** — 500 errors, malformed JSON, mid-stream disconnects at any probability
|
|
56
57
|
- **Per-Request Strict Mode** — `X-AIMock-Strict` header overrides the server-level `--strict` flag per request (`true`/`1` = strict, `false`/`0` = lenient)
|
|
@@ -61,6 +62,8 @@ Run them all on one port with `npx @copilotkit/aimock --config aimock.json`, or
|
|
|
61
62
|
- **[Docker + Helm](https://aimock.copilotkit.dev/docker)** — Container image and Helm chart for CI/CD
|
|
62
63
|
- **[Vitest & Jest Plugins](https://aimock.copilotkit.dev/test-plugins)** — Zero-config `useAimock()` with auto lifecycle and env patching
|
|
63
64
|
- **[Response Overrides](https://aimock.copilotkit.dev/fixtures)** — Control `id`, `model`, `usage`, `finishReason` in fixture responses
|
|
65
|
+
- **[Streaming Usage Chunks](https://aimock.copilotkit.dev/streaming-physics)** — `stream_options.include_usage` support emits a final chunk with token counts, matching OpenAI's streaming usage protocol
|
|
66
|
+
- **[Rate Limiting Headers](https://aimock.copilotkit.dev/chaos-testing)** — `x-ratelimit-*` headers on every response and `Retry-After` on 429 errors for testing retry/backoff logic
|
|
64
67
|
- **Zero dependencies** — Everything from Node.js builtins
|
|
65
68
|
|
|
66
69
|
## GitHub Action
|
|
@@ -94,6 +97,13 @@ npx @copilotkit/aimock --config aimock.json
|
|
|
94
97
|
# Record mode: proxy to real APIs, save fixtures
|
|
95
98
|
npx -p @copilotkit/aimock llmock --record --provider-openai https://api.openai.com
|
|
96
99
|
|
|
100
|
+
# Record with extended timeout for reasoning models
|
|
101
|
+
npx -p @copilotkit/aimock llmock --record --provider-openai https://api.openai.com \
|
|
102
|
+
--body-timeout-ms 180000
|
|
103
|
+
|
|
104
|
+
# Replay recorded fixtures at 2× speed
|
|
105
|
+
npx -p @copilotkit/aimock llmock -p 4010 -f ./fixtures --replay-speed 2
|
|
106
|
+
|
|
97
107
|
# Convert fixtures from other tools
|
|
98
108
|
npx @copilotkit/aimock convert vidaimock ./templates/ ./fixtures/
|
|
99
109
|
npx @copilotkit/aimock convert mockllm ./config.yaml ./fixtures/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agui-types.d.cts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":";KAOY,aAAA;AAAA,UA6CK,aAAA,CA7CQ;EA6CR,IAAA,EACT,aADsB;EAUb,SAAA,CAAA,EAAA,MAAA;EAAoB,QAAA,CAAA,EAAA,OAAA;;AAAQ,UAA5B,mBAAA,SAA4B,aAAA,CAAA;EAAa,IAAA,EAAA,aAAA;EAQzC,QAAA,EAAA,MAAA;EAAqB,KAAA,EAAA,MAAA;aAK1B,CAAA,EAAA,MAAA;OALkC,CAAA,EAHpC,iBAGoC;;AAQ7B,UARA,oBAAA,SAA6B,aAQU,CAAA;EAMvC,IAAA,EAAA,cAAA;EAKA,QAAA,EAAA,MAAA;EAOL,KAAA,EAAA,MAAA;EAEA,MAAA,CAAA,EAAA,OAAA;EASK,OAAA,CAAA,EAhCL,sBAgC+B;;AAGnC,UAhCS,iBAAA,SAA0B,aAgCnC,CAAA;MAH2C,EAAA,WAAA;EAAa,OAAA,EAAA,MAAA;EAO/C,IAAA,CAAA,EAAA,MAAA;AAMjB;AAKiB,UAzCA,oBAAA,SAA6B,aAyCH,CAAA;EAAA,IAAA,EAAA,cAAA;UAGlC,EAAA,MAAA;;AAHuD,UApC/C,qBAAA,SAA8B,aAoCiB,CAAA;EAU/C,IAAA,EAAA,eAAA;EAOA,QAAA,EAAA,MAAA;AAMjB;AAKiB,KAzDL,mBAAA,GAyD4B,WAAQ,GAAA,QAAa,GAAA,WAAA,GAAA,MAAA;AAQ5C,KA/DL,eAAA,GA+D6B,
|
|
1
|
+
{"version":3,"file":"agui-types.d.cts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/agui-types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agui-types.d.ts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":";KAOY,aAAA;AAAA,UA6CK,aAAA,CA7CQ;EA6CR,IAAA,EACT,aADsB;EAUb,SAAA,CAAA,EAAA,MAAA;EAAoB,QAAA,CAAA,EAAA,OAAA;;AAAQ,UAA5B,mBAAA,SAA4B,aAAA,CAAA;EAAa,IAAA,EAAA,aAAA;EAQzC,QAAA,EAAA,MAAA;EAAqB,KAAA,EAAA,MAAA;aAK1B,CAAA,EAAA,MAAA;OALkC,CAAA,EAHpC,iBAGoC;;AAQ7B,UARA,oBAAA,SAA6B,aAQU,CAAA;EAMvC,IAAA,EAAA,cAAA;EAKA,QAAA,EAAA,MAAA;EAOL,KAAA,EAAA,MAAA;EAEA,MAAA,CAAA,EAAA,OAAA;EASK,OAAA,CAAA,EAhCL,sBAgC+B;;AAGnC,UAhCS,iBAAA,SAA0B,aAgCnC,CAAA;MAH2C,EAAA,WAAA;EAAa,OAAA,EAAA,MAAA;EAO/C,IAAA,CAAA,EAAA,MAAA;AAMjB;AAKiB,UAzCA,oBAAA,SAA6B,aAyCH,CAAA;EAAA,IAAA,EAAA,cAAA;UAGlC,EAAA,MAAA;;AAHuD,UApC/C,qBAAA,SAA8B,aAoCiB,CAAA;EAU/C,IAAA,EAAA,eAAA;EAOA,QAAA,EAAA,MAAA;AAMjB;AAKiB,KAzDL,mBAAA,GAyD4B,WAAQ,GAAA,QAAa,GAAA,WAAA,GAAA,MAAA;AAQ5C,KA/DL,eAAA,GA+D6B,
|
|
1
|
+
{"version":3,"file":"agui-types.d.ts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -89,6 +89,7 @@ async function writeEventStream(res, events, options) {
|
|
|
89
89
|
const opts = options ?? {};
|
|
90
90
|
const latency = opts.latency ?? 0;
|
|
91
91
|
const profile = opts.streamingProfile;
|
|
92
|
+
const { recordedTimings, replaySpeed } = opts;
|
|
92
93
|
const signal = opts.signal;
|
|
93
94
|
const onChunkSent = opts.onChunkSent;
|
|
94
95
|
if (res.writableEnded) return true;
|
|
@@ -96,7 +97,7 @@ async function writeEventStream(res, events, options) {
|
|
|
96
97
|
res.setHeader("Transfer-Encoding", "chunked");
|
|
97
98
|
let chunkIndex = 0;
|
|
98
99
|
for (const event of events) {
|
|
99
|
-
const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency);
|
|
100
|
+
const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
100
101
|
if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
|
|
101
102
|
if (signal?.aborted) return false;
|
|
102
103
|
if (res.writableEnded) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-event-stream.cjs","names":["calculateDelay","delay"],"sources":["../src/aws-event-stream.ts"],"sourcesContent":["/**\n * AWS Event Stream binary frame encoder.\n *\n * Implements the AWS binary event stream framing protocol used by Bedrock's\n * streaming (invoke-with-response-stream) endpoint. Each frame carries a set of\n * string headers and a raw-bytes payload, wrapped in a prelude with CRC32\n * checksums for integrity.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B CRC32 of first 8 bytes]\n * [headers: variable]\n * [payload: variable, raw JSON bytes]\n * [message_crc32: 4B CRC32 of entire frame minus last 4 bytes]\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type * as http from \"node:http\";\nimport type { StreamingProfile } from \"./types.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\n\n// ─── Header encoding ────────────────────────────────────────────────────────\n\nfunction encodeHeaders(headers: Record<string, string>): Buffer {\n const parts: Buffer[] = [];\n for (const [name, value] of Object.entries(headers)) {\n const nameBytes = Buffer.from(name, \"utf8\");\n const valueBytes = Buffer.from(value, \"utf8\");\n\n // name_length (1 byte) + name + type (1 byte, 7 = STRING) +\n // value_length (2 bytes BE) + value\n const header = Buffer.alloc(1 + nameBytes.length + 1 + 2 + valueBytes.length);\n let offset = 0;\n header.writeUInt8(nameBytes.length, offset);\n offset += 1;\n nameBytes.copy(header, offset);\n offset += nameBytes.length;\n header.writeUInt8(7, offset); // STRING type\n offset += 1;\n header.writeUInt16BE(valueBytes.length, offset);\n offset += 2;\n valueBytes.copy(header, offset);\n\n parts.push(header);\n }\n return Buffer.concat(parts);\n}\n\n// ─── Frame encoding ─────────────────────────────────────────────────────────\n\n/**\n * Encode a single AWS Event Stream binary frame with the given headers and\n * payload buffer.\n */\nexport function encodeEventStreamFrame(headers: Record<string, string>, payload: Buffer): Buffer {\n const headersBuffer = encodeHeaders(headers);\n const headersLength = headersBuffer.length;\n\n // prelude (8) + prelude_crc (4) + headers + payload + message_crc (4)\n const totalLength = 4 + 4 + 4 + headersLength + payload.length + 4;\n\n const frame = Buffer.alloc(totalLength);\n let offset = 0;\n\n // Prelude\n frame.writeUInt32BE(totalLength, offset);\n offset += 4;\n frame.writeUInt32BE(headersLength, offset);\n offset += 4;\n\n // Prelude CRC32 (covers first 8 bytes)\n const preludeCrc = crc32(frame.subarray(0, 8));\n frame.writeUInt32BE(preludeCrc >>> 0, offset);\n offset += 4;\n\n // Headers\n headersBuffer.copy(frame, offset);\n offset += headersLength;\n\n // Payload\n payload.copy(frame, offset);\n offset += payload.length;\n\n // Message CRC32 (covers entire frame minus last 4 bytes)\n const messageCrc = crc32(frame.subarray(0, totalLength - 4));\n frame.writeUInt32BE(messageCrc >>> 0, offset);\n\n return frame;\n}\n\n// ─── Convenience wrappers ───────────────────────────────────────────────────\n\n/**\n * Encode an event-stream message with standard AWS headers for a JSON event.\n *\n * Sets `:content-type` = `application/json`, `:event-type` = eventType,\n * `:message-type` = `event`.\n */\nexport function encodeEventStreamMessage(eventType: string, jsonPayload: object): Buffer {\n const headers: Record<string, string> = {\n \":content-type\": \"application/json\",\n \":event-type\": eventType,\n \":message-type\": \"event\",\n };\n const payload = Buffer.from(JSON.stringify(jsonPayload), \"utf8\");\n return encodeEventStreamFrame(headers, payload);\n}\n\n/**\n * Write a sequence of event-stream frames to an HTTP response with optional\n * timing control. Mirrors the writeSSEStream pattern from sse-writer.ts.\n *\n * Returns `true` when all events are written (including when the response\n * was already ended before writing began), or `false` if interrupted by\n * the provided abort signal.\n */\nexport async function writeEventStream(\n res: http.ServerResponse,\n events: Array<{ eventType: string; payload: object }>,\n options?: {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n },\n): Promise<boolean> {\n const opts = options ?? {};\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"application/vnd.amazon.eventstream\");\n res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) {\n await delay(chunkDelay, signal);\n }\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n\n const frame = encodeEventStreamMessage(event.eventType, event.payload);\n res.write(frame);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,cAAc,SAAyC;CAC9D,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;EACnD,MAAM,YAAY,OAAO,KAAK,MAAM,OAAO;EAC3C,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO;EAI7C,MAAM,SAAS,OAAO,MAAM,IAAI,UAAU,SAAS,IAAI,IAAI,WAAW,OAAO;EAC7E,IAAI,SAAS;AACb,SAAO,WAAW,UAAU,QAAQ,OAAO;AAC3C,YAAU;AACV,YAAU,KAAK,QAAQ,OAAO;AAC9B,YAAU,UAAU;AACpB,SAAO,WAAW,GAAG,OAAO;AAC5B,YAAU;AACV,SAAO,cAAc,WAAW,QAAQ,OAAO;AAC/C,YAAU;AACV,aAAW,KAAK,QAAQ,OAAO;AAE/B,QAAM,KAAK,OAAO;;AAEpB,QAAO,OAAO,OAAO,MAAM;;;;;;AAS7B,SAAgB,uBAAuB,SAAiC,SAAyB;CAC/F,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,gBAAgB,cAAc;CAGpC,MAAM,cAAc,KAAY,gBAAgB,QAAQ,SAAS;CAEjE,MAAM,QAAQ,OAAO,MAAM,YAAY;CACvC,IAAI,SAAS;AAGb,OAAM,cAAc,aAAa,OAAO;AACxC,WAAU;AACV,OAAM,cAAc,eAAe,OAAO;AAC1C,WAAU;CAGV,MAAM,kCAAmB,MAAM,SAAS,GAAG,EAAE,CAAC;AAC9C,OAAM,cAAc,eAAe,GAAG,OAAO;AAC7C,WAAU;AAGV,eAAc,KAAK,OAAO,OAAO;AACjC,WAAU;AAGV,SAAQ,KAAK,OAAO,OAAO;AAC3B,WAAU,QAAQ;CAGlB,MAAM,kCAAmB,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;AAC5D,OAAM,cAAc,eAAe,GAAG,OAAO;AAE7C,QAAO;;;;;;;;AAWT,SAAgB,yBAAyB,WAAmB,aAA6B;AAOvF,QAAO,uBANiC;EACtC,iBAAiB;EACjB,eAAe;EACf,iBAAiB;EAClB,EACe,OAAO,KAAK,KAAK,UAAU,YAAY,EAAE,OAAO,CACjB;;;;;;;;;;AAWjD,eAAsB,iBACpB,KACA,QACA,
|
|
1
|
+
{"version":3,"file":"aws-event-stream.cjs","names":["calculateDelay","delay"],"sources":["../src/aws-event-stream.ts"],"sourcesContent":["/**\n * AWS Event Stream binary frame encoder.\n *\n * Implements the AWS binary event stream framing protocol used by Bedrock's\n * streaming (invoke-with-response-stream) endpoint. Each frame carries a set of\n * string headers and a raw-bytes payload, wrapped in a prelude with CRC32\n * checksums for integrity.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B CRC32 of first 8 bytes]\n * [headers: variable]\n * [payload: variable, raw JSON bytes]\n * [message_crc32: 4B CRC32 of entire frame minus last 4 bytes]\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type * as http from \"node:http\";\nimport type { StreamingProfile, RecordedTimings } from \"./types.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\n\n// ─── Header encoding ────────────────────────────────────────────────────────\n\nfunction encodeHeaders(headers: Record<string, string>): Buffer {\n const parts: Buffer[] = [];\n for (const [name, value] of Object.entries(headers)) {\n const nameBytes = Buffer.from(name, \"utf8\");\n const valueBytes = Buffer.from(value, \"utf8\");\n\n // name_length (1 byte) + name + type (1 byte, 7 = STRING) +\n // value_length (2 bytes BE) + value\n const header = Buffer.alloc(1 + nameBytes.length + 1 + 2 + valueBytes.length);\n let offset = 0;\n header.writeUInt8(nameBytes.length, offset);\n offset += 1;\n nameBytes.copy(header, offset);\n offset += nameBytes.length;\n header.writeUInt8(7, offset); // STRING type\n offset += 1;\n header.writeUInt16BE(valueBytes.length, offset);\n offset += 2;\n valueBytes.copy(header, offset);\n\n parts.push(header);\n }\n return Buffer.concat(parts);\n}\n\n// ─── Frame encoding ─────────────────────────────────────────────────────────\n\n/**\n * Encode a single AWS Event Stream binary frame with the given headers and\n * payload buffer.\n */\nexport function encodeEventStreamFrame(headers: Record<string, string>, payload: Buffer): Buffer {\n const headersBuffer = encodeHeaders(headers);\n const headersLength = headersBuffer.length;\n\n // prelude (8) + prelude_crc (4) + headers + payload + message_crc (4)\n const totalLength = 4 + 4 + 4 + headersLength + payload.length + 4;\n\n const frame = Buffer.alloc(totalLength);\n let offset = 0;\n\n // Prelude\n frame.writeUInt32BE(totalLength, offset);\n offset += 4;\n frame.writeUInt32BE(headersLength, offset);\n offset += 4;\n\n // Prelude CRC32 (covers first 8 bytes)\n const preludeCrc = crc32(frame.subarray(0, 8));\n frame.writeUInt32BE(preludeCrc >>> 0, offset);\n offset += 4;\n\n // Headers\n headersBuffer.copy(frame, offset);\n offset += headersLength;\n\n // Payload\n payload.copy(frame, offset);\n offset += payload.length;\n\n // Message CRC32 (covers entire frame minus last 4 bytes)\n const messageCrc = crc32(frame.subarray(0, totalLength - 4));\n frame.writeUInt32BE(messageCrc >>> 0, offset);\n\n return frame;\n}\n\n// ─── Convenience wrappers ───────────────────────────────────────────────────\n\n/**\n * Encode an event-stream message with standard AWS headers for a JSON event.\n *\n * Sets `:content-type` = `application/json`, `:event-type` = eventType,\n * `:message-type` = `event`.\n */\nexport function encodeEventStreamMessage(eventType: string, jsonPayload: object): Buffer {\n const headers: Record<string, string> = {\n \":content-type\": \"application/json\",\n \":event-type\": eventType,\n \":message-type\": \"event\",\n };\n const payload = Buffer.from(JSON.stringify(jsonPayload), \"utf8\");\n return encodeEventStreamFrame(headers, payload);\n}\n\n/**\n * Write a sequence of event-stream frames to an HTTP response with optional\n * timing control. Mirrors the writeSSEStream pattern from sse-writer.ts.\n *\n * Returns `true` when all events are written (including when the response\n * was already ended before writing began), or `false` if interrupted by\n * the provided abort signal.\n */\nexport async function writeEventStream(\n res: http.ServerResponse,\n events: Array<{ eventType: string; payload: object }>,\n options?: {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n },\n): Promise<boolean> {\n const opts = options ?? {};\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"application/vnd.amazon.eventstream\");\n res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) {\n await delay(chunkDelay, signal);\n }\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n\n const frame = encodeEventStreamMessage(event.eventType, event.payload);\n res.write(frame);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,cAAc,SAAyC;CAC9D,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;EACnD,MAAM,YAAY,OAAO,KAAK,MAAM,OAAO;EAC3C,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO;EAI7C,MAAM,SAAS,OAAO,MAAM,IAAI,UAAU,SAAS,IAAI,IAAI,WAAW,OAAO;EAC7E,IAAI,SAAS;AACb,SAAO,WAAW,UAAU,QAAQ,OAAO;AAC3C,YAAU;AACV,YAAU,KAAK,QAAQ,OAAO;AAC9B,YAAU,UAAU;AACpB,SAAO,WAAW,GAAG,OAAO;AAC5B,YAAU;AACV,SAAO,cAAc,WAAW,QAAQ,OAAO;AAC/C,YAAU;AACV,aAAW,KAAK,QAAQ,OAAO;AAE/B,QAAM,KAAK,OAAO;;AAEpB,QAAO,OAAO,OAAO,MAAM;;;;;;AAS7B,SAAgB,uBAAuB,SAAiC,SAAyB;CAC/F,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,gBAAgB,cAAc;CAGpC,MAAM,cAAc,KAAY,gBAAgB,QAAQ,SAAS;CAEjE,MAAM,QAAQ,OAAO,MAAM,YAAY;CACvC,IAAI,SAAS;AAGb,OAAM,cAAc,aAAa,OAAO;AACxC,WAAU;AACV,OAAM,cAAc,eAAe,OAAO;AAC1C,WAAU;CAGV,MAAM,kCAAmB,MAAM,SAAS,GAAG,EAAE,CAAC;AAC9C,OAAM,cAAc,eAAe,GAAG,OAAO;AAC7C,WAAU;AAGV,eAAc,KAAK,OAAO,OAAO;AACjC,WAAU;AAGV,SAAQ,KAAK,OAAO,OAAO;AAC3B,WAAU,QAAQ;CAGlB,MAAM,kCAAmB,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;AAC5D,OAAM,cAAc,eAAe,GAAG,OAAO;AAE7C,QAAO;;;;;;;;AAWT,SAAgB,yBAAyB,WAAmB,aAA6B;AAOvF,QAAO,uBANiC;EACtC,iBAAiB;EACjB,eAAe;EACf,iBAAiB;EAClB,EACe,OAAO,KAAK,KAAK,UAAU,YAAY,EAAE,OAAO,CACjB;;;;;;;;;;AAWjD,eAAsB,iBACpB,KACA,QACA,SAQkB;CAClB,MAAM,OAAO,WAAW,EAAE;CAC1B,MAAM,UAAU,KAAK,WAAW;CAChC,MAAM,UAAU,KAAK;CACrB,MAAM,EAAE,iBAAiB,gBAAgB;CACzC,MAAM,SAAS,KAAK;CACpB,MAAM,cAAc,KAAK;AAEzB,KAAI,IAAI,cAAe,QAAO;AAC9B,KAAI,UAAU,gBAAgB,qCAAqC;AACnE,KAAI,UAAU,qBAAqB,UAAU;CAE7C,IAAI,aAAa;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,aAAaA,kCAAe,YAAY,SAAS,SAAS,iBAAiB,YAAY;AAC7F,MAAI,aAAa,EACf,OAAMC,yBAAM,YAAY,OAAO;AAEjC,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,IAAI,cAAe,QAAO;EAE9B,MAAM,QAAQ,yBAAyB,MAAM,WAAW,MAAM,QAAQ;AACtE,MAAI,MAAM,MAAM;AAChB,iBAAe;AACf,MAAI,QAAQ,QAAS,QAAO;AAC5B;;AAGF,KAAI,CAAC,IAAI,cACP,KAAI,KAAK;AAEX,QAAO"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamingProfile } from "./types.cjs";
|
|
1
|
+
import { RecordedTimings, StreamingProfile } from "./types.cjs";
|
|
2
2
|
import * as http$1 from "node:http";
|
|
3
3
|
|
|
4
4
|
//#region src/aws-event-stream.d.ts
|
|
@@ -29,6 +29,8 @@ declare function writeEventStream(res: http$1.ServerResponse, events: Array<{
|
|
|
29
29
|
}>, options?: {
|
|
30
30
|
latency?: number;
|
|
31
31
|
streamingProfile?: StreamingProfile;
|
|
32
|
+
recordedTimings?: RecordedTimings;
|
|
33
|
+
replaySpeed?: number;
|
|
32
34
|
signal?: AbortSignal;
|
|
33
35
|
onChunkSent?: () => void;
|
|
34
36
|
}): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-event-stream.d.cts","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAuDgB,sBAAA,UAAgC,iCAAiC,SAAS;;;;;;;iBA4C1E,wBAAA,0CAAkE;;;;;;;;;iBAkB5D,gBAAA,MACf,MAAA,CAAK,wBACF;;;;;qBAGa;
|
|
1
|
+
{"version":3,"file":"aws-event-stream.d.cts","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAuDgB,sBAAA,UAAgC,iCAAiC,SAAS;;;;;;;iBA4C1E,wBAAA,0CAAkE;;;;;;;;;iBAkB5D,gBAAA,MACf,MAAA,CAAK,wBACF;;;;;qBAGa;oBACD;;WAET;;IAGV"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StreamingProfile } from "./types.js";
|
|
1
|
+
import { RecordedTimings, StreamingProfile } from "./types.js";
|
|
2
2
|
import * as http$1 from "node:http";
|
|
3
3
|
|
|
4
4
|
//#region src/aws-event-stream.d.ts
|
|
@@ -29,6 +29,8 @@ declare function writeEventStream(res: http$1.ServerResponse, events: Array<{
|
|
|
29
29
|
}>, options?: {
|
|
30
30
|
latency?: number;
|
|
31
31
|
streamingProfile?: StreamingProfile;
|
|
32
|
+
recordedTimings?: RecordedTimings;
|
|
33
|
+
replaySpeed?: number;
|
|
32
34
|
signal?: AbortSignal;
|
|
33
35
|
onChunkSent?: () => void;
|
|
34
36
|
}): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-event-stream.d.ts","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAuDgB,sBAAA,UAAgC,iCAAiC,SAAS;;;;;;;iBA4C1E,wBAAA,0CAAkE;;;;;;;;;iBAkB5D,gBAAA,MACf,MAAA,CAAK,wBACF;;;;;qBAGa;
|
|
1
|
+
{"version":3,"file":"aws-event-stream.d.ts","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAuDgB,sBAAA,UAAgC,iCAAiC,SAAS;;;;;;;iBA4C1E,wBAAA,0CAAkE;;;;;;;;;iBAkB5D,gBAAA,MACf,MAAA,CAAK,wBACF;;;;;qBAGa;oBACD;;WAET;;IAGV"}
|
package/dist/aws-event-stream.js
CHANGED
|
@@ -88,6 +88,7 @@ async function writeEventStream(res, events, options) {
|
|
|
88
88
|
const opts = options ?? {};
|
|
89
89
|
const latency = opts.latency ?? 0;
|
|
90
90
|
const profile = opts.streamingProfile;
|
|
91
|
+
const { recordedTimings, replaySpeed } = opts;
|
|
91
92
|
const signal = opts.signal;
|
|
92
93
|
const onChunkSent = opts.onChunkSent;
|
|
93
94
|
if (res.writableEnded) return true;
|
|
@@ -95,7 +96,7 @@ async function writeEventStream(res, events, options) {
|
|
|
95
96
|
res.setHeader("Transfer-Encoding", "chunked");
|
|
96
97
|
let chunkIndex = 0;
|
|
97
98
|
for (const event of events) {
|
|
98
|
-
const chunkDelay = calculateDelay(chunkIndex, profile, latency);
|
|
99
|
+
const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
99
100
|
if (chunkDelay > 0) await delay(chunkDelay, signal);
|
|
100
101
|
if (signal?.aborted) return false;
|
|
101
102
|
if (res.writableEnded) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-event-stream.js","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":["/**\n * AWS Event Stream binary frame encoder.\n *\n * Implements the AWS binary event stream framing protocol used by Bedrock's\n * streaming (invoke-with-response-stream) endpoint. Each frame carries a set of\n * string headers and a raw-bytes payload, wrapped in a prelude with CRC32\n * checksums for integrity.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B CRC32 of first 8 bytes]\n * [headers: variable]\n * [payload: variable, raw JSON bytes]\n * [message_crc32: 4B CRC32 of entire frame minus last 4 bytes]\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type * as http from \"node:http\";\nimport type { StreamingProfile } from \"./types.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\n\n// ─── Header encoding ────────────────────────────────────────────────────────\n\nfunction encodeHeaders(headers: Record<string, string>): Buffer {\n const parts: Buffer[] = [];\n for (const [name, value] of Object.entries(headers)) {\n const nameBytes = Buffer.from(name, \"utf8\");\n const valueBytes = Buffer.from(value, \"utf8\");\n\n // name_length (1 byte) + name + type (1 byte, 7 = STRING) +\n // value_length (2 bytes BE) + value\n const header = Buffer.alloc(1 + nameBytes.length + 1 + 2 + valueBytes.length);\n let offset = 0;\n header.writeUInt8(nameBytes.length, offset);\n offset += 1;\n nameBytes.copy(header, offset);\n offset += nameBytes.length;\n header.writeUInt8(7, offset); // STRING type\n offset += 1;\n header.writeUInt16BE(valueBytes.length, offset);\n offset += 2;\n valueBytes.copy(header, offset);\n\n parts.push(header);\n }\n return Buffer.concat(parts);\n}\n\n// ─── Frame encoding ─────────────────────────────────────────────────────────\n\n/**\n * Encode a single AWS Event Stream binary frame with the given headers and\n * payload buffer.\n */\nexport function encodeEventStreamFrame(headers: Record<string, string>, payload: Buffer): Buffer {\n const headersBuffer = encodeHeaders(headers);\n const headersLength = headersBuffer.length;\n\n // prelude (8) + prelude_crc (4) + headers + payload + message_crc (4)\n const totalLength = 4 + 4 + 4 + headersLength + payload.length + 4;\n\n const frame = Buffer.alloc(totalLength);\n let offset = 0;\n\n // Prelude\n frame.writeUInt32BE(totalLength, offset);\n offset += 4;\n frame.writeUInt32BE(headersLength, offset);\n offset += 4;\n\n // Prelude CRC32 (covers first 8 bytes)\n const preludeCrc = crc32(frame.subarray(0, 8));\n frame.writeUInt32BE(preludeCrc >>> 0, offset);\n offset += 4;\n\n // Headers\n headersBuffer.copy(frame, offset);\n offset += headersLength;\n\n // Payload\n payload.copy(frame, offset);\n offset += payload.length;\n\n // Message CRC32 (covers entire frame minus last 4 bytes)\n const messageCrc = crc32(frame.subarray(0, totalLength - 4));\n frame.writeUInt32BE(messageCrc >>> 0, offset);\n\n return frame;\n}\n\n// ─── Convenience wrappers ───────────────────────────────────────────────────\n\n/**\n * Encode an event-stream message with standard AWS headers for a JSON event.\n *\n * Sets `:content-type` = `application/json`, `:event-type` = eventType,\n * `:message-type` = `event`.\n */\nexport function encodeEventStreamMessage(eventType: string, jsonPayload: object): Buffer {\n const headers: Record<string, string> = {\n \":content-type\": \"application/json\",\n \":event-type\": eventType,\n \":message-type\": \"event\",\n };\n const payload = Buffer.from(JSON.stringify(jsonPayload), \"utf8\");\n return encodeEventStreamFrame(headers, payload);\n}\n\n/**\n * Write a sequence of event-stream frames to an HTTP response with optional\n * timing control. Mirrors the writeSSEStream pattern from sse-writer.ts.\n *\n * Returns `true` when all events are written (including when the response\n * was already ended before writing began), or `false` if interrupted by\n * the provided abort signal.\n */\nexport async function writeEventStream(\n res: http.ServerResponse,\n events: Array<{ eventType: string; payload: object }>,\n options?: {\n latency?: number;\n streamingProfile?: StreamingProfile;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n },\n): Promise<boolean> {\n const opts = options ?? {};\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"application/vnd.amazon.eventstream\");\n res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency);\n if (chunkDelay > 0) {\n await delay(chunkDelay, signal);\n }\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n\n const frame = encodeEventStreamMessage(event.eventType, event.payload);\n res.write(frame);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,cAAc,SAAyC;CAC9D,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;EACnD,MAAM,YAAY,OAAO,KAAK,MAAM,OAAO;EAC3C,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO;EAI7C,MAAM,SAAS,OAAO,MAAM,IAAI,UAAU,SAAS,IAAI,IAAI,WAAW,OAAO;EAC7E,IAAI,SAAS;AACb,SAAO,WAAW,UAAU,QAAQ,OAAO;AAC3C,YAAU;AACV,YAAU,KAAK,QAAQ,OAAO;AAC9B,YAAU,UAAU;AACpB,SAAO,WAAW,GAAG,OAAO;AAC5B,YAAU;AACV,SAAO,cAAc,WAAW,QAAQ,OAAO;AAC/C,YAAU;AACV,aAAW,KAAK,QAAQ,OAAO;AAE/B,QAAM,KAAK,OAAO;;AAEpB,QAAO,OAAO,OAAO,MAAM;;;;;;AAS7B,SAAgB,uBAAuB,SAAiC,SAAyB;CAC/F,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,gBAAgB,cAAc;CAGpC,MAAM,cAAc,KAAY,gBAAgB,QAAQ,SAAS;CAEjE,MAAM,QAAQ,OAAO,MAAM,YAAY;CACvC,IAAI,SAAS;AAGb,OAAM,cAAc,aAAa,OAAO;AACxC,WAAU;AACV,OAAM,cAAc,eAAe,OAAO;AAC1C,WAAU;CAGV,MAAM,aAAa,MAAM,MAAM,SAAS,GAAG,EAAE,CAAC;AAC9C,OAAM,cAAc,eAAe,GAAG,OAAO;AAC7C,WAAU;AAGV,eAAc,KAAK,OAAO,OAAO;AACjC,WAAU;AAGV,SAAQ,KAAK,OAAO,OAAO;AAC3B,WAAU,QAAQ;CAGlB,MAAM,aAAa,MAAM,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;AAC5D,OAAM,cAAc,eAAe,GAAG,OAAO;AAE7C,QAAO;;;;;;;;AAWT,SAAgB,yBAAyB,WAAmB,aAA6B;AAOvF,QAAO,uBANiC;EACtC,iBAAiB;EACjB,eAAe;EACf,iBAAiB;EAClB,EACe,OAAO,KAAK,KAAK,UAAU,YAAY,EAAE,OAAO,CACjB;;;;;;;;;;AAWjD,eAAsB,iBACpB,KACA,QACA,
|
|
1
|
+
{"version":3,"file":"aws-event-stream.js","names":[],"sources":["../src/aws-event-stream.ts"],"sourcesContent":["/**\n * AWS Event Stream binary frame encoder.\n *\n * Implements the AWS binary event stream framing protocol used by Bedrock's\n * streaming (invoke-with-response-stream) endpoint. Each frame carries a set of\n * string headers and a raw-bytes payload, wrapped in a prelude with CRC32\n * checksums for integrity.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B CRC32 of first 8 bytes]\n * [headers: variable]\n * [payload: variable, raw JSON bytes]\n * [message_crc32: 4B CRC32 of entire frame minus last 4 bytes]\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type * as http from \"node:http\";\nimport type { StreamingProfile, RecordedTimings } from \"./types.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\n\n// ─── Header encoding ────────────────────────────────────────────────────────\n\nfunction encodeHeaders(headers: Record<string, string>): Buffer {\n const parts: Buffer[] = [];\n for (const [name, value] of Object.entries(headers)) {\n const nameBytes = Buffer.from(name, \"utf8\");\n const valueBytes = Buffer.from(value, \"utf8\");\n\n // name_length (1 byte) + name + type (1 byte, 7 = STRING) +\n // value_length (2 bytes BE) + value\n const header = Buffer.alloc(1 + nameBytes.length + 1 + 2 + valueBytes.length);\n let offset = 0;\n header.writeUInt8(nameBytes.length, offset);\n offset += 1;\n nameBytes.copy(header, offset);\n offset += nameBytes.length;\n header.writeUInt8(7, offset); // STRING type\n offset += 1;\n header.writeUInt16BE(valueBytes.length, offset);\n offset += 2;\n valueBytes.copy(header, offset);\n\n parts.push(header);\n }\n return Buffer.concat(parts);\n}\n\n// ─── Frame encoding ─────────────────────────────────────────────────────────\n\n/**\n * Encode a single AWS Event Stream binary frame with the given headers and\n * payload buffer.\n */\nexport function encodeEventStreamFrame(headers: Record<string, string>, payload: Buffer): Buffer {\n const headersBuffer = encodeHeaders(headers);\n const headersLength = headersBuffer.length;\n\n // prelude (8) + prelude_crc (4) + headers + payload + message_crc (4)\n const totalLength = 4 + 4 + 4 + headersLength + payload.length + 4;\n\n const frame = Buffer.alloc(totalLength);\n let offset = 0;\n\n // Prelude\n frame.writeUInt32BE(totalLength, offset);\n offset += 4;\n frame.writeUInt32BE(headersLength, offset);\n offset += 4;\n\n // Prelude CRC32 (covers first 8 bytes)\n const preludeCrc = crc32(frame.subarray(0, 8));\n frame.writeUInt32BE(preludeCrc >>> 0, offset);\n offset += 4;\n\n // Headers\n headersBuffer.copy(frame, offset);\n offset += headersLength;\n\n // Payload\n payload.copy(frame, offset);\n offset += payload.length;\n\n // Message CRC32 (covers entire frame minus last 4 bytes)\n const messageCrc = crc32(frame.subarray(0, totalLength - 4));\n frame.writeUInt32BE(messageCrc >>> 0, offset);\n\n return frame;\n}\n\n// ─── Convenience wrappers ───────────────────────────────────────────────────\n\n/**\n * Encode an event-stream message with standard AWS headers for a JSON event.\n *\n * Sets `:content-type` = `application/json`, `:event-type` = eventType,\n * `:message-type` = `event`.\n */\nexport function encodeEventStreamMessage(eventType: string, jsonPayload: object): Buffer {\n const headers: Record<string, string> = {\n \":content-type\": \"application/json\",\n \":event-type\": eventType,\n \":message-type\": \"event\",\n };\n const payload = Buffer.from(JSON.stringify(jsonPayload), \"utf8\");\n return encodeEventStreamFrame(headers, payload);\n}\n\n/**\n * Write a sequence of event-stream frames to an HTTP response with optional\n * timing control. Mirrors the writeSSEStream pattern from sse-writer.ts.\n *\n * Returns `true` when all events are written (including when the response\n * was already ended before writing began), or `false` if interrupted by\n * the provided abort signal.\n */\nexport async function writeEventStream(\n res: http.ServerResponse,\n events: Array<{ eventType: string; payload: object }>,\n options?: {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n },\n): Promise<boolean> {\n const opts = options ?? {};\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"application/vnd.amazon.eventstream\");\n res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) {\n await delay(chunkDelay, signal);\n }\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n\n const frame = encodeEventStreamMessage(event.eventType, event.payload);\n res.write(frame);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,SAAS,cAAc,SAAyC;CAC9D,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;EACnD,MAAM,YAAY,OAAO,KAAK,MAAM,OAAO;EAC3C,MAAM,aAAa,OAAO,KAAK,OAAO,OAAO;EAI7C,MAAM,SAAS,OAAO,MAAM,IAAI,UAAU,SAAS,IAAI,IAAI,WAAW,OAAO;EAC7E,IAAI,SAAS;AACb,SAAO,WAAW,UAAU,QAAQ,OAAO;AAC3C,YAAU;AACV,YAAU,KAAK,QAAQ,OAAO;AAC9B,YAAU,UAAU;AACpB,SAAO,WAAW,GAAG,OAAO;AAC5B,YAAU;AACV,SAAO,cAAc,WAAW,QAAQ,OAAO;AAC/C,YAAU;AACV,aAAW,KAAK,QAAQ,OAAO;AAE/B,QAAM,KAAK,OAAO;;AAEpB,QAAO,OAAO,OAAO,MAAM;;;;;;AAS7B,SAAgB,uBAAuB,SAAiC,SAAyB;CAC/F,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,gBAAgB,cAAc;CAGpC,MAAM,cAAc,KAAY,gBAAgB,QAAQ,SAAS;CAEjE,MAAM,QAAQ,OAAO,MAAM,YAAY;CACvC,IAAI,SAAS;AAGb,OAAM,cAAc,aAAa,OAAO;AACxC,WAAU;AACV,OAAM,cAAc,eAAe,OAAO;AAC1C,WAAU;CAGV,MAAM,aAAa,MAAM,MAAM,SAAS,GAAG,EAAE,CAAC;AAC9C,OAAM,cAAc,eAAe,GAAG,OAAO;AAC7C,WAAU;AAGV,eAAc,KAAK,OAAO,OAAO;AACjC,WAAU;AAGV,SAAQ,KAAK,OAAO,OAAO;AAC3B,WAAU,QAAQ;CAGlB,MAAM,aAAa,MAAM,MAAM,SAAS,GAAG,cAAc,EAAE,CAAC;AAC5D,OAAM,cAAc,eAAe,GAAG,OAAO;AAE7C,QAAO;;;;;;;;AAWT,SAAgB,yBAAyB,WAAmB,aAA6B;AAOvF,QAAO,uBANiC;EACtC,iBAAiB;EACjB,eAAe;EACf,iBAAiB;EAClB,EACe,OAAO,KAAK,KAAK,UAAU,YAAY,EAAE,OAAO,CACjB;;;;;;;;;;AAWjD,eAAsB,iBACpB,KACA,QACA,SAQkB;CAClB,MAAM,OAAO,WAAW,EAAE;CAC1B,MAAM,UAAU,KAAK,WAAW;CAChC,MAAM,UAAU,KAAK;CACrB,MAAM,EAAE,iBAAiB,gBAAgB;CACzC,MAAM,SAAS,KAAK;CACpB,MAAM,cAAc,KAAK;AAEzB,KAAI,IAAI,cAAe,QAAO;AAC9B,KAAI,UAAU,gBAAgB,qCAAqC;AACnE,KAAI,UAAU,qBAAqB,UAAU;CAE7C,IAAI,aAAa;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,aAAa,eAAe,YAAY,SAAS,SAAS,iBAAiB,YAAY;AAC7F,MAAI,aAAa,EACf,OAAM,MAAM,YAAY,OAAO;AAEjC,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,IAAI,cAAe,QAAO;EAE9B,MAAM,QAAQ,yBAAyB,MAAM,WAAW,MAAM,QAAQ;AACtE,MAAI,MAAM,MAAM;AAChB,iBAAe;AACf,MAAI,QAAQ,QAAS,QAAO;AAC5B;;AAGF,KAAI,CAAC,IAAI,cACP,KAAI,KAAK;AAEX,QAAO"}
|
|
@@ -469,7 +469,7 @@ async function handleConverse(req, res, raw, modelId, fixtures, journal, default
|
|
|
469
469
|
message: response.error.message
|
|
470
470
|
}
|
|
471
471
|
};
|
|
472
|
-
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(errBody));
|
|
472
|
+
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(errBody), { retryAfter: response.retryAfter });
|
|
473
473
|
return;
|
|
474
474
|
}
|
|
475
475
|
if (require_helpers.isContentWithToolCallsResponse(response)) {
|
|
@@ -676,7 +676,7 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
|
|
|
676
676
|
message: response.error.message
|
|
677
677
|
}
|
|
678
678
|
};
|
|
679
|
-
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(errBody));
|
|
679
|
+
require_sse_writer.writeErrorResponse(res, status, JSON.stringify(errBody), { retryAfter: response.retryAfter });
|
|
680
680
|
return;
|
|
681
681
|
}
|
|
682
682
|
if (require_helpers.isContentWithToolCallsResponse(response)) {
|
|
@@ -697,6 +697,8 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
|
|
|
697
697
|
if (!await require_aws_event_stream.writeEventStream(res, events, {
|
|
698
698
|
latency,
|
|
699
699
|
streamingProfile: fixture.streamingProfile,
|
|
700
|
+
recordedTimings: fixture.recordedTimings,
|
|
701
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
700
702
|
signal: interruption?.signal,
|
|
701
703
|
onChunkSent: interruption?.tick
|
|
702
704
|
})) {
|
|
@@ -725,6 +727,8 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
|
|
|
725
727
|
if (!await require_aws_event_stream.writeEventStream(res, events, {
|
|
726
728
|
latency,
|
|
727
729
|
streamingProfile: fixture.streamingProfile,
|
|
730
|
+
recordedTimings: fixture.recordedTimings,
|
|
731
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
728
732
|
signal: interruption?.signal,
|
|
729
733
|
onChunkSent: interruption?.tick
|
|
730
734
|
})) {
|
|
@@ -753,6 +757,8 @@ async function handleConverseStream(req, res, raw, modelId, fixtures, journal, d
|
|
|
753
757
|
if (!await require_aws_event_stream.writeEventStream(res, events, {
|
|
754
758
|
latency,
|
|
755
759
|
streamingProfile: fixture.streamingProfile,
|
|
760
|
+
recordedTimings: fixture.recordedTimings,
|
|
761
|
+
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
756
762
|
signal: interruption?.signal,
|
|
757
763
|
onChunkSent: interruption?.tick
|
|
758
764
|
})) {
|