@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.d.mts +43 -52
- package/dist/index.d.ts +43 -52
- package/dist/index.js +422 -558
- package/dist/index.mjs +417 -547
- package/dist/{jsx-dev-runtime-Kh2H5_Cf.d.mts → jsx-dev-runtime-uCZBuaDe.d.mts} +60 -40
- package/dist/{jsx-dev-runtime-Kh2H5_Cf.d.ts → jsx-dev-runtime-uCZBuaDe.d.ts} +60 -40
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/package.json +2 -1
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: () =>
|
|
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
|
-
|
|
73
|
-
|
|
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.
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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:
|
|
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 || !
|
|
1614
|
+
if (hasYieldedData || retries >= maxRetries || !shouldRetry3(e)) {
|
|
1467
1615
|
throw e;
|
|
1468
1616
|
}
|
|
1469
1617
|
await backoff(retries);
|
|
1470
1618
|
yield* Retry(
|
|
1471
|
-
{ shouldRetry:
|
|
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
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
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 (
|
|
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/
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
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
|
-
|
|
1818
|
-
|
|
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
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
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
|
-
|
|
1828
|
-
}
|
|
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(
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
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
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
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:
|
|
2040
|
+
text: part.text
|
|
2046
2041
|
};
|
|
2047
|
-
}
|
|
2048
|
-
|
|
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
|
|
2052
|
-
|
|
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
|
-
|
|
2061
|
+
throw new Error("Invalid role");
|
|
2062
|
+
});
|
|
2079
2063
|
}
|
|
2080
|
-
var
|
|
2064
|
+
var shouldRetry = (error) => {
|
|
2081
2065
|
return error instanceof ChatCompletionError && error.shouldRetry;
|
|
2082
2066
|
};
|
|
2083
|
-
function
|
|
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
|
|
2090
|
-
children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(
|
|
2073
|
+
shouldRetry,
|
|
2074
|
+
children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) })
|
|
2091
2075
|
}
|
|
2092
2076
|
);
|
|
2093
2077
|
}
|
|
2094
|
-
async function*
|
|
2078
|
+
async function* OpenAIChatCompletionInner(props, ctx) {
|
|
2095
2079
|
const startTime = performance.now();
|
|
2096
|
-
const
|
|
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
|
|
2104
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
2235
|
-
|
|
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
|
-
|
|
2248
|
-
|
|
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
|
|
2199
|
+
if (typeof part === "string") {
|
|
2266
2200
|
return part;
|
|
2267
|
-
}
|
|
2268
|
-
|
|
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
|
-
|
|
2207
|
+
...part,
|
|
2271
2208
|
image_url: {
|
|
2272
|
-
|
|
2273
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2267
|
+
var buildAnthropicMessages = (chatMesssages) => {
|
|
2312
2268
|
let system = "";
|
|
2313
2269
|
const messages = [];
|
|
2314
|
-
|
|
2315
|
-
"
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
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
|
-
|
|
2333
|
-
|
|
2279
|
+
role,
|
|
2280
|
+
content: userContent[0].text
|
|
2334
2281
|
});
|
|
2335
|
-
|
|
2282
|
+
return;
|
|
2336
2283
|
}
|
|
2337
|
-
const
|
|
2338
|
-
if (
|
|
2339
|
-
return
|
|
2340
|
-
|
|
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
|
-
|
|
2349
|
-
|
|
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
|
-
|
|
2356
|
-
)
|
|
2357
|
-
|
|
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
|
-
|
|
2360
|
-
|
|
2307
|
+
role,
|
|
2308
|
+
content: c
|
|
2361
2309
|
});
|
|
2362
|
-
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
if (role === "assistant") {
|
|
2363
2313
|
messages.push({
|
|
2364
|
-
|
|
2365
|
-
|
|
2314
|
+
role,
|
|
2315
|
+
content
|
|
2366
2316
|
});
|
|
2367
|
-
|
|
2368
|
-
system = node.textContent;
|
|
2317
|
+
return;
|
|
2369
2318
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
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:
|
|
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,
|
|
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
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
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
|
|
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
|
-
|
|
2431
|
+
status,
|
|
2483
2432
|
retry
|
|
2484
2433
|
);
|
|
2485
2434
|
} else if (err instanceof Error) {
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
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
|
|
2496
|
-
let inputUsage
|
|
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
|
|
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
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
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
|
-
|
|
2703
|
-
|
|
2704
|
-
tokenLimitForChatModel,
|
|
2705
|
-
tokenizer,
|
|
2568
|
+
openaiTokenizer,
|
|
2569
|
+
toDebugMessage,
|
|
2706
2570
|
tracing
|
|
2707
2571
|
});
|