Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: a new API design to allow customizations #15

Merged
merged 10 commits into from
Nov 18, 2024

Conversation

lqhuang
Copy link
Contributor

@lqhuang lqhuang commented Nov 15, 2024

Hi,

I'm switching to very similar solutions like jotai-ai (ai sdk to unified api call + jotai for client-side state management) from ai/rsc recently.

Thanks for providing excellent reference implementations. But when try to use it in my case, I feel it's not that easy to adapt widely. I have implemented extended types based CoreMessage (which also could be seen as DB message or server state), but both original useChat and current chatAtoms are all difficult to customize client side message state, I almost need to write almost everything. Actually, the latest reference example ai-chatbot has been using CoreMessage for server state, Message (from @ai/ui-utils) for client state (which should be better to name as UIMessage). I hope the new design would achieve that easier.

This PR implements some basic MVP to demonstrate my ideas:

  1. Split main user interface to two functions makeChatAtoms and useChat. Let useChat expose useful and similar handlers/states like upstream useChat (ai/react), and left makeChatAtoms contains low level atoms for advanced users. Indeed, makeChatAtoms only controls how client interactive with remote stream data (append and receive new messages), and left all others into useChat in order that advanced users could implement theirs inputAtoms, sumbitAtoms. Built-in useChat could be just an official reference implementation has the same features with upstream projects.

  2. Accept a user defined messagesAtom from outside. Like

const messagesAtom = atom<Message[]>([]);

const {
  // state data containers,
  isLoadingAtom,
  errorAtom,
  dataAtom,

  // actions
  stopAtom,
  appendAtom,
  reloadAtom,

  // handlers
  onErrorAtom,
  onResponseAtom,
  onToolCallAtom,
  onFinishAtom,
} = makeChatAtoms({ messagesAtom });

In this way, users could decide how to prepare their UI messages, whatever derived from other states or initialed from fetch or even SSR.

// Initial messages in SSR approach
// so `makeChatAtoms` doesn't need to consider 
// how to deal with initial messages or initial input.
import { useHydrateAtoms } from 'jotai/utils';

const messagesAtom = atom<Message[]>([]);

const Component = ({ initChatMesssages }:{ initChatMesssages: Message[] }) => {
  useHydrateAtoms([[messagesAtom, initChatMesssages]])

  const messages = useAtomValue(messagesAtom)

  return <Chat>{messages}<Chat/>
}
  1. It would be easier to introduce more features and keep flexibility by using this architecture. We're able to add more states like firstTokenReceived or action like deleteAtom in the future without breaking up signatures of useChat.

Also, there are several issues I haven't confirmed and figured out clearly:

  1. In current implements, all deprecated API and parameters are literally deprecated. And the ai project has already released alpha candidates of the next v4 version.

  2. Current callback handler like onErrorAtom, ..., onResponseAtom is initialed by makeChatAtoms and cloud be updated by useChat. I'm not sure whether it's a correct design or proper usage of atoms. For now, it's just implemented to fit upstream UI test cases easily.

  3. I hope we could add a generic type interface for Body into makeChatAtoms. This data type can be shared between server endpoint and user client. It's more safe and ergonomic to make sure correct data being sent and parsed.

That's all. Thanks for your efforts!

Reject or feedback is all appreciate :). Have a nice day 🎉.

Sincerely,
Lanqing

@lqhuang
Copy link
Contributor Author

lqhuang commented Nov 15, 2024

@himself65 Could help me to check why test cases "setData" and "onFinish" cannot pass correctly.

I'm not an expert on React and jotai. My bad 🥲.

@himself65 himself65 changed the title Proposal a new API design to allow customizations feat: a new API design to allow customizations Nov 17, 2024
@himself65
Copy link
Member

Thanks for your contribution! I will check

packages/jotai-ai/package.json Show resolved Hide resolved
packages/jotai-ai/src/make-chat-atoms.ts Outdated Show resolved Hide resolved
packages/jotai-ai/src/make-chat-atoms.ts Outdated Show resolved Hide resolved
Copy link
Member

@himself65 himself65 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, what we can do more in the next steps:

  • combine makeChatAtoms and chatAtoms
  • align with vercel ai latest version
  • add, update examples, docs

@himself65 himself65 merged commit 7127cb3 into jotaijs:main Nov 18, 2024
2 checks passed
@lqhuang
Copy link
Contributor Author

lqhuang commented Nov 18, 2024

Love you 😍

@lqhuang lqhuang deleted the customise-type-of-messages branch November 18, 2024 02:47
@himself65
Copy link
Member

I think ui test failed?

@lqhuang
Copy link
Contributor Author

lqhuang commented Nov 18, 2024

I think ui test failed?

In where, CI or local env?

@lqhuang
Copy link
Contributor Author

lqhuang commented Nov 18, 2024

I see, you haven't fixed setData and onFinish cases yet.

Could help me to check why test cases "setData" and "onFinish" cannot pass correctly.

Yeah, I can't understand why they failed before

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants