@gammatech/aijsx 0.8.1-dev.2024-05-24 → 0.9.0-dev.2024-05-28

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
@@ -32,26 +32,22 @@ __export(src_exports, {
32
32
  AIFragment: () => AIFragment,
33
33
  AISpanProcessor: () => AISpanProcessor,
34
34
  AnthropicChatCompletion: () => AnthropicChatCompletion,
35
- AnthropicChatCompletionInner: () => AnthropicChatCompletionInner,
36
35
  AnthropicClient: () => import_sdk2.default,
37
36
  AnthropicClientContext: () => AnthropicClientContext,
38
37
  AssistantMessage: () => AssistantMessage,
39
38
  BoundLogger: () => BoundLogger,
40
39
  ChatCompletionError: () => ChatCompletionError,
41
- ClaudeImageBlock: () => ClaudeImageBlock,
42
40
  CombinedLogger: () => CombinedLogger,
43
41
  ConsoleLogger: () => ConsoleLogger,
44
- ContentTypeImage: () => ContentTypeImage,
45
42
  DefaultMaxRetriesContext: () => DefaultMaxRetriesContext,
46
43
  EnrichingSpanProcessor: () => EnrichingSpanProcessor,
47
44
  Fallback: () => Fallback,
45
+ ImagePart: () => ImagePart,
48
46
  LogImplementation: () => LogImplementation,
49
47
  NoopLogImplementation: () => NoopLogImplementation,
50
48
  OpenAIChatCompletion: () => OpenAIChatCompletion,
51
- OpenAIClient: () => import_openai4.OpenAI,
49
+ OpenAIClient: () => import_openai3.OpenAI,
52
50
  OpenAIClientContext: () => OpenAIClientContext,
53
- OpenAIVisionChatCompletion: () => OpenAIVisionChatCompletion,
54
- OpenAIVisionChatCompletionInner: () => OpenAIVisionChatCompletionInner,
55
51
  ParseVariablesError: () => ParseVariablesError,
56
52
  PromptInvalidOutputError: () => PromptInvalidOutputError,
57
53
  Retry: () => Retry,
@@ -59,9 +55,9 @@ __export(src_exports, {
59
55
  SystemMessage: () => SystemMessage,
60
56
  Trace: () => Trace,
61
57
  UserMessage: () => UserMessage,
58
+ anthropicTokenizer: () => anthropicTokenizer,
62
59
  attachedContextSymbol: () => attachedContextSymbol,
63
60
  computeUsage: () => computeUsage,
64
- countAnthropicTokens: () => import_tokenizer4.countTokens,
65
61
  createAIElement: () => createAIElement,
66
62
  createContext: () => createContext,
67
63
  createFunctionChain: () => createFunctionChain,
@@ -69,15 +65,160 @@ __export(src_exports, {
69
65
  createRenderContext: () => createRenderContext,
70
66
  createStreamChain: () => createStreamChain,
71
67
  evaluatePrompt: () => evaluatePrompt,
72
- tokenCountForOpenAIMessage: () => tokenCountForOpenAIMessage,
73
- tokenCountForOpenAIVisionMessage: () => tokenCountForOpenAIVisionMessage,
74
- tokenLimitForChatModel: () => tokenLimitForChatModel,
75
- tokenizer: () => tokenizer,
68
+ openaiTokenizer: () => openaiTokenizer,
69
+ toDebugMessage: () => toDebugMessage,
76
70
  tracing: () => tracing
77
71
  });
78
72
  module.exports = __toCommonJS(src_exports);
79
73
 
80
- // src/chat.tsx
74
+ // src/chat/errors.ts
75
+ var ChatCompletionError = class extends Error {
76
+ constructor(message, chatCompletionRequest, status, shouldRetry3 = false) {
77
+ super(message);
78
+ this.chatCompletionRequest = chatCompletionRequest;
79
+ this.status = status;
80
+ this.shouldRetry = shouldRetry3;
81
+ }
82
+ name = "ChatCompletionError";
83
+ };
84
+
85
+ // src/chat/image.ts
86
+ var IMAGE_RENDERED_PROPS = {
87
+ ImagePart: {
88
+ url: true,
89
+ data: true,
90
+ mediaType: true,
91
+ dimensions: true,
92
+ detail: true
93
+ }
94
+ };
95
+ var ImagePart = (_props) => {
96
+ return null;
97
+ };
98
+ async function fetchImageAndConvertToBase64(url) {
99
+ const response = await fetch(url);
100
+ const contentType = response.headers.get("content-type");
101
+ const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
102
+ if (!contentType || !allowedTypes.includes(contentType)) {
103
+ throw new Error(`Unsupported media type: ${contentType}`);
104
+ }
105
+ const blob = await response.blob();
106
+ const buffer = Buffer.from(await blob.arrayBuffer());
107
+ const base64 = buffer.toString("base64");
108
+ return {
109
+ base64,
110
+ mediaType: contentType
111
+ };
112
+ }
113
+ async function processImageMessageProps(props, downloadUrl) {
114
+ const { dimensions, detail, ...rest } = props;
115
+ if ("url" in rest) {
116
+ if (downloadUrl) {
117
+ const { base64, mediaType } = await fetchImageAndConvertToBase64(rest.url);
118
+ return {
119
+ url: rest.url,
120
+ data: base64,
121
+ mediaType,
122
+ dimensions,
123
+ detail
124
+ };
125
+ }
126
+ return {
127
+ url: rest.url,
128
+ data: null,
129
+ mediaType: null,
130
+ dimensions,
131
+ detail
132
+ };
133
+ }
134
+ if ("data" in rest) {
135
+ return {
136
+ url: null,
137
+ mediaType: rest.mediaType,
138
+ data: rest.data,
139
+ dimensions,
140
+ detail
141
+ };
142
+ }
143
+ throw new Error(
144
+ `Invalid ImageMessageProps: ${JSON.stringify(cleanProps(props))}`
145
+ );
146
+ }
147
+ var cleanProps = (props) => {
148
+ const res = {
149
+ ...props
150
+ };
151
+ if ("data" in res) {
152
+ res.data = res.data.slice(0, 64) + "...";
153
+ }
154
+ return res;
155
+ };
156
+
157
+ // src/chat/ChatMessage.ts
158
+ var toDebugMessage = (message) => {
159
+ if (message.role === "assistant" || message.role === "system") {
160
+ return message;
161
+ }
162
+ const content = message.content.map((part) => {
163
+ if (typeof part === "string") {
164
+ return part;
165
+ } else if ("text" in part) {
166
+ return part.text;
167
+ } else if ("image" in part && typeof part.image === "object") {
168
+ const attributes = Object.entries(part.image).map(([key, value]) => {
169
+ const valueToUse = key === "data" && value != null ? value.slice(0, 22) + "..." : value;
170
+ return `${key}=${JSON.stringify(valueToUse)}`;
171
+ }).join(" ");
172
+ return `<ImagePart ${attributes} />`;
173
+ }
174
+ throw new Error("Invalid ImagePart param");
175
+ }).join("\n\n");
176
+ return {
177
+ role: message.role,
178
+ content
179
+ };
180
+ };
181
+ var UserChatMessageBuilder = class {
182
+ constructor(useBase64) {
183
+ this.useBase64 = useBase64;
184
+ }
185
+ content = [];
186
+ text(text) {
187
+ this.content.push(text);
188
+ return this;
189
+ }
190
+ image(props) {
191
+ this.content.push(props);
192
+ return this;
193
+ }
194
+ async build() {
195
+ const promises = this.content.map(
196
+ async (part) => {
197
+ if (typeof part === "string") {
198
+ return {
199
+ type: "text",
200
+ text: part
201
+ };
202
+ } else {
203
+ const image = await processImageMessageProps(part, this.useBase64);
204
+ return {
205
+ type: "image",
206
+ image
207
+ };
208
+ }
209
+ }
210
+ );
211
+ return {
212
+ role: "user",
213
+ content: await Promise.all(promises)
214
+ };
215
+ }
216
+ };
217
+ var userChatMessageBuilder = (opts) => {
218
+ return new UserChatMessageBuilder(opts.useBase64);
219
+ };
220
+
221
+ // src/chat/components.ts
81
222
  var SystemMessage = (props) => {
82
223
  return props.children;
83
224
  };
@@ -87,24 +228,22 @@ var UserMessage = (props) => {
87
228
  var AssistantMessage = (props) => {
88
229
  return props.children;
89
230
  };
90
- var computeUsage = (messages) => {
91
- const prompt = messages.filter((m) => m.role === "user" || m.role === "system").reduce((acc, m) => acc + m.tokens, 0);
92
- const completion = messages.filter((m) => m.role === "assistant").reduce((acc, m) => acc + m.tokens, 0);
231
+
232
+ // src/chat/tokenizer.ts
233
+ var computeUsage = (messages, tokenizer2) => {
234
+ const promptMessages = [...messages];
235
+ let assistantMessage;
236
+ if (promptMessages[promptMessages.length - 1].role === "assistant") {
237
+ assistantMessage = promptMessages.pop();
238
+ }
239
+ const prompt = promptMessages.reduce((acc, m) => acc + tokenizer2(m), 0);
240
+ const completion = assistantMessage ? tokenizer2(assistantMessage) : 0;
93
241
  return {
94
242
  prompt,
95
243
  completion,
96
244
  total: prompt + completion
97
245
  };
98
246
  };
99
- var ChatCompletionError = class extends Error {
100
- constructor(message, chatCompletionRequest, status, shouldRetry4 = false) {
101
- super(message);
102
- this.chatCompletionRequest = chatCompletionRequest;
103
- this.status = status;
104
- this.shouldRetry = shouldRetry4;
105
- }
106
- name = "ChatCompletionError";
107
- };
108
247
 
109
248
  // src/createElement.ts
110
249
  function createAIElement(tag, props, ...children) {
@@ -1029,7 +1168,7 @@ var attachedContextSymbol = Symbol("AI.attachedContext");
1029
1168
 
1030
1169
  // src/xml.ts
1031
1170
  var import_fast_xml_parser = require("fast-xml-parser");
1032
- var XmlNode = class {
1171
+ var XmlNode = class _XmlNode {
1033
1172
  constructor(parent, nodeName, attributes, value, childNodes) {
1034
1173
  this.parent = parent;
1035
1174
  this.nodeName = nodeName;
@@ -1041,6 +1180,15 @@ var XmlNode = class {
1041
1180
  childNodes.forEach((n) => n.parent = this);
1042
1181
  }
1043
1182
  }
1183
+ clone() {
1184
+ return new _XmlNode(
1185
+ this.parent,
1186
+ this.nodeName,
1187
+ this.attributes,
1188
+ this.value,
1189
+ this.childNodes
1190
+ );
1191
+ }
1044
1192
  toObject() {
1045
1193
  if (this.value) {
1046
1194
  return {
@@ -1451,7 +1599,7 @@ function renderCloseTag(element) {
1451
1599
  // src/retry.tsx
1452
1600
  var RetryCountContext = createContext(0);
1453
1601
  var DefaultMaxRetriesContext = createContext(0);
1454
- async function* Retry({ shouldRetry: shouldRetry4, retries = 0, maxRetries = 3, children }, ctx) {
1602
+ async function* Retry({ shouldRetry: shouldRetry3, retries = 0, maxRetries = 3, children }, ctx) {
1455
1603
  const { render } = ctx;
1456
1604
  let hasYieldedData = false;
1457
1605
  try {
@@ -1463,12 +1611,12 @@ async function* Retry({ shouldRetry: shouldRetry4, retries = 0, maxRetries = 3,
1463
1611
  yield value;
1464
1612
  }
1465
1613
  } catch (e) {
1466
- if (hasYieldedData || retries >= maxRetries || !shouldRetry4(e)) {
1614
+ if (hasYieldedData || retries >= maxRetries || !shouldRetry3(e)) {
1467
1615
  throw e;
1468
1616
  }
1469
1617
  await backoff(retries);
1470
1618
  yield* Retry(
1471
- { shouldRetry: shouldRetry4, retries: retries + 1, maxRetries, children },
1619
+ { shouldRetry: shouldRetry3, retries: retries + 1, maxRetries, children },
1472
1620
  ctx
1473
1621
  );
1474
1622
  }
@@ -1736,57 +1884,17 @@ var tokenizer = {
1736
1884
  encode: (text) => cl100kTokenizer.encode(text),
1737
1885
  decode: (tokens) => cl100kTokenizer.decode(tokens)
1738
1886
  };
1739
- function tokenLimitForChatModel(model) {
1740
- const TOKENS_CONSUMED_BY_REPLY_PREFIX = 3;
1741
- switch (model) {
1742
- case "gpt-4":
1743
- case "gpt-4-0314":
1744
- case "gpt-4-0613":
1745
- return 8192 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
1746
- case "gpt-4-32k":
1747
- case "gpt-4-32k-0314":
1748
- case "gpt-4-32k-0613":
1749
- return 32768 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
1750
- case "gpt-4o":
1751
- case "gpt-4o-2024-05-13":
1752
- case "gpt-4-turbo-2024-04-09":
1753
- case "gpt-4-turbo":
1754
- case "gpt-4-1106-preview":
1755
- case "gpt-4-0125-preview":
1756
- return 128e3 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
1757
- case "gpt-3.5-turbo":
1758
- case "gpt-3.5-turbo-0301":
1759
- case "gpt-3.5-turbo-0613":
1760
- return 4096 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
1761
- case "gpt-3.5-turbo-16k":
1762
- case "gpt-3.5-turbo-16k-0613":
1763
- case "gpt-3.5-turbo-1106":
1764
- case "gpt-3.5-turbo-0125":
1765
- return 16384 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
1766
- default: {
1767
- const _ = model;
1768
- return void 0;
1769
- }
1770
- }
1771
- }
1772
- function tokenCountForOpenAIMessage(message) {
1773
- const TOKENS_PER_MESSAGE = 3;
1774
- switch (message.role) {
1775
- case "assistant":
1776
- case "system":
1777
- case "user":
1778
- return (
1779
- // NOTE: this function should only be called for non vision requests,
1780
- // so message.content will be a string and not ChatCompletionContentPart[]
1781
- TOKENS_PER_MESSAGE + tokenizer.encode(message.content).length
1782
- );
1783
- }
1784
- }
1785
- function tokenCountForOpenAIVisionMessage(message) {
1786
- const TOKENS_PER_MESSAGE = 3;
1787
- const textCost = (content) => {
1788
- return TOKENS_PER_MESSAGE + tokenizer.encode(content).length;
1789
- };
1887
+ var TOKENS_PER_MESSAGE = 3;
1888
+ var textCost = (content) => {
1889
+ return TOKENS_PER_MESSAGE + tokenizer.encode(content).length;
1890
+ };
1891
+ var COST_PER_LOW = 85;
1892
+ var COST_PER_512x512 = 170;
1893
+ var imageCost = (w, h) => {
1894
+ const area = w * h;
1895
+ return Math.ceil(area / (512 * 512)) * COST_PER_512x512 + COST_PER_LOW;
1896
+ };
1897
+ var openaiTokenizer = (message) => {
1790
1898
  switch (message.role) {
1791
1899
  case "assistant":
1792
1900
  case "system":
@@ -1799,34 +1907,86 @@ function tokenCountForOpenAIVisionMessage(message) {
1799
1907
  if (part.type === "text") {
1800
1908
  return acc + textCost(part.text);
1801
1909
  } else {
1802
- if (!part.image_url.detail || part.image_url.detail === "low") {
1910
+ if (part.image.detail === "low") {
1803
1911
  return acc + 85;
1804
- } else {
1805
- return acc + (170 * 4 + 85);
1806
1912
  }
1913
+ if (part.image.dimensions) {
1914
+ return acc + imageCost(
1915
+ part.image.dimensions.width,
1916
+ part.image.dimensions.height
1917
+ );
1918
+ }
1919
+ return acc + imageCost(1024, 1024);
1807
1920
  }
1808
1921
  }, 0);
1809
1922
  }
1810
- }
1923
+ };
1811
1924
 
1812
- // src/lib/openai/utils.ts
1813
- var renderChatMessageContent = (content) => {
1814
- if (content == null) {
1815
- return "";
1925
+ // src/chat/buildMessages.ts
1926
+ async function toXml(ctx, children) {
1927
+ const childrenXml = await ctx.render(children, {
1928
+ preserveTags: true,
1929
+ renderedProps: IMAGE_RENDERED_PROPS
1930
+ });
1931
+ const topLevelTags = ["UserMessage", "AssistantMessage", "SystemMessage"];
1932
+ const chatMessageTags = [...topLevelTags, "ImagePart"];
1933
+ const parsed = parseXml(childrenXml).collapse(chatMessageTags);
1934
+ const topLevelValid = parsed.childNodes.every(
1935
+ (node) => topLevelTags.includes(node.nodeName)
1936
+ );
1937
+ if (!topLevelValid) {
1938
+ throw new Error("Invalid top level chat message tags");
1816
1939
  }
1817
- if (typeof content === "string") {
1818
- return content;
1940
+ return parsed.childNodes;
1941
+ }
1942
+ async function iterateChatMessageXml(nodes, visitFn) {
1943
+ const promises = [];
1944
+ for (const node of nodes) {
1945
+ if (node.nodeName === "UserMessage") {
1946
+ const newNode = node.clone();
1947
+ newNode.childNodes = collapseTextNodes(node.childNodes);
1948
+ promises.push(visitFn(newNode));
1949
+ } else {
1950
+ promises.push(visitFn(node));
1951
+ }
1819
1952
  }
1820
- return content.map((part) => {
1821
- if (part.type === "text") {
1822
- return part.text;
1823
- } else if (part.type === "image_url") {
1824
- let imageUrl = part.image_url.url.startsWith("data:image") ? part.image_url.url.slice(0, 22) + "..." : part.image_url.url;
1825
- return `<ContentTypeImage url="${imageUrl}" detail="${part.image_url.detail || "auto"}" />`;
1953
+ return Promise.all(promises);
1954
+ }
1955
+ async function buildChatMessages(ctx, children, opts) {
1956
+ const nodes = await toXml(ctx, children);
1957
+ const handleUserMessage = async (node) => {
1958
+ const childNodes = node.childNodes;
1959
+ const builder = userChatMessageBuilder({ useBase64: opts.useBase64Images });
1960
+ for (const n of childNodes) {
1961
+ if (n.nodeName === "#text") {
1962
+ builder.text(n.value);
1963
+ } else if (n.nodeName === "ImagePart") {
1964
+ builder.image(n.attributes);
1965
+ } else {
1966
+ throw new Error("Invalid User ChildNode, expecting Text or ImagePart");
1967
+ }
1826
1968
  }
1827
- throw new Error("Invalid ChatCompletionContentPart type");
1828
- }).join("\n\n");
1829
- };
1969
+ return builder.build();
1970
+ };
1971
+ return iterateChatMessageXml(nodes, async (node) => {
1972
+ switch (node.nodeName) {
1973
+ case "UserMessage":
1974
+ return handleUserMessage(node);
1975
+ case "SystemMessage":
1976
+ return {
1977
+ role: "system",
1978
+ content: node.textContent
1979
+ };
1980
+ case "AssistantMessage":
1981
+ return {
1982
+ role: "assistant",
1983
+ content: node.textContent
1984
+ };
1985
+ default:
1986
+ throw new Error("Invalid top level chat message tags");
1987
+ }
1988
+ });
1989
+ }
1830
1990
 
1831
1991
  // src/utils.ts
1832
1992
  function getEnvVar(name, shouldThrow = true) {
@@ -1858,311 +2018,86 @@ var OpenAIClientContext = createContext(() => {
1858
2018
  };
1859
2019
  return defaultClient;
1860
2020
  });
1861
- function buildOpenAIMessages(childrenXml) {
1862
- const messages = [];
1863
- const chatMessageTags = ["UserMessage", "AssistantMessage", "SystemMessage"];
1864
- const parsed = parseXml(childrenXml).collapse(chatMessageTags);
1865
- const topLevelValid = parsed.childNodes.every(
1866
- (node) => chatMessageTags.includes(node.nodeName)
1867
- );
1868
- if (!topLevelValid) {
1869
- throw new Error("Invalid top level chat message tags");
1870
- }
1871
- for (const node of parsed.childNodes) {
1872
- if (node.nodeName === "UserMessage") {
1873
- messages.push({
1874
- content: node.textContent,
1875
- role: "user"
1876
- });
1877
- } else if (node.nodeName === "AssistantMessage") {
1878
- messages.push({
1879
- content: node.textContent,
1880
- role: "assistant"
1881
- });
1882
- } else if (node.nodeName === "SystemMessage") {
1883
- messages.push({
1884
- content: node.textContent,
1885
- role: "system"
1886
- });
1887
- }
1888
- }
1889
- return messages;
1890
- }
1891
- var shouldRetry = (error) => {
1892
- return error instanceof ChatCompletionError && error.shouldRetry;
1893
- };
1894
- function OpenAIChatCompletion(props, ctx) {
1895
- const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
1896
- return /* @__PURE__ */ jsx(
1897
- Retry,
1898
- {
1899
- maxRetries: props.maxRetries || defaultMaxRetries,
1900
- shouldRetry,
1901
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) })
1902
- }
1903
- );
1904
- }
1905
- async function* OpenAIChatCompletionInner(props, { logger, render, tracer, getContext }) {
1906
- const startTime = performance.now();
1907
- const retryCount = getContext(RetryCountContext);
1908
- const span = tracer.getActiveSpan();
1909
- const { client, provider, providerRegion, costFn } = getContext(OpenAIClientContext)();
1910
- if (!client) {
1911
- throw new Error("[OpenAI] must supply OpenAI model via context");
1912
- }
1913
- const openAIMessages = buildOpenAIMessages(
1914
- await render(props.children, {
1915
- preserveTags: true
1916
- })
1917
- );
1918
- const renderedMessages = openAIMessages.map((message) => {
1919
- return {
1920
- role: message.role,
1921
- content: renderChatMessageContent(message.content),
1922
- tokens: tokenCountForOpenAIMessage(message)
1923
- };
1924
- });
1925
- const chatCompletionRequest = {
1926
- model: props.model,
1927
- max_tokens: props.maxTokens,
1928
- temperature: props.temperature,
1929
- stop: props.stop,
1930
- messages: openAIMessages,
1931
- response_format: props.responseFormat ? {
1932
- type: props.responseFormat
1933
- } : void 0,
1934
- stream: true
1935
- };
1936
- const logRequestData = {
1937
- startTime,
1938
- model: props.model,
1939
- provider,
1940
- providerRegion,
1941
- inputMessages: renderedMessages,
1942
- request: chatCompletionRequest
1943
- };
1944
- logger.chatCompletionRequest("openai", logRequestData);
1945
- span.setAttributes({
1946
- model: props.model,
1947
- provider,
1948
- providerRegion,
1949
- requestType: "openai",
1950
- chatCompletionRequest,
1951
- inputMessages: renderedMessages,
1952
- retryCount
1953
- });
1954
- let chatResponse;
1955
- try {
1956
- chatResponse = await client.chat.completions.create(chatCompletionRequest);
1957
- } catch (ex) {
1958
- const retry = shouldRetryOpenAI(ex);
1959
- if (ex instanceof import_openai2.OpenAI.APIError) {
1960
- throw new ChatCompletionError(
1961
- `OpenAIClient.APIError: ${ex.message}`,
1962
- logRequestData,
1963
- ex.status,
1964
- retry
1965
- );
1966
- } else if (ex instanceof Error) {
1967
- throw new ChatCompletionError(
1968
- ex.message,
1969
- logRequestData,
1970
- void 0,
1971
- retry
1972
- );
1973
- }
1974
- throw ex;
1975
- }
1976
- let finishReason = void 0;
1977
- let content = "";
1978
- for await (const message of chatResponse) {
1979
- if (!message.choices || !message.choices[0]) {
1980
- continue;
1981
- }
1982
- const delta = message.choices[0].delta;
1983
- if (message.choices[0].finish_reason) {
1984
- finishReason = message.choices[0].finish_reason;
1985
- }
1986
- if (delta.content) {
1987
- content += delta.content;
1988
- yield delta.content;
2021
+ function buildOpenAIMessages(chatMessages) {
2022
+ return chatMessages.map(({ role, content }) => {
2023
+ if (role === "system" || role === "assistant") {
2024
+ return {
2025
+ role,
2026
+ content
2027
+ };
1989
2028
  }
1990
- }
1991
- const outputMessage = {
1992
- role: "assistant",
1993
- content,
1994
- tokens: tokenCountForOpenAIMessage({
1995
- role: "assistant",
1996
- content
1997
- })
1998
- };
1999
- const tokensUsed = computeUsage([...renderedMessages, outputMessage]);
2000
- const cost = costFn?.(props.model, tokensUsed) ?? void 0;
2001
- const responseData = {
2002
- ...logRequestData,
2003
- finishReason,
2004
- latency: performance.now() - startTime,
2005
- outputMessage,
2006
- tokensUsed
2007
- };
2008
- logger.chatCompletionResponse("openai", responseData);
2009
- span.setAttributes({
2010
- tokensUsed,
2011
- output: content,
2012
- finishReason,
2013
- cost
2014
- });
2015
- }
2016
-
2017
- // src/lib/openai/OpenAIVision.tsx
2018
- var import_openai3 = require("openai");
2019
- var DEFAULT_MODEL = "gpt-4-vision-preview";
2020
- var ContentTypeImage = (_props) => {
2021
- return null;
2022
- };
2023
- function buildOpenAIVisionChatMessages(childrenXml) {
2024
- const messages = [];
2025
- const chatMessageTags = [
2026
- "UserMessage",
2027
- "AssistantMessage",
2028
- "SystemMessage",
2029
- "ContentTypeImage"
2030
- ];
2031
- const parsed = parseXml(childrenXml).collapse(chatMessageTags);
2032
- const topLevelValid = parsed.childNodes.every(
2033
- (node) => chatMessageTags.includes(node.nodeName)
2034
- );
2035
- if (!topLevelValid) {
2036
- throw new Error("Invalid top level chat message tags");
2037
- }
2038
- const dimensions = /* @__PURE__ */ new WeakMap();
2039
- for (const node of parsed.childNodes) {
2040
- if (node.nodeName === "UserMessage") {
2041
- const parts = node.childNodes.map((n) => {
2042
- if (n.nodeName === "#text") {
2029
+ if (role === "user") {
2030
+ if (content.length === 1 && content[0].type === "text") {
2031
+ return {
2032
+ role,
2033
+ content: content[0].text
2034
+ };
2035
+ }
2036
+ const c = content.map((part) => {
2037
+ if (part.type === "text") {
2043
2038
  return {
2044
2039
  type: "text",
2045
- text: n.value
2040
+ text: part.text
2046
2041
  };
2047
- } else if (n.nodeName === "ContentTypeImage") {
2048
- const imagePart = {
2042
+ }
2043
+ if (part.type === "image") {
2044
+ const url = part.image.url || part.image.data;
2045
+ return {
2049
2046
  type: "image_url",
2050
2047
  image_url: {
2051
- url: n.attributes.url,
2052
- detail: n.attributes.detail || "auto"
2048
+ url,
2049
+ // default to auto
2050
+ detail: part.image.detail || "auto"
2053
2051
  }
2054
2052
  };
2055
- dimensions.set(imagePart, n.attributes.dimensions);
2056
- return imagePart;
2057
2053
  }
2058
- throw new Error(
2059
- "Invalid ChatCompletionContentPart, expecting text or ContentTypeImage"
2060
- );
2061
- });
2062
- messages.push({
2063
- content: parts,
2064
- role: "user"
2065
- });
2066
- } else if (node.nodeName === "AssistantMessage") {
2067
- messages.push({
2068
- content: node.textContent,
2069
- role: "assistant"
2070
- });
2071
- } else if (node.nodeName === "SystemMessage") {
2072
- messages.push({
2073
- content: node.textContent,
2074
- role: "system"
2054
+ throw new Error("Invalid part");
2075
2055
  });
2056
+ return {
2057
+ role: "user",
2058
+ content: c
2059
+ };
2076
2060
  }
2077
- }
2078
- return { messages, dimensions };
2061
+ throw new Error("Invalid role");
2062
+ });
2079
2063
  }
2080
- var shouldRetry2 = (error) => {
2064
+ var shouldRetry = (error) => {
2081
2065
  return error instanceof ChatCompletionError && error.shouldRetry;
2082
2066
  };
2083
- function OpenAIVisionChatCompletion(props, ctx) {
2067
+ function OpenAIChatCompletion(props, ctx) {
2084
2068
  const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
2085
2069
  return /* @__PURE__ */ jsx(
2086
2070
  Retry,
2087
2071
  {
2088
2072
  maxRetries: props.maxRetries || defaultMaxRetries,
2089
- shouldRetry: shouldRetry2,
2090
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIVisionChatCompletionInner, { ...props }) })
2073
+ shouldRetry,
2074
+ children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) })
2091
2075
  }
2092
2076
  );
2093
2077
  }
2094
- async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer, getContext }) {
2078
+ async function* OpenAIChatCompletionInner(props, ctx) {
2095
2079
  const startTime = performance.now();
2096
- const model = props.model || DEFAULT_MODEL;
2080
+ const { logger, tracer, getContext } = ctx;
2097
2081
  const retryCount = getContext(RetryCountContext);
2098
2082
  const span = tracer.getActiveSpan();
2099
2083
  const { client, provider, providerRegion, costFn } = getContext(OpenAIClientContext)();
2100
2084
  if (!client) {
2101
2085
  throw new Error("[OpenAI] must supply OpenAI model via context");
2102
2086
  }
2103
- const { messages: openAIMessages, dimensions } = buildOpenAIVisionChatMessages(
2104
- await render(props.children, {
2105
- preserveTags: true,
2106
- renderedProps: {
2107
- ContentTypeImage: {
2108
- url: true,
2109
- dimensions: true,
2110
- detail: true
2111
- }
2112
- }
2113
- })
2114
- );
2115
- const renderedMessages = openAIMessages.map((message) => {
2116
- if (message.role === "user") {
2117
- if (typeof message.content === "string") {
2118
- return {
2119
- role: message.role,
2120
- content: message.content,
2121
- tokens: tokenCountForOpenAIMessage(message)
2122
- };
2123
- }
2124
- const BASE_COST = 85;
2125
- const tokens = message.content.reduce((acc, part) => {
2126
- if (part.type === "text") {
2127
- return acc + tokenCountForOpenAIMessage({
2128
- role: message.role,
2129
- content: part.text
2130
- });
2131
- }
2132
- const detail = part.image_url.detail || "auto";
2133
- if (detail === "low") {
2134
- return acc + BASE_COST;
2135
- } else if (detail === "high") {
2136
- const dim = dimensions.get(part);
2137
- if (!dim) {
2138
- return acc + (170 * 4 + BASE_COST);
2139
- }
2140
- const area = dim.width * dim.height;
2141
- const num512Images = area / (512 * 512);
2142
- const highCost = num512Images * 170;
2143
- return acc + highCost + BASE_COST;
2144
- } else {
2145
- return acc + (170 * 4 + BASE_COST);
2146
- }
2147
- }, 0);
2148
- return {
2149
- role: message.role,
2150
- content: renderChatMessageContent(message.content),
2151
- tokens
2152
- };
2153
- }
2154
- return {
2155
- role: message.role,
2156
- content: renderChatMessageContent(message.content),
2157
- tokens: tokenCountForOpenAIMessage(message)
2158
- };
2087
+ const chatMessages = await buildChatMessages(ctx, props.children, {
2088
+ useBase64Images: false
2159
2089
  });
2090
+ const openAIMessages = buildOpenAIMessages(chatMessages);
2091
+ const inputMessages = chatMessages.map((m) => toDebugMessage(m));
2160
2092
  const chatCompletionRequest = {
2161
- model,
2093
+ model: props.model,
2162
2094
  max_tokens: props.maxTokens,
2163
2095
  temperature: props.temperature,
2164
2096
  stop: props.stop,
2165
2097
  messages: openAIMessages,
2098
+ response_format: props.responseFormat ? {
2099
+ type: props.responseFormat
2100
+ } : void 0,
2166
2101
  stream: true
2167
2102
  };
2168
2103
  const chatCompletionRequestToLog = cleanChatCompletionRequest(
@@ -2170,10 +2105,10 @@ async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer,
2170
2105
  );
2171
2106
  const logRequestData = {
2172
2107
  startTime,
2173
- model,
2108
+ model: props.model,
2174
2109
  provider,
2175
2110
  providerRegion,
2176
- inputMessages: renderedMessages,
2111
+ inputMessages,
2177
2112
  request: chatCompletionRequestToLog
2178
2113
  };
2179
2114
  logger.chatCompletionRequest("openai", logRequestData);
@@ -2183,7 +2118,7 @@ async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer,
2183
2118
  providerRegion,
2184
2119
  requestType: "openai",
2185
2120
  chatCompletionRequest: chatCompletionRequestToLog,
2186
- inputMessages: renderedMessages,
2121
+ inputMessages,
2187
2122
  retryCount
2188
2123
  });
2189
2124
  let chatResponse;
@@ -2191,7 +2126,7 @@ async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer,
2191
2126
  chatResponse = await client.chat.completions.create(chatCompletionRequest);
2192
2127
  } catch (ex) {
2193
2128
  const retry = shouldRetryOpenAI(ex);
2194
- if (ex instanceof import_openai3.OpenAI.APIError) {
2129
+ if (ex instanceof import_openai2.OpenAI.APIError) {
2195
2130
  throw new ChatCompletionError(
2196
2131
  `OpenAIClient.APIError: ${ex.message}`,
2197
2132
  logRequestData,
@@ -2225,14 +2160,13 @@ async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer,
2225
2160
  }
2226
2161
  const outputMessage = {
2227
2162
  role: "assistant",
2228
- content,
2229
- tokens: tokenCountForOpenAIMessage({
2230
- role: "assistant",
2231
- content
2232
- })
2163
+ content
2233
2164
  };
2234
- const tokensUsed = computeUsage([...renderedMessages, outputMessage]);
2235
- const cost = costFn?.(model, tokensUsed) ?? void 0;
2165
+ const tokensUsed = computeUsage(
2166
+ [...chatMessages, outputMessage],
2167
+ openaiTokenizer
2168
+ );
2169
+ const cost = costFn?.(props.model, tokensUsed) ?? void 0;
2236
2170
  const responseData = {
2237
2171
  ...logRequestData,
2238
2172
  finishReason,
@@ -2244,8 +2178,8 @@ async function* OpenAIVisionChatCompletionInner(props, { logger, render, tracer,
2244
2178
  span.setAttributes({
2245
2179
  tokensUsed,
2246
2180
  output: content,
2247
- cost,
2248
- finishReason
2181
+ finishReason,
2182
+ cost
2249
2183
  });
2250
2184
  }
2251
2185
  function cleanChatCompletionRequest(chatCompletionRequest) {
@@ -2262,25 +2196,22 @@ function cleanChatCompletionRequest(chatCompletionRequest) {
2262
2196
  return {
2263
2197
  ...message,
2264
2198
  content: message.content.map((part) => {
2265
- if (part.type === "text") {
2199
+ if (typeof part === "string") {
2266
2200
  return part;
2267
- }
2268
- if (part.image_url.url.startsWith("data:image")) {
2201
+ } else if (part.type === "text") {
2202
+ return part;
2203
+ } else if (part.type === "image_url") {
2204
+ const url = part.image_url.url;
2205
+ const cleanedUrl = url.startsWith("http") ? url : url.slice(0, 22) + "...";
2269
2206
  return {
2270
- type: "image_url",
2207
+ ...part,
2271
2208
  image_url: {
2272
- url: part.image_url.url.slice(0, 22) + "...",
2273
- detail: part.image_url.detail
2209
+ ...part.image_url,
2210
+ url: cleanedUrl
2274
2211
  }
2275
2212
  };
2276
2213
  }
2277
- return {
2278
- type: "image_url",
2279
- image_url: {
2280
- url: part.image_url.url,
2281
- detail: part.image_url.detail
2282
- }
2283
- };
2214
+ return part;
2284
2215
  })
2285
2216
  };
2286
2217
  })
@@ -2288,11 +2219,36 @@ function cleanChatCompletionRequest(chatCompletionRequest) {
2288
2219
  }
2289
2220
 
2290
2221
  // src/lib/openai/index.ts
2291
- var import_openai4 = require("openai");
2222
+ var import_openai3 = require("openai");
2292
2223
 
2293
2224
  // src/lib/anthropic/Anthropic.tsx
2294
2225
  var import_sdk = __toESM(require("@anthropic-ai/sdk"));
2295
- var import_tokenizer3 = require("@anthropic-ai/tokenizer");
2226
+
2227
+ // src/lib/anthropic/tokenizer.ts
2228
+ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2229
+ var DEFAULT_IMAGE_TOKEN_COST = 1334;
2230
+ var imageTokens = (w, h) => {
2231
+ return Math.ceil(w * h / 750);
2232
+ };
2233
+ var anthropicTokenizer = (message) => {
2234
+ if (message.role === "system") {
2235
+ return (0, import_tokenizer4.countTokens)(message.content);
2236
+ }
2237
+ if (message.role === "assistant") {
2238
+ return (0, import_tokenizer4.countTokens)(message.content);
2239
+ }
2240
+ return message.content.reduce((carry, item) => {
2241
+ let tokens = 0;
2242
+ if (item.type === "text") {
2243
+ tokens = (0, import_tokenizer4.countTokens)(item.text);
2244
+ } else {
2245
+ tokens = item.image.dimensions ? imageTokens(item.image.dimensions.width, item.image.dimensions.height) : DEFAULT_IMAGE_TOKEN_COST;
2246
+ }
2247
+ return carry + tokens;
2248
+ }, 0);
2249
+ };
2250
+
2251
+ // src/lib/anthropic/Anthropic.tsx
2296
2252
  var defaultClient2 = null;
2297
2253
  var AnthropicClientContext = createContext(() => {
2298
2254
  if (defaultClient2) {
@@ -2308,71 +2264,69 @@ var AnthropicClientContext = createContext(() => {
2308
2264
  return defaultClient2;
2309
2265
  });
2310
2266
  var defaultMaxTokens = 4096;
2311
- function buildAnthropicMessages(childrenXml) {
2267
+ var buildAnthropicMessages = (chatMesssages) => {
2312
2268
  let system = "";
2313
2269
  const messages = [];
2314
- const chatMessageTags = [
2315
- "UserMessage",
2316
- "AssistantMessage",
2317
- "SystemMessage",
2318
- "ClaudeImageBlockParam"
2319
- ];
2320
- const parsed = parseXml(childrenXml).collapse(chatMessageTags);
2321
- const topLevelValid = parsed.childNodes.every(
2322
- (node) => chatMessageTags.includes(node.nodeName)
2323
- );
2324
- if (!topLevelValid) {
2325
- throw new Error("Invalid top level chat message tags");
2326
- }
2327
- for (const node of parsed.childNodes) {
2328
- if (node.nodeName === "UserMessage") {
2329
- const childNodes = collapseTextNodes(node.childNodes);
2330
- if (childNodes.length === 1 && childNodes[0].nodeName === "#text") {
2270
+ chatMesssages.forEach(({ role, content }) => {
2271
+ if (role === "system") {
2272
+ system = content;
2273
+ return;
2274
+ }
2275
+ if (role === "user") {
2276
+ const userContent = content;
2277
+ if (userContent.length === 1 && userContent[0].type === "text") {
2331
2278
  messages.push({
2332
- content: childNodes[0].value,
2333
- role: "user"
2279
+ role,
2280
+ content: userContent[0].text
2334
2281
  });
2335
- continue;
2282
+ return;
2336
2283
  }
2337
- const parts = childNodes.map((n) => {
2338
- if (n.nodeName === "#text") {
2339
- return {
2340
- type: "text",
2341
- text: n.value
2342
- };
2343
- } else if (n.nodeName === "ClaudeImageBlockParam") {
2284
+ const c = userContent.map((part) => {
2285
+ if (part.type === "text") {
2286
+ return part;
2287
+ } else if (part.type === "image") {
2344
2288
  const imagePart = {
2345
2289
  type: "image",
2346
2290
  source: {
2347
2291
  type: "base64",
2348
- media_type: n.attributes.mediaType,
2349
- data: n.attributes.data
2292
+ // NOTE: these `!` rely on the fact that we used `useBase64: true` in `userMessageBuilder``
2293
+ media_type: part.image.mediaType,
2294
+ data: part.image.data
2350
2295
  }
2351
2296
  };
2352
2297
  return imagePart;
2353
2298
  }
2354
- throw new Error(
2355
- "Invalid ChatCompletionContentPart, expecting text or ContentTypeImage"
2356
- );
2357
- }).filter((n) => n.type !== "text" || n.text.trim().length > 0);
2299
+ throw new Error("Invalid part");
2300
+ }).filter(
2301
+ (a) => (
2302
+ // allow image messages
2303
+ a.type === "image" || a.type === "text" && a.text.trim().length > 0
2304
+ )
2305
+ );
2358
2306
  messages.push({
2359
- content: parts,
2360
- role: "user"
2307
+ role,
2308
+ content: c
2361
2309
  });
2362
- } else if (node.nodeName === "AssistantMessage") {
2310
+ return;
2311
+ }
2312
+ if (role === "assistant") {
2363
2313
  messages.push({
2364
- content: node.textContent,
2365
- role: "assistant"
2314
+ role,
2315
+ content
2366
2316
  });
2367
- } else if (node.nodeName === "SystemMessage") {
2368
- system = node.textContent;
2317
+ return;
2369
2318
  }
2370
- }
2371
- return { messages, system };
2372
- }
2373
- var shouldRetry3 = (error) => {
2319
+ throw new Error(`Invalid role: ${role}`);
2320
+ });
2321
+ return {
2322
+ system,
2323
+ messages
2324
+ };
2325
+ };
2326
+ var shouldRetry2 = (error) => {
2374
2327
  return error instanceof ChatCompletionError && error.shouldRetry;
2375
2328
  };
2329
+ var shouldRetryFromStatus = (status) => Boolean(status && [424, 429, 500].includes(status));
2376
2330
  var RE_INTERNAL_SERVER_MESSASGE = /The system encountered an unexpected error during processing/i;
2377
2331
  var RE_RATE_LIMIT_MESSAGE = /Too many requests, please wait before trying again/;
2378
2332
  var extractStatusFromError = (error) => {
@@ -2406,13 +2360,14 @@ function AnthropicChatCompletion(props, ctx) {
2406
2360
  Retry,
2407
2361
  {
2408
2362
  maxRetries: props.maxRetries || defaultMaxRetries,
2409
- shouldRetry: shouldRetry3,
2363
+ shouldRetry: shouldRetry2,
2410
2364
  children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(AnthropicChatCompletionInner, { ...props }) })
2411
2365
  }
2412
2366
  );
2413
2367
  }
2414
- async function* AnthropicChatCompletionInner(props, { render, logger, tracer, getContext }) {
2368
+ async function* AnthropicChatCompletionInner(props, ctx) {
2415
2369
  const startTime = performance.now();
2370
+ const { logger, tracer, getContext } = ctx;
2416
2371
  const retryCount = getContext(RetryCountContext);
2417
2372
  const span = tracer.getActiveSpan();
2418
2373
  const { client, provider, providerRegion, costFn } = getContext(
@@ -2423,18 +2378,11 @@ async function* AnthropicChatCompletionInner(props, { render, logger, tracer, ge
2423
2378
  "[AnthropicChatCompletion] must supply AnthropicClient via context"
2424
2379
  );
2425
2380
  }
2426
- const { system, messages } = buildAnthropicMessages(
2427
- await render(props.children, {
2428
- preserveTags: true,
2429
- renderedProps: {
2430
- ClaudeImageBlockParam: {
2431
- data: true,
2432
- mediaType: true
2433
- }
2434
- }
2435
- })
2436
- );
2437
- const inputMessages = getRenderedMessages(system, messages);
2381
+ const chatMessages = await buildChatMessages(ctx, props.children, {
2382
+ useBase64Images: true
2383
+ });
2384
+ const { system, messages } = buildAnthropicMessages(chatMessages);
2385
+ const inputMessages = chatMessages.map((m) => toDebugMessage(m));
2438
2386
  let stopSequences;
2439
2387
  if (props.stop && typeof props.stop === "string") {
2440
2388
  stopSequences = [props.stop];
@@ -2475,25 +2423,24 @@ async function* AnthropicChatCompletionInner(props, { render, logger, tracer, ge
2475
2423
  response = client.messages.stream(anthropicCompletionRequest);
2476
2424
  } catch (err) {
2477
2425
  if (err instanceof import_sdk.default.APIError) {
2478
- const retry = "status" in err && typeof err.status === "number" && err.status !== 400;
2426
+ const status = extractStatusFromError(err);
2427
+ const retry = shouldRetryFromStatus(status);
2479
2428
  throw new ChatCompletionError(
2480
2429
  `AnthropicClient.APIError: ${err.message}`,
2481
2430
  logRequestData,
2482
- extractStatusFromError(err),
2431
+ status,
2483
2432
  retry
2484
2433
  );
2485
2434
  } else if (err instanceof Error) {
2486
- throw new ChatCompletionError(
2487
- err.message,
2488
- logRequestData,
2489
- extractStatusFromError(err)
2490
- );
2435
+ const status = extractStatusFromError(err);
2436
+ const retry = shouldRetryFromStatus(status);
2437
+ throw new ChatCompletionError(err.message, logRequestData, status, retry);
2491
2438
  }
2492
2439
  throw err;
2493
2440
  }
2494
2441
  let content = "";
2495
- let outputUsage = 0;
2496
- let inputUsage = 0;
2442
+ let outputUsage;
2443
+ let inputUsage;
2497
2444
  let finishReason = null;
2498
2445
  try {
2499
2446
  for await (const event of response) {
@@ -2512,25 +2459,18 @@ async function* AnthropicChatCompletionInner(props, { render, logger, tracer, ge
2512
2459
  }
2513
2460
  } catch (e) {
2514
2461
  const status = extractStatusFromError(e);
2515
- const retry = status === 429 || status === 500;
2462
+ const retry = shouldRetryFromStatus(status);
2516
2463
  throw new ChatCompletionError(e.message, logRequestData, status, retry);
2517
2464
  }
2518
2465
  const outputMessage = {
2519
2466
  role: "assistant",
2520
- content,
2521
- tokens: outputUsage
2467
+ content
2522
2468
  };
2523
- const userMessage = inputMessages.find(
2524
- (m) => m.role === "user" && m.tokens === 0
2525
- );
2526
- const restMessages = inputMessages.filter(
2527
- (m) => m.role !== "assistant" && m.tokens > 0
2528
- );
2529
- const restMessagesTokens = restMessages.reduce((acc, m) => acc + m.tokens, 0);
2530
- if (userMessage && inputUsage) {
2531
- userMessage.tokens = inputUsage - restMessagesTokens;
2532
- }
2533
- const tokensUsed = computeUsage([...inputMessages, outputMessage]);
2469
+ const tokensUsed = inputUsage !== void 0 && outputUsage !== void 0 ? {
2470
+ prompt: inputUsage,
2471
+ completion: outputUsage,
2472
+ total: inputUsage + outputUsage
2473
+ } : computeUsage([...chatMessages, outputMessage], anthropicTokenizer);
2534
2474
  const cost = costFn?.(props.model, tokensUsed) ?? void 0;
2535
2475
  const responseData = {
2536
2476
  ...logRequestData,
@@ -2551,47 +2491,6 @@ async function* AnthropicChatCompletionInner(props, { render, logger, tracer, ge
2551
2491
  finishReason
2552
2492
  });
2553
2493
  }
2554
- function getRenderedMessages(system, messages) {
2555
- const systemMessage = {
2556
- role: "system",
2557
- content: system,
2558
- tokens: (0, import_tokenizer3.countTokens)(system)
2559
- };
2560
- const renderedMessages = messages.map((message) => {
2561
- if (message.role === "user") {
2562
- return {
2563
- role: message.role,
2564
- content: renderChatMessageContent2(message.content),
2565
- // we keep user message tokens 0 and just let anthropic tell us
2566
- tokens: 0
2567
- };
2568
- }
2569
- return {
2570
- role: message.role,
2571
- content: renderChatMessageContent2(message.content),
2572
- tokens: (0, import_tokenizer3.countTokens)(message.content)
2573
- };
2574
- });
2575
- return [systemMessage, ...renderedMessages];
2576
- }
2577
- var renderChatMessageContent2 = (content) => {
2578
- if (content == null) {
2579
- return "";
2580
- }
2581
- if (typeof content === "string") {
2582
- return content;
2583
- }
2584
- return content.map((part) => {
2585
- if (typeof part === "string") {
2586
- return part;
2587
- } else if ("text" in part) {
2588
- return part.text;
2589
- } else if ("source" in part && typeof part.source === "object") {
2590
- return `<ImageBlockParam data="base64..." />`;
2591
- }
2592
- throw new Error("Invalid ImageBlockParam type");
2593
- }).join("\n\n");
2594
- };
2595
2494
  function cleanChatCompletionRequest2(chatCompletionRequest) {
2596
2495
  const { messages, ...rest } = chatCompletionRequest;
2597
2496
  return {
@@ -2626,62 +2525,29 @@ function cleanChatCompletionRequest2(chatCompletionRequest) {
2626
2525
  };
2627
2526
  }
2628
2527
 
2629
- // src/lib/anthropic/ClaudeImageBlock.tsx
2630
- var ClaudeImageBlockParam = (_props) => {
2631
- return null;
2632
- };
2633
- async function fetchImageAndConvertToBase64(url) {
2634
- const response = await fetch(url);
2635
- const contentType = response.headers.get("content-type");
2636
- const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
2637
- if (!contentType || !allowedTypes.includes(contentType)) {
2638
- throw new Error(`Unsupported media type: ${contentType}`);
2639
- }
2640
- const blob = await response.blob();
2641
- const buffer = Buffer.from(await blob.arrayBuffer());
2642
- const base64 = buffer.toString("base64");
2643
- return {
2644
- base64,
2645
- mediaType: contentType
2646
- };
2647
- }
2648
- var ClaudeImageBlock = async (props) => {
2649
- if ("data" in props) {
2650
- return /* @__PURE__ */ jsx(ClaudeImageBlockParam, { ...props });
2651
- }
2652
- const { url } = props;
2653
- const { base64, mediaType } = await fetchImageAndConvertToBase64(url);
2654
- return /* @__PURE__ */ jsx(ClaudeImageBlockParam, { mediaType, data: base64 });
2655
- };
2656
-
2657
2528
  // src/lib/anthropic/index.ts
2658
2529
  var import_sdk2 = __toESM(require("@anthropic-ai/sdk"));
2659
- var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2660
2530
  // Annotate the CommonJS export names for ESM import in node:
2661
2531
  0 && (module.exports = {
2662
2532
  AIFragment,
2663
2533
  AISpanProcessor,
2664
2534
  AnthropicChatCompletion,
2665
- AnthropicChatCompletionInner,
2666
2535
  AnthropicClient,
2667
2536
  AnthropicClientContext,
2668
2537
  AssistantMessage,
2669
2538
  BoundLogger,
2670
2539
  ChatCompletionError,
2671
- ClaudeImageBlock,
2672
2540
  CombinedLogger,
2673
2541
  ConsoleLogger,
2674
- ContentTypeImage,
2675
2542
  DefaultMaxRetriesContext,
2676
2543
  EnrichingSpanProcessor,
2677
2544
  Fallback,
2545
+ ImagePart,
2678
2546
  LogImplementation,
2679
2547
  NoopLogImplementation,
2680
2548
  OpenAIChatCompletion,
2681
2549
  OpenAIClient,
2682
2550
  OpenAIClientContext,
2683
- OpenAIVisionChatCompletion,
2684
- OpenAIVisionChatCompletionInner,
2685
2551
  ParseVariablesError,
2686
2552
  PromptInvalidOutputError,
2687
2553
  Retry,
@@ -2689,9 +2555,9 @@ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2689
2555
  SystemMessage,
2690
2556
  Trace,
2691
2557
  UserMessage,
2558
+ anthropicTokenizer,
2692
2559
  attachedContextSymbol,
2693
2560
  computeUsage,
2694
- countAnthropicTokens,
2695
2561
  createAIElement,
2696
2562
  createContext,
2697
2563
  createFunctionChain,
@@ -2699,9 +2565,7 @@ var import_tokenizer4 = require("@anthropic-ai/tokenizer");
2699
2565
  createRenderContext,
2700
2566
  createStreamChain,
2701
2567
  evaluatePrompt,
2702
- tokenCountForOpenAIMessage,
2703
- tokenCountForOpenAIVisionMessage,
2704
- tokenLimitForChatModel,
2705
- tokenizer,
2568
+ openaiTokenizer,
2569
+ toDebugMessage,
2706
2570
  tracing
2707
2571
  });