nlkitai / nlux

The π—£π—Όπ˜„π—²π—Ώπ—³π˜‚π—Ή Conversational AI JavaScript Library πŸ’¬ β€”Β UI for any LLM, supporting LangChain / HuggingFace / Vercel AI, and more 🧑 React, Next.js, and plain JavaScript ⭐️
https://docs.nlkit.com/nlux
Other
937 stars 48 forks source link

Custom renderer `content` prop is defined as an Array but is a string #75

Closed ScreamZ closed 1 week ago

ScreamZ commented 3 weeks ago

Following what we exchanged on #73 and in the vibe of #74

[!note] Suggestion: It should be an Array all time with one (batch/streaming) or more elements (streaming).

Currently we have

type StreamResponseComponentProps<AiMsg> = {
    uid: string;
    dataTransferMode: 'stream';
    status: 'streaming' | 'complete';
    content?: AiMsg[];
    serverResponse?: unknown[];
    containerRef: RefObject<never>;
};

And if we look at the payload it's a string

image

salmenus commented 3 weeks ago

Investigating. Thanks for reporting @ScreamZ

salmenus commented 3 weeks ago

Fixed in 2.5

Just published

Content is now an array, all the time

salmenus commented 3 weeks ago

Screenshot 2024-06-11 at 13 08 58

ScreamZ commented 3 weeks ago

My current Hybrid renderer:

 <div className="flex flex-col">
      {props.containerRef ? <div ref={props.containerRef} /> : props.content}
      <div className="grid grid-cols-3">
        <button onClick={() => console.log("I like it!")}>πŸ‘</button>
        <button onClick={() => console.log("I love it!")}>❀️</button>
        <button onClick={() => console.log("I hate it!")}>😡</button>
      </div>
    </div>

But we might need to access the markdown parser from nlux, or i could use my own. But we need to mutualize it somewhere. Maybe could be nice to have something, just like we have htmlSanitizer.

I concede I can also just rely on props.content and implement my own logic. Maybe will do something like this along with framer motion.

[!TIP] You can also do this

import { StreamResponseComponentProps } from "@nlux/react";
import React from "react";
import Markdown from "react-markdown";

function _ResponseRenderer(props: StreamResponseComponentProps<string>) {
  return (
    <div className="flex w-full flex-col">
      <Markdown
        components={{
          h1: ({ children }) => <h1 className="text-2xl font-bold">{children}</h1>,
          // ...
        }}
      >
        {props.content?.join("")}
      </Markdown>
      <div className="grid grid-cols-3">
        <button onClick={() => console.log("I like it!")}>πŸ‘</button>
        <button onClick={() => console.log("I love it!")}>❀️</button>
        <button onClick={() => console.log("I hate it!")}>😡</button>
      </div>
    </div>
  );
}

export const ResponseRenderer = React.memo(_ResponseRenderer);
salmenus commented 1 week ago

Alright β€” With <Markdown> ... </Markdown> primitive delivered, now we can consider all the requirements related to content satisfied:

I'm closing this issue, unless you still have further requirements @ScreamZ