@gammatech/aijsx 0.1.2 → 0.2.0-vision.2

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.
@@ -112,8 +112,8 @@ declare class CombinedLogger extends LogImplementation {
112
112
  private readonly loggers;
113
113
  constructor(loggers: LogImplementation[]);
114
114
  log(...args: Parameters<LogImplementation['log']>): void;
115
- chatCompletionRequest<K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionRequest']>): void;
116
- chatCompletionResponse<K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionResponse']>): void;
115
+ chatCompletionRequest<_K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionRequest']>): void;
116
+ chatCompletionResponse<_K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionResponse']>): void;
117
117
  }
118
118
 
119
119
  type ChatCompletionRole = 'user' | 'system' | 'assistant';
@@ -156,4 +156,4 @@ declare function AIFragment({ children }: {
156
156
  children: AINode;
157
157
  }): Renderable;
158
158
 
159
- export { type AIElement as A, BoundLogger as B, type Context as C, LogImplementation as L, NoopLogImplementation as N, type PropsOfAIComponent as P, type RenderContext as R, SystemMessage as S, UserMessage as U, type RenderedConversationMessage as a, AIFragment as b, createAIElement as c, LoggerContext as d, createContext as e, AssistantMessage as f, type ConversationMessage as g, childrenToConversationMessage as h, computeUsage as i, ChatCompletionError as j, type ChatCompletionRequestPayloads as k, type LogChatCompletionRequest as l, type LogChatCompletionResponse as m, type LogLevel as n, type Logger as o, ConsoleLogger as p, CombinedLogger as q, type Literal as r, type RenderableStream as s, type RenderResult as t, type AIComponent as u, attachedContextSymbol as v, type AINode as w, type Renderable as x };
159
+ export { type AIComponent as A, BoundLogger as B, type Context as C, LogImplementation as L, NoopLogImplementation as N, type PropsOfAIComponent as P, type RenderContext as R, SystemMessage as S, UserMessage as U, type AINode as a, type AIElement as b, createAIElement as c, AIFragment as d, LoggerContext as e, createContext as f, AssistantMessage as g, type ConversationMessage as h, type RenderedConversationMessage as i, childrenToConversationMessage as j, computeUsage as k, ChatCompletionError as l, type ChatCompletionRequestPayloads as m, type LogChatCompletionRequest as n, type LogChatCompletionResponse as o, type LogLevel as p, type Logger as q, ConsoleLogger as r, CombinedLogger as s, type Literal as t, type RenderableStream as u, type RenderResult as v, attachedContextSymbol as w, type Renderable as x };
@@ -112,8 +112,8 @@ declare class CombinedLogger extends LogImplementation {
112
112
  private readonly loggers;
113
113
  constructor(loggers: LogImplementation[]);
114
114
  log(...args: Parameters<LogImplementation['log']>): void;
115
- chatCompletionRequest<K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionRequest']>): void;
116
- chatCompletionResponse<K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionResponse']>): void;
115
+ chatCompletionRequest<_K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionRequest']>): void;
116
+ chatCompletionResponse<_K extends keyof ChatCompletionRequestPayloads>(...args: Parameters<LogImplementation['chatCompletionResponse']>): void;
117
117
  }
118
118
 
119
119
  type ChatCompletionRole = 'user' | 'system' | 'assistant';
@@ -156,4 +156,4 @@ declare function AIFragment({ children }: {
156
156
  children: AINode;
157
157
  }): Renderable;
158
158
 
159
- export { type AIElement as A, BoundLogger as B, type Context as C, LogImplementation as L, NoopLogImplementation as N, type PropsOfAIComponent as P, type RenderContext as R, SystemMessage as S, UserMessage as U, type RenderedConversationMessage as a, AIFragment as b, createAIElement as c, LoggerContext as d, createContext as e, AssistantMessage as f, type ConversationMessage as g, childrenToConversationMessage as h, computeUsage as i, ChatCompletionError as j, type ChatCompletionRequestPayloads as k, type LogChatCompletionRequest as l, type LogChatCompletionResponse as m, type LogLevel as n, type Logger as o, ConsoleLogger as p, CombinedLogger as q, type Literal as r, type RenderableStream as s, type RenderResult as t, type AIComponent as u, attachedContextSymbol as v, type AINode as w, type Renderable as x };
159
+ export { type AIComponent as A, BoundLogger as B, type Context as C, LogImplementation as L, NoopLogImplementation as N, type PropsOfAIComponent as P, type RenderContext as R, SystemMessage as S, UserMessage as U, type AINode as a, type AIElement as b, createAIElement as c, AIFragment as d, LoggerContext as e, createContext as f, AssistantMessage as g, type ConversationMessage as h, type RenderedConversationMessage as i, childrenToConversationMessage as j, computeUsage as k, ChatCompletionError as l, type ChatCompletionRequestPayloads as m, type LogChatCompletionRequest as n, type LogChatCompletionResponse as o, type LogLevel as p, type Logger as q, ConsoleLogger as r, CombinedLogger as s, type Literal as t, type RenderableStream as u, type RenderResult as v, attachedContextSymbol as w, type Renderable as x };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import { L as LogImplementation, R as RenderContext, C as Context, A as AIElement, a as RenderedConversationMessage } from './createElement-Q_LxUYf8.mjs';
2
- export { u as AIComponent, b as AIFragment, w as AINode, f as AssistantMessage, B as BoundLogger, j as ChatCompletionError, k as ChatCompletionRequestPayloads, q as CombinedLogger, p as ConsoleLogger, g as ConversationMessage, r as Literal, l as LogChatCompletionRequest, m as LogChatCompletionResponse, n as LogLevel, o as Logger, d as LoggerContext, N as NoopLogImplementation, P as PropsOfAIComponent, t as RenderResult, x as Renderable, s as RenderableStream, S as SystemMessage, U as UserMessage, v as attachedContextSymbol, h as childrenToConversationMessage, i as computeUsage, c as createAIElement, e as createContext } from './createElement-Q_LxUYf8.mjs';
1
+ import { L as LogImplementation, R as RenderContext, A as AIComponent, C as Context, a as AINode, b as AIElement } from './createElement-y5NHsp6D.mjs';
2
+ export { d as AIFragment, g as AssistantMessage, B as BoundLogger, l as ChatCompletionError, m as ChatCompletionRequestPayloads, s as CombinedLogger, r as ConsoleLogger, h as ConversationMessage, t as Literal, n as LogChatCompletionRequest, o as LogChatCompletionResponse, p as LogLevel, q as Logger, e as LoggerContext, N as NoopLogImplementation, P as PropsOfAIComponent, v as RenderResult, x as Renderable, u as RenderableStream, i as RenderedConversationMessage, S as SystemMessage, U as UserMessage, w as attachedContextSymbol, j as childrenToConversationMessage, k as computeUsage, c as createAIElement, f as createContext } from './createElement-y5NHsp6D.mjs';
3
3
  import { OpenAI } from 'openai';
4
4
  export { OpenAI as OpenAIClient } from 'openai';
5
+ import { ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam, ChatCompletionAssistantMessageParam } from 'openai/resources';
5
6
  import AnthropicClient from '@anthropic-ai/sdk';
6
7
  export { default as AnthropicClient } from '@anthropic-ai/sdk';
7
8
  export { countTokens as countAnthropicTokens } from '@anthropic-ai/tokenizer';
@@ -12,12 +13,21 @@ declare function createRenderContext({ logger, rootRenderId, }?: {
12
13
  }): RenderContext;
13
14
 
14
15
  type OpenAIChatCompletionRequest = OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming;
16
+ type OpenAIChatMessage = ChatCompletionSystemMessageParam | ChatCompletionUserMessageParam | ChatCompletionAssistantMessageParam;
15
17
  declare module '@gammatech/aijsx' {
16
18
  interface ChatCompletionRequestPayloads {
17
19
  openai: OpenAIChatCompletionRequest;
18
20
  }
19
21
  }
20
- type ValidOpenAIChatModel = 'gpt-4' | 'gpt-4-0314' | 'gpt-4-0613' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-32k-0613' | 'gpt-4-1106-preview' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-3.5-turbo-1106';
22
+ type UserMessageWithImagesProps = {
23
+ children: AINode;
24
+ images: {
25
+ url: string;
26
+ detail?: 'low' | 'high' | 'auto';
27
+ }[];
28
+ };
29
+ declare const UserMessageWithImages: AIComponent<UserMessageWithImagesProps>;
30
+ type ValidOpenAIChatModel = 'gpt-4' | 'gpt-4-0314' | 'gpt-4-0613' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-32k-0613' | 'gpt-4-1106-preview' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-3.5-turbo-1106' | 'gpt-4-vision-preview';
21
31
  declare const OpenAIClientContext: Context<() => OpenAI>;
22
32
  type OpenAIChatCompletionProps = {
23
33
  model: ValidOpenAIChatModel;
@@ -34,7 +44,7 @@ declare const tokenizer: {
34
44
  decode: (tokens: number[]) => string;
35
45
  };
36
46
  declare function tokenLimitForChatModel(model: ValidOpenAIChatModel): number | undefined;
37
- declare function tokenCountForConversationMessage(message: Pick<RenderedConversationMessage, 'type' | 'content'>): number;
47
+ declare function tokenCountForChatMessage(message: OpenAIChatMessage): number;
38
48
 
39
49
  type AnthropicChatCompletionRequest = AnthropicClient.CompletionCreateParams;
40
50
  declare module '@gammatech/aijsx' {
@@ -69,4 +79,4 @@ type AnthropicChatCompletionProps = {
69
79
  */
70
80
  declare function AnthropicChatCompletion(props: AnthropicChatCompletionProps, { render, logger, getContext }: RenderContext): AsyncGenerator<string, void, unknown>;
71
81
 
72
- export { AIElement, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, Context, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, OpenAIClientContext, RenderContext, RenderedConversationMessage, type ValidAnthropicChatModel, type ValidOpenAIChatModel, createRenderContext, defaultMaxTokens, tokenCountForConversationMessage, tokenLimitForChatModel, tokenizer };
82
+ export { AIComponent, AIElement, AINode, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, Context, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, RenderContext, UserMessageWithImages, type ValidAnthropicChatModel, type ValidOpenAIChatModel, createRenderContext, defaultMaxTokens, tokenCountForChatMessage, tokenLimitForChatModel, tokenizer };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { L as LogImplementation, R as RenderContext, C as Context, A as AIElement, a as RenderedConversationMessage } from './createElement-Q_LxUYf8.js';
2
- export { u as AIComponent, b as AIFragment, w as AINode, f as AssistantMessage, B as BoundLogger, j as ChatCompletionError, k as ChatCompletionRequestPayloads, q as CombinedLogger, p as ConsoleLogger, g as ConversationMessage, r as Literal, l as LogChatCompletionRequest, m as LogChatCompletionResponse, n as LogLevel, o as Logger, d as LoggerContext, N as NoopLogImplementation, P as PropsOfAIComponent, t as RenderResult, x as Renderable, s as RenderableStream, S as SystemMessage, U as UserMessage, v as attachedContextSymbol, h as childrenToConversationMessage, i as computeUsage, c as createAIElement, e as createContext } from './createElement-Q_LxUYf8.js';
1
+ import { L as LogImplementation, R as RenderContext, A as AIComponent, C as Context, a as AINode, b as AIElement } from './createElement-y5NHsp6D.js';
2
+ export { d as AIFragment, g as AssistantMessage, B as BoundLogger, l as ChatCompletionError, m as ChatCompletionRequestPayloads, s as CombinedLogger, r as ConsoleLogger, h as ConversationMessage, t as Literal, n as LogChatCompletionRequest, o as LogChatCompletionResponse, p as LogLevel, q as Logger, e as LoggerContext, N as NoopLogImplementation, P as PropsOfAIComponent, v as RenderResult, x as Renderable, u as RenderableStream, i as RenderedConversationMessage, S as SystemMessage, U as UserMessage, w as attachedContextSymbol, j as childrenToConversationMessage, k as computeUsage, c as createAIElement, f as createContext } from './createElement-y5NHsp6D.js';
3
3
  import { OpenAI } from 'openai';
4
4
  export { OpenAI as OpenAIClient } from 'openai';
5
+ import { ChatCompletionSystemMessageParam, ChatCompletionUserMessageParam, ChatCompletionAssistantMessageParam } from 'openai/resources';
5
6
  import AnthropicClient from '@anthropic-ai/sdk';
6
7
  export { default as AnthropicClient } from '@anthropic-ai/sdk';
7
8
  export { countTokens as countAnthropicTokens } from '@anthropic-ai/tokenizer';
@@ -12,12 +13,21 @@ declare function createRenderContext({ logger, rootRenderId, }?: {
12
13
  }): RenderContext;
13
14
 
14
15
  type OpenAIChatCompletionRequest = OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming;
16
+ type OpenAIChatMessage = ChatCompletionSystemMessageParam | ChatCompletionUserMessageParam | ChatCompletionAssistantMessageParam;
15
17
  declare module '@gammatech/aijsx' {
16
18
  interface ChatCompletionRequestPayloads {
17
19
  openai: OpenAIChatCompletionRequest;
18
20
  }
19
21
  }
20
- type ValidOpenAIChatModel = 'gpt-4' | 'gpt-4-0314' | 'gpt-4-0613' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-32k-0613' | 'gpt-4-1106-preview' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-3.5-turbo-1106';
22
+ type UserMessageWithImagesProps = {
23
+ children: AINode;
24
+ images: {
25
+ url: string;
26
+ detail?: 'low' | 'high' | 'auto';
27
+ }[];
28
+ };
29
+ declare const UserMessageWithImages: AIComponent<UserMessageWithImagesProps>;
30
+ type ValidOpenAIChatModel = 'gpt-4' | 'gpt-4-0314' | 'gpt-4-0613' | 'gpt-4-32k' | 'gpt-4-32k-0314' | 'gpt-4-32k-0613' | 'gpt-4-1106-preview' | 'gpt-3.5-turbo' | 'gpt-3.5-turbo-0301' | 'gpt-3.5-turbo-0613' | 'gpt-3.5-turbo-16k' | 'gpt-3.5-turbo-16k-0613' | 'gpt-3.5-turbo-1106' | 'gpt-4-vision-preview';
21
31
  declare const OpenAIClientContext: Context<() => OpenAI>;
22
32
  type OpenAIChatCompletionProps = {
23
33
  model: ValidOpenAIChatModel;
@@ -34,7 +44,7 @@ declare const tokenizer: {
34
44
  decode: (tokens: number[]) => string;
35
45
  };
36
46
  declare function tokenLimitForChatModel(model: ValidOpenAIChatModel): number | undefined;
37
- declare function tokenCountForConversationMessage(message: Pick<RenderedConversationMessage, 'type' | 'content'>): number;
47
+ declare function tokenCountForChatMessage(message: OpenAIChatMessage): number;
38
48
 
39
49
  type AnthropicChatCompletionRequest = AnthropicClient.CompletionCreateParams;
40
50
  declare module '@gammatech/aijsx' {
@@ -69,4 +79,4 @@ type AnthropicChatCompletionProps = {
69
79
  */
70
80
  declare function AnthropicChatCompletion(props: AnthropicChatCompletionProps, { render, logger, getContext }: RenderContext): AsyncGenerator<string, void, unknown>;
71
81
 
72
- export { AIElement, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, Context, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, OpenAIClientContext, RenderContext, RenderedConversationMessage, type ValidAnthropicChatModel, type ValidOpenAIChatModel, createRenderContext, defaultMaxTokens, tokenCountForConversationMessage, tokenLimitForChatModel, tokenizer };
82
+ export { AIComponent, AIElement, AINode, AnthropicChatCompletion, type AnthropicChatCompletionRequest, AnthropicClientContext, Context, LogImplementation, OpenAIChatCompletion, type OpenAIChatCompletionRequest, type OpenAIChatMessage, OpenAIClientContext, RenderContext, UserMessageWithImages, type ValidAnthropicChatModel, type ValidOpenAIChatModel, createRenderContext, defaultMaxTokens, tokenCountForChatMessage, tokenLimitForChatModel, tokenizer };
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ __export(src_exports, {
46
46
  OpenAIClientContext: () => OpenAIClientContext,
47
47
  SystemMessage: () => SystemMessage,
48
48
  UserMessage: () => UserMessage,
49
+ UserMessageWithImages: () => UserMessageWithImages,
49
50
  attachedContextSymbol: () => attachedContextSymbol,
50
51
  childrenToConversationMessage: () => childrenToConversationMessage,
51
52
  computeUsage: () => computeUsage,
@@ -54,7 +55,7 @@ __export(src_exports, {
54
55
  createContext: () => createContext,
55
56
  createRenderContext: () => createRenderContext,
56
57
  defaultMaxTokens: () => defaultMaxTokens,
57
- tokenCountForConversationMessage: () => tokenCountForConversationMessage,
58
+ tokenCountForChatMessage: () => tokenCountForChatMessage,
58
59
  tokenLimitForChatModel: () => tokenLimitForChatModel,
59
60
  tokenizer: () => tokenizer
60
61
  });
@@ -73,7 +74,7 @@ var AssistantMessage = (props) => {
73
74
  var childrenToConversationMessage = (c) => {
74
75
  const children = Array.isArray(c) ? c : [c];
75
76
  return children.map((child) => {
76
- if (child.tag.name === "UserMessage") {
77
+ if (child.tag.name.startsWith("UserMessage")) {
77
78
  return {
78
79
  type: "user",
79
80
  element: child
@@ -568,6 +569,7 @@ function tokenLimitForChatModel(model) {
568
569
  case "gpt-4-0314":
569
570
  case "gpt-4-0613":
570
571
  return 8192 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
572
+ case "gpt-4-vision-preview":
571
573
  case "gpt-4-32k":
572
574
  case "gpt-4-32k-0314":
573
575
  case "gpt-4-32k-0613":
@@ -588,14 +590,23 @@ function tokenLimitForChatModel(model) {
588
590
  }
589
591
  }
590
592
  }
591
- function tokenCountForConversationMessage(message) {
593
+ var DETAIL_COST_PLACEHOLDERS = {
594
+ // auto and high are based on 1024x1024
595
+ auto: 765,
596
+ high: 765,
597
+ low: 85
598
+ };
599
+ function tokenCountForChatMessage(message) {
592
600
  const TOKENS_PER_MESSAGE = 3;
593
- switch (message.type) {
594
- case "assistant":
595
- case "system":
596
- case "user":
597
- return TOKENS_PER_MESSAGE + tokenizer.encode(message.content).length;
598
- }
601
+ const contentTokens = Array.isArray(message.content) ? message.content.reduce((acc, cur) => {
602
+ if ("text" in cur) {
603
+ return acc + tokenizer.encode(cur.text).length;
604
+ }
605
+ const detail = cur.image_url.detail || "auto";
606
+ const detailCost = DETAIL_COST_PLACEHOLDERS[detail];
607
+ return acc + detailCost;
608
+ }, 0) : tokenizer.encode(message.content || "").length;
609
+ return TOKENS_PER_MESSAGE + contentTokens;
599
610
  }
600
611
 
601
612
  // src/jsx-runtime.ts
@@ -607,6 +618,10 @@ function jsx(type, config, maybeKey) {
607
618
  var jsxs = jsx;
608
619
 
609
620
  // src/lib/openai/OpenAI.tsx
621
+ var VISION_MODELS = ["gpt-4-vision-preview"];
622
+ var UserMessageWithImages = (props) => {
623
+ return /* @__PURE__ */ jsx(UserMessage, { children: props.children });
624
+ };
610
625
  var defaultClient = null;
611
626
  var OpenAIClientContext = createContext(() => {
612
627
  if (defaultClient) {
@@ -622,24 +637,78 @@ async function* OpenAIChatCompletion(props, { logger, render, getContext }) {
622
637
  if (!client) {
623
638
  throw new Error("[OpenAI] must supply OpenAI model via context");
624
639
  }
625
- const renderedMessages = await Promise.all(
626
- childrenToConversationMessage(props.children).map(async (message) => {
627
- const partiallyRendered = {
628
- ...message,
629
- content: await render(message.element)
630
- };
640
+ const conversationMessages = childrenToConversationMessage(props.children);
641
+ const chatMessagesWithElement = await Promise.all(
642
+ conversationMessages.map(async (m) => {
643
+ const content2 = await render(m.element);
644
+ if (VISION_MODELS.includes(props.model) && m.element.tag.name === "UserMessageWithImages") {
645
+ const element = m.element;
646
+ return {
647
+ content: [
648
+ {
649
+ type: "text",
650
+ text: content2
651
+ },
652
+ ...element.props.images.map((image) => ({
653
+ type: "image_url",
654
+ image_url: {
655
+ url: image.url,
656
+ detail: image.detail || "auto"
657
+ }
658
+ }))
659
+ ],
660
+ role: "user",
661
+ element
662
+ };
663
+ }
631
664
  return {
632
- ...partiallyRendered,
633
- tokens: tokenCountForConversationMessage(partiallyRendered)
665
+ content: content2,
666
+ role: m.type,
667
+ element: m.element
634
668
  };
635
669
  })
636
670
  );
637
- const chatMessages = renderedMessages.map((m) => {
671
+ const renderedMessages = chatMessagesWithElement.map((chatMessage) => {
672
+ const stringContent = typeof chatMessage.content === "string" ? chatMessage.content : (chatMessage.content || []).map((c) => {
673
+ if (c.type === "text") {
674
+ return c.text;
675
+ }
676
+ if (c.type === "image_url") {
677
+ return `<ContentTypeImage url="${c.image_url.url.slice(
678
+ 0,
679
+ 256
680
+ // in case of base64
681
+ )}" detail="${c.image_url.detail}" />`;
682
+ }
683
+ return "";
684
+ }).join("\n");
638
685
  return {
639
- content: m.content,
640
- role: m.type
686
+ type: chatMessage.role,
687
+ element: chatMessage.element,
688
+ content: stringContent,
689
+ tokens: tokenCountForChatMessage(chatMessage)
641
690
  };
642
691
  });
692
+ const chatMessages = chatMessagesWithElement.map(
693
+ ({ content: content2, role }) => {
694
+ if (role === "user") {
695
+ return {
696
+ content: content2,
697
+ role
698
+ };
699
+ }
700
+ if (role === "system") {
701
+ return {
702
+ content: content2,
703
+ role
704
+ };
705
+ }
706
+ return {
707
+ content: content2,
708
+ role
709
+ };
710
+ }
711
+ );
643
712
  const chatCompletionRequest = {
644
713
  model: props.model,
645
714
  max_tokens: props.maxTokens,
@@ -689,8 +758,8 @@ async function* OpenAIChatCompletion(props, { logger, render, getContext }) {
689
758
  type: "assistant",
690
759
  element: /* @__PURE__ */ jsx(AssistantMessage, { children: content }),
691
760
  content,
692
- tokens: tokenCountForConversationMessage({
693
- type: "assistant",
761
+ tokens: tokenCountForChatMessage({
762
+ role: "assistant",
694
763
  content
695
764
  })
696
765
  };
@@ -845,6 +914,7 @@ var import_tokenizer3 = require("@anthropic-ai/tokenizer");
845
914
  OpenAIClientContext,
846
915
  SystemMessage,
847
916
  UserMessage,
917
+ UserMessageWithImages,
848
918
  attachedContextSymbol,
849
919
  childrenToConversationMessage,
850
920
  computeUsage,
@@ -853,7 +923,7 @@ var import_tokenizer3 = require("@anthropic-ai/tokenizer");
853
923
  createContext,
854
924
  createRenderContext,
855
925
  defaultMaxTokens,
856
- tokenCountForConversationMessage,
926
+ tokenCountForChatMessage,
857
927
  tokenLimitForChatModel,
858
928
  tokenizer
859
929
  });
package/dist/index.mjs CHANGED
@@ -20,7 +20,7 @@ var AssistantMessage = (props) => {
20
20
  var childrenToConversationMessage = (c) => {
21
21
  const children = Array.isArray(c) ? c : [c];
22
22
  return children.map((child) => {
23
- if (child.tag.name === "UserMessage") {
23
+ if (child.tag.name.startsWith("UserMessage")) {
24
24
  return {
25
25
  type: "user",
26
26
  element: child
@@ -489,6 +489,7 @@ function tokenLimitForChatModel(model) {
489
489
  case "gpt-4-0314":
490
490
  case "gpt-4-0613":
491
491
  return 8192 - TOKENS_CONSUMED_BY_REPLY_PREFIX;
492
+ case "gpt-4-vision-preview":
492
493
  case "gpt-4-32k":
493
494
  case "gpt-4-32k-0314":
494
495
  case "gpt-4-32k-0613":
@@ -509,17 +510,30 @@ function tokenLimitForChatModel(model) {
509
510
  }
510
511
  }
511
512
  }
512
- function tokenCountForConversationMessage(message) {
513
+ var DETAIL_COST_PLACEHOLDERS = {
514
+ // auto and high are based on 1024x1024
515
+ auto: 765,
516
+ high: 765,
517
+ low: 85
518
+ };
519
+ function tokenCountForChatMessage(message) {
513
520
  const TOKENS_PER_MESSAGE = 3;
514
- switch (message.type) {
515
- case "assistant":
516
- case "system":
517
- case "user":
518
- return TOKENS_PER_MESSAGE + tokenizer.encode(message.content).length;
519
- }
521
+ const contentTokens = Array.isArray(message.content) ? message.content.reduce((acc, cur) => {
522
+ if ("text" in cur) {
523
+ return acc + tokenizer.encode(cur.text).length;
524
+ }
525
+ const detail = cur.image_url.detail || "auto";
526
+ const detailCost = DETAIL_COST_PLACEHOLDERS[detail];
527
+ return acc + detailCost;
528
+ }, 0) : tokenizer.encode(message.content || "").length;
529
+ return TOKENS_PER_MESSAGE + contentTokens;
520
530
  }
521
531
 
522
532
  // src/lib/openai/OpenAI.tsx
533
+ var VISION_MODELS = ["gpt-4-vision-preview"];
534
+ var UserMessageWithImages = (props) => {
535
+ return /* @__PURE__ */ jsx(UserMessage, { children: props.children });
536
+ };
523
537
  var defaultClient = null;
524
538
  var OpenAIClientContext = createContext(() => {
525
539
  if (defaultClient) {
@@ -535,24 +549,78 @@ async function* OpenAIChatCompletion(props, { logger, render, getContext }) {
535
549
  if (!client) {
536
550
  throw new Error("[OpenAI] must supply OpenAI model via context");
537
551
  }
538
- const renderedMessages = await Promise.all(
539
- childrenToConversationMessage(props.children).map(async (message) => {
540
- const partiallyRendered = {
541
- ...message,
542
- content: await render(message.element)
543
- };
552
+ const conversationMessages = childrenToConversationMessage(props.children);
553
+ const chatMessagesWithElement = await Promise.all(
554
+ conversationMessages.map(async (m) => {
555
+ const content2 = await render(m.element);
556
+ if (VISION_MODELS.includes(props.model) && m.element.tag.name === "UserMessageWithImages") {
557
+ const element = m.element;
558
+ return {
559
+ content: [
560
+ {
561
+ type: "text",
562
+ text: content2
563
+ },
564
+ ...element.props.images.map((image) => ({
565
+ type: "image_url",
566
+ image_url: {
567
+ url: image.url,
568
+ detail: image.detail || "auto"
569
+ }
570
+ }))
571
+ ],
572
+ role: "user",
573
+ element
574
+ };
575
+ }
544
576
  return {
545
- ...partiallyRendered,
546
- tokens: tokenCountForConversationMessage(partiallyRendered)
577
+ content: content2,
578
+ role: m.type,
579
+ element: m.element
547
580
  };
548
581
  })
549
582
  );
550
- const chatMessages = renderedMessages.map((m) => {
583
+ const renderedMessages = chatMessagesWithElement.map((chatMessage) => {
584
+ const stringContent = typeof chatMessage.content === "string" ? chatMessage.content : (chatMessage.content || []).map((c) => {
585
+ if (c.type === "text") {
586
+ return c.text;
587
+ }
588
+ if (c.type === "image_url") {
589
+ return `<ContentTypeImage url="${c.image_url.url.slice(
590
+ 0,
591
+ 256
592
+ // in case of base64
593
+ )}" detail="${c.image_url.detail}" />`;
594
+ }
595
+ return "";
596
+ }).join("\n");
551
597
  return {
552
- content: m.content,
553
- role: m.type
598
+ type: chatMessage.role,
599
+ element: chatMessage.element,
600
+ content: stringContent,
601
+ tokens: tokenCountForChatMessage(chatMessage)
554
602
  };
555
603
  });
604
+ const chatMessages = chatMessagesWithElement.map(
605
+ ({ content: content2, role }) => {
606
+ if (role === "user") {
607
+ return {
608
+ content: content2,
609
+ role
610
+ };
611
+ }
612
+ if (role === "system") {
613
+ return {
614
+ content: content2,
615
+ role
616
+ };
617
+ }
618
+ return {
619
+ content: content2,
620
+ role
621
+ };
622
+ }
623
+ );
556
624
  const chatCompletionRequest = {
557
625
  model: props.model,
558
626
  max_tokens: props.maxTokens,
@@ -602,8 +670,8 @@ async function* OpenAIChatCompletion(props, { logger, render, getContext }) {
602
670
  type: "assistant",
603
671
  element: /* @__PURE__ */ jsx(AssistantMessage, { children: content }),
604
672
  content,
605
- tokens: tokenCountForConversationMessage({
606
- type: "assistant",
673
+ tokens: tokenCountForChatMessage({
674
+ role: "assistant",
607
675
  content
608
676
  })
609
677
  };
@@ -757,6 +825,7 @@ export {
757
825
  OpenAIClientContext,
758
826
  SystemMessage,
759
827
  UserMessage,
828
+ UserMessageWithImages,
760
829
  attachedContextSymbol,
761
830
  childrenToConversationMessage,
762
831
  computeUsage,
@@ -765,7 +834,7 @@ export {
765
834
  createContext,
766
835
  createRenderContext,
767
836
  defaultMaxTokens,
768
- tokenCountForConversationMessage,
837
+ tokenCountForChatMessage,
769
838
  tokenLimitForChatModel,
770
839
  tokenizer
771
840
  };
@@ -1,2 +1,2 @@
1
1
  export { Fragment, JSX, jsx, jsxDEV, jsxs } from './jsx-runtime.mjs';
2
- import './createElement-Q_LxUYf8.mjs';
2
+ import './createElement-y5NHsp6D.mjs';
@@ -1,2 +1,2 @@
1
1
  export { Fragment, JSX, jsx, jsxDEV, jsxs } from './jsx-runtime.js';
2
- import './createElement-Q_LxUYf8.js';
2
+ import './createElement-y5NHsp6D.js';
@@ -1,4 +1,4 @@
1
- import { u as AIComponent, A as AIElement, b as AIFragment } from './createElement-Q_LxUYf8.mjs';
1
+ import { A as AIComponent, b as AIElement, d as AIFragment } from './createElement-y5NHsp6D.mjs';
2
2
 
3
3
  /**
4
4
  * The is used as an import source for ts/js files as the JSX transpile functinos
@@ -1,4 +1,4 @@
1
- import { u as AIComponent, A as AIElement, b as AIFragment } from './createElement-Q_LxUYf8.js';
1
+ import { A as AIComponent, b as AIElement, d as AIFragment } from './createElement-y5NHsp6D.js';
2
2
 
3
3
  /**
4
4
  * The is used as an import source for ts/js files as the JSX transpile functinos
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gammatech/aijsx",
3
- "version": "0.1.2",
3
+ "version": "0.2.0-vision.2",
4
4
  "description": "Rewrite of aijsx",
5
5
  "author": "Jordan Garcia",
6
6
  "license": "MIT",
@@ -13,7 +13,6 @@
13
13
  "test:watch": "jest --watch --verbose",
14
14
  "build": "yarn check-types && yarn clean-symlinks && tsup",
15
15
  "clean-symlinks": "rm ./jsx-* || true",
16
- "symlink": "ln -s ./dist/jsx-runtime.js . && ln -s ./dist/jsx-runtime.d.ts && ln -s ./dist/jsx-runtime.js ./jsx-dev-runtime.js && ln -s ./dist/jsx-runtime.d.ts ./jsx-dev-runtime.d.ts",
17
16
  "prepublishOnly": "yarn build",
18
17
  "lint": "eslint \"{src,test}/**/*.ts\" && yarn check-types",
19
18
  "check-types": "tsc --skipLibCheck --noEmit"