https://github.com/jotaijs/jotai-ai
A Jōtai utility package compatible with Vercel AI SDK
https://github.com/jotaijs/jotai-ai
Last synced: 6 months ago
JSON representation
A Jōtai utility package compatible with Vercel AI SDK
- Host: GitHub
- URL: https://github.com/jotaijs/jotai-ai
- Owner: jotaijs
- License: mit
- Created: 2023-11-20T01:44:29.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-11-18T05:55:30.000Z (8 months ago)
- Last Synced: 2024-12-11T11:39:02.743Z (7 months ago)
- Language: TypeScript
- Homepage:
- Size: 533 KB
- Stars: 49
- Watchers: 1
- Forks: 1
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# AI
[](https://bundlephobia.com/result?p=jotai-ai)
[](https://www.npmjs.com/package/jotai-ai)[jotai-ai](https://github.com/himself65/jotai-ai) is a utility package compatible
with [Vercel AI SDK](https://sdk.vercel.ai/docs).## install
```
yarn add ai jotai-ai
```## chatAtoms
`chatAtoms` is a collection of atoms for a chatbot like [`useChat`](https://sdk.vercel.ai/docs/api-reference/use-chat).
```js
import { useAtomValue, useAtom, useSetAtom } from 'jotai';
import { chatAtoms } from 'jotai-ai';const { messagesAtom, inputAtom, submitAtom, isLoadingAtom } = chatAtoms();
function Messages() {
const messages = useAtomValue(messagesAtom);
return (
<>
{messages.length > 0
? messages.map(m => (
{m.role === 'user' ? 'User: ' : 'AI: '}
{m.content}
))
: null}
>
);
}function ChatInput() {
const [input, handleInputChange] = useAtom(inputAtom);
const handleSubmit = useSetAtom(submitAtom);
return (
);
}function App() {
const isLoading = useAtomValue(isLoadingAtom);
return (
{isLoading ?Loading...: null}
);
}
```### Comparison with `useChat`
#### Less headache
`useChat` is a hook provided by the Vercel AI SDK, which acts as a wrapper for `swr` in React, `swrv` in Vue, and `sswr`
in Svelte.
Each of these has different behaviors in their respective frameworks.
Although `swr` is a powerful tool with a rich set of features designed for data fetching and caching, including
automatic revalidation, request deduplication, and interval polling, the `useChat` hook opts for a simplified
interaction model.
It enables users to post messages with a single click, interpret responses, and maintain an updated
message history, all without tapping into the extensive capabilities of `swr`.However, `chatAtoms` provides a more flexible way to create a chatbot.
Built on the foundation of `jotai` atoms, it
offers an atomic global state management system that is both powerful and flexible.For example, you can customize the `messagesAtom` to add more functionality, such as `clearMessagesAtom`:
```js
const { messagesAtom } = chatAtoms();const clearMessagesAtom = atom(null, async (get, set) => set(messagesAtom, []));
const Actions = () => {
const clear = useSetAtom(clearMessagesAtom);
return Clear Messages;
};
```Also, `chatAtoms` is created out of the Component lifecycle,
so you can share the state between different components easily.```js
const { messagesAtom } = chatAtoms();const Messages = () => {
const messages = useAtomValue(messagesAtom);
return (
{messages.map(m => (
{m.role === 'user' ? 'User: ' : 'AI: '}
{m.content}
))}
);
};const UserMessages = () => {
const messages = useAtomValue(messagesAtom);
return (
{messages
.filter(m => m.role === 'user')
.map(m => (
User: {m.content}
))}
);
};
```#### Load messages on demand with React Suspense
`chatAtoms` also allows you to pass async fetch function to `initialMessage` option, which is not supported
by `useChat`.```js
const { messagesAtom, inputAtom, submitAtom } = chatAtoms({
initialMessages: async () => {
// fetch messages from anywhere
const messages = await fetchMessages();
return messages;
},
});
```With the combination with [`jotai-effect`](https://github.com/jotaijs/jotai-effect),
you can create a chatbot with local storage support.```js
const { messagesAtom } = chatAtoms({
initialMessages: async () => {
/**
* call `noSSR` function if you are using next.js.
* @link https://foxact.skk.moe/no-ssr
*/
// noSSR()
const idb = await import('idb-keyval');
return (await idb.get('messages')) ?? [];
},
});import { atomEffect } from 'jotai-effect';
const saveMessagesEffectAtom = atomEffect((get, set) => {
const messages = get(messagesAtom);
const idbPromise = import('idb-keyval');
const abortController = new AbortController();
idbPromise.then(async idb => {
if (abortController.signal.aborted) {
return;
}
await idb.set('messages', await messages);
});
return () => {
abortController.abort();
};
});const Messages = () => {
const messages = useAtomValue(messagesAtom);
return (
<>
{messages.length > 0
? messages.map(m => (
{m.role === 'user' ? 'User: ' : 'AI: '}
{m.content}
))
: null}
>
);
};const App = () => {
useAtomValue(saveMessagesEffectAtom);
return (
);
};
```## makeChatAtoms
`makeChatAtoms` is a function that creates a set of atoms for a chatbot from `messagesAtom`.
```typescript
import { makeChatAtoms } from 'jotai-ai';const messagesAtom = atom([]);
const {
// state data containers,
isLoadingAtom,
errorAtom,
dataAtom,// actions
stopAtom,
appendAtom,
reloadAtom,// handlers
onErrorAtom,
onResponseAtom,
onToolCallAtom,
onFinishAtom,
} = makeChatAtoms({ messagesAtom });
```## useChat
`useChat` is the equivalent of vercel `ai`'s `useChat` hook.
```tsx
import { useChat } from 'jotai-ai/react';export const App = () => {
const { messages, isPending } = useChat();
return (
{isPending &&Loading...
}
{messages.map((message) => (
{message.text}
))}
);
};
```## LICENSE
[MIT](LICENSE)
[Vercel AI SDK]: https://sdk.vercel.ai/docs