diff --git a/packages/ai-jsx/package.json b/packages/ai-jsx/package.json index 449baf958..d161b8cad 100644 --- a/packages/ai-jsx/package.json +++ b/packages/ai-jsx/package.json @@ -4,7 +4,7 @@ "repository": "fixie-ai/ai-jsx", "bugs": "https://github.com/fixie-ai/ai-jsx/issues", "homepage": "https://ai-jsx.com", - "version": "0.18.2", + "version": "0.18.3", "volta": { "extends": "../../package.json" }, diff --git a/packages/ai-jsx/src/core/conversation.tsx b/packages/ai-jsx/src/core/conversation.tsx index 295f439bd..61ebdcca2 100644 --- a/packages/ai-jsx/src/core/conversation.tsx +++ b/packages/ai-jsx/src/core/conversation.tsx @@ -174,7 +174,7 @@ export function isConversationalComponent(element: AI.Element): boolean { ).includes(element.tag); } -function assertAllElementsAreConversationalComponents(partialRendering: AI.PartiallyRendered[]) { +function assertNoMeaningfulStringContent(partialRendering: AI.PartiallyRendered[]): AI.Element[] { const invalidChildren = partialRendering.filter((el) => typeof el === 'string' && el.trim()) as string[]; if (invalidChildren.length) { throw new AIJSXError( @@ -188,12 +188,12 @@ function assertAllElementsAreConversationalComponents(partialRendering: AI.Parti } ); } + + return partialRendering.filter(AI.isElement); } function toConversationMessages(partialRendering: AI.PartiallyRendered[]): ConversationMessage[] { - assertAllElementsAreConversationalComponents(partialRendering); - - return (partialRendering as AI.Element[]).map((e) => { + return assertNoMeaningfulStringContent(partialRendering).map((e) => { switch (e.tag) { case UserMessage: return { type: 'user', element: e }; @@ -436,18 +436,14 @@ export async function ShrinkConversation( /** Converts a conversational `AI.Node` into a shrinkable tree. */ async function conversationToTreeRoots(conversation: AI.Node): Promise { - const rendered = await render(conversation, { - stop: (e) => isConversationalComponent(e) || e.tag === InternalShrinkable, - }); - - assertAllElementsAreConversationalComponents(rendered); - - const asTreeNodes = await Promise.all( - rendered.map>(async (value) => { - if (typeof value === 'string') { - return null; - } + const rendered = assertNoMeaningfulStringContent( + await render(conversation, { + stop: (e) => isConversationalComponent(e) || e.tag === InternalShrinkable, + }) + ); + return Promise.all( + rendered.map>(async (value) => { if (value.tag === InternalShrinkable) { const children = await conversationToTreeRoots(value.props.children); return { type: 'shrinkable', element: value, cost: aggregateCost(children), children }; @@ -460,8 +456,6 @@ export async function ShrinkConversation( }; }) ); - - return asTreeNodes.filter((n): n is TreeNode => n !== null); } /** Finds the least important node in any of the trees, considering cost as a second factor. */ @@ -531,10 +525,8 @@ export async function ShrinkConversation( stop: (e) => isConversationalComponent(e) || e.tag === InternalShrinkable, }); - assertAllElementsAreConversationalComponents(rendered); - // If there are no shrinkable elements, there's no need to evaluate the cost. - const shrinkableOrConversationElements = rendered.filter(AI.isElement); + const shrinkableOrConversationElements = assertNoMeaningfulStringContent(rendered); if (!shrinkableOrConversationElements.find((value) => value.tag === InternalShrinkable)) { return shrinkableOrConversationElements; } diff --git a/packages/docs/docs/changelog.md b/packages/docs/docs/changelog.md index 3b62e2a64..d3e082c8f 100644 --- a/packages/docs/docs/changelog.md +++ b/packages/docs/docs/changelog.md @@ -1,6 +1,10 @@ # Changelog -## 0.18.2 +## 0.18.3 + +- Fix an issue where empty strings in conversational prompts cause errors to be thrown. + +## [0.18.2](https://github.com/fixie-ai/ai-jsx/commit/fc8ada2d9900b179252d377292835dc28998b86f) - Modified `lib/openai` to preload the tokenizer to avoid a stall on first use - Fixed an issue where `debug(component)` would throw an exception if a component had a prop that could not be JSON-serialized.