@assemble-dev/providers 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28,12 +28,16 @@ var ModelToolCallSchema = z.object({
28
28
  toolName: z.string(),
29
29
  input: JsonValueSchema
30
30
  });
31
+ var ChatMessageCacheControlSchema = z.object({
32
+ ttl: z.enum(["5m", "1h"]).optional()
33
+ });
31
34
  var ChatMessageSchema = z.object({
32
35
  role: ChatMessageRoleSchema,
33
36
  content: z.union([z.string(), z.array(MessageContentBlockSchema)]),
34
37
  toolName: z.string().optional(),
35
38
  toolCallId: z.string().optional(),
36
- toolCalls: z.array(ModelToolCallSchema).optional()
39
+ toolCalls: z.array(ModelToolCallSchema).optional(),
40
+ cacheControl: ChatMessageCacheControlSchema.optional()
37
41
  });
38
42
  var ChatStopReasonSchema = z.enum([
39
43
  "end_turn",
@@ -267,6 +271,15 @@ async function executeAbortableProviderFetch(providerLabel, performFetch) {
267
271
  }
268
272
  }
269
273
 
274
+ // src/provider-extra-body.ts
275
+ function mergeProviderRequestExtraBody(requestBody, adapterExtraBody, requestExtraBody) {
276
+ return {
277
+ ...requestBody,
278
+ ...adapterExtraBody ?? {},
279
+ ...requestExtraBody ?? {}
280
+ };
281
+ }
282
+
270
283
  // src/provider-fallback.ts
271
284
  import { AppError as AppError6 } from "@assemble-dev/shared-utils/errors";
272
285
 
@@ -434,7 +447,8 @@ function openai(options) {
434
447
  requireJsonObjectResponse: false,
435
448
  tools: request.tools,
436
449
  toolChoice: request.toolChoice,
437
- abortSignal: request.abortSignal
450
+ abortSignal: request.abortSignal,
451
+ extraBody: request.extraBody
438
452
  });
439
453
  },
440
454
  async generateStructuredOutput(request) {
@@ -476,7 +490,11 @@ async function requestOpenAiChatCompletion(options, input) {
476
490
  });
477
491
  const fetchImplementation = options.fetchImplementation ?? fetch;
478
492
  const baseUrl = options.baseURL ?? defaultOpenAiBaseUrl;
479
- const requestBody = buildOpenAiRequestBody(options.model, input);
493
+ const requestBody = mergeProviderRequestExtraBody(
494
+ buildOpenAiRequestBody(options.model, input),
495
+ options.extraBody,
496
+ input.extraBody
497
+ );
480
498
  const startedAt = Date.now();
481
499
  const response = await executeAbortableProviderFetch(
482
500
  "OpenAI",
@@ -572,6 +590,36 @@ function mapOpenAiUsageToTokenUsage(response) {
572
590
  };
573
591
  }
574
592
 
593
+ // src/anthropic-cache-control.ts
594
+ function resolveAnthropicPromptCachingSettings(promptCaching) {
595
+ if (promptCaching === void 0) {
596
+ return { mode: "off" };
597
+ }
598
+ if (typeof promptCaching === "string") {
599
+ return { mode: promptCaching };
600
+ }
601
+ return promptCaching;
602
+ }
603
+ function buildAnthropicCacheControl(ttl) {
604
+ if (ttl === void 0) {
605
+ return { type: "ephemeral" };
606
+ }
607
+ return { type: "ephemeral", ttl };
608
+ }
609
+ function applyCacheControlToLastContentBlock(requestMessage, cacheControl) {
610
+ if (typeof requestMessage.content === "string") {
611
+ if (requestMessage.content.length === 0) {
612
+ return;
613
+ }
614
+ requestMessage.content = [{ type: "text", text: requestMessage.content }];
615
+ }
616
+ const lastContentBlock = requestMessage.content[requestMessage.content.length - 1];
617
+ if (lastContentBlock === void 0) {
618
+ return;
619
+ }
620
+ lastContentBlock.cache_control = cacheControl;
621
+ }
622
+
575
623
  // src/anthropic-request-mapping.ts
576
624
  function buildAnthropicRequestBody(settings, input) {
577
625
  const requestBody = {
@@ -581,6 +629,7 @@ function buildAnthropicRequestBody(settings, input) {
581
629
  };
582
630
  applyAnthropicSystemField(requestBody, settings, input);
583
631
  applyAnthropicToolFields(requestBody, settings, input);
632
+ applyAnthropicMessageCacheBreakpoints(requestBody, settings, input);
584
633
  if (input.temperature !== void 0) {
585
634
  requestBody.temperature = input.temperature;
586
635
  }
@@ -593,18 +642,46 @@ function buildAnthropicRequestBody(settings, input) {
593
642
  return requestBody;
594
643
  }
595
644
  function applyAnthropicSystemField(requestBody, settings, input) {
645
+ const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
596
646
  const systemText = joinSystemMessageText(input.messages);
597
647
  if (systemText.length === 0) {
598
648
  return;
599
649
  }
600
- if (settings.promptCaching !== "auto") {
650
+ if (caching.mode === "off") {
601
651
  requestBody.system = systemText;
602
652
  return;
603
653
  }
604
654
  requestBody.system = [
605
- { type: "text", text: systemText, cache_control: { type: "ephemeral" } }
655
+ {
656
+ type: "text",
657
+ text: systemText,
658
+ cache_control: buildAnthropicCacheControl(caching.ttl)
659
+ }
606
660
  ];
607
661
  }
662
+ function applyAnthropicMessageCacheBreakpoints(requestBody, settings, input) {
663
+ const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
664
+ const finalRequestMessage = requestBody.messages[requestBody.messages.length - 1];
665
+ if (caching.mode === "messages" && finalRequestMessage !== void 0) {
666
+ applyCacheControlToLastContentBlock(
667
+ finalRequestMessage,
668
+ buildAnthropicCacheControl(caching.ttl)
669
+ );
670
+ }
671
+ const nonSystemMessages = input.messages.filter(
672
+ (message) => message.role !== "system"
673
+ );
674
+ nonSystemMessages.forEach((message, messageIndex) => {
675
+ const requestMessage = requestBody.messages[messageIndex];
676
+ if (message.cacheControl === void 0 || requestMessage === void 0) {
677
+ return;
678
+ }
679
+ applyCacheControlToLastContentBlock(
680
+ requestMessage,
681
+ buildAnthropicCacheControl(message.cacheControl.ttl ?? caching.ttl)
682
+ );
683
+ });
684
+ }
608
685
  function applyAnthropicToolFields(requestBody, settings, input) {
609
686
  if (input.tools !== void 0 && input.tools.length > 0) {
610
687
  requestBody.tools = mapModelToolDefinitionsToAnthropicTools(
@@ -619,14 +696,15 @@ function applyAnthropicToolFields(requestBody, settings, input) {
619
696
  }
620
697
  }
621
698
  function mapModelToolDefinitionsToAnthropicTools(tools, promptCaching) {
699
+ const caching = resolveAnthropicPromptCachingSettings(promptCaching);
622
700
  return tools.map((tool, toolIndex) => {
623
701
  const anthropicTool = {
624
702
  name: tool.name,
625
703
  description: tool.description,
626
704
  input_schema: tool.inputJsonSchema
627
705
  };
628
- if (promptCaching === "auto" && toolIndex === tools.length - 1) {
629
- anthropicTool.cache_control = { type: "ephemeral" };
706
+ if (caching.mode !== "off" && toolIndex === tools.length - 1) {
707
+ anthropicTool.cache_control = buildAnthropicCacheControl(caching.ttl);
630
708
  }
631
709
  return anthropicTool;
632
710
  });
@@ -1131,7 +1209,8 @@ function buildAnthropicCallInput(options, request) {
1131
1209
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1132
1210
  tools: request.tools,
1133
1211
  toolChoice: request.toolChoice,
1134
- abortSignal: request.abortSignal
1212
+ abortSignal: request.abortSignal,
1213
+ extraBody: request.extraBody
1135
1214
  };
1136
1215
  }
1137
1216
  async function executeAnthropicAdapterCall(options, input) {
@@ -1148,9 +1227,10 @@ async function executeAnthropicAdapterCall(options, input) {
1148
1227
  );
1149
1228
  }
1150
1229
  async function requestAnthropicMessages(options, input) {
1151
- const requestBody = buildAnthropicRequestBody(
1152
- buildAnthropicRequestSettings(options),
1153
- input
1230
+ const requestBody = mergeProviderRequestExtraBody(
1231
+ buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
1232
+ options.extraBody,
1233
+ input.extraBody
1154
1234
  );
1155
1235
  const startedAt = Date.now();
1156
1236
  const response = await executeAbortableProviderFetch(
@@ -1179,9 +1259,10 @@ async function requestAnthropicMessages(options, input) {
1179
1259
  }
1180
1260
  function buildAnthropicStreamInput(options, request) {
1181
1261
  const input = buildAnthropicCallInput(options, request);
1182
- const requestBody = buildAnthropicRequestBody(
1183
- buildAnthropicRequestSettings(options),
1184
- input
1262
+ const requestBody = mergeProviderRequestExtraBody(
1263
+ buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
1264
+ options.extraBody,
1265
+ input.extraBody
1185
1266
  );
1186
1267
  return {
1187
1268
  url: `${resolveAnthropicBaseUrl(options)}/v1/messages`,
@@ -1253,7 +1334,8 @@ function gemini(options) {
1253
1334
  temperature: request.temperature ?? options.temperature,
1254
1335
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1255
1336
  requireJsonResponse: false,
1256
- abortSignal: request.abortSignal
1337
+ abortSignal: request.abortSignal,
1338
+ extraBody: request.extraBody
1257
1339
  });
1258
1340
  },
1259
1341
  async generateStructuredOutput(request) {
@@ -1295,7 +1377,11 @@ async function requestGeminiGenerateContent(options, input) {
1295
1377
  });
1296
1378
  const fetchImplementation = options.fetchImplementation ?? fetch;
1297
1379
  const baseUrl = options.baseURL ?? defaultGeminiBaseUrl;
1298
- const requestBody = buildGeminiRequestBody(input);
1380
+ const requestBody = mergeProviderRequestExtraBody(
1381
+ buildGeminiRequestBody(input),
1382
+ options.extraBody,
1383
+ input.extraBody
1384
+ );
1299
1385
  const requestUrl = `${baseUrl}/v1beta/models/${options.model}:generateContent`;
1300
1386
  const startedAt = Date.now();
1301
1387
  const response = await executeAbortableProviderFetch(
@@ -1583,9 +1669,212 @@ function calculateModelCallCostUsd(pricing, tokenUsage) {
1583
1669
  }
1584
1670
 
1585
1671
  // src/anthropic-batch.ts
1672
+ import { AppErrorCode as AppErrorCode13 } from "@assemble-dev/shared-types/errors";
1673
+ import { JsonValueSchema as JsonValueSchema7 } from "@assemble-dev/shared-types/json";
1674
+ import { AppError as AppError14 } from "@assemble-dev/shared-utils/errors";
1675
+
1676
+ // src/anthropic-batch-structured.ts
1586
1677
  import { AppErrorCode as AppErrorCode12 } from "@assemble-dev/shared-types/errors";
1587
1678
  import { JsonValueSchema as JsonValueSchema6 } from "@assemble-dev/shared-types/json";
1588
1679
  import { AppError as AppError13 } from "@assemble-dev/shared-utils/errors";
1680
+ var anthropicBatchJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
1681
+ function parseAnthropicBatchStructuredContent(content, schema) {
1682
+ const jsonValue = parseStructuredJsonText(
1683
+ content,
1684
+ JsonValueSchema6
1685
+ );
1686
+ const validationResult = schema.safeParse(jsonValue);
1687
+ if (!validationResult.success) {
1688
+ throw new AppError13({
1689
+ code: AppErrorCode12.ValidationFailed,
1690
+ message: "Anthropic batch structured output did not match the expected schema",
1691
+ statusCode: 422,
1692
+ details: { validationErrorMessage: validationResult.error.message }
1693
+ });
1694
+ }
1695
+ return validationResult.data;
1696
+ }
1697
+
1698
+ // src/anthropic-batch-request.ts
1699
+ function mapBatchMessageRequestToApiBatchRequest(request) {
1700
+ const params = {
1701
+ model: request.model,
1702
+ max_tokens: request.maxOutputTokens ?? defaultAnthropicMaxOutputTokens,
1703
+ messages: request.messages.filter((message) => message.role !== "system").map(mapChatMessageToBatchApiRequestMessage)
1704
+ };
1705
+ applyBatchSystemField(params, request);
1706
+ if (request.temperature !== void 0) {
1707
+ params.temperature = request.temperature;
1708
+ }
1709
+ return { custom_id: request.customId, params };
1710
+ }
1711
+ function applyBatchSystemField(params, request) {
1712
+ const systemText = buildBatchSystemText(request);
1713
+ if (systemText.length === 0) {
1714
+ return;
1715
+ }
1716
+ if (request.promptCaching !== "auto") {
1717
+ params.system = systemText;
1718
+ return;
1719
+ }
1720
+ params.system = [
1721
+ { type: "text", text: systemText, cache_control: { type: "ephemeral" } }
1722
+ ];
1723
+ }
1724
+ function buildBatchSystemText(request) {
1725
+ const joinedSystemText = joinBatchSystemMessageText(request.messages);
1726
+ if (request.requireJsonResponse !== true) {
1727
+ return joinedSystemText;
1728
+ }
1729
+ if (joinedSystemText.length === 0) {
1730
+ return anthropicBatchJsonOnlySystemInstruction;
1731
+ }
1732
+ return `${anthropicBatchJsonOnlySystemInstruction}
1733
+
1734
+ ${joinedSystemText}`;
1735
+ }
1736
+ function joinBatchSystemMessageText(messages) {
1737
+ return messages.filter((message) => message.role === "system").map(extractMessageText).join("\n\n");
1738
+ }
1739
+ function mapChatMessageToBatchApiRequestMessage(message) {
1740
+ if (message.role === "assistant") {
1741
+ return { role: "assistant", content: extractMessageText(message) };
1742
+ }
1743
+ if (message.role === "tool") {
1744
+ return { role: "user", content: buildBatchToolResultText(message) };
1745
+ }
1746
+ return { role: "user", content: extractMessageText(message) };
1747
+ }
1748
+ function buildBatchToolResultText(message) {
1749
+ if (message.toolName === void 0) {
1750
+ return `Tool result: ${extractMessageText(message)}`;
1751
+ }
1752
+ return `Tool result (${message.toolName}): ${extractMessageText(message)}`;
1753
+ }
1754
+
1755
+ // src/anthropic-batch-trace-events.ts
1756
+ import {
1757
+ generateRunId,
1758
+ generateTraceEventId
1759
+ } from "@assemble-dev/shared-utils/ids";
1760
+ var anthropicBatchTraceWorkflowName = "anthropic-message-batch";
1761
+ function createAnthropicBatchTraceEmitter(input) {
1762
+ if (input.onTraceEvent === void 0) {
1763
+ return createNoopAnthropicBatchTraceEmitter();
1764
+ }
1765
+ const onTraceEvent = input.onTraceEvent;
1766
+ const runId = generateRunId();
1767
+ const completedBatchIds = /* @__PURE__ */ new Set();
1768
+ const submittedEventIdByBatchId = /* @__PURE__ */ new Map();
1769
+ const modelByBatchScopedCustomId = /* @__PURE__ */ new Map();
1770
+ function buildBatchTraceEventBase(parentEventId) {
1771
+ return {
1772
+ id: generateTraceEventId(),
1773
+ runId,
1774
+ workflowName: anthropicBatchTraceWorkflowName,
1775
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1776
+ parentEventId,
1777
+ redacted: false
1778
+ };
1779
+ }
1780
+ function stampMetadataOntoEvent(event) {
1781
+ if (input.metadata === void 0) {
1782
+ return event;
1783
+ }
1784
+ return { ...event, metadata: input.metadata };
1785
+ }
1786
+ function emitBatchSubmittedEvent(batch, requestSummaries) {
1787
+ const submittedEvent = {
1788
+ ...buildBatchTraceEventBase(null),
1789
+ type: "batch.submitted",
1790
+ batchId: batch.id,
1791
+ requestCount: requestSummaries.length,
1792
+ models: requestSummaries.map((requestSummary) => requestSummary.model)
1793
+ };
1794
+ submittedEventIdByBatchId.set(batch.id, submittedEvent.id);
1795
+ for (const requestSummary of requestSummaries) {
1796
+ modelByBatchScopedCustomId.set(
1797
+ buildBatchScopedCustomIdKey(batch.id, requestSummary.customId),
1798
+ requestSummary.model
1799
+ );
1800
+ }
1801
+ onTraceEvent(stampMetadataOntoEvent(submittedEvent));
1802
+ }
1803
+ function emitBatchCompletedEventOnce(batch) {
1804
+ if (batch.processingStatus !== "ended" || completedBatchIds.has(batch.id)) {
1805
+ return;
1806
+ }
1807
+ completedBatchIds.add(batch.id);
1808
+ const completedEvent = {
1809
+ ...buildBatchTraceEventBase(
1810
+ submittedEventIdByBatchId.get(batch.id) ?? null
1811
+ ),
1812
+ type: "batch.completed",
1813
+ batchId: batch.id,
1814
+ requestCounts: batch.requestCounts,
1815
+ latencyMs: calculateBatchLatencyMs(batch)
1816
+ };
1817
+ onTraceEvent(stampMetadataOntoEvent(completedEvent));
1818
+ }
1819
+ function emitBatchResultEvent(batchId, result) {
1820
+ const resultEvent = buildBatchResultEvent(
1821
+ buildBatchTraceEventBase(submittedEventIdByBatchId.get(batchId) ?? null),
1822
+ batchId,
1823
+ result,
1824
+ modelByBatchScopedCustomId.get(
1825
+ buildBatchScopedCustomIdKey(batchId, result.customId)
1826
+ )
1827
+ );
1828
+ onTraceEvent(stampMetadataOntoEvent(resultEvent));
1829
+ }
1830
+ return {
1831
+ emitBatchSubmittedEvent,
1832
+ emitBatchCompletedEventOnce,
1833
+ emitBatchResultEvent
1834
+ };
1835
+ }
1836
+ function createNoopAnthropicBatchTraceEmitter() {
1837
+ return {
1838
+ emitBatchSubmittedEvent() {
1839
+ },
1840
+ emitBatchCompletedEventOnce() {
1841
+ },
1842
+ emitBatchResultEvent() {
1843
+ }
1844
+ };
1845
+ }
1846
+ function buildBatchScopedCustomIdKey(batchId, customId) {
1847
+ return `${batchId}:${customId}`;
1848
+ }
1849
+ function calculateBatchLatencyMs(batch) {
1850
+ const createdAtMs = Date.parse(batch.createdAt);
1851
+ const endedAtMs = batch.endedAt === null ? Date.now() : Date.parse(batch.endedAt);
1852
+ if (Number.isNaN(createdAtMs) || Number.isNaN(endedAtMs)) {
1853
+ return 0;
1854
+ }
1855
+ return Math.max(0, endedAtMs - createdAtMs);
1856
+ }
1857
+ function buildBatchResultEvent(eventBase, batchId, result, modelName) {
1858
+ if (result.outcome.type !== "succeeded") {
1859
+ return {
1860
+ ...eventBase,
1861
+ type: "batch.result",
1862
+ batchId,
1863
+ customId: result.customId,
1864
+ outcomeType: result.outcome.type,
1865
+ costUsd: null
1866
+ };
1867
+ }
1868
+ return {
1869
+ ...eventBase,
1870
+ type: "batch.result",
1871
+ batchId,
1872
+ customId: result.customId,
1873
+ outcomeType: "succeeded",
1874
+ tokenUsage: result.outcome.tokenUsage,
1875
+ costUsd: modelName === void 0 ? null : estimateModelCallCostUsd(modelName, result.outcome.tokenUsage)
1876
+ };
1877
+ }
1589
1878
 
1590
1879
  // src/anthropic-batch-types.ts
1591
1880
  import { z as z7 } from "zod";
@@ -1690,19 +1979,26 @@ var defaultAnthropicBatchPollIntervalMs = 5e3;
1690
1979
  var defaultAnthropicBatchTimeoutMs = 36e5;
1691
1980
  var anthropicBatchApiVersion = "2023-06-01";
1692
1981
  function createAnthropicBatchClient(options) {
1982
+ const clientContext = {
1983
+ options,
1984
+ traceEmitter: createAnthropicBatchTraceEmitter({
1985
+ onTraceEvent: options.onTraceEvent,
1986
+ metadata: options.metadata
1987
+ })
1988
+ };
1693
1989
  return {
1694
1990
  async submitMessageBatch(requests) {
1695
- return await submitAnthropicMessageBatch(options, requests);
1991
+ return await submitAnthropicMessageBatch(clientContext, requests);
1696
1992
  },
1697
1993
  async fetchMessageBatch(batchId) {
1698
- return await fetchAnthropicMessageBatch(options, batchId);
1994
+ return await fetchAnthropicMessageBatch(clientContext, batchId);
1699
1995
  },
1700
1996
  async listMessageBatchResults(batchId) {
1701
- return await listAnthropicMessageBatchResults(options, batchId);
1997
+ return await listAnthropicMessageBatchResults(clientContext, batchId);
1702
1998
  },
1703
1999
  async waitForMessageBatchCompletion(batchId, waitOptions) {
1704
2000
  return await waitForAnthropicMessageBatchCompletion(
1705
- options,
2001
+ clientContext,
1706
2002
  batchId,
1707
2003
  waitOptions
1708
2004
  );
@@ -1729,8 +2025,8 @@ function buildAnthropicBatchRequestHeaders(apiKey) {
1729
2025
  "anthropic-version": anthropicBatchApiVersion
1730
2026
  };
1731
2027
  }
1732
- async function submitAnthropicMessageBatch(options, requests) {
1733
- const context = resolveAnthropicBatchRequestContext(options);
2028
+ async function submitAnthropicMessageBatch(clientContext, requests) {
2029
+ const context = resolveAnthropicBatchRequestContext(clientContext.options);
1734
2030
  const response = await context.fetchImplementation(
1735
2031
  `${context.baseUrl}/v1/messages/batches`,
1736
2032
  {
@@ -1741,10 +2037,18 @@ async function submitAnthropicMessageBatch(options, requests) {
1741
2037
  })
1742
2038
  }
1743
2039
  );
1744
- return await parseAnthropicMessageBatchHttpResponse(response);
2040
+ const batch = await parseAnthropicMessageBatchHttpResponse(response);
2041
+ clientContext.traceEmitter.emitBatchSubmittedEvent(
2042
+ batch,
2043
+ requests.map((request) => ({
2044
+ customId: request.customId,
2045
+ model: request.model
2046
+ }))
2047
+ );
2048
+ return batch;
1745
2049
  }
1746
- async function fetchAnthropicMessageBatch(options, batchId) {
1747
- const context = resolveAnthropicBatchRequestContext(options);
2050
+ async function fetchAnthropicMessageBatch(clientContext, batchId) {
2051
+ const context = resolveAnthropicBatchRequestContext(clientContext.options);
1748
2052
  const response = await context.fetchImplementation(
1749
2053
  `${context.baseUrl}/v1/messages/batches/${batchId}`,
1750
2054
  {
@@ -1752,7 +2056,9 @@ async function fetchAnthropicMessageBatch(options, batchId) {
1752
2056
  headers: buildAnthropicBatchRequestHeaders(context.apiKey)
1753
2057
  }
1754
2058
  );
1755
- return await parseAnthropicMessageBatchHttpResponse(response);
2059
+ const batch = await parseAnthropicMessageBatchHttpResponse(response);
2060
+ clientContext.traceEmitter.emitBatchCompletedEventOnce(batch);
2061
+ return batch;
1756
2062
  }
1757
2063
  async function parseAnthropicMessageBatchHttpResponse(response) {
1758
2064
  if (!response.ok) {
@@ -1766,17 +2072,17 @@ async function parseAnthropicMessageBatchHttpResponse(response) {
1766
2072
  }
1767
2073
  return mapAnthropicMessageBatchResponseToMessageBatch(parseResult.data);
1768
2074
  }
1769
- async function listAnthropicMessageBatchResults(options, batchId) {
1770
- const batch = await fetchAnthropicMessageBatch(options, batchId);
2075
+ async function listAnthropicMessageBatchResults(clientContext, batchId) {
2076
+ const batch = await fetchAnthropicMessageBatch(clientContext, batchId);
1771
2077
  if (batch.resultsUrl === null) {
1772
- throw new AppError13({
1773
- code: AppErrorCode12.Conflict,
2078
+ throw new AppError14({
2079
+ code: AppErrorCode13.Conflict,
1774
2080
  message: `Anthropic message batch ${batchId} has no results because processing has not ended`,
1775
2081
  statusCode: 409,
1776
2082
  details: { batchId, processingStatus: batch.processingStatus }
1777
2083
  });
1778
2084
  }
1779
- const context = resolveAnthropicBatchRequestContext(options);
2085
+ const context = resolveAnthropicBatchRequestContext(clientContext.options);
1780
2086
  const response = await context.fetchImplementation(batch.resultsUrl, {
1781
2087
  method: "GET",
1782
2088
  headers: buildAnthropicBatchRequestHeaders(context.apiKey)
@@ -1784,7 +2090,11 @@ async function listAnthropicMessageBatchResults(options, batchId) {
1784
2090
  if (!response.ok) {
1785
2091
  throw buildProviderStatusError("Anthropic", response.status);
1786
2092
  }
1787
- return parseAnthropicBatchResultsJsonl(await response.text());
2093
+ const results = parseAnthropicBatchResultsJsonl(await response.text());
2094
+ for (const result of results) {
2095
+ clientContext.traceEmitter.emitBatchResultEvent(batchId, result);
2096
+ }
2097
+ return results;
1788
2098
  }
1789
2099
  function parseAnthropicBatchResultsJsonl(jsonlText) {
1790
2100
  return jsonlText.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map(parseAnthropicBatchResultLine);
@@ -1803,7 +2113,7 @@ function parseAnthropicBatchResultLine(line) {
1803
2113
  }
1804
2114
  function parseAnthropicBatchResultLineJson(line) {
1805
2115
  try {
1806
- return JsonValueSchema6.parse(JSON.parse(line));
2116
+ return JsonValueSchema7.parse(JSON.parse(line));
1807
2117
  } catch {
1808
2118
  throw buildProviderInvalidResponseError(
1809
2119
  "Anthropic",
@@ -1811,71 +2121,43 @@ function parseAnthropicBatchResultLineJson(line) {
1811
2121
  );
1812
2122
  }
1813
2123
  }
1814
- async function waitForAnthropicMessageBatchCompletion(options, batchId, waitOptions) {
2124
+ async function waitForAnthropicMessageBatchCompletion(clientContext, batchId, waitOptions) {
1815
2125
  const pollIntervalMs = waitOptions?.pollIntervalMs ?? defaultAnthropicBatchPollIntervalMs;
1816
2126
  const timeoutMs = waitOptions?.timeoutMs ?? defaultAnthropicBatchTimeoutMs;
1817
2127
  const waitFor = waitOptions?.waitFor ?? waitForMilliseconds;
1818
2128
  const startedAt = Date.now();
1819
- let batch = await fetchAnthropicMessageBatch(options, batchId);
2129
+ let batch = await fetchAnthropicMessageBatch(clientContext, batchId);
1820
2130
  while (batch.processingStatus !== "ended") {
1821
2131
  if (Date.now() - startedAt >= timeoutMs) {
1822
2132
  throw buildAnthropicBatchTimeoutError(batchId, timeoutMs);
1823
2133
  }
1824
2134
  await waitFor(pollIntervalMs);
1825
- batch = await fetchAnthropicMessageBatch(options, batchId);
2135
+ batch = await fetchAnthropicMessageBatch(clientContext, batchId);
1826
2136
  }
1827
2137
  return batch;
1828
2138
  }
1829
2139
  function buildAnthropicBatchTimeoutError(batchId, timeoutMs) {
1830
- return new AppError13({
1831
- code: AppErrorCode12.ServiceUnavailable,
2140
+ return new AppError14({
2141
+ code: AppErrorCode13.ServiceUnavailable,
1832
2142
  message: `Anthropic message batch ${batchId} did not complete within ${String(timeoutMs)}ms`,
1833
2143
  statusCode: 504,
1834
2144
  details: { batchId, timeoutMs }
1835
2145
  });
1836
2146
  }
1837
- function mapBatchMessageRequestToApiBatchRequest(request) {
1838
- const params = {
1839
- model: request.model,
1840
- max_tokens: request.maxOutputTokens ?? defaultAnthropicMaxOutputTokens,
1841
- messages: request.messages.filter((message) => message.role !== "system").map(mapChatMessageToBatchApiRequestMessage)
1842
- };
1843
- const systemText = joinBatchSystemMessageText(request.messages);
1844
- if (systemText.length > 0) {
1845
- params.system = systemText;
1846
- }
1847
- if (request.temperature !== void 0) {
1848
- params.temperature = request.temperature;
1849
- }
1850
- return { custom_id: request.customId, params };
1851
- }
1852
- function joinBatchSystemMessageText(messages) {
1853
- return messages.filter((message) => message.role === "system").map(extractMessageText).join("\n\n");
1854
- }
1855
- function mapChatMessageToBatchApiRequestMessage(message) {
1856
- if (message.role === "assistant") {
1857
- return { role: "assistant", content: extractMessageText(message) };
1858
- }
1859
- if (message.role === "tool") {
1860
- return { role: "user", content: buildBatchToolResultText(message) };
1861
- }
1862
- return { role: "user", content: extractMessageText(message) };
1863
- }
1864
- function buildBatchToolResultText(message) {
1865
- if (message.toolName === void 0) {
1866
- return `Tool result: ${extractMessageText(message)}`;
1867
- }
1868
- return `Tool result (${message.toolName}): ${extractMessageText(message)}`;
1869
- }
1870
2147
  export {
1871
2148
  AnthropicMessagesResponseSchema,
1872
2149
  AnthropicStreamEventSchema,
2150
+ ChatMessageCacheControlSchema,
1873
2151
  ChatMessageRoleSchema,
1874
2152
  ChatMessageSchema,
1875
2153
  ChatStopReasonSchema,
1876
2154
  MessageContentBlockSchema,
1877
2155
  ModelToolCallSchema,
1878
2156
  anthropic,
2157
+ anthropicBatchJsonOnlySystemInstruction,
2158
+ anthropicBatchTraceWorkflowName,
2159
+ applyCacheControlToLastContentBlock,
2160
+ buildAnthropicCacheControl,
1879
2161
  buildAnthropicRequestBody,
1880
2162
  buildOpenAiRequestBody,
1881
2163
  buildProviderCanceledError,
@@ -1909,11 +2191,14 @@ export {
1909
2191
  mapModelToolChoiceToOpenAiToolChoice,
1910
2192
  mapModelToolDefinitionToOpenAiTool,
1911
2193
  mapModelToolDefinitionsToAnthropicTools,
2194
+ mergeProviderRequestExtraBody,
1912
2195
  mockModel,
1913
2196
  normalizeAnthropicStopReason,
1914
2197
  openai,
2198
+ parseAnthropicBatchStructuredContent,
1915
2199
  parseAnthropicStreamEvents,
1916
2200
  parseAnthropicToolInputJson,
2201
+ resolveAnthropicPromptCachingSettings,
1917
2202
  resolveProviderApiKey,
1918
2203
  streamAnthropicChatCompletion
1919
2204
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assemble-dev/providers",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "Elastic-2.0",
5
5
  "description": "Model provider adapters for the Assemble SDK",
6
6
  "repository": {
@@ -27,8 +27,8 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "zod": "^4.1.13",
30
- "@assemble-dev/shared-types": "0.1.0",
31
- "@assemble-dev/shared-utils": "0.1.0"
30
+ "@assemble-dev/shared-types": "0.2.0",
31
+ "@assemble-dev/shared-utils": "0.2.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^20.19.0",