Compare commits

...

1 Commits

Author SHA1 Message Date
Weves
d723bd2920 Fix image gen 2025-09-08 16:19:54 -07:00
3 changed files with 90 additions and 29 deletions

View File

@@ -1,4 +1,3 @@
import json
from typing import Any
from typing import Optional
from typing import Type
@@ -21,12 +20,12 @@ class PydanticType(TypeDecorator):
self, value: Optional[BaseModel], dialect: Any
) -> Optional[dict]:
if value is not None:
return json.loads(value.json())
return value.model_dump()
return None
def process_result_value(
self, value: Optional[dict], dialect: Any
) -> Optional[BaseModel]:
if value is not None:
return self.pydantic_model.parse_obj(value)
return self.pydantic_model.model_validate(value)
return None

View File

@@ -272,11 +272,6 @@ export function AIMessage({
)
: [];
const lastDisplayGroup =
displayGroups.length > 0
? displayGroups[displayGroups.length - 1]
: null;
return (
<>
{/* Render tool groups in multi-tool renderer */}
@@ -296,25 +291,59 @@ export function AIMessage({
)}
{/* Render non-tool groups (messages + image generation) in main area */}
{lastDisplayGroup && (
<RendererComponent
key={lastDisplayGroup.ind}
packets={lastDisplayGroup.packets}
chatState={chatState}
onComplete={() => {
// if we've reverted to final answer not coming, don't set display complete
// this happens when using claude and a tool calling packet comes after
// some message packets
if (finalAnswerComingRef.current) {
setDisplayComplete(true);
{displayGroups.map((displayGroup, index) => {
// Skip rendering empty message packets
const hasContent = displayGroup.packets.some(
(packet) => {
if (
packet.obj.type ===
PacketType.MESSAGE_START ||
packet.obj.type ===
PacketType.MESSAGE_DELTA
) {
return (
(packet.obj as any).content &&
(packet.obj as any).content.trim() !==
""
);
}
}}
animate={false}
stopPacketSeen={stopPacketSeen}
>
{({ content }) => <div>{content}</div>}
</RendererComponent>
)}
// Always render image generation packets
return (
packet.obj.type ===
PacketType.IMAGE_GENERATION_TOOL_START ||
packet.obj.type ===
PacketType.IMAGE_GENERATION_TOOL_DELTA
);
}
);
if (!hasContent) {
return null;
}
return (
<RendererComponent
key={displayGroup.ind}
packets={displayGroup.packets}
chatState={chatState}
onComplete={() => {
// if we've reverted to final answer not coming, don't set display complete
// this happens when using claude and a tool calling packet comes after
// some message packets
if (
finalAnswerComingRef.current &&
index === displayGroups.length - 1
) {
setDisplayComplete(true);
}
}}
animate={false}
stopPacketSeen={stopPacketSeen}
>
{({ content }) => <div>{content}</div>}
</RendererComponent>
);
})}
</>
);
})()

View File

@@ -113,9 +113,42 @@ function MultiToolRenderer({
const [isStreamingExpanded, setIsStreamingExpanded] = useState(false);
const toolGroups = useMemo(() => {
return packetGroups.filter(
(group) => group.packets[0] && isToolPacket(group.packets[0])
);
return packetGroups.filter((group) => {
if (!group.packets[0] || !isToolPacket(group.packets[0])) {
return false;
}
// Filter out groups that only contain empty reasoning or empty messages
const hasContent = group.packets.some((packet) => {
const type = packet.obj.type;
const obj = packet.obj as any;
// Check if reasoning has content
if (
type === "reasoning_delta" &&
obj.reasoning &&
obj.reasoning.trim() !== ""
) {
return true;
}
// Check if it's a non-empty tool
if (
type === "search_tool_start" ||
type === "search_tool_delta" ||
type === "custom_tool_start" ||
type === "custom_tool_delta" ||
type === "image_generation_tool_start" ||
type === "image_generation_tool_delta"
) {
return true;
}
return false;
});
return hasContent;
});
}, [packetGroups]);
// Use the custom hook to manage tool display timing