@gammatech/aijsx 0.13.0-dev.2024-07-15 → 0.15.0-dev.2024-07-30

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 CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LogChatCompletionRequest, A as AINode, C as ChatMessage, R as RenderContext, a as LogImplementation, S as SpanProcessor, b as ContextValues, c as Context, T as Tracer, d as ReadableSpan, e as SpanAttributes, f as SpanExporter, g as AIComponent, P as Prompt, h as ChatCompletionRequestPayloads, D as DebugMessage, E as EvaluatorResult, i as EvaluatorFn, j as PromptParsed, J as JSX } from './jsx-dev-runtime-S--1HQKU.mjs';
2
- export { y as AIElement, l as AIFragment, B as BoundLogger, n as ChatRole, u as CombinedLogger, s as ConsoleLogger, I as ImagePart, o as ImagePartProps, v as Literal, p as LogChatCompletionResponse, q as LogLevel, r as Logger, N as NoopLogImplementation, V as OutputParser, F as PropsOfAIComponent, w as RenderResult, z as Renderable, K as Span, G as SpanContext, M as SpanEvent, H as SpanStatus, Q as TracingContext, O as TracingContextKey, U as TracingContextManager, x as attachedContextSymbol, k as createAIElement, m as createContext, t as toDebugMessage } from './jsx-dev-runtime-S--1HQKU.mjs';
1
+ import { L as LogChatCompletionRequest, A as AINode, C as ChatMessage, R as RenderContext, a as LogImplementation, S as SpanProcessor, b as ContextValues, c as Context, T as Tracer, d as ReadableSpan, e as SpanAttributes, f as SpanExporter, g as AIComponent, P as Prompt, h as ChatCompletionRequestPayloads, D as DebugMessage, E as EvaluatorResult, i as EvaluatorFn, j as PromptParsed, J as JSX } from './jsx-dev-runtime-lYsmphqH.mjs';
2
+ export { y as AIElement, l as AIFragment, B as BoundLogger, n as ChatRole, u as CombinedLogger, s as ConsoleLogger, I as ImagePart, o as ImagePartProps, v as Literal, p as LogChatCompletionResponse, q as LogLevel, r as Logger, N as NoopLogImplementation, V as OutputParser, F as PropsOfAIComponent, w as RenderResult, z as Renderable, K as Span, G as SpanContext, M as SpanEvent, H as SpanStatus, Q as TracingContext, O as TracingContextKey, U as TracingContextManager, x as attachedContextSymbol, k as createAIElement, m as createContext, t as toDebugMessage } from './jsx-dev-runtime-lYsmphqH.mjs';
3
3
  import { ZodObject, ZodRawShape, ZodTypeAny, ZodString, z } from 'zod';
4
4
  import { OpenAI } from 'openai';
5
5
  export { OpenAI as OpenAIClient } from 'openai';
@@ -76,6 +76,14 @@ type RetryProps = {
76
76
  children: AINode;
77
77
  };
78
78
  declare function Retry({ shouldRetry, retries, maxRetries, lastError, children }: RetryProps, ctx: RenderContext): AsyncGenerator<string, void, unknown>;
79
+ type AccumulatorProps = {
80
+ enabled?: boolean;
81
+ children: AINode;
82
+ };
83
+ /**
84
+ * If enabled, renders its children and accumulates the output instead of passing the stream through
85
+ */
86
+ declare function Accumulate({ enabled, children }: AccumulatorProps, ctx: RenderContext): Promise<string | AINode>;
79
87
 
80
88
  type FallbackProps = {
81
89
  fallback: AINode;
@@ -254,6 +262,7 @@ type OpenAIChatCompletionProps = {
254
262
  stop?: string | string[];
255
263
  responseFormat?: ChatCompletionCreateParams.ResponseFormat['type'];
256
264
  maxRetries?: number;
265
+ stream?: boolean;
257
266
  children: AINode;
258
267
  };
259
268
  declare function OpenAIChatCompletion(props: OpenAIChatCompletionProps, ctx: RenderContext): AINode;
@@ -281,6 +290,7 @@ type AnthropicChatCompletionProps = {
281
290
  stop?: string | string[];
282
291
  maxRetries?: number;
283
292
  extraHeaders?: Record<string, string>;
293
+ stream?: boolean;
284
294
  children: AINode;
285
295
  };
286
296
  declare function AnthropicChatCompletion(props: AnthropicChatCompletionProps, ctx: RenderContext): JSX.Element;
@@ -305,6 +315,7 @@ type GoogleChatCompletionProps = {
305
315
  maxTokens?: number;
306
316
  temperature?: number;
307
317
  stop?: string | string[];
318
+ stream?: boolean;
308
319
  maxRetries?: number;
309
320
  children: AINode;
310
321
  safetySettings?: {
@@ -314,4 +325,4 @@ type GoogleChatCompletionProps = {
314
325
  };
315
326
  declare function GoogleChatCompletion(props: GoogleChatCompletionProps, ctx: RenderContext): JSX.Element;
316
327
 
317
- export { AIComponent, AINode, AISpanAttributes, AISpanProcessor, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, AssistantMessage, type ChatCompletionClientAndProvider, ChatCompletionError, type ChatCompletionRequestPayloads, ChatMessage, Context, type CostFn, DebugMessage, DefaultMaxRetriesContext, EvaluatorFn, EvaluatorResult, Fallback, type GetChatCompletionClientAndProvider, GoogleChatCompletion, type GoogleChatCompletionRequest, GoogleClientContext, LogChatCompletionRequest, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, ParseVariablesError, ProcessedAISpanAttributes, type Prompt, PromptInvalidOutputError, PromptParsed, ReadableSpan, RenderContext, Retry, RetryCountContext, RetryLastErrorContext, SpanAttributes, SpanExporter, SpanProcessor, SystemMessage, type TokenizerFn, Trace, Tracer, UserMessage, type ValidAnthropicChatModel, type ValidGoogleChatModel, type ValidOpenAIChatModel, type ValidOpenAIVisionModel, anthropicTokenizer, computeUsage, createPrompt, createRenderContext, evaluatePrompt, isPromptParsed, openaiTokenizer, tracing };
328
+ export { AIComponent, AINode, AISpanAttributes, AISpanProcessor, Accumulate, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, AssistantMessage, type ChatCompletionClientAndProvider, ChatCompletionError, type ChatCompletionRequestPayloads, ChatMessage, Context, type CostFn, DebugMessage, DefaultMaxRetriesContext, EvaluatorFn, EvaluatorResult, Fallback, type GetChatCompletionClientAndProvider, GoogleChatCompletion, type GoogleChatCompletionRequest, GoogleClientContext, LogChatCompletionRequest, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, ParseVariablesError, ProcessedAISpanAttributes, type Prompt, PromptInvalidOutputError, PromptParsed, ReadableSpan, RenderContext, Retry, RetryCountContext, RetryLastErrorContext, SpanAttributes, SpanExporter, SpanProcessor, SystemMessage, type TokenizerFn, Trace, Tracer, UserMessage, type ValidAnthropicChatModel, type ValidGoogleChatModel, type ValidOpenAIChatModel, type ValidOpenAIVisionModel, anthropicTokenizer, computeUsage, createPrompt, createRenderContext, evaluatePrompt, isPromptParsed, openaiTokenizer, tracing };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LogChatCompletionRequest, A as AINode, C as ChatMessage, R as RenderContext, a as LogImplementation, S as SpanProcessor, b as ContextValues, c as Context, T as Tracer, d as ReadableSpan, e as SpanAttributes, f as SpanExporter, g as AIComponent, P as Prompt, h as ChatCompletionRequestPayloads, D as DebugMessage, E as EvaluatorResult, i as EvaluatorFn, j as PromptParsed, J as JSX } from './jsx-dev-runtime-S--1HQKU.js';
2
- export { y as AIElement, l as AIFragment, B as BoundLogger, n as ChatRole, u as CombinedLogger, s as ConsoleLogger, I as ImagePart, o as ImagePartProps, v as Literal, p as LogChatCompletionResponse, q as LogLevel, r as Logger, N as NoopLogImplementation, V as OutputParser, F as PropsOfAIComponent, w as RenderResult, z as Renderable, K as Span, G as SpanContext, M as SpanEvent, H as SpanStatus, Q as TracingContext, O as TracingContextKey, U as TracingContextManager, x as attachedContextSymbol, k as createAIElement, m as createContext, t as toDebugMessage } from './jsx-dev-runtime-S--1HQKU.js';
1
+ import { L as LogChatCompletionRequest, A as AINode, C as ChatMessage, R as RenderContext, a as LogImplementation, S as SpanProcessor, b as ContextValues, c as Context, T as Tracer, d as ReadableSpan, e as SpanAttributes, f as SpanExporter, g as AIComponent, P as Prompt, h as ChatCompletionRequestPayloads, D as DebugMessage, E as EvaluatorResult, i as EvaluatorFn, j as PromptParsed, J as JSX } from './jsx-dev-runtime-lYsmphqH.js';
2
+ export { y as AIElement, l as AIFragment, B as BoundLogger, n as ChatRole, u as CombinedLogger, s as ConsoleLogger, I as ImagePart, o as ImagePartProps, v as Literal, p as LogChatCompletionResponse, q as LogLevel, r as Logger, N as NoopLogImplementation, V as OutputParser, F as PropsOfAIComponent, w as RenderResult, z as Renderable, K as Span, G as SpanContext, M as SpanEvent, H as SpanStatus, Q as TracingContext, O as TracingContextKey, U as TracingContextManager, x as attachedContextSymbol, k as createAIElement, m as createContext, t as toDebugMessage } from './jsx-dev-runtime-lYsmphqH.js';
3
3
  import { ZodObject, ZodRawShape, ZodTypeAny, ZodString, z } from 'zod';
4
4
  import { OpenAI } from 'openai';
5
5
  export { OpenAI as OpenAIClient } from 'openai';
@@ -76,6 +76,14 @@ type RetryProps = {
76
76
  children: AINode;
77
77
  };
78
78
  declare function Retry({ shouldRetry, retries, maxRetries, lastError, children }: RetryProps, ctx: RenderContext): AsyncGenerator<string, void, unknown>;
79
+ type AccumulatorProps = {
80
+ enabled?: boolean;
81
+ children: AINode;
82
+ };
83
+ /**
84
+ * If enabled, renders its children and accumulates the output instead of passing the stream through
85
+ */
86
+ declare function Accumulate({ enabled, children }: AccumulatorProps, ctx: RenderContext): Promise<string | AINode>;
79
87
 
80
88
  type FallbackProps = {
81
89
  fallback: AINode;
@@ -254,6 +262,7 @@ type OpenAIChatCompletionProps = {
254
262
  stop?: string | string[];
255
263
  responseFormat?: ChatCompletionCreateParams.ResponseFormat['type'];
256
264
  maxRetries?: number;
265
+ stream?: boolean;
257
266
  children: AINode;
258
267
  };
259
268
  declare function OpenAIChatCompletion(props: OpenAIChatCompletionProps, ctx: RenderContext): AINode;
@@ -281,6 +290,7 @@ type AnthropicChatCompletionProps = {
281
290
  stop?: string | string[];
282
291
  maxRetries?: number;
283
292
  extraHeaders?: Record<string, string>;
293
+ stream?: boolean;
284
294
  children: AINode;
285
295
  };
286
296
  declare function AnthropicChatCompletion(props: AnthropicChatCompletionProps, ctx: RenderContext): JSX.Element;
@@ -305,6 +315,7 @@ type GoogleChatCompletionProps = {
305
315
  maxTokens?: number;
306
316
  temperature?: number;
307
317
  stop?: string | string[];
318
+ stream?: boolean;
308
319
  maxRetries?: number;
309
320
  children: AINode;
310
321
  safetySettings?: {
@@ -314,4 +325,4 @@ type GoogleChatCompletionProps = {
314
325
  };
315
326
  declare function GoogleChatCompletion(props: GoogleChatCompletionProps, ctx: RenderContext): JSX.Element;
316
327
 
317
- export { AIComponent, AINode, AISpanAttributes, AISpanProcessor, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, AssistantMessage, type ChatCompletionClientAndProvider, ChatCompletionError, type ChatCompletionRequestPayloads, ChatMessage, Context, type CostFn, DebugMessage, DefaultMaxRetriesContext, EvaluatorFn, EvaluatorResult, Fallback, type GetChatCompletionClientAndProvider, GoogleChatCompletion, type GoogleChatCompletionRequest, GoogleClientContext, LogChatCompletionRequest, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, ParseVariablesError, ProcessedAISpanAttributes, type Prompt, PromptInvalidOutputError, PromptParsed, ReadableSpan, RenderContext, Retry, RetryCountContext, RetryLastErrorContext, SpanAttributes, SpanExporter, SpanProcessor, SystemMessage, type TokenizerFn, Trace, Tracer, UserMessage, type ValidAnthropicChatModel, type ValidGoogleChatModel, type ValidOpenAIChatModel, type ValidOpenAIVisionModel, anthropicTokenizer, computeUsage, createPrompt, createRenderContext, evaluatePrompt, isPromptParsed, openaiTokenizer, tracing };
328
+ export { AIComponent, AINode, AISpanAttributes, AISpanProcessor, Accumulate, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, AssistantMessage, type ChatCompletionClientAndProvider, ChatCompletionError, type ChatCompletionRequestPayloads, ChatMessage, Context, type CostFn, DebugMessage, DefaultMaxRetriesContext, EvaluatorFn, EvaluatorResult, Fallback, type GetChatCompletionClientAndProvider, GoogleChatCompletion, type GoogleChatCompletionRequest, GoogleClientContext, LogChatCompletionRequest, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, ParseVariablesError, ProcessedAISpanAttributes, type Prompt, PromptInvalidOutputError, PromptParsed, ReadableSpan, RenderContext, Retry, RetryCountContext, RetryLastErrorContext, SpanAttributes, SpanExporter, SpanProcessor, SystemMessage, type TokenizerFn, Trace, Tracer, UserMessage, type ValidAnthropicChatModel, type ValidGoogleChatModel, type ValidOpenAIChatModel, type ValidOpenAIVisionModel, anthropicTokenizer, computeUsage, createPrompt, createRenderContext, evaluatePrompt, isPromptParsed, openaiTokenizer, tracing };
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var src_exports = {};
31
31
  __export(src_exports, {
32
32
  AIFragment: () => AIFragment,
33
33
  AISpanProcessor: () => AISpanProcessor,
34
+ Accumulate: () => Accumulate,
34
35
  AnthropicChatCompletion: () => AnthropicChatCompletion,
35
36
  AnthropicClient: () => import_sdk2.default,
36
37
  AnthropicClientContext: () => AnthropicClientContext,
@@ -88,6 +89,12 @@ var ChatCompletionError = class extends Error {
88
89
  };
89
90
 
90
91
  // src/chat/image.ts
92
+ var SUPPORTED_TYPES = [
93
+ "image/jpeg",
94
+ "image/png",
95
+ "image/gif",
96
+ "image/webp"
97
+ ];
91
98
  var IMAGE_RENDERED_PROPS = {
92
99
  ImagePart: {
93
100
  url: true,
@@ -103,8 +110,7 @@ var ImagePart = (_props) => {
103
110
  async function fetchImageAndConvertToBase64(url) {
104
111
  const response = await fetch(url);
105
112
  const contentType = response.headers.get("content-type");
106
- const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
107
- if (!contentType || !allowedTypes.includes(contentType)) {
113
+ if (!contentType || !RE_ALLOWED_MEDIA_TYPES.test(contentType)) {
108
114
  throw new Error(`Unsupported media type: ${contentType}`);
109
115
  }
110
116
  const blob = await response.blob();
@@ -115,10 +121,40 @@ async function fetchImageAndConvertToBase64(url) {
115
121
  mediaType: contentType
116
122
  };
117
123
  }
118
- async function processImageMessageProps(props, downloadUrl) {
124
+ var RE_ALLOWED_MEDIA_TYPES = new RegExp(
125
+ `^(${SUPPORTED_TYPES.join("|")})$`,
126
+ "i"
127
+ );
128
+ var RE_DATA_IMAGE = new RegExp(
129
+ `^data:(${SUPPORTED_TYPES.join("|")});base64,(.+)$`,
130
+ "i"
131
+ );
132
+ var parseDataUrl = (dataUrl) => {
133
+ const matches = dataUrl.match(RE_DATA_IMAGE);
134
+ if (!matches) {
135
+ return null;
136
+ }
137
+ const [, mediaType, base64] = matches;
138
+ return {
139
+ mediaType,
140
+ base64
141
+ };
142
+ };
143
+ async function processImageMessageProps(props, opts) {
119
144
  const { dimensions, detail, ...rest } = props;
120
- if ("url" in rest) {
121
- if (downloadUrl) {
145
+ if ("url" in rest && rest.url) {
146
+ const parsed = parseDataUrl(rest.url);
147
+ if (parsed) {
148
+ const { mediaType, base64 } = parsed;
149
+ return {
150
+ url: null,
151
+ data: base64,
152
+ mediaType,
153
+ dimensions,
154
+ detail
155
+ };
156
+ }
157
+ if (opts.downloadUrl) {
122
158
  const { base64, mediaType } = await fetchImageAndConvertToBase64(rest.url);
123
159
  return {
124
160
  url: rest.url,
@@ -137,6 +173,16 @@ async function processImageMessageProps(props, downloadUrl) {
137
173
  };
138
174
  }
139
175
  if ("data" in rest) {
176
+ const parsed = parseDataUrl(rest.data);
177
+ if (parsed) {
178
+ return {
179
+ url: null,
180
+ mediaType: parsed.mediaType,
181
+ data: parsed.base64,
182
+ dimensions,
183
+ detail
184
+ };
185
+ }
140
186
  return {
141
187
  url: null,
142
188
  mediaType: rest.mediaType,
@@ -158,6 +204,9 @@ var cleanProps = (props) => {
158
204
  }
159
205
  return res;
160
206
  };
207
+ var buildDataUri = (mediaType, base64) => {
208
+ return `data:${mediaType};base64,${base64}`;
209
+ };
161
210
 
162
211
  // src/chat/ChatMessage.ts
163
212
  var toDebugMessage = (message) => {
@@ -184,8 +233,8 @@ var toDebugMessage = (message) => {
184
233
  };
185
234
  };
186
235
  var UserChatMessageBuilder = class {
187
- constructor(useBase64) {
188
- this.useBase64 = useBase64;
236
+ constructor(opts) {
237
+ this.opts = opts;
189
238
  }
190
239
  content = [];
191
240
  text(text) {
@@ -205,7 +254,9 @@ var UserChatMessageBuilder = class {
205
254
  text: part
206
255
  };
207
256
  } else {
208
- const image = await processImageMessageProps(part, this.useBase64);
257
+ const image = await processImageMessageProps(part, {
258
+ downloadUrl: this.opts.useBase64Images
259
+ });
209
260
  return {
210
261
  type: "image",
211
262
  image
@@ -220,7 +271,7 @@ var UserChatMessageBuilder = class {
220
271
  }
221
272
  };
222
273
  var userChatMessageBuilder = (opts) => {
223
- return new UserChatMessageBuilder(opts.useBase64);
274
+ return new UserChatMessageBuilder(opts);
224
275
  };
225
276
 
226
277
  // src/chat/components.ts
@@ -1603,6 +1654,17 @@ async function* Retry({ shouldRetry: shouldRetry4, retries = 0, maxRetries = 3,
1603
1654
  );
1604
1655
  }
1605
1656
  }
1657
+ async function Accumulate({ enabled = true, children }, ctx) {
1658
+ if (!enabled) {
1659
+ return children;
1660
+ }
1661
+ let accum = "";
1662
+ const stream = ctx.render(children);
1663
+ for await (const value of stream) {
1664
+ accum += value;
1665
+ }
1666
+ return accum;
1667
+ }
1606
1668
  var BASE_BACKOFF = 100;
1607
1669
  var backoff = (retries) => {
1608
1670
  const waitTime = BASE_BACKOFF * Math.pow(4, retries);
@@ -1952,6 +2014,36 @@ function isPromptParsed2(prompt) {
1952
2014
  // src/lib/openai/OpenAI.tsx
1953
2015
  var import_openai2 = require("openai");
1954
2016
 
2017
+ // src/lib/openai/errors.ts
2018
+ var import_openai = require("openai");
2019
+ var extractStatusFromError = (error) => {
2020
+ if (error instanceof import_openai.OpenAI.APIError) {
2021
+ return error.status;
2022
+ } else if (error instanceof import_openai.OpenAI.APIConnectionError) {
2023
+ return void 0;
2024
+ } else {
2025
+ return void 0;
2026
+ }
2027
+ };
2028
+ var errorToChatCompletionError = (error, requestData) => {
2029
+ const castError = castToError(error);
2030
+ const status = extractStatusFromError(castError);
2031
+ let messagePrefix = "";
2032
+ if (error instanceof import_openai.OpenAI.APIError) {
2033
+ messagePrefix = "OpenAIClient.APIError: ";
2034
+ } else if (error instanceof import_openai.OpenAI.APIConnectionError) {
2035
+ messagePrefix = "OpenAIClient.APIConnectionError: ";
2036
+ }
2037
+ const shouldRetry4 = status !== 400;
2038
+ return new ChatCompletionError(
2039
+ `${messagePrefix}${castError.message}`,
2040
+ requestData,
2041
+ status,
2042
+ shouldRetry4,
2043
+ error instanceof Error ? error : void 0
2044
+ );
2045
+ };
2046
+
1955
2047
  // src/lib/openai/tokenizer.ts
1956
2048
  var import_js_tiktoken = require("js-tiktoken");
1957
2049
  var cl100kTokenizer = (0, import_js_tiktoken.getEncoding)("cl100k_base");
@@ -2031,7 +2123,7 @@ async function buildChatMessages(ctx, children, opts) {
2031
2123
  const nodes = await toXml(ctx, children);
2032
2124
  const handleUserMessage = async (node) => {
2033
2125
  const childNodes = node.childNodes;
2034
- const builder = userChatMessageBuilder({ useBase64: opts.useBase64Images });
2126
+ const builder = userChatMessageBuilder(opts);
2035
2127
  for (const n of childNodes) {
2036
2128
  if (n.nodeName === "#text") {
2037
2129
  builder.text(n.value);
@@ -2063,36 +2155,6 @@ async function buildChatMessages(ctx, children, opts) {
2063
2155
  });
2064
2156
  }
2065
2157
 
2066
- // src/lib/openai/errors.ts
2067
- var import_openai = require("openai");
2068
- var extractStatusFromError = (error) => {
2069
- if (error instanceof import_openai.OpenAI.APIError) {
2070
- return error.status;
2071
- } else if (error instanceof import_openai.OpenAI.APIConnectionError) {
2072
- return void 0;
2073
- } else {
2074
- return void 0;
2075
- }
2076
- };
2077
- var errorToChatCompletionError = (error, requestData) => {
2078
- const castError = castToError(error);
2079
- const status = extractStatusFromError(castError);
2080
- let messagePrefix = "";
2081
- if (error instanceof import_openai.OpenAI.APIError) {
2082
- messagePrefix = "OpenAIClient.APIError: ";
2083
- } else if (error instanceof import_openai.OpenAI.APIConnectionError) {
2084
- messagePrefix = "OpenAIClient.APIConnectionError: ";
2085
- }
2086
- const shouldRetry4 = status !== 400;
2087
- return new ChatCompletionError(
2088
- `${messagePrefix}${castError.message}`,
2089
- requestData,
2090
- status,
2091
- shouldRetry4,
2092
- error instanceof Error ? error : void 0
2093
- );
2094
- };
2095
-
2096
2158
  // src/lib/openai/OpenAI.tsx
2097
2159
  var defaultClient = null;
2098
2160
  var OpenAIClientContext = createContext(async () => {
@@ -2130,7 +2192,7 @@ function buildOpenAIMessages(chatMessages) {
2130
2192
  };
2131
2193
  }
2132
2194
  if (part.type === "image") {
2133
- const url = part.image.url || part.image.data;
2195
+ const url = part.image.url ?? buildDataUri(part.image.mediaType, part.image.data);
2134
2196
  return {
2135
2197
  type: "image_url",
2136
2198
  image_url: {
@@ -2155,12 +2217,13 @@ var shouldRetry = (error) => {
2155
2217
  };
2156
2218
  function OpenAIChatCompletion(props, ctx) {
2157
2219
  const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
2220
+ const shouldAccumulate = props.stream === false;
2158
2221
  return /* @__PURE__ */ jsx(
2159
2222
  Retry,
2160
2223
  {
2161
2224
  maxRetries: props.maxRetries || defaultMaxRetries,
2162
2225
  shouldRetry,
2163
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) })
2226
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: shouldAccumulate, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) }) })
2164
2227
  }
2165
2228
  );
2166
2229
  }
@@ -2446,12 +2509,13 @@ var extractStatusFromError2 = (error) => {
2446
2509
  };
2447
2510
  function AnthropicChatCompletion(props, ctx) {
2448
2511
  const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
2512
+ const shouldAccumulate = props.stream === false;
2449
2513
  return /* @__PURE__ */ jsx(
2450
2514
  Retry,
2451
2515
  {
2452
2516
  maxRetries: props.maxRetries || defaultMaxRetries,
2453
2517
  shouldRetry: shouldRetry2,
2454
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(AnthropicChatCompletionInner, { ...props }) })
2518
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: shouldAccumulate, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(AnthropicChatCompletionInner, { ...props }) }) })
2455
2519
  }
2456
2520
  );
2457
2521
  }
@@ -2761,7 +2825,7 @@ function GoogleChatCompletion(props, ctx) {
2761
2825
  {
2762
2826
  maxRetries: props.maxRetries || defaultMaxRetries,
2763
2827
  shouldRetry: shouldRetry3,
2764
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(GoogleChatCompletionInner, { ...props }) })
2828
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: props.stream === false, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(GoogleChatCompletionInner, { ...props }) }) })
2765
2829
  }
2766
2830
  );
2767
2831
  }
@@ -2946,6 +3010,7 @@ var import_vertexai2 = require("@google-cloud/vertexai");
2946
3010
  0 && (module.exports = {
2947
3011
  AIFragment,
2948
3012
  AISpanProcessor,
3013
+ Accumulate,
2949
3014
  AnthropicChatCompletion,
2950
3015
  AnthropicClient,
2951
3016
  AnthropicClientContext,
package/dist/index.mjs CHANGED
@@ -19,6 +19,12 @@ var ChatCompletionError = class extends Error {
19
19
  };
20
20
 
21
21
  // src/chat/image.ts
22
+ var SUPPORTED_TYPES = [
23
+ "image/jpeg",
24
+ "image/png",
25
+ "image/gif",
26
+ "image/webp"
27
+ ];
22
28
  var IMAGE_RENDERED_PROPS = {
23
29
  ImagePart: {
24
30
  url: true,
@@ -34,8 +40,7 @@ var ImagePart = (_props) => {
34
40
  async function fetchImageAndConvertToBase64(url) {
35
41
  const response = await fetch(url);
36
42
  const contentType = response.headers.get("content-type");
37
- const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
38
- if (!contentType || !allowedTypes.includes(contentType)) {
43
+ if (!contentType || !RE_ALLOWED_MEDIA_TYPES.test(contentType)) {
39
44
  throw new Error(`Unsupported media type: ${contentType}`);
40
45
  }
41
46
  const blob = await response.blob();
@@ -46,10 +51,40 @@ async function fetchImageAndConvertToBase64(url) {
46
51
  mediaType: contentType
47
52
  };
48
53
  }
49
- async function processImageMessageProps(props, downloadUrl) {
54
+ var RE_ALLOWED_MEDIA_TYPES = new RegExp(
55
+ `^(${SUPPORTED_TYPES.join("|")})$`,
56
+ "i"
57
+ );
58
+ var RE_DATA_IMAGE = new RegExp(
59
+ `^data:(${SUPPORTED_TYPES.join("|")});base64,(.+)$`,
60
+ "i"
61
+ );
62
+ var parseDataUrl = (dataUrl) => {
63
+ const matches = dataUrl.match(RE_DATA_IMAGE);
64
+ if (!matches) {
65
+ return null;
66
+ }
67
+ const [, mediaType, base64] = matches;
68
+ return {
69
+ mediaType,
70
+ base64
71
+ };
72
+ };
73
+ async function processImageMessageProps(props, opts) {
50
74
  const { dimensions, detail, ...rest } = props;
51
- if ("url" in rest) {
52
- if (downloadUrl) {
75
+ if ("url" in rest && rest.url) {
76
+ const parsed = parseDataUrl(rest.url);
77
+ if (parsed) {
78
+ const { mediaType, base64 } = parsed;
79
+ return {
80
+ url: null,
81
+ data: base64,
82
+ mediaType,
83
+ dimensions,
84
+ detail
85
+ };
86
+ }
87
+ if (opts.downloadUrl) {
53
88
  const { base64, mediaType } = await fetchImageAndConvertToBase64(rest.url);
54
89
  return {
55
90
  url: rest.url,
@@ -68,6 +103,16 @@ async function processImageMessageProps(props, downloadUrl) {
68
103
  };
69
104
  }
70
105
  if ("data" in rest) {
106
+ const parsed = parseDataUrl(rest.data);
107
+ if (parsed) {
108
+ return {
109
+ url: null,
110
+ mediaType: parsed.mediaType,
111
+ data: parsed.base64,
112
+ dimensions,
113
+ detail
114
+ };
115
+ }
71
116
  return {
72
117
  url: null,
73
118
  mediaType: rest.mediaType,
@@ -89,6 +134,9 @@ var cleanProps = (props) => {
89
134
  }
90
135
  return res;
91
136
  };
137
+ var buildDataUri = (mediaType, base64) => {
138
+ return `data:${mediaType};base64,${base64}`;
139
+ };
92
140
 
93
141
  // src/chat/ChatMessage.ts
94
142
  var toDebugMessage = (message) => {
@@ -115,8 +163,8 @@ var toDebugMessage = (message) => {
115
163
  };
116
164
  };
117
165
  var UserChatMessageBuilder = class {
118
- constructor(useBase64) {
119
- this.useBase64 = useBase64;
166
+ constructor(opts) {
167
+ this.opts = opts;
120
168
  }
121
169
  content = [];
122
170
  text(text) {
@@ -136,7 +184,9 @@ var UserChatMessageBuilder = class {
136
184
  text: part
137
185
  };
138
186
  } else {
139
- const image = await processImageMessageProps(part, this.useBase64);
187
+ const image = await processImageMessageProps(part, {
188
+ downloadUrl: this.opts.useBase64Images
189
+ });
140
190
  return {
141
191
  type: "image",
142
192
  image
@@ -151,7 +201,7 @@ var UserChatMessageBuilder = class {
151
201
  }
152
202
  };
153
203
  var userChatMessageBuilder = (opts) => {
154
- return new UserChatMessageBuilder(opts.useBase64);
204
+ return new UserChatMessageBuilder(opts);
155
205
  };
156
206
 
157
207
  // src/chat/components.ts
@@ -1501,6 +1551,17 @@ async function* Retry({ shouldRetry: shouldRetry4, retries = 0, maxRetries = 3,
1501
1551
  );
1502
1552
  }
1503
1553
  }
1554
+ async function Accumulate({ enabled = true, children }, ctx) {
1555
+ if (!enabled) {
1556
+ return children;
1557
+ }
1558
+ let accum = "";
1559
+ const stream = ctx.render(children);
1560
+ for await (const value of stream) {
1561
+ accum += value;
1562
+ }
1563
+ return accum;
1564
+ }
1504
1565
  var BASE_BACKOFF = 100;
1505
1566
  var backoff = (retries) => {
1506
1567
  const waitTime = BASE_BACKOFF * Math.pow(4, retries);
@@ -1850,6 +1911,36 @@ function isPromptParsed2(prompt) {
1850
1911
  // src/lib/openai/OpenAI.tsx
1851
1912
  import { OpenAI as OpenAIClient2 } from "openai";
1852
1913
 
1914
+ // src/lib/openai/errors.ts
1915
+ import { OpenAI as OpenAIClient } from "openai";
1916
+ var extractStatusFromError = (error) => {
1917
+ if (error instanceof OpenAIClient.APIError) {
1918
+ return error.status;
1919
+ } else if (error instanceof OpenAIClient.APIConnectionError) {
1920
+ return void 0;
1921
+ } else {
1922
+ return void 0;
1923
+ }
1924
+ };
1925
+ var errorToChatCompletionError = (error, requestData) => {
1926
+ const castError = castToError(error);
1927
+ const status = extractStatusFromError(castError);
1928
+ let messagePrefix = "";
1929
+ if (error instanceof OpenAIClient.APIError) {
1930
+ messagePrefix = "OpenAIClient.APIError: ";
1931
+ } else if (error instanceof OpenAIClient.APIConnectionError) {
1932
+ messagePrefix = "OpenAIClient.APIConnectionError: ";
1933
+ }
1934
+ const shouldRetry4 = status !== 400;
1935
+ return new ChatCompletionError(
1936
+ `${messagePrefix}${castError.message}`,
1937
+ requestData,
1938
+ status,
1939
+ shouldRetry4,
1940
+ error instanceof Error ? error : void 0
1941
+ );
1942
+ };
1943
+
1853
1944
  // src/lib/openai/tokenizer.ts
1854
1945
  import { getEncoding } from "js-tiktoken";
1855
1946
  var cl100kTokenizer = getEncoding("cl100k_base");
@@ -1929,7 +2020,7 @@ async function buildChatMessages(ctx, children, opts) {
1929
2020
  const nodes = await toXml(ctx, children);
1930
2021
  const handleUserMessage = async (node) => {
1931
2022
  const childNodes = node.childNodes;
1932
- const builder = userChatMessageBuilder({ useBase64: opts.useBase64Images });
2023
+ const builder = userChatMessageBuilder(opts);
1933
2024
  for (const n of childNodes) {
1934
2025
  if (n.nodeName === "#text") {
1935
2026
  builder.text(n.value);
@@ -1961,36 +2052,6 @@ async function buildChatMessages(ctx, children, opts) {
1961
2052
  });
1962
2053
  }
1963
2054
 
1964
- // src/lib/openai/errors.ts
1965
- import { OpenAI as OpenAIClient } from "openai";
1966
- var extractStatusFromError = (error) => {
1967
- if (error instanceof OpenAIClient.APIError) {
1968
- return error.status;
1969
- } else if (error instanceof OpenAIClient.APIConnectionError) {
1970
- return void 0;
1971
- } else {
1972
- return void 0;
1973
- }
1974
- };
1975
- var errorToChatCompletionError = (error, requestData) => {
1976
- const castError = castToError(error);
1977
- const status = extractStatusFromError(castError);
1978
- let messagePrefix = "";
1979
- if (error instanceof OpenAIClient.APIError) {
1980
- messagePrefix = "OpenAIClient.APIError: ";
1981
- } else if (error instanceof OpenAIClient.APIConnectionError) {
1982
- messagePrefix = "OpenAIClient.APIConnectionError: ";
1983
- }
1984
- const shouldRetry4 = status !== 400;
1985
- return new ChatCompletionError(
1986
- `${messagePrefix}${castError.message}`,
1987
- requestData,
1988
- status,
1989
- shouldRetry4,
1990
- error instanceof Error ? error : void 0
1991
- );
1992
- };
1993
-
1994
2055
  // src/lib/openai/OpenAI.tsx
1995
2056
  var defaultClient = null;
1996
2057
  var OpenAIClientContext = createContext(async () => {
@@ -2028,7 +2089,7 @@ function buildOpenAIMessages(chatMessages) {
2028
2089
  };
2029
2090
  }
2030
2091
  if (part.type === "image") {
2031
- const url = part.image.url || part.image.data;
2092
+ const url = part.image.url ?? buildDataUri(part.image.mediaType, part.image.data);
2032
2093
  return {
2033
2094
  type: "image_url",
2034
2095
  image_url: {
@@ -2053,12 +2114,13 @@ var shouldRetry = (error) => {
2053
2114
  };
2054
2115
  function OpenAIChatCompletion(props, ctx) {
2055
2116
  const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
2117
+ const shouldAccumulate = props.stream === false;
2056
2118
  return /* @__PURE__ */ jsx(
2057
2119
  Retry,
2058
2120
  {
2059
2121
  maxRetries: props.maxRetries || defaultMaxRetries,
2060
2122
  shouldRetry,
2061
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) })
2123
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: shouldAccumulate, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(OpenAIChatCompletionInner, { ...props }) }) })
2062
2124
  }
2063
2125
  );
2064
2126
  }
@@ -2344,12 +2406,13 @@ var extractStatusFromError2 = (error) => {
2344
2406
  };
2345
2407
  function AnthropicChatCompletion(props, ctx) {
2346
2408
  const defaultMaxRetries = ctx.getContext(DefaultMaxRetriesContext);
2409
+ const shouldAccumulate = props.stream === false;
2347
2410
  return /* @__PURE__ */ jsx(
2348
2411
  Retry,
2349
2412
  {
2350
2413
  maxRetries: props.maxRetries || defaultMaxRetries,
2351
2414
  shouldRetry: shouldRetry2,
2352
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(AnthropicChatCompletionInner, { ...props }) })
2415
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: shouldAccumulate, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(AnthropicChatCompletionInner, { ...props }) }) })
2353
2416
  }
2354
2417
  );
2355
2418
  }
@@ -2663,7 +2726,7 @@ function GoogleChatCompletion(props, ctx) {
2663
2726
  {
2664
2727
  maxRetries: props.maxRetries || defaultMaxRetries,
2665
2728
  shouldRetry: shouldRetry3,
2666
- children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(GoogleChatCompletionInner, { ...props }) })
2729
+ children: /* @__PURE__ */ jsx(Accumulate, { enabled: props.stream === false, children: /* @__PURE__ */ jsx(Trace, { name: "ai.chatCompletion", children: /* @__PURE__ */ jsx(GoogleChatCompletionInner, { ...props }) }) })
2667
2730
  }
2668
2731
  );
2669
2732
  }
@@ -2851,6 +2914,7 @@ import {
2851
2914
  export {
2852
2915
  AIFragment,
2853
2916
  AISpanProcessor,
2917
+ Accumulate,
2854
2918
  AnthropicChatCompletion,
2855
2919
  AnthropicClient2 as AnthropicClient,
2856
2920
  AnthropicClientContext,
@@ -1,6 +1,7 @@
1
1
  import { ZodTypeAny, ZodObject, ZodRawShape } from 'zod';
2
2
 
3
- type ImageMediaType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
3
+ declare const SUPPORTED_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp"];
4
+ type ImageMediaType = (typeof SUPPORTED_TYPES)[number];
4
5
  type ImageMessageExtraProps = {
5
6
  dimensions?: {
6
7
  width: number;
@@ -1,6 +1,7 @@
1
1
  import { ZodTypeAny, ZodObject, ZodRawShape } from 'zod';
2
2
 
3
- type ImageMediaType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
3
+ declare const SUPPORTED_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp"];
4
+ type ImageMediaType = (typeof SUPPORTED_TYPES)[number];
4
5
  type ImageMessageExtraProps = {
5
6
  dimensions?: {
6
7
  width: number;
@@ -1,2 +1,2 @@
1
- export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-S--1HQKU.mjs';
1
+ export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-lYsmphqH.mjs';
2
2
  import 'zod';
@@ -1,2 +1,2 @@
1
- export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-S--1HQKU.js';
1
+ export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-lYsmphqH.js';
2
2
  import 'zod';
@@ -1,2 +1,2 @@
1
- export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-S--1HQKU.mjs';
1
+ export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-lYsmphqH.mjs';
2
2
  import 'zod';
@@ -1,2 +1,2 @@
1
- export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-S--1HQKU.js';
1
+ export { Z as Fragment, J as JSX, W as jsx, X as jsxDEV, Y as jsxs } from './jsx-dev-runtime-lYsmphqH.js';
2
2
  import 'zod';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gammatech/aijsx",
3
- "version": "0.13.0-dev.2024-07-15",
3
+ "version": "0.15.0-dev.2024-07-30",
4
4
  "description": "Rewrite of aijsx",
5
5
  "author": "Jordan Garcia",
6
6
  "license": "MIT",
@@ -58,6 +58,7 @@
58
58
  "eslint-plugin-prettier": "^4.2.1",
59
59
  "eslint-plugin-react": "^7.24.0",
60
60
  "eslint-plugin-react-hooks": "^4.2.0",
61
+ "fetch-mock-jest": "^1.5.1",
61
62
  "jest": "^29.7.0",
62
63
  "prettier": "^2.2.1",
63
64
  "tinybench": "^2.5.1",