@adzenai/ai 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -123,7 +123,14 @@ new AdzenAsyncMiddleware({
123
123
 
124
124
  `endpointUrl` is a base URL; the SDK appends `/process` and `/stream`. Include any environment-specific path prefix (such as `/ai` on stage and prod) in the value you pass.
125
125
 
126
- `AdzenStreamMiddleware` accepts the same options plus `prefetch` (POST `/process` with the prompt before opening the stream), `placementPollIntervalMs`, and the `onInlineAd` / `onPlacement` / `onError` / `onStreamStart` / `onStreamEnd` callbacks.
126
+ `AdzenStreamMiddleware` accepts the same options plus `prefetch` (POST `/process` with the prompt before opening the stream), `placementPollIntervalMs`, `sidecar`, and the `onInlineAd` / `onPlacement` / `onError` / `onStreamStart` / `onStreamEnd` callbacks.
127
+
128
+ <!-- INTERNAL / DEBUGGING-MODE ONLY — do not surface in the public shared-vision/developer-docs.
129
+ There is also an optional `headers` (`Record<string, string>`) option, merged into every outbound
130
+ Adzen request (`/stream`, `/process`, placement polling) for internal debugging (e.g. `X-Adzen-Debug-*`).
131
+ SDK-managed headers (auth, content-type, correlation IDs) always take precedence. -->
132
+
133
+
127
134
 
128
135
  The middleware never makes the assistant message wait on the ad pipeline: the prefetch is dispatched without being awaited, and content events are forwarded as they arrive whether or not an ad has been matched yet.
129
136
 
@@ -312,6 +312,7 @@ var AdzenStreamClient = class {
312
312
  placementPollIntervalMs;
313
313
  prefetchEnabled;
314
314
  sidecarEnabled;
315
+ customHeaders;
315
316
  conversationId;
316
317
  abortController = null;
317
318
  placementEndpoint = null;
@@ -334,6 +335,7 @@ var AdzenStreamClient = class {
334
335
  this.placementPollIntervalMs = config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
335
336
  this.prefetchEnabled = config.prefetch ?? false;
336
337
  this.sidecarEnabled = config.sidecar ?? true;
338
+ this.customHeaders = config.headers ?? {};
337
339
  this.conversationId = config.conversationId ?? (0, import_core.uuidv4)();
338
340
  this.onInlineAd = config.onInlineAd;
339
341
  this.onPlacement = config.onPlacement;
@@ -385,7 +387,7 @@ var AdzenStreamClient = class {
385
387
  if (this.profileId) headers["X-Profile-Id"] = this.profileId;
386
388
  await fetch(`${this.endpointUrl}/process`, {
387
389
  method: "POST",
388
- headers,
390
+ headers: this.mergeHeaders(headers),
389
391
  body: JSON.stringify(body),
390
392
  signal: controller.signal
391
393
  });
@@ -428,7 +430,7 @@ var AdzenStreamClient = class {
428
430
  };
429
431
  if (this.profileId) headers["X-Profile-Id"] = this.profileId;
430
432
  if (this.location) headers["X-Location"] = this.location;
431
- this.streamPromise = this.executeStreamFetch(body, headers);
433
+ this.streamPromise = this.executeStreamFetch(body, this.mergeHeaders(headers));
432
434
  }
433
435
  /**
434
436
  * Waits for the SSE response stream from the API to finish, along with any
@@ -437,6 +439,14 @@ var AdzenStreamClient = class {
437
439
  async finalize() {
438
440
  await Promise.all([this.prefetchPromise, this.streamPromise]);
439
441
  }
442
+ /**
443
+ * Merges caller-provided passthrough headers with the SDK's fixed headers.
444
+ * Fixed headers are spread last so SDK-managed keys (auth, content-type,
445
+ * correlation IDs) always win over any passthrough value.
446
+ */
447
+ mergeHeaders(fixed) {
448
+ return { ...this.customHeaders, ...fixed };
449
+ }
440
450
  async executeStreamFetch(body, headers) {
441
451
  const timeout = setTimeout(
442
452
  () => this.abortController?.abort(),
@@ -475,7 +485,7 @@ var AdzenStreamClient = class {
475
485
  try {
476
486
  const res = await fetch(this.placementEndpoint, {
477
487
  method: "GET",
478
- headers: { "X-API-Key": this.apiKey }
488
+ headers: this.mergeHeaders({ "X-API-Key": this.apiKey })
479
489
  });
480
490
  if (!res.ok) return;
481
491
  const data = await res.json();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/copilotkit/index.ts","../../src/copilotkit/middleware.ts","../../src/copilotkit/AdzenStreamClient.ts","../../src/copilotkit/parseSSE.ts","../../src/copilotkit/streamMiddleware.ts"],"sourcesContent":["export { AdzenAsyncMiddleware } from \"./middleware.js\";\nexport type { AdzenAsyncConfig } from \"./middleware.js\";\nexport { AdzenStreamMiddleware } from \"./streamMiddleware.js\";\nexport type { AdzenStreamMiddlewareConfig } from \"./streamMiddleware.js\";\nexport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n","import type { AdzenPlacement, ProcessResponse, ProcessResponseAd } from \"@adzenai/core\";\n\nexport interface AdzenAsyncConfig {\n endpointUrl?: string;\n apiKey: string;\n timeoutMs?: number;\n adUnitPosition?: string;\n location?: string;\n conversationId?: string;\n profileId?: string;\n}\n\ninterface MessageBuffer {\n content: string;\n startedAt: number;\n fetched: boolean;\n}\n\nfunction generateIdempotencyKey(): string {\n const globalCrypto = (globalThis as { crypto?: { randomUUID?: () => string } })\n .crypto;\n if (globalCrypto?.randomUUID) return globalCrypto.randomUUID();\n return `adzen-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isFinalOutputEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n if (type === \"RUN_FINISHED\" || type === \"RunFinished\") return false;\n if (type !== \"CUSTOM\" && type !== \"CUSTOM_EVENT\" && type !== \"CustomEvent\")\n return false;\n if (event.final === true || event.isFinal === true) return true;\n const name = String(event.name ?? event.event ?? \"\").toLowerCase();\n return name.includes(\"assistant_final\") || name.includes(\"final_output\");\n}\n\nfunction extractContentFromEvent(event: Record<string, unknown>): string | null {\n for (const key of [\"output\", \"result\", \"data\", \"payload\", \"message\"]) {\n const val = event[key];\n if (typeof val === \"string\" && val.length > 0) return val;\n if (typeof val === \"object\" && val !== null) {\n try {\n return JSON.stringify(val);\n } catch {\n continue;\n }\n }\n }\n return null;\n}\n\n/**\n * AG-UI middleware that intercepts the event stream and calls AI-API after\n * each assistant message, emitting `adzen_placement` custom events when an\n * ad is returned.\n *\n * All upstream events pass through immediately — zero latency is added to\n * the text stream. Ad fetches run as a side-effect and `RUN_FINISHED` is\n * held until all pending fetches settle.\n */\nexport class AdzenAsyncMiddleware {\n private config: Required<Pick<AdzenAsyncConfig, \"endpointUrl\" | \"timeoutMs\" | \"adUnitPosition\">> &\n AdzenAsyncConfig;\n private buffers = new Map<string, MessageBuffer>();\n private fetchedIds = new Set<string>();\n private pendingFetches: Promise<void>[] = [];\n private fallbackCounter = 0;\n private conversationId: string | undefined;\n\n constructor(config: AdzenAsyncConfig) {\n this.config = {\n ...config,\n endpointUrl: config.endpointUrl ?? \"https://api.adzen.ai/v1\",\n timeoutMs: config.timeoutMs ?? 3000,\n adUnitPosition: config.adUnitPosition ?? \"chin\",\n };\n this.conversationId = config.conversationId;\n }\n\n /**\n * Process a single AG-UI event. Returns an array of events to emit\n * downstream (always includes the original event, possibly followed\n * by an `adzen_placement` custom event).\n */\n async processEvent(event: Record<string, unknown>): Promise<Record<string, unknown>[]> {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n if (threadId) this.conversationId = threadId;\n return [event];\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n const messageId = this.resolveMessageId(event);\n this.evictStaleBuffers();\n this.buffers.set(messageId, {\n content: \"\",\n startedAt: Date.now(),\n fetched: false,\n });\n return [event];\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n if (buf) buf.content += String(event.delta ?? \"\");\n return [event];\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n const out: Record<string, unknown>[] = [event];\n\n if (buf && !buf.fetched) {\n buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(buf.content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n this.buffers.delete(messageId);\n }\n\n return out;\n }\n\n case \"CUSTOM\":\n case \"CUSTOM_EVENT\":\n case \"CustomEvent\": {\n if (isFinalOutputEvent(event)) {\n const messageId = String(event.messageId ?? \"\");\n if (!this.fetchedIds.has(messageId)) {\n const buf = this.buffers.get(messageId);\n const content = extractContentFromEvent(event);\n if (content) {\n const out: Record<string, unknown>[] = [event];\n if (buf) buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n return out;\n }\n }\n }\n return [event];\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n await Promise.allSettled(this.pendingFetches);\n this.pendingFetches = [];\n return [event];\n }\n\n default:\n return [event];\n }\n }\n\n private resolveMessageId(event: Record<string, unknown>): string {\n const id = String(event.messageId ?? \"\");\n if (id) return id;\n return `__adzen_fallback_${Date.now()}_${this.fallbackCounter++}`;\n }\n\n private evictStaleBuffers(): void {\n const cutoff = Date.now() - this.config.timeoutMs * 2;\n for (const [key, buf] of this.buffers) {\n if (buf.startedAt < cutoff) this.buffers.delete(key);\n }\n }\n\n private async fetchAd(\n content: string,\n messageId: string,\n ): Promise<AdzenPlacement | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content,\n message_id: messageId,\n };\n if (this.config.location) body.location = this.config.location;\n if (this.conversationId) body.conversation_id = this.conversationId;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n // AI-API requires an Idempotency-Key on /process (400 otherwise).\n // One per ad fetch — each completed message is a distinct request.\n \"Idempotency-Key\": generateIdempotencyKey(),\n };\n if (this.config.profileId) headers[\"X-Profile-Id\"] = this.config.profileId;\n\n const res = await fetch(`${this.config.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as ProcessResponse;\n const first = data.ads?.[0];\n if (!first) return null;\n return this.mapResponseToPlacement(first);\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private mapResponseToPlacement(ad: ProcessResponseAd): AdzenPlacement {\n return {\n ad_id: String(ad.ad_id),\n advertiser_name: ad.advertiser_name,\n advertiser_image_url: ad.sponsor_logo_url || undefined,\n headline: ad.title,\n description: ad.description,\n cta_text: ad.cta,\n destination_url: ad.click_through_url,\n creative_url: ad.creative_url ?? undefined,\n adUnitPosition: ad.placement ?? this.config.adUnitPosition,\n render_impression_url: ad.render_impression_url,\n view_impression_url: ad.view_impression_url,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n messageId: string,\n ): Record<string, unknown> {\n return {\n // AG-UI's event schema only accepts \"CUSTOM\"; @ag-ui/client zod-validates\n // every SSE frame and aborts the run on an unknown type. \"CUSTOM_EVENT\"\n // was rejected downstream.\n type: \"CUSTOM\",\n name: \"adzen_placement\",\n messageId,\n value: {\n ...placement,\n message_id: messageId,\n },\n };\n }\n}\n","import type {\n InlineAdEvent,\n PlacementRecord,\n PlacementsResponse,\n AdzenStreamStartEvent,\n AdzenStreamEndEvent,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { uuidv4 } from \"@adzenai/core\";\nimport { parseSSE } from \"./parseSSE.js\";\n\nexport interface AdzenStreamConfig {\n apiKey: string;\n endpointUrl?: string;\n timeoutMs?: number;\n location?: string;\n conversationId?: string;\n profileId?: string;\n placementPollIntervalMs?: number;\n prefetch?: boolean;\n /**\n * Whether to poll the sidecar placement endpoint for out-of-stream ads\n * rendered outside the message body. Defaults to `true`. Set to `false`\n * to run inline-only, leaving `adzen_inline` events unaffected.\n */\n sidecar?: boolean;\n onInlineAd?: (ad: InlineAdEvent) => void;\n onPlacement?: (placement: PlacementRecord) => void;\n onError?: (error: AdzenStreamErrorEvent) => void;\n onStreamStart?: (event: AdzenStreamStartEvent) => void;\n onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n}\n\nconst DEFAULT_ENDPOINT = \"https://api.adzen.ai/v1\";\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\n\n/**\n * Whether the current runtime can send a ReadableStream as a fetch request\n * body. Node 18+ (undici) supports this; Safari and Firefox do not, and\n * silently coerce the stream to the string \"[object ReadableStream]\" via the\n * WebIDL BodyInit union's USVString member.\n *\n * Detection works by checking whether the `duplex` getter is read and whether\n * the runtime set a Content-Type — a coerced string body gets `text/plain`.\n */\nconst SUPPORTS_REQUEST_STREAMS = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\"http://localhost\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexAccessed = true;\n return \"half\";\n },\n } as RequestInit).headers.has(\"Content-Type\");\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n\n/**\n * Framework-agnostic client for the Adzen streaming ad enrichment API.\n *\n * Handles connecting to `/v1/stream`, parsing SSE events, optional prefetch\n * via `/process`, and sidecar placement polling. Framework adapters (AG-UI\n * middleware, React hooks) wrap this class.\n */\nexport class AdzenStreamClient {\n private endpointUrl: string;\n private apiKey: string;\n private timeoutMs: number;\n private location?: string;\n private profileId?: string;\n private placementPollIntervalMs: number;\n private prefetchEnabled: boolean;\n private sidecarEnabled: boolean;\n\n private conversationId: string;\n private abortController: AbortController | null = null;\n private placementEndpoint: string | null = null;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private streamEnded = false;\n private inlineAdQueue: InlineAdEvent[] = [];\n private streamPromise: Promise<void> | null = null;\n private prefetchPromise: Promise<void> | null = null;\n\n private onInlineAd?: (ad: InlineAdEvent) => void;\n private onPlacement?: (placement: PlacementRecord) => void;\n private onError?: (error: AdzenStreamErrorEvent) => void;\n private onStreamStart?: (event: AdzenStreamStartEvent) => void;\n private onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n\n constructor(config: AdzenStreamConfig) {\n this.endpointUrl = config.endpointUrl ?? DEFAULT_ENDPOINT;\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.location = config.location;\n this.profileId = config.profileId;\n this.placementPollIntervalMs =\n config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.prefetchEnabled = config.prefetch ?? false;\n this.sidecarEnabled = config.sidecar ?? true;\n this.conversationId = config.conversationId ?? uuidv4();\n\n this.onInlineAd = config.onInlineAd;\n this.onPlacement = config.onPlacement;\n this.onError = config.onError;\n this.onStreamStart = config.onStreamStart;\n this.onStreamEnd = config.onStreamEnd;\n }\n\n getConversationId(): string {\n return this.conversationId;\n }\n\n setConversationId(id: string): void {\n this.conversationId = id;\n }\n\n /**\n * Dispatches the prefetch without waiting for it.\n *\n * The request leaves before `connect()` opens `/stream`, which is all the\n * 2-step chained flow needs — the two calls are correlated server-side by\n * `conversation_id`. Awaiting the response here would stall the assistant\n * message behind a full ad-matching round trip while the LLM keeps\n * streaming, so the buffered tokens would then arrive in one burst.\n */\n startPrefetch(prompt: string): void {\n if (!this.prefetchEnabled) return;\n this.prefetchPromise = this.prefetchProcess(prompt);\n }\n\n /**\n * 2-step chained flow: POST /process with the user prompt and\n * conversationId. Awaits the response (or timeout) but discards the\n * result. If it fails, logs and continues — the stream will proceed\n * in degraded mode.\n */\n async prefetchProcess(prompt: string): Promise<void> {\n if (!this.prefetchEnabled) return;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content: prompt,\n message_id: uuidv4(),\n conversation_id: this.conversationId,\n };\n if (this.location) body.location = this.location;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n\n await fetch(`${this.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch {\n // Prefetch failure is non-fatal — stream continues without pre-match context\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Opens a streaming POST to /stream, piping the upstream LLM response\n * body directly. The API reads the raw LLM stream in real time and\n * sends SSE ad events back on the response side.\n *\n * @param messageId Unique ID for this assistant message\n * @param body The upstream LLM response body (one branch of a tee)\n */\n connect(messageId: string, body: ReadableStream<Uint8Array>): void {\n this.streamEnded = false;\n this.inlineAdQueue = [];\n this.placementEndpoint = null;\n this.streamPromise = null;\n this.abortController = new AbortController();\n\n if (!SUPPORTS_REQUEST_STREAMS) {\n this.streamEnded = true;\n this.onError?.({\n type: \"adzen_error\",\n code: \"request_streams_unsupported\",\n message:\n \"This runtime cannot send a ReadableStream as a request body, so the \" +\n \"LLM stream cannot be proxied. Run AdzenStreamMiddleware server-side \" +\n \"(Node 18+) — browsers other than Chromium do not support fetch upload streams.\",\n });\n void body.cancel().catch(() => {});\n return;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/octet-stream\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n \"X-Message-Id\": messageId,\n \"X-Conversation-Id\": this.conversationId,\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n if (this.location) headers[\"X-Location\"] = this.location;\n\n this.streamPromise = this.executeStreamFetch(body, headers);\n }\n\n /**\n * Waits for the SSE response stream from the API to finish, along with any\n * prefetch still in flight. Neither can reject — both swallow their errors.\n */\n async finalize(): Promise<void> {\n await Promise.all([this.prefetchPromise, this.streamPromise]);\n }\n\n private async executeStreamFetch(\n body: ReadableStream<Uint8Array>,\n headers: Record<string, string>,\n ): Promise<void> {\n const timeout = setTimeout(\n () => this.abortController?.abort(),\n this.timeoutMs,\n );\n\n try {\n const res = await fetch(`${this.endpointUrl}/stream`, {\n method: \"POST\",\n headers,\n body,\n signal: this.abortController?.signal,\n // @ts-expect-error -- required for streaming request bodies (Node/undici)\n duplex: \"half\",\n });\n\n clearTimeout(timeout);\n\n if (!res.ok || !res.body) return;\n\n await this.readStream(res.body);\n } catch {\n clearTimeout(timeout);\n }\n }\n\n /** Drains and returns any queued inline ad events. */\n drainInlineAds(): InlineAdEvent[] {\n const ads = this.inlineAdQueue.splice(0);\n return ads;\n }\n\n /** Cancels pending requests and cleans up state. */\n abort(): void {\n this.abortController?.abort();\n this.stopPolling();\n this.streamEnded = true;\n }\n\n /** Polls the placement endpoint once. Called on an interval internally. */\n async pollPlacements(): Promise<void> {\n if (!this.sidecarEnabled || !this.placementEndpoint) return;\n\n try {\n const res = await fetch(this.placementEndpoint, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n });\n if (!res.ok) return;\n\n const data = (await res.json()) as PlacementsResponse;\n for (const placement of data.placements) {\n this.onPlacement?.(placement);\n }\n } catch {\n // Polling failure is non-fatal\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n try {\n for await (const frame of parseSSE(body)) {\n if (this.abortController?.signal.aborted) break;\n this.handleFrame(frame.event, frame.data);\n }\n } catch {\n // Stream read error — degrade gracefully\n } finally {\n this.streamEnded = true;\n this.stopPolling();\n }\n }\n\n private handleFrame(event: string | undefined, data: string): void {\n switch (event) {\n case \"adzen_stream_start\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamStartEvent, \"type\">;\n const evt: AdzenStreamStartEvent = { type: \"adzen_stream_start\", ...parsed };\n this.placementEndpoint = parsed.placement_endpoint;\n this.onStreamStart?.(evt);\n this.startPolling();\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_inline\": {\n try {\n const ad = JSON.parse(data) as InlineAdEvent;\n this.inlineAdQueue.push(ad);\n this.onInlineAd?.(ad);\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_error\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamErrorEvent, \"type\">;\n this.onError?.({ type: \"adzen_error\", ...parsed });\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_stream_end\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamEndEvent, \"type\">;\n const evt: AdzenStreamEndEvent = { type: \"adzen_stream_end\", ...parsed };\n this.onStreamEnd?.(evt);\n } catch { /* malformed — skip */ }\n this.streamEnded = true;\n this.stopPolling();\n break;\n }\n default: {\n if (event?.startsWith(\"adzen_debug_\")) break;\n break;\n }\n }\n }\n\n private startPolling(): void {\n if (!this.sidecarEnabled) return;\n if (this.pollTimer || !this.placementEndpoint) return;\n this.pollTimer = setInterval(\n () => this.pollPlacements(),\n this.placementPollIntervalMs,\n );\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n}\n","/**\n * Lightweight SSE parser that reads a ReadableStream<Uint8Array> and yields\n * parsed frames. Handles multi-line `data:` fields, named event types, and\n * ignores `id:` / `retry:` fields per the SSE spec.\n */\n\nexport interface SSEFrame {\n event?: string;\n data: string;\n}\n\nexport async function* parseSSE(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<SSEFrame> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent: string | undefined;\n let dataLines: string[] = [];\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop()!;\n\n for (const raw of lines) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n dataLines = [];\n currentEvent = undefined;\n }\n continue;\n }\n\n if (line.startsWith(\":\")) continue;\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n\n const field = line.slice(0, colonIdx);\n const value = line.slice(colonIdx + 1).replace(/^ /, \"\");\n\n switch (field) {\n case \"data\":\n dataLines.push(value);\n break;\n case \"event\":\n currentEvent = value;\n break;\n // id and retry are intentionally ignored\n }\n }\n }\n\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AdzenPlacement,\n InlineAdEvent,\n InlineAdEventPayload,\n PlacementRecord,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { AdzenStreamClient } from \"./AdzenStreamClient.js\";\nimport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n\nexport interface AdzenStreamMiddlewareConfig extends AdzenStreamConfig {\n adUnitPosition?: string;\n}\n\n/**\n * AG-UI middleware that proxies the LLM response stream through Adzen's\n * streaming endpoint for real-time ad enrichment.\n *\n * Uses a stream transformer pattern: `processStream()` takes an\n * AsyncIterable of AG-UI events and yields an enriched stream that\n * includes inline ad events and sidecar placements.\n *\n * Supports the 2-step chained flow: when `prefetch: true` and a `prompt`\n * is provided, fires POST /process before opening the stream connection.\n */\nexport class AdzenStreamMiddleware {\n private config: AdzenStreamMiddlewareConfig;\n private adUnitPosition: string;\n\n constructor(config: AdzenStreamMiddlewareConfig) {\n this.config = config;\n this.adUnitPosition = config.adUnitPosition ?? \"chin\";\n }\n\n async *processStream(\n events: AsyncIterable<Record<string, unknown>>,\n options?: { prompt?: string; upstreamBody?: ReadableStream<Uint8Array> },\n ): AsyncGenerator<Record<string, unknown>> {\n const sidecarPlacements: AdzenPlacement[] = [];\n let client: AdzenStreamClient | null = null;\n let currentMessageId = \"\";\n let contentOffset = 0;\n\n const onPlacement = (record: PlacementRecord) => {\n sidecarPlacements.push(this.mapPlacementRecord(record, currentMessageId));\n };\n\n const onError = (error: AdzenStreamErrorEvent) => {\n console.warn(`[adzen-stream] ${error.code}: ${error.message}`);\n };\n\n for await (const event of events) {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n\n client = new AdzenStreamClient({\n ...this.config,\n conversationId: threadId || this.config.conversationId,\n onPlacement,\n onError,\n });\n\n if (this.config.prefetch && options?.prompt) {\n client.startPrefetch(options.prompt);\n }\n\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n currentMessageId = String(event.messageId ?? \"\");\n contentOffset = 0;\n if (client && options?.upstreamBody) {\n client.connect(currentMessageId, options.upstreamBody);\n }\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n yield event;\n contentOffset += String(event.delta ?? \"\").length;\n\n if (client) {\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n break;\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n if (client) {\n await client.finalize();\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n yield event;\n break;\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n if (client) {\n await client.pollPlacements();\n client.abort();\n }\n\n for (const placement of sidecarPlacements) {\n yield this.makePlacementEvent(placement, placement.ad_id);\n }\n\n yield event;\n break;\n }\n\n default:\n yield event;\n }\n }\n }\n\n /**\n * Wraps an inline ad as an AG-UI custom event anchored to `contentOffset`,\n * the number of message characters emitted before the ad arrived. Clients\n * use it to render the ad directly after that content instead of at the\n * end of the message.\n */\n private makeInlineAdEvent(\n ad: InlineAdEvent,\n messageId: string,\n contentOffset: number,\n ): Record<string, unknown> {\n const value: InlineAdEventPayload = {\n ...ad,\n message_id: messageId,\n content_offset: contentOffset,\n };\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_inline_ad\",\n messageId,\n value,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n adId: string,\n ): Record<string, unknown> {\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_placement\",\n messageId: adId,\n value: {\n ...placement,\n message_id: placement.ad_id,\n },\n };\n }\n\n private mapPlacementRecord(\n record: PlacementRecord,\n messageId: string,\n ): AdzenPlacement {\n return {\n ad_id: String(record.ad_id),\n advertiser_name: \"\",\n headline: record.headline,\n cta_text: record.cta_text,\n destination_url: record.destination_url,\n adUnitPosition: record.placement || this.adUnitPosition,\n render_impression_url: record.tracking.impression_url,\n view_impression_url: record.tracking.impression_url,\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACkBA,SAAS,yBAAiC;AACxC,QAAM,eAAgB,WACnB;AACH,MAAI,cAAc,WAAY,QAAO,aAAa,WAAW;AAC7D,SAAO,SAAS,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACnE;AAEA,SAAS,mBAAmB,OAAyC;AACnE,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AACpC,MAAI,SAAS,kBAAkB,SAAS,cAAe,QAAO;AAC9D,MAAI,SAAS,YAAY,SAAS,kBAAkB,SAAS;AAC3D,WAAO;AACT,MAAI,MAAM,UAAU,QAAQ,MAAM,YAAY,KAAM,QAAO;AAC3D,QAAM,OAAO,OAAO,MAAM,QAAQ,MAAM,SAAS,EAAE,EAAE,YAAY;AACjE,SAAO,KAAK,SAAS,iBAAiB,KAAK,KAAK,SAAS,cAAc;AACzE;AAEA,SAAS,wBAAwB,OAA+C;AAC9E,aAAW,OAAO,CAAC,UAAU,UAAU,QAAQ,WAAW,SAAS,GAAG;AACpE,UAAM,MAAM,MAAM,GAAG;AACrB,QAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAG,QAAO;AACtD,QAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,UAAI;AACF,eAAO,KAAK,UAAU,GAAG;AAAA,MAC3B,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAWO,IAAM,uBAAN,MAA2B;AAAA,EACxB;AAAA,EAEA,UAAU,oBAAI,IAA2B;AAAA,EACzC,aAAa,oBAAI,IAAY;AAAA,EAC7B,iBAAkC,CAAC;AAAA,EACnC,kBAAkB;AAAA,EAClB;AAAA,EAER,YAAY,QAA0B;AACpC,SAAK,SAAS;AAAA,MACZ,GAAG;AAAA,MACH,aAAa,OAAO,eAAe;AAAA,MACnC,WAAW,OAAO,aAAa;AAAA,MAC/B,gBAAgB,OAAO,kBAAkB;AAAA,IAC3C;AACA,SAAK,iBAAiB,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAa,OAAoE;AACrF,UAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AAEpC,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAA,MACL,KAAK,cAAc;AACjB,cAAM,WAAW,OAAO,MAAM,YAAY,EAAE;AAC5C,YAAI,SAAU,MAAK,iBAAiB;AACpC,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,oBAAoB;AACvB,cAAM,YAAY,KAAK,iBAAiB,KAAK;AAC7C,aAAK,kBAAkB;AACvB,aAAK,QAAQ,IAAI,WAAW;AAAA,UAC1B,SAAS;AAAA,UACT,WAAW,KAAK,IAAI;AAAA,UACpB,SAAS;AAAA,QACX,CAAC;AACD,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,sBAAsB;AACzB,cAAM,YAAY,OAAO,MAAM,aAAa,EAAE;AAC9C,cAAM,MAAM,KAAK,QAAQ,IAAI,SAAS;AACtC,YAAI,IAAK,KAAI,WAAW,OAAO,MAAM,SAAS,EAAE;AAChD,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,kBAAkB;AACrB,cAAM,YAAY,OAAO,MAAM,aAAa,EAAE;AAC9C,cAAM,MAAM,KAAK,QAAQ,IAAI,SAAS;AACtC,cAAM,MAAiC,CAAC,KAAK;AAE7C,YAAI,OAAO,CAAC,IAAI,SAAS;AACvB,cAAI,UAAU;AACd,eAAK,WAAW,IAAI,SAAS;AAC7B,gBAAM,eAAe,KAAK,QAAQ,IAAI,SAAS,SAAS,EAAE;AAAA,YACxD,CAAC,cAAc;AACb,kBAAI,UAAW,KAAI,KAAK,KAAK,mBAAmB,WAAW,SAAS,CAAC;AAAA,YACvE;AAAA,UACF;AACA,eAAK,eAAe,KAAK,YAAY;AACrC,gBAAM;AACN,eAAK,QAAQ,OAAO,SAAS;AAAA,QAC/B;AAEA,eAAO;AAAA,MACT;AAAA,MAEA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,eAAe;AAClB,YAAI,mBAAmB,KAAK,GAAG;AAC7B,gBAAM,YAAY,OAAO,MAAM,aAAa,EAAE;AAC9C,cAAI,CAAC,KAAK,WAAW,IAAI,SAAS,GAAG;AACnC,kBAAM,MAAM,KAAK,QAAQ,IAAI,SAAS;AACtC,kBAAM,UAAU,wBAAwB,KAAK;AAC7C,gBAAI,SAAS;AACX,oBAAM,MAAiC,CAAC,KAAK;AAC7C,kBAAI,IAAK,KAAI,UAAU;AACvB,mBAAK,WAAW,IAAI,SAAS;AAC7B,oBAAM,eAAe,KAAK,QAAQ,SAAS,SAAS,EAAE;AAAA,gBACpD,CAAC,cAAc;AACb,sBAAI,UAAW,KAAI,KAAK,KAAK,mBAAmB,WAAW,SAAS,CAAC;AAAA,gBACvE;AAAA,cACF;AACA,mBAAK,eAAe,KAAK,YAAY;AACrC,oBAAM;AACN,qBAAO;AAAA,YACT;AAAA,UACF;AAAA,QACF;AACA,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,eAAe;AAClB,cAAM,QAAQ,WAAW,KAAK,cAAc;AAC5C,aAAK,iBAAiB,CAAC;AACvB,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,MAEA;AACE,eAAO,CAAC,KAAK;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,iBAAiB,OAAwC;AAC/D,UAAM,KAAK,OAAO,MAAM,aAAa,EAAE;AACvC,QAAI,GAAI,QAAO;AACf,WAAO,oBAAoB,KAAK,IAAI,CAAC,IAAI,KAAK,iBAAiB;AAAA,EACjE;AAAA,EAEQ,oBAA0B;AAChC,UAAM,SAAS,KAAK,IAAI,IAAI,KAAK,OAAO,YAAY;AACpD,eAAW,CAAC,KAAK,GAAG,KAAK,KAAK,SAAS;AACrC,UAAI,IAAI,YAAY,OAAQ,MAAK,QAAQ,OAAO,GAAG;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,SACA,WACgC;AAChC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO,SAAS;AAE1E,QAAI;AACF,YAAM,OAAgC;AAAA,QACpC;AAAA,QACA,YAAY;AAAA,MACd;AACA,UAAI,KAAK,OAAO,SAAU,MAAK,WAAW,KAAK,OAAO;AACtD,UAAI,KAAK,eAAgB,MAAK,kBAAkB,KAAK;AAErD,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,aAAa,KAAK,OAAO;AAAA;AAAA;AAAA,QAGzB,mBAAmB,uBAAuB;AAAA,MAC5C;AACA,UAAI,KAAK,OAAO,UAAW,SAAQ,cAAc,IAAI,KAAK,OAAO;AAEjE,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,WAAW,YAAY;AAAA,QAC5D,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,UAAI,CAAC,IAAI,GAAI,QAAO;AAEpB,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,YAAM,QAAQ,KAAK,MAAM,CAAC;AAC1B,UAAI,CAAC,MAAO,QAAO;AACnB,aAAO,KAAK,uBAAuB,KAAK;AAAA,IAC1C,QAAQ;AACN,aAAO;AAAA,IACT,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,uBAAuB,IAAuC;AACpE,WAAO;AAAA,MACL,OAAO,OAAO,GAAG,KAAK;AAAA,MACtB,iBAAiB,GAAG;AAAA,MACpB,sBAAsB,GAAG,oBAAoB;AAAA,MAC7C,UAAU,GAAG;AAAA,MACb,aAAa,GAAG;AAAA,MAChB,UAAU,GAAG;AAAA,MACb,iBAAiB,GAAG;AAAA,MACpB,cAAc,GAAG,gBAAgB;AAAA,MACjC,gBAAgB,GAAG,aAAa,KAAK,OAAO;AAAA,MAC5C,uBAAuB,GAAG;AAAA,MAC1B,qBAAqB,GAAG;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,mBACN,WACA,WACyB;AACzB,WAAO;AAAA;AAAA;AAAA;AAAA,MAIL,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA,OAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;;;AChQA,kBAAuB;;;ACGvB,gBAAuB,SACrB,QAC0B;AAC1B,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI;AACJ,MAAI,YAAsB,CAAC;AAE3B,MAAI;AACF,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AAEV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,eAAS,MAAM,IAAI;AAEnB,iBAAW,OAAO,OAAO;AACvB,cAAM,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AAErD,YAAI,SAAS,IAAI;AACf,cAAI,UAAU,SAAS,GAAG;AACxB,kBAAM,EAAE,OAAO,cAAc,MAAM,UAAU,KAAK,IAAI,EAAE;AACxD,wBAAY,CAAC;AACb,2BAAe;AAAA,UACjB;AACA;AAAA,QACF;AAEA,YAAI,KAAK,WAAW,GAAG,EAAG;AAE1B,cAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,YAAI,aAAa,GAAI;AAErB,cAAM,QAAQ,KAAK,MAAM,GAAG,QAAQ;AACpC,cAAMA,SAAQ,KAAK,MAAM,WAAW,CAAC,EAAE,QAAQ,MAAM,EAAE;AAEvD,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,sBAAU,KAAKA,MAAK;AACpB;AAAA,UACF,KAAK;AACH,2BAAeA;AACf;AAAA,QAEJ;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,EAAE,OAAO,cAAc,MAAM,UAAU,KAAK,IAAI,EAAE;AAAA,IAC1D;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;;;ADlCA,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAC3B,IAAM,2BAA2B;AAWjC,IAAM,4BAA4B,MAAM;AACtC,MAAI;AACF,QAAI,iBAAiB;AACrB,UAAM,iBAAiB,IAAI,QAAQ,oBAAoB;AAAA,MACrD,QAAQ;AAAA,MACR,MAAM,IAAI,eAAe;AAAA,MACzB,IAAI,SAAS;AACX,yBAAiB;AACjB,eAAO;AAAA,MACT;AAAA,IACF,CAAgB,EAAE,QAAQ,IAAI,cAAc;AAC5C,WAAO,kBAAkB,CAAC;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF,GAAG;AASI,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA,kBAA0C;AAAA,EAC1C,oBAAmC;AAAA,EACnC,YAAmD;AAAA,EACnD,cAAc;AAAA,EACd,gBAAiC,CAAC;AAAA,EAClC,gBAAsC;AAAA,EACtC,kBAAwC;AAAA,EAExC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA2B;AACrC,SAAK,cAAc,OAAO,eAAe;AACzC,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,WAAW,OAAO;AACvB,SAAK,YAAY,OAAO;AACxB,SAAK,0BACH,OAAO,2BAA2B;AACpC,SAAK,kBAAkB,OAAO,YAAY;AAC1C,SAAK,iBAAiB,OAAO,WAAW;AACxC,SAAK,iBAAiB,OAAO,sBAAkB,oBAAO;AAEtD,SAAK,aAAa,OAAO;AACzB,SAAK,cAAc,OAAO;AAC1B,SAAK,UAAU,OAAO;AACtB,SAAK,gBAAgB,OAAO;AAC5B,SAAK,cAAc,OAAO;AAAA,EAC5B;AAAA,EAEA,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,kBAAkB,IAAkB;AAClC,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,cAAc,QAAsB;AAClC,QAAI,CAAC,KAAK,gBAAiB;AAC3B,SAAK,kBAAkB,KAAK,gBAAgB,MAAM;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBAAgB,QAA+B;AACnD,QAAI,CAAC,KAAK,gBAAiB;AAE3B,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,SAAS;AAEnE,QAAI;AACF,YAAM,OAAgC;AAAA,QACpC,SAAS;AAAA,QACT,gBAAY,oBAAO;AAAA,QACnB,iBAAiB,KAAK;AAAA,MACxB;AACA,UAAI,KAAK,SAAU,MAAK,WAAW,KAAK;AAExC,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,uBAAmB,oBAAO;AAAA,MAC5B;AACA,UAAI,KAAK,UAAW,SAAQ,cAAc,IAAI,KAAK;AAEnD,YAAM,MAAM,GAAG,KAAK,WAAW,YAAY;AAAA,QACzC,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,WAAW;AAAA,MACrB,CAAC;AAAA,IACH,QAAQ;AAAA,IAER,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QAAQ,WAAmB,MAAwC;AACjE,SAAK,cAAc;AACnB,SAAK,gBAAgB,CAAC;AACtB,SAAK,oBAAoB;AACzB,SAAK,gBAAgB;AACrB,SAAK,kBAAkB,IAAI,gBAAgB;AAE3C,QAAI,CAAC,0BAA0B;AAC7B,WAAK,cAAc;AACnB,WAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SACE;AAAA,MAGJ,CAAC;AACD,WAAK,KAAK,OAAO,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AACjC;AAAA,IACF;AAEA,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,uBAAmB,oBAAO;AAAA,MAC1B,gBAAgB;AAAA,MAChB,qBAAqB,KAAK;AAAA,IAC5B;AACA,QAAI,KAAK,UAAW,SAAQ,cAAc,IAAI,KAAK;AACnD,QAAI,KAAK,SAAU,SAAQ,YAAY,IAAI,KAAK;AAEhD,SAAK,gBAAgB,KAAK,mBAAmB,MAAM,OAAO;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAA0B;AAC9B,UAAM,QAAQ,IAAI,CAAC,KAAK,iBAAiB,KAAK,aAAa,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAc,mBACZ,MACA,SACe;AACf,UAAM,UAAU;AAAA,MACd,MAAM,KAAK,iBAAiB,MAAM;AAAA,MAClC,KAAK;AAAA,IACP;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,WAAW,WAAW;AAAA,QACpD,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,KAAK,iBAAiB;AAAA;AAAA,QAE9B,QAAQ;AAAA,MACV,CAAC;AAED,mBAAa,OAAO;AAEpB,UAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAAM;AAE1B,YAAM,KAAK,WAAW,IAAI,IAAI;AAAA,IAChC,QAAQ;AACN,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA;AAAA,EAGA,iBAAkC;AAChC,UAAM,MAAM,KAAK,cAAc,OAAO,CAAC;AACvC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,iBAAiB,MAAM;AAC5B,SAAK,YAAY;AACjB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA,EAGA,MAAM,iBAAgC;AACpC,QAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,kBAAmB;AAErD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK,mBAAmB;AAAA,QAC9C,QAAQ;AAAA,QACR,SAAS,EAAE,aAAa,KAAK,OAAO;AAAA,MACtC,CAAC;AACD,UAAI,CAAC,IAAI,GAAI;AAEb,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,iBAAW,aAAa,KAAK,YAAY;AACvC,aAAK,cAAc,SAAS;AAAA,MAC9B;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,MAAiD;AACxE,QAAI;AACF,uBAAiB,SAAS,SAAS,IAAI,GAAG;AACxC,YAAI,KAAK,iBAAiB,OAAO,QAAS;AAC1C,aAAK,YAAY,MAAM,OAAO,MAAM,IAAI;AAAA,MAC1C;AAAA,IACF,QAAQ;AAAA,IAER,UAAE;AACA,WAAK,cAAc;AACnB,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEQ,YAAY,OAA2B,MAAoB;AACjE,YAAQ,OAAO;AAAA,MACb,KAAK,sBAAsB;AACzB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,gBAAM,MAA6B,EAAE,MAAM,sBAAsB,GAAG,OAAO;AAC3E,eAAK,oBAAoB,OAAO;AAChC,eAAK,gBAAgB,GAAG;AACxB,eAAK,aAAa;AAAA,QACpB,QAAQ;AAAA,QAAyB;AACjC;AAAA,MACF;AAAA,MACA,KAAK,gBAAgB;AACnB,YAAI;AACF,gBAAM,KAAK,KAAK,MAAM,IAAI;AAC1B,eAAK,cAAc,KAAK,EAAE;AAC1B,eAAK,aAAa,EAAE;AAAA,QACtB,QAAQ;AAAA,QAAyB;AACjC;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,eAAK,UAAU,EAAE,MAAM,eAAe,GAAG,OAAO,CAAC;AAAA,QACnD,QAAQ;AAAA,QAAyB;AACjC;AAAA,MACF;AAAA,MACA,KAAK,oBAAoB;AACvB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,gBAAM,MAA2B,EAAE,MAAM,oBAAoB,GAAG,OAAO;AACvE,eAAK,cAAc,GAAG;AAAA,QACxB,QAAQ;AAAA,QAAyB;AACjC,aAAK,cAAc;AACnB,aAAK,YAAY;AACjB;AAAA,MACF;AAAA,MACA,SAAS;AACP,YAAI,OAAO,WAAW,cAAc,EAAG;AACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,eAAqB;AAC3B,QAAI,CAAC,KAAK,eAAgB;AAC1B,QAAI,KAAK,aAAa,CAAC,KAAK,kBAAmB;AAC/C,SAAK,YAAY;AAAA,MACf,MAAM,KAAK,eAAe;AAAA,MAC1B,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,QAAI,KAAK,WAAW;AAClB,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AACF;;;AEhVO,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EACA;AAAA,EAER,YAAY,QAAqC;AAC/C,SAAK,SAAS;AACd,SAAK,iBAAiB,OAAO,kBAAkB;AAAA,EACjD;AAAA,EAEA,OAAO,cACL,QACA,SACyC;AACzC,UAAM,oBAAsC,CAAC;AAC7C,QAAI,SAAmC;AACvC,QAAI,mBAAmB;AACvB,QAAI,gBAAgB;AAEpB,UAAM,cAAc,CAAC,WAA4B;AAC/C,wBAAkB,KAAK,KAAK,mBAAmB,QAAQ,gBAAgB,CAAC;AAAA,IAC1E;AAEA,UAAM,UAAU,CAAC,UAAiC;AAChD,cAAQ,KAAK,kBAAkB,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE;AAAA,IAC/D;AAEA,qBAAiB,SAAS,QAAQ;AAChC,YAAM,OAAO,OAAO,MAAM,QAAQ,EAAE;AAEpC,cAAQ,MAAM;AAAA,QACZ,KAAK;AAAA,QACL,KAAK,cAAc;AACjB,gBAAM,WAAW,OAAO,MAAM,YAAY,EAAE;AAE5C,mBAAS,IAAI,kBAAkB;AAAA,YAC7B,GAAG,KAAK;AAAA,YACR,gBAAgB,YAAY,KAAK,OAAO;AAAA,YACxC;AAAA,YACA;AAAA,UACF,CAAC;AAED,cAAI,KAAK,OAAO,YAAY,SAAS,QAAQ;AAC3C,mBAAO,cAAc,QAAQ,MAAM;AAAA,UACrC;AAEA,gBAAM;AACN;AAAA,QACF;AAAA,QAEA,KAAK;AAAA,QACL,KAAK,oBAAoB;AACvB,6BAAmB,OAAO,MAAM,aAAa,EAAE;AAC/C,0BAAgB;AAChB,cAAI,UAAU,SAAS,cAAc;AACnC,mBAAO,QAAQ,kBAAkB,QAAQ,YAAY;AAAA,UACvD;AACA,gBAAM;AACN;AAAA,QACF;AAAA,QAEA,KAAK;AAAA,QACL,KAAK,sBAAsB;AACzB,gBAAM;AACN,2BAAiB,OAAO,MAAM,SAAS,EAAE,EAAE;AAE3C,cAAI,QAAQ;AACV,uBAAW,MAAM,OAAO,eAAe,GAAG;AACxC,oBAAM,KAAK,kBAAkB,IAAI,kBAAkB,aAAa;AAAA,YAClE;AAAA,UACF;AACA;AAAA,QACF;AAAA,QAEA,KAAK;AAAA,QACL,KAAK,kBAAkB;AACrB,cAAI,QAAQ;AACV,kBAAM,OAAO,SAAS;AACtB,uBAAW,MAAM,OAAO,eAAe,GAAG;AACxC,oBAAM,KAAK,kBAAkB,IAAI,kBAAkB,aAAa;AAAA,YAClE;AAAA,UACF;AACA,gBAAM;AACN;AAAA,QACF;AAAA,QAEA,KAAK;AAAA,QACL,KAAK,eAAe;AAClB,cAAI,QAAQ;AACV,kBAAM,OAAO,eAAe;AAC5B,mBAAO,MAAM;AAAA,UACf;AAEA,qBAAW,aAAa,mBAAmB;AACzC,kBAAM,KAAK,mBAAmB,WAAW,UAAU,KAAK;AAAA,UAC1D;AAEA,gBAAM;AACN;AAAA,QACF;AAAA,QAEA;AACE,gBAAM;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBACN,IACA,WACA,eACyB;AACzB,UAAM,QAA8B;AAAA,MAClC,GAAG;AAAA,MACH,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,mBACN,WACA,MACyB;AACzB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,MACX,OAAO;AAAA,QACL,GAAG;AAAA,QACH,YAAY,UAAU;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,mBACN,QACA,WACgB;AAChB,WAAO;AAAA,MACL,OAAO,OAAO,OAAO,KAAK;AAAA,MAC1B,iBAAiB;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,iBAAiB,OAAO;AAAA,MACxB,gBAAgB,OAAO,aAAa,KAAK;AAAA,MACzC,uBAAuB,OAAO,SAAS;AAAA,MACvC,qBAAqB,OAAO,SAAS;AAAA,IACvC;AAAA,EACF;AACF;","names":["value"]}
1
+ {"version":3,"sources":["../../src/copilotkit/index.ts","../../src/copilotkit/middleware.ts","../../src/copilotkit/AdzenStreamClient.ts","../../src/copilotkit/parseSSE.ts","../../src/copilotkit/streamMiddleware.ts"],"sourcesContent":["export { AdzenAsyncMiddleware } from \"./middleware.js\";\nexport type { AdzenAsyncConfig } from \"./middleware.js\";\nexport { AdzenStreamMiddleware } from \"./streamMiddleware.js\";\nexport type { AdzenStreamMiddlewareConfig } from \"./streamMiddleware.js\";\nexport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n","import type { AdzenPlacement, ProcessResponse, ProcessResponseAd } from \"@adzenai/core\";\n\nexport interface AdzenAsyncConfig {\n endpointUrl?: string;\n apiKey: string;\n timeoutMs?: number;\n adUnitPosition?: string;\n location?: string;\n conversationId?: string;\n profileId?: string;\n}\n\ninterface MessageBuffer {\n content: string;\n startedAt: number;\n fetched: boolean;\n}\n\nfunction generateIdempotencyKey(): string {\n const globalCrypto = (globalThis as { crypto?: { randomUUID?: () => string } })\n .crypto;\n if (globalCrypto?.randomUUID) return globalCrypto.randomUUID();\n return `adzen-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isFinalOutputEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n if (type === \"RUN_FINISHED\" || type === \"RunFinished\") return false;\n if (type !== \"CUSTOM\" && type !== \"CUSTOM_EVENT\" && type !== \"CustomEvent\")\n return false;\n if (event.final === true || event.isFinal === true) return true;\n const name = String(event.name ?? event.event ?? \"\").toLowerCase();\n return name.includes(\"assistant_final\") || name.includes(\"final_output\");\n}\n\nfunction extractContentFromEvent(event: Record<string, unknown>): string | null {\n for (const key of [\"output\", \"result\", \"data\", \"payload\", \"message\"]) {\n const val = event[key];\n if (typeof val === \"string\" && val.length > 0) return val;\n if (typeof val === \"object\" && val !== null) {\n try {\n return JSON.stringify(val);\n } catch {\n continue;\n }\n }\n }\n return null;\n}\n\n/**\n * AG-UI middleware that intercepts the event stream and calls AI-API after\n * each assistant message, emitting `adzen_placement` custom events when an\n * ad is returned.\n *\n * All upstream events pass through immediately — zero latency is added to\n * the text stream. Ad fetches run as a side-effect and `RUN_FINISHED` is\n * held until all pending fetches settle.\n */\nexport class AdzenAsyncMiddleware {\n private config: Required<Pick<AdzenAsyncConfig, \"endpointUrl\" | \"timeoutMs\" | \"adUnitPosition\">> &\n AdzenAsyncConfig;\n private buffers = new Map<string, MessageBuffer>();\n private fetchedIds = new Set<string>();\n private pendingFetches: Promise<void>[] = [];\n private fallbackCounter = 0;\n private conversationId: string | undefined;\n\n constructor(config: AdzenAsyncConfig) {\n this.config = {\n ...config,\n endpointUrl: config.endpointUrl ?? \"https://api.adzen.ai/v1\",\n timeoutMs: config.timeoutMs ?? 3000,\n adUnitPosition: config.adUnitPosition ?? \"chin\",\n };\n this.conversationId = config.conversationId;\n }\n\n /**\n * Process a single AG-UI event. Returns an array of events to emit\n * downstream (always includes the original event, possibly followed\n * by an `adzen_placement` custom event).\n */\n async processEvent(event: Record<string, unknown>): Promise<Record<string, unknown>[]> {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n if (threadId) this.conversationId = threadId;\n return [event];\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n const messageId = this.resolveMessageId(event);\n this.evictStaleBuffers();\n this.buffers.set(messageId, {\n content: \"\",\n startedAt: Date.now(),\n fetched: false,\n });\n return [event];\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n if (buf) buf.content += String(event.delta ?? \"\");\n return [event];\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n const out: Record<string, unknown>[] = [event];\n\n if (buf && !buf.fetched) {\n buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(buf.content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n this.buffers.delete(messageId);\n }\n\n return out;\n }\n\n case \"CUSTOM\":\n case \"CUSTOM_EVENT\":\n case \"CustomEvent\": {\n if (isFinalOutputEvent(event)) {\n const messageId = String(event.messageId ?? \"\");\n if (!this.fetchedIds.has(messageId)) {\n const buf = this.buffers.get(messageId);\n const content = extractContentFromEvent(event);\n if (content) {\n const out: Record<string, unknown>[] = [event];\n if (buf) buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n return out;\n }\n }\n }\n return [event];\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n await Promise.allSettled(this.pendingFetches);\n this.pendingFetches = [];\n return [event];\n }\n\n default:\n return [event];\n }\n }\n\n private resolveMessageId(event: Record<string, unknown>): string {\n const id = String(event.messageId ?? \"\");\n if (id) return id;\n return `__adzen_fallback_${Date.now()}_${this.fallbackCounter++}`;\n }\n\n private evictStaleBuffers(): void {\n const cutoff = Date.now() - this.config.timeoutMs * 2;\n for (const [key, buf] of this.buffers) {\n if (buf.startedAt < cutoff) this.buffers.delete(key);\n }\n }\n\n private async fetchAd(\n content: string,\n messageId: string,\n ): Promise<AdzenPlacement | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content,\n message_id: messageId,\n };\n if (this.config.location) body.location = this.config.location;\n if (this.conversationId) body.conversation_id = this.conversationId;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n // AI-API requires an Idempotency-Key on /process (400 otherwise).\n // One per ad fetch — each completed message is a distinct request.\n \"Idempotency-Key\": generateIdempotencyKey(),\n };\n if (this.config.profileId) headers[\"X-Profile-Id\"] = this.config.profileId;\n\n const res = await fetch(`${this.config.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as ProcessResponse;\n const first = data.ads?.[0];\n if (!first) return null;\n return this.mapResponseToPlacement(first);\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private mapResponseToPlacement(ad: ProcessResponseAd): AdzenPlacement {\n return {\n ad_id: String(ad.ad_id),\n advertiser_name: ad.advertiser_name,\n advertiser_image_url: ad.sponsor_logo_url || undefined,\n headline: ad.title,\n description: ad.description,\n cta_text: ad.cta,\n destination_url: ad.click_through_url,\n creative_url: ad.creative_url ?? undefined,\n adUnitPosition: ad.placement ?? this.config.adUnitPosition,\n render_impression_url: ad.render_impression_url,\n view_impression_url: ad.view_impression_url,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n messageId: string,\n ): Record<string, unknown> {\n return {\n // AG-UI's event schema only accepts \"CUSTOM\"; @ag-ui/client zod-validates\n // every SSE frame and aborts the run on an unknown type. \"CUSTOM_EVENT\"\n // was rejected downstream.\n type: \"CUSTOM\",\n name: \"adzen_placement\",\n messageId,\n value: {\n ...placement,\n message_id: messageId,\n },\n };\n }\n}\n","import type {\n InlineAdEvent,\n PlacementRecord,\n PlacementsResponse,\n AdzenStreamStartEvent,\n AdzenStreamEndEvent,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { uuidv4 } from \"@adzenai/core\";\nimport { parseSSE } from \"./parseSSE.js\";\n\nexport interface AdzenStreamConfig {\n apiKey: string;\n endpointUrl?: string;\n timeoutMs?: number;\n location?: string;\n conversationId?: string;\n profileId?: string;\n placementPollIntervalMs?: number;\n prefetch?: boolean;\n /**\n * Whether to poll the sidecar placement endpoint for out-of-stream ads\n * rendered outside the message body. Defaults to `true`. Set to `false`\n * to run inline-only, leaving `adzen_inline` events unaffected.\n */\n sidecar?: boolean;\n /**\n * Extra request headers merged into every outbound Adzen request\n * (/stream, /process, placement polling). Intended for internal\n * debugging (e.g. `X-Adzen-Debug-*`). SDK-managed headers\n * (auth, content-type, correlation IDs) always take precedence.\n */\n headers?: Record<string, string>;\n onInlineAd?: (ad: InlineAdEvent) => void;\n onPlacement?: (placement: PlacementRecord) => void;\n onError?: (error: AdzenStreamErrorEvent) => void;\n onStreamStart?: (event: AdzenStreamStartEvent) => void;\n onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n}\n\nconst DEFAULT_ENDPOINT = \"https://api.adzen.ai/v1\";\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\n\n/**\n * Whether the current runtime can send a ReadableStream as a fetch request\n * body. Node 18+ (undici) supports this; Safari and Firefox do not, and\n * silently coerce the stream to the string \"[object ReadableStream]\" via the\n * WebIDL BodyInit union's USVString member.\n *\n * Detection works by checking whether the `duplex` getter is read and whether\n * the runtime set a Content-Type — a coerced string body gets `text/plain`.\n */\nconst SUPPORTS_REQUEST_STREAMS = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\"http://localhost\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexAccessed = true;\n return \"half\";\n },\n } as RequestInit).headers.has(\"Content-Type\");\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n\n/**\n * Framework-agnostic client for the Adzen streaming ad enrichment API.\n *\n * Handles connecting to `/v1/stream`, parsing SSE events, optional prefetch\n * via `/process`, and sidecar placement polling. Framework adapters (AG-UI\n * middleware, React hooks) wrap this class.\n */\nexport class AdzenStreamClient {\n private endpointUrl: string;\n private apiKey: string;\n private timeoutMs: number;\n private location?: string;\n private profileId?: string;\n private placementPollIntervalMs: number;\n private prefetchEnabled: boolean;\n private sidecarEnabled: boolean;\n private customHeaders: Record<string, string>;\n\n private conversationId: string;\n private abortController: AbortController | null = null;\n private placementEndpoint: string | null = null;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private streamEnded = false;\n private inlineAdQueue: InlineAdEvent[] = [];\n private streamPromise: Promise<void> | null = null;\n private prefetchPromise: Promise<void> | null = null;\n\n private onInlineAd?: (ad: InlineAdEvent) => void;\n private onPlacement?: (placement: PlacementRecord) => void;\n private onError?: (error: AdzenStreamErrorEvent) => void;\n private onStreamStart?: (event: AdzenStreamStartEvent) => void;\n private onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n\n constructor(config: AdzenStreamConfig) {\n this.endpointUrl = config.endpointUrl ?? DEFAULT_ENDPOINT;\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.location = config.location;\n this.profileId = config.profileId;\n this.placementPollIntervalMs =\n config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.prefetchEnabled = config.prefetch ?? false;\n this.sidecarEnabled = config.sidecar ?? true;\n this.customHeaders = config.headers ?? {};\n this.conversationId = config.conversationId ?? uuidv4();\n\n this.onInlineAd = config.onInlineAd;\n this.onPlacement = config.onPlacement;\n this.onError = config.onError;\n this.onStreamStart = config.onStreamStart;\n this.onStreamEnd = config.onStreamEnd;\n }\n\n getConversationId(): string {\n return this.conversationId;\n }\n\n setConversationId(id: string): void {\n this.conversationId = id;\n }\n\n /**\n * Dispatches the prefetch without waiting for it.\n *\n * The request leaves before `connect()` opens `/stream`, which is all the\n * 2-step chained flow needs — the two calls are correlated server-side by\n * `conversation_id`. Awaiting the response here would stall the assistant\n * message behind a full ad-matching round trip while the LLM keeps\n * streaming, so the buffered tokens would then arrive in one burst.\n */\n startPrefetch(prompt: string): void {\n if (!this.prefetchEnabled) return;\n this.prefetchPromise = this.prefetchProcess(prompt);\n }\n\n /**\n * 2-step chained flow: POST /process with the user prompt and\n * conversationId. Awaits the response (or timeout) but discards the\n * result. If it fails, logs and continues — the stream will proceed\n * in degraded mode.\n */\n async prefetchProcess(prompt: string): Promise<void> {\n if (!this.prefetchEnabled) return;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content: prompt,\n message_id: uuidv4(),\n conversation_id: this.conversationId,\n };\n if (this.location) body.location = this.location;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n\n await fetch(`${this.endpointUrl}/process`, {\n method: \"POST\",\n headers: this.mergeHeaders(headers),\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch {\n // Prefetch failure is non-fatal — stream continues without pre-match context\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Opens a streaming POST to /stream, piping the upstream LLM response\n * body directly. The API reads the raw LLM stream in real time and\n * sends SSE ad events back on the response side.\n *\n * @param messageId Unique ID for this assistant message\n * @param body The upstream LLM response body (one branch of a tee)\n */\n connect(messageId: string, body: ReadableStream<Uint8Array>): void {\n this.streamEnded = false;\n this.inlineAdQueue = [];\n this.placementEndpoint = null;\n this.streamPromise = null;\n this.abortController = new AbortController();\n\n if (!SUPPORTS_REQUEST_STREAMS) {\n this.streamEnded = true;\n this.onError?.({\n type: \"adzen_error\",\n code: \"request_streams_unsupported\",\n message:\n \"This runtime cannot send a ReadableStream as a request body, so the \" +\n \"LLM stream cannot be proxied. Run AdzenStreamMiddleware server-side \" +\n \"(Node 18+) — browsers other than Chromium do not support fetch upload streams.\",\n });\n void body.cancel().catch(() => {});\n return;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/octet-stream\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n \"X-Message-Id\": messageId,\n \"X-Conversation-Id\": this.conversationId,\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n if (this.location) headers[\"X-Location\"] = this.location;\n\n this.streamPromise = this.executeStreamFetch(body, this.mergeHeaders(headers));\n }\n\n /**\n * Waits for the SSE response stream from the API to finish, along with any\n * prefetch still in flight. Neither can reject — both swallow their errors.\n */\n async finalize(): Promise<void> {\n await Promise.all([this.prefetchPromise, this.streamPromise]);\n }\n\n /**\n * Merges caller-provided passthrough headers with the SDK's fixed headers.\n * Fixed headers are spread last so SDK-managed keys (auth, content-type,\n * correlation IDs) always win over any passthrough value.\n */\n private mergeHeaders(\n fixed: Record<string, string>,\n ): Record<string, string> {\n return { ...this.customHeaders, ...fixed };\n }\n\n private async executeStreamFetch(\n body: ReadableStream<Uint8Array>,\n headers: Record<string, string>,\n ): Promise<void> {\n const timeout = setTimeout(\n () => this.abortController?.abort(),\n this.timeoutMs,\n );\n\n try {\n const res = await fetch(`${this.endpointUrl}/stream`, {\n method: \"POST\",\n headers,\n body,\n signal: this.abortController?.signal,\n // @ts-expect-error -- required for streaming request bodies (Node/undici)\n duplex: \"half\",\n });\n\n clearTimeout(timeout);\n\n if (!res.ok || !res.body) return;\n\n await this.readStream(res.body);\n } catch {\n clearTimeout(timeout);\n }\n }\n\n /** Drains and returns any queued inline ad events. */\n drainInlineAds(): InlineAdEvent[] {\n const ads = this.inlineAdQueue.splice(0);\n return ads;\n }\n\n /** Cancels pending requests and cleans up state. */\n abort(): void {\n this.abortController?.abort();\n this.stopPolling();\n this.streamEnded = true;\n }\n\n /** Polls the placement endpoint once. Called on an interval internally. */\n async pollPlacements(): Promise<void> {\n if (!this.sidecarEnabled || !this.placementEndpoint) return;\n\n try {\n const res = await fetch(this.placementEndpoint, {\n method: \"GET\",\n headers: this.mergeHeaders({ \"X-API-Key\": this.apiKey }),\n });\n if (!res.ok) return;\n\n const data = (await res.json()) as PlacementsResponse;\n for (const placement of data.placements) {\n this.onPlacement?.(placement);\n }\n } catch {\n // Polling failure is non-fatal\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n try {\n for await (const frame of parseSSE(body)) {\n if (this.abortController?.signal.aborted) break;\n this.handleFrame(frame.event, frame.data);\n }\n } catch {\n // Stream read error — degrade gracefully\n } finally {\n this.streamEnded = true;\n this.stopPolling();\n }\n }\n\n private handleFrame(event: string | undefined, data: string): void {\n switch (event) {\n case \"adzen_stream_start\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamStartEvent, \"type\">;\n const evt: AdzenStreamStartEvent = { type: \"adzen_stream_start\", ...parsed };\n this.placementEndpoint = parsed.placement_endpoint;\n this.onStreamStart?.(evt);\n this.startPolling();\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_inline\": {\n try {\n const ad = JSON.parse(data) as InlineAdEvent;\n this.inlineAdQueue.push(ad);\n this.onInlineAd?.(ad);\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_error\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamErrorEvent, \"type\">;\n this.onError?.({ type: \"adzen_error\", ...parsed });\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_stream_end\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamEndEvent, \"type\">;\n const evt: AdzenStreamEndEvent = { type: \"adzen_stream_end\", ...parsed };\n this.onStreamEnd?.(evt);\n } catch { /* malformed — skip */ }\n this.streamEnded = true;\n this.stopPolling();\n break;\n }\n default: {\n if (event?.startsWith(\"adzen_debug_\")) break;\n break;\n }\n }\n }\n\n private startPolling(): void {\n if (!this.sidecarEnabled) return;\n if (this.pollTimer || !this.placementEndpoint) return;\n this.pollTimer = setInterval(\n () => this.pollPlacements(),\n this.placementPollIntervalMs,\n );\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n}\n","/**\n * Lightweight SSE parser that reads a ReadableStream<Uint8Array> and yields\n * parsed frames. Handles multi-line `data:` fields, named event types, and\n * ignores `id:` / `retry:` fields per the SSE spec.\n */\n\nexport interface SSEFrame {\n event?: string;\n data: string;\n}\n\nexport async function* parseSSE(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<SSEFrame> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent: string | undefined;\n let dataLines: string[] = [];\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop()!;\n\n for (const raw of lines) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n dataLines = [];\n currentEvent = undefined;\n }\n continue;\n }\n\n if (line.startsWith(\":\")) continue;\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n\n const field = line.slice(0, colonIdx);\n const value = line.slice(colonIdx + 1).replace(/^ /, \"\");\n\n switch (field) {\n case \"data\":\n dataLines.push(value);\n break;\n case \"event\":\n currentEvent = value;\n break;\n // id and retry are intentionally ignored\n }\n }\n }\n\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AdzenPlacement,\n InlineAdEvent,\n InlineAdEventPayload,\n PlacementRecord,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { AdzenStreamClient } from \"./AdzenStreamClient.js\";\nimport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n\nexport interface AdzenStreamMiddlewareConfig extends AdzenStreamConfig {\n adUnitPosition?: string;\n}\n\n/**\n * AG-UI middleware that proxies the LLM response stream through Adzen's\n * streaming endpoint for real-time ad enrichment.\n *\n * Uses a stream transformer pattern: `processStream()` takes an\n * AsyncIterable of AG-UI events and yields an enriched stream that\n * includes inline ad events and sidecar placements.\n *\n * Supports the 2-step chained flow: when `prefetch: true` and a `prompt`\n * is provided, fires POST /process before opening the stream connection.\n */\nexport class AdzenStreamMiddleware {\n private config: AdzenStreamMiddlewareConfig;\n private adUnitPosition: string;\n\n constructor(config: AdzenStreamMiddlewareConfig) {\n this.config = config;\n this.adUnitPosition = config.adUnitPosition ?? \"chin\";\n }\n\n async *processStream(\n events: AsyncIterable<Record<string, unknown>>,\n options?: { prompt?: string; upstreamBody?: ReadableStream<Uint8Array> },\n ): AsyncGenerator<Record<string, unknown>> {\n const sidecarPlacements: AdzenPlacement[] = [];\n let client: AdzenStreamClient | null = null;\n let currentMessageId = \"\";\n let contentOffset = 0;\n\n const onPlacement = (record: PlacementRecord) => {\n sidecarPlacements.push(this.mapPlacementRecord(record, currentMessageId));\n };\n\n const onError = (error: AdzenStreamErrorEvent) => {\n console.warn(`[adzen-stream] ${error.code}: ${error.message}`);\n };\n\n for await (const event of events) {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n\n client = new AdzenStreamClient({\n ...this.config,\n conversationId: threadId || this.config.conversationId,\n onPlacement,\n onError,\n });\n\n if (this.config.prefetch && options?.prompt) {\n client.startPrefetch(options.prompt);\n }\n\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n currentMessageId = String(event.messageId ?? \"\");\n contentOffset = 0;\n if (client && options?.upstreamBody) {\n client.connect(currentMessageId, options.upstreamBody);\n }\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n yield event;\n contentOffset += String(event.delta ?? \"\").length;\n\n if (client) {\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n break;\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n if (client) {\n await client.finalize();\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n yield event;\n break;\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n if (client) {\n await client.pollPlacements();\n client.abort();\n }\n\n for (const placement of sidecarPlacements) {\n yield this.makePlacementEvent(placement, placement.ad_id);\n }\n\n yield event;\n break;\n }\n\n default:\n yield event;\n }\n }\n }\n\n /**\n * Wraps an inline ad as an AG-UI custom event anchored to `contentOffset`,\n * the number of message characters emitted before the ad arrived. Clients\n * use it to render the ad directly after that content instead of at the\n * end of the message.\n */\n private makeInlineAdEvent(\n ad: InlineAdEvent,\n messageId: string,\n contentOffset: number,\n ): Record<string, unknown> {\n const value: InlineAdEventPayload = {\n ...ad,\n message_id: messageId,\n content_offset: contentOffset,\n };\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_inline_ad\",\n messageId,\n value,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n adId: string,\n ): Record<string, unknown> {\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_placement\",\n messageId: adId,\n value: {\n ...placement,\n message_id: placement.ad_id,\n },\n };\n }\n\n private mapPlacementRecord(\n record: PlacementRecord,\n messageId: string,\n ): AdzenPlacement {\n return {\n ad_id: String(record.ad_id),\n advertiser_name: \"\",\n headline: record.headline,\n cta_text: record.cta_text,\n destination_url: record.destination_url,\n adUnitPosition: record.placement || this.adUnitPosition,\n render_impression_url: record.tracking.impression_url,\n view_impression_url: record.tracking.impression_url,\n };\n }\n}\n"],"mappings":"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","names":["value"]}
@@ -54,6 +54,13 @@ interface AdzenStreamConfig {
54
54
  * to run inline-only, leaving `adzen_inline` events unaffected.
55
55
  */
56
56
  sidecar?: boolean;
57
+ /**
58
+ * Extra request headers merged into every outbound Adzen request
59
+ * (/stream, /process, placement polling). Intended for internal
60
+ * debugging (e.g. `X-Adzen-Debug-*`). SDK-managed headers
61
+ * (auth, content-type, correlation IDs) always take precedence.
62
+ */
63
+ headers?: Record<string, string>;
57
64
  onInlineAd?: (ad: InlineAdEvent) => void;
58
65
  onPlacement?: (placement: PlacementRecord) => void;
59
66
  onError?: (error: AdzenStreamErrorEvent) => void;
@@ -54,6 +54,13 @@ interface AdzenStreamConfig {
54
54
  * to run inline-only, leaving `adzen_inline` events unaffected.
55
55
  */
56
56
  sidecar?: boolean;
57
+ /**
58
+ * Extra request headers merged into every outbound Adzen request
59
+ * (/stream, /process, placement polling). Intended for internal
60
+ * debugging (e.g. `X-Adzen-Debug-*`). SDK-managed headers
61
+ * (auth, content-type, correlation IDs) always take precedence.
62
+ */
63
+ headers?: Record<string, string>;
57
64
  onInlineAd?: (ad: InlineAdEvent) => void;
58
65
  onPlacement?: (placement: PlacementRecord) => void;
59
66
  onError?: (error: AdzenStreamErrorEvent) => void;
@@ -285,6 +285,7 @@ var AdzenStreamClient = class {
285
285
  placementPollIntervalMs;
286
286
  prefetchEnabled;
287
287
  sidecarEnabled;
288
+ customHeaders;
288
289
  conversationId;
289
290
  abortController = null;
290
291
  placementEndpoint = null;
@@ -307,6 +308,7 @@ var AdzenStreamClient = class {
307
308
  this.placementPollIntervalMs = config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
308
309
  this.prefetchEnabled = config.prefetch ?? false;
309
310
  this.sidecarEnabled = config.sidecar ?? true;
311
+ this.customHeaders = config.headers ?? {};
310
312
  this.conversationId = config.conversationId ?? uuidv4();
311
313
  this.onInlineAd = config.onInlineAd;
312
314
  this.onPlacement = config.onPlacement;
@@ -358,7 +360,7 @@ var AdzenStreamClient = class {
358
360
  if (this.profileId) headers["X-Profile-Id"] = this.profileId;
359
361
  await fetch(`${this.endpointUrl}/process`, {
360
362
  method: "POST",
361
- headers,
363
+ headers: this.mergeHeaders(headers),
362
364
  body: JSON.stringify(body),
363
365
  signal: controller.signal
364
366
  });
@@ -401,7 +403,7 @@ var AdzenStreamClient = class {
401
403
  };
402
404
  if (this.profileId) headers["X-Profile-Id"] = this.profileId;
403
405
  if (this.location) headers["X-Location"] = this.location;
404
- this.streamPromise = this.executeStreamFetch(body, headers);
406
+ this.streamPromise = this.executeStreamFetch(body, this.mergeHeaders(headers));
405
407
  }
406
408
  /**
407
409
  * Waits for the SSE response stream from the API to finish, along with any
@@ -410,6 +412,14 @@ var AdzenStreamClient = class {
410
412
  async finalize() {
411
413
  await Promise.all([this.prefetchPromise, this.streamPromise]);
412
414
  }
415
+ /**
416
+ * Merges caller-provided passthrough headers with the SDK's fixed headers.
417
+ * Fixed headers are spread last so SDK-managed keys (auth, content-type,
418
+ * correlation IDs) always win over any passthrough value.
419
+ */
420
+ mergeHeaders(fixed) {
421
+ return { ...this.customHeaders, ...fixed };
422
+ }
413
423
  async executeStreamFetch(body, headers) {
414
424
  const timeout = setTimeout(
415
425
  () => this.abortController?.abort(),
@@ -448,7 +458,7 @@ var AdzenStreamClient = class {
448
458
  try {
449
459
  const res = await fetch(this.placementEndpoint, {
450
460
  method: "GET",
451
- headers: { "X-API-Key": this.apiKey }
461
+ headers: this.mergeHeaders({ "X-API-Key": this.apiKey })
452
462
  });
453
463
  if (!res.ok) return;
454
464
  const data = await res.json();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/copilotkit/middleware.ts","../../src/copilotkit/AdzenStreamClient.ts","../../src/copilotkit/parseSSE.ts","../../src/copilotkit/streamMiddleware.ts"],"sourcesContent":["import type { AdzenPlacement, ProcessResponse, ProcessResponseAd } from \"@adzenai/core\";\n\nexport interface AdzenAsyncConfig {\n endpointUrl?: string;\n apiKey: string;\n timeoutMs?: number;\n adUnitPosition?: string;\n location?: string;\n conversationId?: string;\n profileId?: string;\n}\n\ninterface MessageBuffer {\n content: string;\n startedAt: number;\n fetched: boolean;\n}\n\nfunction generateIdempotencyKey(): string {\n const globalCrypto = (globalThis as { crypto?: { randomUUID?: () => string } })\n .crypto;\n if (globalCrypto?.randomUUID) return globalCrypto.randomUUID();\n return `adzen-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isFinalOutputEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n if (type === \"RUN_FINISHED\" || type === \"RunFinished\") return false;\n if (type !== \"CUSTOM\" && type !== \"CUSTOM_EVENT\" && type !== \"CustomEvent\")\n return false;\n if (event.final === true || event.isFinal === true) return true;\n const name = String(event.name ?? event.event ?? \"\").toLowerCase();\n return name.includes(\"assistant_final\") || name.includes(\"final_output\");\n}\n\nfunction extractContentFromEvent(event: Record<string, unknown>): string | null {\n for (const key of [\"output\", \"result\", \"data\", \"payload\", \"message\"]) {\n const val = event[key];\n if (typeof val === \"string\" && val.length > 0) return val;\n if (typeof val === \"object\" && val !== null) {\n try {\n return JSON.stringify(val);\n } catch {\n continue;\n }\n }\n }\n return null;\n}\n\n/**\n * AG-UI middleware that intercepts the event stream and calls AI-API after\n * each assistant message, emitting `adzen_placement` custom events when an\n * ad is returned.\n *\n * All upstream events pass through immediately — zero latency is added to\n * the text stream. Ad fetches run as a side-effect and `RUN_FINISHED` is\n * held until all pending fetches settle.\n */\nexport class AdzenAsyncMiddleware {\n private config: Required<Pick<AdzenAsyncConfig, \"endpointUrl\" | \"timeoutMs\" | \"adUnitPosition\">> &\n AdzenAsyncConfig;\n private buffers = new Map<string, MessageBuffer>();\n private fetchedIds = new Set<string>();\n private pendingFetches: Promise<void>[] = [];\n private fallbackCounter = 0;\n private conversationId: string | undefined;\n\n constructor(config: AdzenAsyncConfig) {\n this.config = {\n ...config,\n endpointUrl: config.endpointUrl ?? \"https://api.adzen.ai/v1\",\n timeoutMs: config.timeoutMs ?? 3000,\n adUnitPosition: config.adUnitPosition ?? \"chin\",\n };\n this.conversationId = config.conversationId;\n }\n\n /**\n * Process a single AG-UI event. Returns an array of events to emit\n * downstream (always includes the original event, possibly followed\n * by an `adzen_placement` custom event).\n */\n async processEvent(event: Record<string, unknown>): Promise<Record<string, unknown>[]> {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n if (threadId) this.conversationId = threadId;\n return [event];\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n const messageId = this.resolveMessageId(event);\n this.evictStaleBuffers();\n this.buffers.set(messageId, {\n content: \"\",\n startedAt: Date.now(),\n fetched: false,\n });\n return [event];\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n if (buf) buf.content += String(event.delta ?? \"\");\n return [event];\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n const out: Record<string, unknown>[] = [event];\n\n if (buf && !buf.fetched) {\n buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(buf.content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n this.buffers.delete(messageId);\n }\n\n return out;\n }\n\n case \"CUSTOM\":\n case \"CUSTOM_EVENT\":\n case \"CustomEvent\": {\n if (isFinalOutputEvent(event)) {\n const messageId = String(event.messageId ?? \"\");\n if (!this.fetchedIds.has(messageId)) {\n const buf = this.buffers.get(messageId);\n const content = extractContentFromEvent(event);\n if (content) {\n const out: Record<string, unknown>[] = [event];\n if (buf) buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n return out;\n }\n }\n }\n return [event];\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n await Promise.allSettled(this.pendingFetches);\n this.pendingFetches = [];\n return [event];\n }\n\n default:\n return [event];\n }\n }\n\n private resolveMessageId(event: Record<string, unknown>): string {\n const id = String(event.messageId ?? \"\");\n if (id) return id;\n return `__adzen_fallback_${Date.now()}_${this.fallbackCounter++}`;\n }\n\n private evictStaleBuffers(): void {\n const cutoff = Date.now() - this.config.timeoutMs * 2;\n for (const [key, buf] of this.buffers) {\n if (buf.startedAt < cutoff) this.buffers.delete(key);\n }\n }\n\n private async fetchAd(\n content: string,\n messageId: string,\n ): Promise<AdzenPlacement | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content,\n message_id: messageId,\n };\n if (this.config.location) body.location = this.config.location;\n if (this.conversationId) body.conversation_id = this.conversationId;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n // AI-API requires an Idempotency-Key on /process (400 otherwise).\n // One per ad fetch — each completed message is a distinct request.\n \"Idempotency-Key\": generateIdempotencyKey(),\n };\n if (this.config.profileId) headers[\"X-Profile-Id\"] = this.config.profileId;\n\n const res = await fetch(`${this.config.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as ProcessResponse;\n const first = data.ads?.[0];\n if (!first) return null;\n return this.mapResponseToPlacement(first);\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private mapResponseToPlacement(ad: ProcessResponseAd): AdzenPlacement {\n return {\n ad_id: String(ad.ad_id),\n advertiser_name: ad.advertiser_name,\n advertiser_image_url: ad.sponsor_logo_url || undefined,\n headline: ad.title,\n description: ad.description,\n cta_text: ad.cta,\n destination_url: ad.click_through_url,\n creative_url: ad.creative_url ?? undefined,\n adUnitPosition: ad.placement ?? this.config.adUnitPosition,\n render_impression_url: ad.render_impression_url,\n view_impression_url: ad.view_impression_url,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n messageId: string,\n ): Record<string, unknown> {\n return {\n // AG-UI's event schema only accepts \"CUSTOM\"; @ag-ui/client zod-validates\n // every SSE frame and aborts the run on an unknown type. \"CUSTOM_EVENT\"\n // was rejected downstream.\n type: \"CUSTOM\",\n name: \"adzen_placement\",\n messageId,\n value: {\n ...placement,\n message_id: messageId,\n },\n };\n }\n}\n","import type {\n InlineAdEvent,\n PlacementRecord,\n PlacementsResponse,\n AdzenStreamStartEvent,\n AdzenStreamEndEvent,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { uuidv4 } from \"@adzenai/core\";\nimport { parseSSE } from \"./parseSSE.js\";\n\nexport interface AdzenStreamConfig {\n apiKey: string;\n endpointUrl?: string;\n timeoutMs?: number;\n location?: string;\n conversationId?: string;\n profileId?: string;\n placementPollIntervalMs?: number;\n prefetch?: boolean;\n /**\n * Whether to poll the sidecar placement endpoint for out-of-stream ads\n * rendered outside the message body. Defaults to `true`. Set to `false`\n * to run inline-only, leaving `adzen_inline` events unaffected.\n */\n sidecar?: boolean;\n onInlineAd?: (ad: InlineAdEvent) => void;\n onPlacement?: (placement: PlacementRecord) => void;\n onError?: (error: AdzenStreamErrorEvent) => void;\n onStreamStart?: (event: AdzenStreamStartEvent) => void;\n onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n}\n\nconst DEFAULT_ENDPOINT = \"https://api.adzen.ai/v1\";\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\n\n/**\n * Whether the current runtime can send a ReadableStream as a fetch request\n * body. Node 18+ (undici) supports this; Safari and Firefox do not, and\n * silently coerce the stream to the string \"[object ReadableStream]\" via the\n * WebIDL BodyInit union's USVString member.\n *\n * Detection works by checking whether the `duplex` getter is read and whether\n * the runtime set a Content-Type — a coerced string body gets `text/plain`.\n */\nconst SUPPORTS_REQUEST_STREAMS = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\"http://localhost\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexAccessed = true;\n return \"half\";\n },\n } as RequestInit).headers.has(\"Content-Type\");\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n\n/**\n * Framework-agnostic client for the Adzen streaming ad enrichment API.\n *\n * Handles connecting to `/v1/stream`, parsing SSE events, optional prefetch\n * via `/process`, and sidecar placement polling. Framework adapters (AG-UI\n * middleware, React hooks) wrap this class.\n */\nexport class AdzenStreamClient {\n private endpointUrl: string;\n private apiKey: string;\n private timeoutMs: number;\n private location?: string;\n private profileId?: string;\n private placementPollIntervalMs: number;\n private prefetchEnabled: boolean;\n private sidecarEnabled: boolean;\n\n private conversationId: string;\n private abortController: AbortController | null = null;\n private placementEndpoint: string | null = null;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private streamEnded = false;\n private inlineAdQueue: InlineAdEvent[] = [];\n private streamPromise: Promise<void> | null = null;\n private prefetchPromise: Promise<void> | null = null;\n\n private onInlineAd?: (ad: InlineAdEvent) => void;\n private onPlacement?: (placement: PlacementRecord) => void;\n private onError?: (error: AdzenStreamErrorEvent) => void;\n private onStreamStart?: (event: AdzenStreamStartEvent) => void;\n private onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n\n constructor(config: AdzenStreamConfig) {\n this.endpointUrl = config.endpointUrl ?? DEFAULT_ENDPOINT;\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.location = config.location;\n this.profileId = config.profileId;\n this.placementPollIntervalMs =\n config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.prefetchEnabled = config.prefetch ?? false;\n this.sidecarEnabled = config.sidecar ?? true;\n this.conversationId = config.conversationId ?? uuidv4();\n\n this.onInlineAd = config.onInlineAd;\n this.onPlacement = config.onPlacement;\n this.onError = config.onError;\n this.onStreamStart = config.onStreamStart;\n this.onStreamEnd = config.onStreamEnd;\n }\n\n getConversationId(): string {\n return this.conversationId;\n }\n\n setConversationId(id: string): void {\n this.conversationId = id;\n }\n\n /**\n * Dispatches the prefetch without waiting for it.\n *\n * The request leaves before `connect()` opens `/stream`, which is all the\n * 2-step chained flow needs — the two calls are correlated server-side by\n * `conversation_id`. Awaiting the response here would stall the assistant\n * message behind a full ad-matching round trip while the LLM keeps\n * streaming, so the buffered tokens would then arrive in one burst.\n */\n startPrefetch(prompt: string): void {\n if (!this.prefetchEnabled) return;\n this.prefetchPromise = this.prefetchProcess(prompt);\n }\n\n /**\n * 2-step chained flow: POST /process with the user prompt and\n * conversationId. Awaits the response (or timeout) but discards the\n * result. If it fails, logs and continues — the stream will proceed\n * in degraded mode.\n */\n async prefetchProcess(prompt: string): Promise<void> {\n if (!this.prefetchEnabled) return;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content: prompt,\n message_id: uuidv4(),\n conversation_id: this.conversationId,\n };\n if (this.location) body.location = this.location;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n\n await fetch(`${this.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch {\n // Prefetch failure is non-fatal — stream continues without pre-match context\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Opens a streaming POST to /stream, piping the upstream LLM response\n * body directly. The API reads the raw LLM stream in real time and\n * sends SSE ad events back on the response side.\n *\n * @param messageId Unique ID for this assistant message\n * @param body The upstream LLM response body (one branch of a tee)\n */\n connect(messageId: string, body: ReadableStream<Uint8Array>): void {\n this.streamEnded = false;\n this.inlineAdQueue = [];\n this.placementEndpoint = null;\n this.streamPromise = null;\n this.abortController = new AbortController();\n\n if (!SUPPORTS_REQUEST_STREAMS) {\n this.streamEnded = true;\n this.onError?.({\n type: \"adzen_error\",\n code: \"request_streams_unsupported\",\n message:\n \"This runtime cannot send a ReadableStream as a request body, so the \" +\n \"LLM stream cannot be proxied. Run AdzenStreamMiddleware server-side \" +\n \"(Node 18+) — browsers other than Chromium do not support fetch upload streams.\",\n });\n void body.cancel().catch(() => {});\n return;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/octet-stream\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n \"X-Message-Id\": messageId,\n \"X-Conversation-Id\": this.conversationId,\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n if (this.location) headers[\"X-Location\"] = this.location;\n\n this.streamPromise = this.executeStreamFetch(body, headers);\n }\n\n /**\n * Waits for the SSE response stream from the API to finish, along with any\n * prefetch still in flight. Neither can reject — both swallow their errors.\n */\n async finalize(): Promise<void> {\n await Promise.all([this.prefetchPromise, this.streamPromise]);\n }\n\n private async executeStreamFetch(\n body: ReadableStream<Uint8Array>,\n headers: Record<string, string>,\n ): Promise<void> {\n const timeout = setTimeout(\n () => this.abortController?.abort(),\n this.timeoutMs,\n );\n\n try {\n const res = await fetch(`${this.endpointUrl}/stream`, {\n method: \"POST\",\n headers,\n body,\n signal: this.abortController?.signal,\n // @ts-expect-error -- required for streaming request bodies (Node/undici)\n duplex: \"half\",\n });\n\n clearTimeout(timeout);\n\n if (!res.ok || !res.body) return;\n\n await this.readStream(res.body);\n } catch {\n clearTimeout(timeout);\n }\n }\n\n /** Drains and returns any queued inline ad events. */\n drainInlineAds(): InlineAdEvent[] {\n const ads = this.inlineAdQueue.splice(0);\n return ads;\n }\n\n /** Cancels pending requests and cleans up state. */\n abort(): void {\n this.abortController?.abort();\n this.stopPolling();\n this.streamEnded = true;\n }\n\n /** Polls the placement endpoint once. Called on an interval internally. */\n async pollPlacements(): Promise<void> {\n if (!this.sidecarEnabled || !this.placementEndpoint) return;\n\n try {\n const res = await fetch(this.placementEndpoint, {\n method: \"GET\",\n headers: { \"X-API-Key\": this.apiKey },\n });\n if (!res.ok) return;\n\n const data = (await res.json()) as PlacementsResponse;\n for (const placement of data.placements) {\n this.onPlacement?.(placement);\n }\n } catch {\n // Polling failure is non-fatal\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n try {\n for await (const frame of parseSSE(body)) {\n if (this.abortController?.signal.aborted) break;\n this.handleFrame(frame.event, frame.data);\n }\n } catch {\n // Stream read error — degrade gracefully\n } finally {\n this.streamEnded = true;\n this.stopPolling();\n }\n }\n\n private handleFrame(event: string | undefined, data: string): void {\n switch (event) {\n case \"adzen_stream_start\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamStartEvent, \"type\">;\n const evt: AdzenStreamStartEvent = { type: \"adzen_stream_start\", ...parsed };\n this.placementEndpoint = parsed.placement_endpoint;\n this.onStreamStart?.(evt);\n this.startPolling();\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_inline\": {\n try {\n const ad = JSON.parse(data) as InlineAdEvent;\n this.inlineAdQueue.push(ad);\n this.onInlineAd?.(ad);\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_error\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamErrorEvent, \"type\">;\n this.onError?.({ type: \"adzen_error\", ...parsed });\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_stream_end\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamEndEvent, \"type\">;\n const evt: AdzenStreamEndEvent = { type: \"adzen_stream_end\", ...parsed };\n this.onStreamEnd?.(evt);\n } catch { /* malformed — skip */ }\n this.streamEnded = true;\n this.stopPolling();\n break;\n }\n default: {\n if (event?.startsWith(\"adzen_debug_\")) break;\n break;\n }\n }\n }\n\n private startPolling(): void {\n if (!this.sidecarEnabled) return;\n if (this.pollTimer || !this.placementEndpoint) return;\n this.pollTimer = setInterval(\n () => this.pollPlacements(),\n this.placementPollIntervalMs,\n );\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n}\n","/**\n * Lightweight SSE parser that reads a ReadableStream<Uint8Array> and yields\n * parsed frames. Handles multi-line `data:` fields, named event types, and\n * ignores `id:` / `retry:` fields per the SSE spec.\n */\n\nexport interface SSEFrame {\n event?: string;\n data: string;\n}\n\nexport async function* parseSSE(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<SSEFrame> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent: string | undefined;\n let dataLines: string[] = [];\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop()!;\n\n for (const raw of lines) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n dataLines = [];\n currentEvent = undefined;\n }\n continue;\n }\n\n if (line.startsWith(\":\")) continue;\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n\n const field = line.slice(0, colonIdx);\n const value = line.slice(colonIdx + 1).replace(/^ /, \"\");\n\n switch (field) {\n case \"data\":\n dataLines.push(value);\n break;\n case \"event\":\n currentEvent = value;\n break;\n // id and retry are intentionally ignored\n }\n }\n }\n\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AdzenPlacement,\n InlineAdEvent,\n InlineAdEventPayload,\n PlacementRecord,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { AdzenStreamClient } from \"./AdzenStreamClient.js\";\nimport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n\nexport interface AdzenStreamMiddlewareConfig extends AdzenStreamConfig {\n adUnitPosition?: string;\n}\n\n/**\n * AG-UI middleware that proxies the LLM response stream through Adzen's\n * streaming endpoint for real-time ad enrichment.\n *\n * Uses a stream transformer pattern: `processStream()` takes an\n * AsyncIterable of AG-UI events and yields an enriched stream that\n * includes inline ad events and sidecar placements.\n *\n * Supports the 2-step chained flow: when `prefetch: true` and a `prompt`\n * is provided, fires POST /process before opening the stream connection.\n */\nexport class AdzenStreamMiddleware {\n private config: AdzenStreamMiddlewareConfig;\n private adUnitPosition: string;\n\n constructor(config: AdzenStreamMiddlewareConfig) {\n this.config = config;\n this.adUnitPosition = config.adUnitPosition ?? \"chin\";\n }\n\n async *processStream(\n events: AsyncIterable<Record<string, unknown>>,\n options?: { prompt?: string; upstreamBody?: ReadableStream<Uint8Array> },\n ): AsyncGenerator<Record<string, unknown>> {\n const sidecarPlacements: AdzenPlacement[] = [];\n let client: AdzenStreamClient | null = null;\n let currentMessageId = \"\";\n let contentOffset = 0;\n\n const onPlacement = (record: PlacementRecord) => {\n sidecarPlacements.push(this.mapPlacementRecord(record, currentMessageId));\n };\n\n const onError = (error: AdzenStreamErrorEvent) => {\n console.warn(`[adzen-stream] ${error.code}: ${error.message}`);\n };\n\n for await (const event of events) {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n\n client = new AdzenStreamClient({\n ...this.config,\n conversationId: threadId || this.config.conversationId,\n onPlacement,\n onError,\n });\n\n if (this.config.prefetch && options?.prompt) {\n client.startPrefetch(options.prompt);\n }\n\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n currentMessageId = String(event.messageId ?? \"\");\n contentOffset = 0;\n if (client && options?.upstreamBody) {\n client.connect(currentMessageId, options.upstreamBody);\n }\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n yield event;\n contentOffset += String(event.delta ?? \"\").length;\n\n if (client) {\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n break;\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n if (client) {\n await client.finalize();\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n yield event;\n break;\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n if (client) {\n await client.pollPlacements();\n client.abort();\n }\n\n for (const placement of sidecarPlacements) {\n yield this.makePlacementEvent(placement, placement.ad_id);\n }\n\n yield event;\n break;\n }\n\n default:\n yield event;\n }\n }\n }\n\n /**\n * Wraps an inline ad as an AG-UI custom event anchored to `contentOffset`,\n * the number of message characters emitted before the ad arrived. Clients\n * use it to render the ad directly after that content instead of at the\n * end of the message.\n */\n private makeInlineAdEvent(\n ad: InlineAdEvent,\n messageId: string,\n contentOffset: number,\n ): Record<string, unknown> {\n const value: InlineAdEventPayload = {\n ...ad,\n message_id: messageId,\n content_offset: contentOffset,\n };\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_inline_ad\",\n messageId,\n value,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n adId: string,\n ): Record<string, unknown> {\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_placement\",\n messageId: adId,\n value: {\n ...placement,\n message_id: placement.ad_id,\n },\n };\n }\n\n private mapPlacementRecord(\n record: PlacementRecord,\n messageId: string,\n ): AdzenPlacement {\n return {\n ad_id: String(record.ad_id),\n advertiser_name: \"\",\n headline: record.headline,\n cta_text: record.cta_text,\n destination_url: record.destination_url,\n adUnitPosition: record.placement || this.adUnitPosition,\n render_impression_url: record.tracking.impression_url,\n view_impression_url: record.tracking.impression_url,\n };\n }\n}\n"],"mappings":"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","names":["value"]}
1
+ {"version":3,"sources":["../../src/copilotkit/middleware.ts","../../src/copilotkit/AdzenStreamClient.ts","../../src/copilotkit/parseSSE.ts","../../src/copilotkit/streamMiddleware.ts"],"sourcesContent":["import type { AdzenPlacement, ProcessResponse, ProcessResponseAd } from \"@adzenai/core\";\n\nexport interface AdzenAsyncConfig {\n endpointUrl?: string;\n apiKey: string;\n timeoutMs?: number;\n adUnitPosition?: string;\n location?: string;\n conversationId?: string;\n profileId?: string;\n}\n\ninterface MessageBuffer {\n content: string;\n startedAt: number;\n fetched: boolean;\n}\n\nfunction generateIdempotencyKey(): string {\n const globalCrypto = (globalThis as { crypto?: { randomUUID?: () => string } })\n .crypto;\n if (globalCrypto?.randomUUID) return globalCrypto.randomUUID();\n return `adzen-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction isFinalOutputEvent(event: Record<string, unknown>): boolean {\n const type = String(event.type ?? \"\");\n if (type === \"RUN_FINISHED\" || type === \"RunFinished\") return false;\n if (type !== \"CUSTOM\" && type !== \"CUSTOM_EVENT\" && type !== \"CustomEvent\")\n return false;\n if (event.final === true || event.isFinal === true) return true;\n const name = String(event.name ?? event.event ?? \"\").toLowerCase();\n return name.includes(\"assistant_final\") || name.includes(\"final_output\");\n}\n\nfunction extractContentFromEvent(event: Record<string, unknown>): string | null {\n for (const key of [\"output\", \"result\", \"data\", \"payload\", \"message\"]) {\n const val = event[key];\n if (typeof val === \"string\" && val.length > 0) return val;\n if (typeof val === \"object\" && val !== null) {\n try {\n return JSON.stringify(val);\n } catch {\n continue;\n }\n }\n }\n return null;\n}\n\n/**\n * AG-UI middleware that intercepts the event stream and calls AI-API after\n * each assistant message, emitting `adzen_placement` custom events when an\n * ad is returned.\n *\n * All upstream events pass through immediately — zero latency is added to\n * the text stream. Ad fetches run as a side-effect and `RUN_FINISHED` is\n * held until all pending fetches settle.\n */\nexport class AdzenAsyncMiddleware {\n private config: Required<Pick<AdzenAsyncConfig, \"endpointUrl\" | \"timeoutMs\" | \"adUnitPosition\">> &\n AdzenAsyncConfig;\n private buffers = new Map<string, MessageBuffer>();\n private fetchedIds = new Set<string>();\n private pendingFetches: Promise<void>[] = [];\n private fallbackCounter = 0;\n private conversationId: string | undefined;\n\n constructor(config: AdzenAsyncConfig) {\n this.config = {\n ...config,\n endpointUrl: config.endpointUrl ?? \"https://api.adzen.ai/v1\",\n timeoutMs: config.timeoutMs ?? 3000,\n adUnitPosition: config.adUnitPosition ?? \"chin\",\n };\n this.conversationId = config.conversationId;\n }\n\n /**\n * Process a single AG-UI event. Returns an array of events to emit\n * downstream (always includes the original event, possibly followed\n * by an `adzen_placement` custom event).\n */\n async processEvent(event: Record<string, unknown>): Promise<Record<string, unknown>[]> {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n if (threadId) this.conversationId = threadId;\n return [event];\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n const messageId = this.resolveMessageId(event);\n this.evictStaleBuffers();\n this.buffers.set(messageId, {\n content: \"\",\n startedAt: Date.now(),\n fetched: false,\n });\n return [event];\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n if (buf) buf.content += String(event.delta ?? \"\");\n return [event];\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n const messageId = String(event.messageId ?? \"\");\n const buf = this.buffers.get(messageId);\n const out: Record<string, unknown>[] = [event];\n\n if (buf && !buf.fetched) {\n buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(buf.content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n this.buffers.delete(messageId);\n }\n\n return out;\n }\n\n case \"CUSTOM\":\n case \"CUSTOM_EVENT\":\n case \"CustomEvent\": {\n if (isFinalOutputEvent(event)) {\n const messageId = String(event.messageId ?? \"\");\n if (!this.fetchedIds.has(messageId)) {\n const buf = this.buffers.get(messageId);\n const content = extractContentFromEvent(event);\n if (content) {\n const out: Record<string, unknown>[] = [event];\n if (buf) buf.fetched = true;\n this.fetchedIds.add(messageId);\n const fetchPromise = this.fetchAd(content, messageId).then(\n (placement) => {\n if (placement) out.push(this.makePlacementEvent(placement, messageId));\n },\n );\n this.pendingFetches.push(fetchPromise);\n await fetchPromise;\n return out;\n }\n }\n }\n return [event];\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n await Promise.allSettled(this.pendingFetches);\n this.pendingFetches = [];\n return [event];\n }\n\n default:\n return [event];\n }\n }\n\n private resolveMessageId(event: Record<string, unknown>): string {\n const id = String(event.messageId ?? \"\");\n if (id) return id;\n return `__adzen_fallback_${Date.now()}_${this.fallbackCounter++}`;\n }\n\n private evictStaleBuffers(): void {\n const cutoff = Date.now() - this.config.timeoutMs * 2;\n for (const [key, buf] of this.buffers) {\n if (buf.startedAt < cutoff) this.buffers.delete(key);\n }\n }\n\n private async fetchAd(\n content: string,\n messageId: string,\n ): Promise<AdzenPlacement | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content,\n message_id: messageId,\n };\n if (this.config.location) body.location = this.config.location;\n if (this.conversationId) body.conversation_id = this.conversationId;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.config.apiKey,\n // AI-API requires an Idempotency-Key on /process (400 otherwise).\n // One per ad fetch — each completed message is a distinct request.\n \"Idempotency-Key\": generateIdempotencyKey(),\n };\n if (this.config.profileId) headers[\"X-Profile-Id\"] = this.config.profileId;\n\n const res = await fetch(`${this.config.endpointUrl}/process`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as ProcessResponse;\n const first = data.ads?.[0];\n if (!first) return null;\n return this.mapResponseToPlacement(first);\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private mapResponseToPlacement(ad: ProcessResponseAd): AdzenPlacement {\n return {\n ad_id: String(ad.ad_id),\n advertiser_name: ad.advertiser_name,\n advertiser_image_url: ad.sponsor_logo_url || undefined,\n headline: ad.title,\n description: ad.description,\n cta_text: ad.cta,\n destination_url: ad.click_through_url,\n creative_url: ad.creative_url ?? undefined,\n adUnitPosition: ad.placement ?? this.config.adUnitPosition,\n render_impression_url: ad.render_impression_url,\n view_impression_url: ad.view_impression_url,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n messageId: string,\n ): Record<string, unknown> {\n return {\n // AG-UI's event schema only accepts \"CUSTOM\"; @ag-ui/client zod-validates\n // every SSE frame and aborts the run on an unknown type. \"CUSTOM_EVENT\"\n // was rejected downstream.\n type: \"CUSTOM\",\n name: \"adzen_placement\",\n messageId,\n value: {\n ...placement,\n message_id: messageId,\n },\n };\n }\n}\n","import type {\n InlineAdEvent,\n PlacementRecord,\n PlacementsResponse,\n AdzenStreamStartEvent,\n AdzenStreamEndEvent,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { uuidv4 } from \"@adzenai/core\";\nimport { parseSSE } from \"./parseSSE.js\";\n\nexport interface AdzenStreamConfig {\n apiKey: string;\n endpointUrl?: string;\n timeoutMs?: number;\n location?: string;\n conversationId?: string;\n profileId?: string;\n placementPollIntervalMs?: number;\n prefetch?: boolean;\n /**\n * Whether to poll the sidecar placement endpoint for out-of-stream ads\n * rendered outside the message body. Defaults to `true`. Set to `false`\n * to run inline-only, leaving `adzen_inline` events unaffected.\n */\n sidecar?: boolean;\n /**\n * Extra request headers merged into every outbound Adzen request\n * (/stream, /process, placement polling). Intended for internal\n * debugging (e.g. `X-Adzen-Debug-*`). SDK-managed headers\n * (auth, content-type, correlation IDs) always take precedence.\n */\n headers?: Record<string, string>;\n onInlineAd?: (ad: InlineAdEvent) => void;\n onPlacement?: (placement: PlacementRecord) => void;\n onError?: (error: AdzenStreamErrorEvent) => void;\n onStreamStart?: (event: AdzenStreamStartEvent) => void;\n onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n}\n\nconst DEFAULT_ENDPOINT = \"https://api.adzen.ai/v1\";\nconst DEFAULT_TIMEOUT_MS = 3000;\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\n\n/**\n * Whether the current runtime can send a ReadableStream as a fetch request\n * body. Node 18+ (undici) supports this; Safari and Firefox do not, and\n * silently coerce the stream to the string \"[object ReadableStream]\" via the\n * WebIDL BodyInit union's USVString member.\n *\n * Detection works by checking whether the `duplex` getter is read and whether\n * the runtime set a Content-Type — a coerced string body gets `text/plain`.\n */\nconst SUPPORTS_REQUEST_STREAMS = (() => {\n try {\n let duplexAccessed = false;\n const hasContentType = new Request(\"http://localhost\", {\n method: \"POST\",\n body: new ReadableStream(),\n get duplex() {\n duplexAccessed = true;\n return \"half\";\n },\n } as RequestInit).headers.has(\"Content-Type\");\n return duplexAccessed && !hasContentType;\n } catch {\n return false;\n }\n})();\n\n/**\n * Framework-agnostic client for the Adzen streaming ad enrichment API.\n *\n * Handles connecting to `/v1/stream`, parsing SSE events, optional prefetch\n * via `/process`, and sidecar placement polling. Framework adapters (AG-UI\n * middleware, React hooks) wrap this class.\n */\nexport class AdzenStreamClient {\n private endpointUrl: string;\n private apiKey: string;\n private timeoutMs: number;\n private location?: string;\n private profileId?: string;\n private placementPollIntervalMs: number;\n private prefetchEnabled: boolean;\n private sidecarEnabled: boolean;\n private customHeaders: Record<string, string>;\n\n private conversationId: string;\n private abortController: AbortController | null = null;\n private placementEndpoint: string | null = null;\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private streamEnded = false;\n private inlineAdQueue: InlineAdEvent[] = [];\n private streamPromise: Promise<void> | null = null;\n private prefetchPromise: Promise<void> | null = null;\n\n private onInlineAd?: (ad: InlineAdEvent) => void;\n private onPlacement?: (placement: PlacementRecord) => void;\n private onError?: (error: AdzenStreamErrorEvent) => void;\n private onStreamStart?: (event: AdzenStreamStartEvent) => void;\n private onStreamEnd?: (event: AdzenStreamEndEvent) => void;\n\n constructor(config: AdzenStreamConfig) {\n this.endpointUrl = config.endpointUrl ?? DEFAULT_ENDPOINT;\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.location = config.location;\n this.profileId = config.profileId;\n this.placementPollIntervalMs =\n config.placementPollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.prefetchEnabled = config.prefetch ?? false;\n this.sidecarEnabled = config.sidecar ?? true;\n this.customHeaders = config.headers ?? {};\n this.conversationId = config.conversationId ?? uuidv4();\n\n this.onInlineAd = config.onInlineAd;\n this.onPlacement = config.onPlacement;\n this.onError = config.onError;\n this.onStreamStart = config.onStreamStart;\n this.onStreamEnd = config.onStreamEnd;\n }\n\n getConversationId(): string {\n return this.conversationId;\n }\n\n setConversationId(id: string): void {\n this.conversationId = id;\n }\n\n /**\n * Dispatches the prefetch without waiting for it.\n *\n * The request leaves before `connect()` opens `/stream`, which is all the\n * 2-step chained flow needs — the two calls are correlated server-side by\n * `conversation_id`. Awaiting the response here would stall the assistant\n * message behind a full ad-matching round trip while the LLM keeps\n * streaming, so the buffered tokens would then arrive in one burst.\n */\n startPrefetch(prompt: string): void {\n if (!this.prefetchEnabled) return;\n this.prefetchPromise = this.prefetchProcess(prompt);\n }\n\n /**\n * 2-step chained flow: POST /process with the user prompt and\n * conversationId. Awaits the response (or timeout) but discards the\n * result. If it fails, logs and continues — the stream will proceed\n * in degraded mode.\n */\n async prefetchProcess(prompt: string): Promise<void> {\n if (!this.prefetchEnabled) return;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const body: Record<string, unknown> = {\n content: prompt,\n message_id: uuidv4(),\n conversation_id: this.conversationId,\n };\n if (this.location) body.location = this.location;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n\n await fetch(`${this.endpointUrl}/process`, {\n method: \"POST\",\n headers: this.mergeHeaders(headers),\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } catch {\n // Prefetch failure is non-fatal — stream continues without pre-match context\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Opens a streaming POST to /stream, piping the upstream LLM response\n * body directly. The API reads the raw LLM stream in real time and\n * sends SSE ad events back on the response side.\n *\n * @param messageId Unique ID for this assistant message\n * @param body The upstream LLM response body (one branch of a tee)\n */\n connect(messageId: string, body: ReadableStream<Uint8Array>): void {\n this.streamEnded = false;\n this.inlineAdQueue = [];\n this.placementEndpoint = null;\n this.streamPromise = null;\n this.abortController = new AbortController();\n\n if (!SUPPORTS_REQUEST_STREAMS) {\n this.streamEnded = true;\n this.onError?.({\n type: \"adzen_error\",\n code: \"request_streams_unsupported\",\n message:\n \"This runtime cannot send a ReadableStream as a request body, so the \" +\n \"LLM stream cannot be proxied. Run AdzenStreamMiddleware server-side \" +\n \"(Node 18+) — browsers other than Chromium do not support fetch upload streams.\",\n });\n void body.cancel().catch(() => {});\n return;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/octet-stream\",\n \"X-API-Key\": this.apiKey,\n \"Idempotency-Key\": uuidv4(),\n \"X-Message-Id\": messageId,\n \"X-Conversation-Id\": this.conversationId,\n };\n if (this.profileId) headers[\"X-Profile-Id\"] = this.profileId;\n if (this.location) headers[\"X-Location\"] = this.location;\n\n this.streamPromise = this.executeStreamFetch(body, this.mergeHeaders(headers));\n }\n\n /**\n * Waits for the SSE response stream from the API to finish, along with any\n * prefetch still in flight. Neither can reject — both swallow their errors.\n */\n async finalize(): Promise<void> {\n await Promise.all([this.prefetchPromise, this.streamPromise]);\n }\n\n /**\n * Merges caller-provided passthrough headers with the SDK's fixed headers.\n * Fixed headers are spread last so SDK-managed keys (auth, content-type,\n * correlation IDs) always win over any passthrough value.\n */\n private mergeHeaders(\n fixed: Record<string, string>,\n ): Record<string, string> {\n return { ...this.customHeaders, ...fixed };\n }\n\n private async executeStreamFetch(\n body: ReadableStream<Uint8Array>,\n headers: Record<string, string>,\n ): Promise<void> {\n const timeout = setTimeout(\n () => this.abortController?.abort(),\n this.timeoutMs,\n );\n\n try {\n const res = await fetch(`${this.endpointUrl}/stream`, {\n method: \"POST\",\n headers,\n body,\n signal: this.abortController?.signal,\n // @ts-expect-error -- required for streaming request bodies (Node/undici)\n duplex: \"half\",\n });\n\n clearTimeout(timeout);\n\n if (!res.ok || !res.body) return;\n\n await this.readStream(res.body);\n } catch {\n clearTimeout(timeout);\n }\n }\n\n /** Drains and returns any queued inline ad events. */\n drainInlineAds(): InlineAdEvent[] {\n const ads = this.inlineAdQueue.splice(0);\n return ads;\n }\n\n /** Cancels pending requests and cleans up state. */\n abort(): void {\n this.abortController?.abort();\n this.stopPolling();\n this.streamEnded = true;\n }\n\n /** Polls the placement endpoint once. Called on an interval internally. */\n async pollPlacements(): Promise<void> {\n if (!this.sidecarEnabled || !this.placementEndpoint) return;\n\n try {\n const res = await fetch(this.placementEndpoint, {\n method: \"GET\",\n headers: this.mergeHeaders({ \"X-API-Key\": this.apiKey }),\n });\n if (!res.ok) return;\n\n const data = (await res.json()) as PlacementsResponse;\n for (const placement of data.placements) {\n this.onPlacement?.(placement);\n }\n } catch {\n // Polling failure is non-fatal\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n try {\n for await (const frame of parseSSE(body)) {\n if (this.abortController?.signal.aborted) break;\n this.handleFrame(frame.event, frame.data);\n }\n } catch {\n // Stream read error — degrade gracefully\n } finally {\n this.streamEnded = true;\n this.stopPolling();\n }\n }\n\n private handleFrame(event: string | undefined, data: string): void {\n switch (event) {\n case \"adzen_stream_start\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamStartEvent, \"type\">;\n const evt: AdzenStreamStartEvent = { type: \"adzen_stream_start\", ...parsed };\n this.placementEndpoint = parsed.placement_endpoint;\n this.onStreamStart?.(evt);\n this.startPolling();\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_inline\": {\n try {\n const ad = JSON.parse(data) as InlineAdEvent;\n this.inlineAdQueue.push(ad);\n this.onInlineAd?.(ad);\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_error\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamErrorEvent, \"type\">;\n this.onError?.({ type: \"adzen_error\", ...parsed });\n } catch { /* malformed — skip */ }\n break;\n }\n case \"adzen_stream_end\": {\n try {\n const parsed = JSON.parse(data) as Omit<AdzenStreamEndEvent, \"type\">;\n const evt: AdzenStreamEndEvent = { type: \"adzen_stream_end\", ...parsed };\n this.onStreamEnd?.(evt);\n } catch { /* malformed — skip */ }\n this.streamEnded = true;\n this.stopPolling();\n break;\n }\n default: {\n if (event?.startsWith(\"adzen_debug_\")) break;\n break;\n }\n }\n }\n\n private startPolling(): void {\n if (!this.sidecarEnabled) return;\n if (this.pollTimer || !this.placementEndpoint) return;\n this.pollTimer = setInterval(\n () => this.pollPlacements(),\n this.placementPollIntervalMs,\n );\n }\n\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n}\n","/**\n * Lightweight SSE parser that reads a ReadableStream<Uint8Array> and yields\n * parsed frames. Handles multi-line `data:` fields, named event types, and\n * ignores `id:` / `retry:` fields per the SSE spec.\n */\n\nexport interface SSEFrame {\n event?: string;\n data: string;\n}\n\nexport async function* parseSSE(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<SSEFrame> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent: string | undefined;\n let dataLines: string[] = [];\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop()!;\n\n for (const raw of lines) {\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n\n if (line === \"\") {\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n dataLines = [];\n currentEvent = undefined;\n }\n continue;\n }\n\n if (line.startsWith(\":\")) continue;\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n\n const field = line.slice(0, colonIdx);\n const value = line.slice(colonIdx + 1).replace(/^ /, \"\");\n\n switch (field) {\n case \"data\":\n dataLines.push(value);\n break;\n case \"event\":\n currentEvent = value;\n break;\n // id and retry are intentionally ignored\n }\n }\n }\n\n if (dataLines.length > 0) {\n yield { event: currentEvent, data: dataLines.join(\"\\n\") };\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type {\n AdzenPlacement,\n InlineAdEvent,\n InlineAdEventPayload,\n PlacementRecord,\n AdzenStreamErrorEvent,\n} from \"@adzenai/core\";\nimport { AdzenStreamClient } from \"./AdzenStreamClient.js\";\nimport type { AdzenStreamConfig } from \"./AdzenStreamClient.js\";\n\nexport interface AdzenStreamMiddlewareConfig extends AdzenStreamConfig {\n adUnitPosition?: string;\n}\n\n/**\n * AG-UI middleware that proxies the LLM response stream through Adzen's\n * streaming endpoint for real-time ad enrichment.\n *\n * Uses a stream transformer pattern: `processStream()` takes an\n * AsyncIterable of AG-UI events and yields an enriched stream that\n * includes inline ad events and sidecar placements.\n *\n * Supports the 2-step chained flow: when `prefetch: true` and a `prompt`\n * is provided, fires POST /process before opening the stream connection.\n */\nexport class AdzenStreamMiddleware {\n private config: AdzenStreamMiddlewareConfig;\n private adUnitPosition: string;\n\n constructor(config: AdzenStreamMiddlewareConfig) {\n this.config = config;\n this.adUnitPosition = config.adUnitPosition ?? \"chin\";\n }\n\n async *processStream(\n events: AsyncIterable<Record<string, unknown>>,\n options?: { prompt?: string; upstreamBody?: ReadableStream<Uint8Array> },\n ): AsyncGenerator<Record<string, unknown>> {\n const sidecarPlacements: AdzenPlacement[] = [];\n let client: AdzenStreamClient | null = null;\n let currentMessageId = \"\";\n let contentOffset = 0;\n\n const onPlacement = (record: PlacementRecord) => {\n sidecarPlacements.push(this.mapPlacementRecord(record, currentMessageId));\n };\n\n const onError = (error: AdzenStreamErrorEvent) => {\n console.warn(`[adzen-stream] ${error.code}: ${error.message}`);\n };\n\n for await (const event of events) {\n const type = String(event.type ?? \"\");\n\n switch (type) {\n case \"RUN_STARTED\":\n case \"RunStarted\": {\n const threadId = String(event.threadId ?? \"\");\n\n client = new AdzenStreamClient({\n ...this.config,\n conversationId: threadId || this.config.conversationId,\n onPlacement,\n onError,\n });\n\n if (this.config.prefetch && options?.prompt) {\n client.startPrefetch(options.prompt);\n }\n\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_START\":\n case \"TextMessageStart\": {\n currentMessageId = String(event.messageId ?? \"\");\n contentOffset = 0;\n if (client && options?.upstreamBody) {\n client.connect(currentMessageId, options.upstreamBody);\n }\n yield event;\n break;\n }\n\n case \"TEXT_MESSAGE_CONTENT\":\n case \"TextMessageContent\": {\n yield event;\n contentOffset += String(event.delta ?? \"\").length;\n\n if (client) {\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n break;\n }\n\n case \"TEXT_MESSAGE_END\":\n case \"TextMessageEnd\": {\n if (client) {\n await client.finalize();\n for (const ad of client.drainInlineAds()) {\n yield this.makeInlineAdEvent(ad, currentMessageId, contentOffset);\n }\n }\n yield event;\n break;\n }\n\n case \"RUN_FINISHED\":\n case \"RunFinished\": {\n if (client) {\n await client.pollPlacements();\n client.abort();\n }\n\n for (const placement of sidecarPlacements) {\n yield this.makePlacementEvent(placement, placement.ad_id);\n }\n\n yield event;\n break;\n }\n\n default:\n yield event;\n }\n }\n }\n\n /**\n * Wraps an inline ad as an AG-UI custom event anchored to `contentOffset`,\n * the number of message characters emitted before the ad arrived. Clients\n * use it to render the ad directly after that content instead of at the\n * end of the message.\n */\n private makeInlineAdEvent(\n ad: InlineAdEvent,\n messageId: string,\n contentOffset: number,\n ): Record<string, unknown> {\n const value: InlineAdEventPayload = {\n ...ad,\n message_id: messageId,\n content_offset: contentOffset,\n };\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_inline_ad\",\n messageId,\n value,\n };\n }\n\n private makePlacementEvent(\n placement: AdzenPlacement,\n adId: string,\n ): Record<string, unknown> {\n return {\n type: \"CUSTOM_EVENT\",\n name: \"adzen_placement\",\n messageId: adId,\n value: {\n ...placement,\n message_id: placement.ad_id,\n },\n };\n }\n\n private mapPlacementRecord(\n record: PlacementRecord,\n messageId: string,\n ): AdzenPlacement {\n return {\n ad_id: String(record.ad_id),\n advertiser_name: \"\",\n headline: record.headline,\n cta_text: record.cta_text,\n destination_url: record.destination_url,\n adUnitPosition: record.placement || this.adUnitPosition,\n render_impression_url: record.tracking.impression_url,\n view_impression_url: record.tracking.impression_url,\n };\n }\n}\n"],"mappings":"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","names":["value"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adzenai/ai",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "AI-powered ad integration for CopilotKit and AG-UI agents",
5
5
  "license": "MIT",
6
6
  "type": "module",