Skip to content

Commit

Permalink
Implement custom id generator parameter (#733)
Browse files Browse the repository at this point in the history
Co-authored-by: Max Leiter <max.leiter@vercel.com>
  • Loading branch information
aberezkin and MaxLeiter committed Nov 21, 2023
1 parent 2353533 commit 3ff8a56
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/eighty-spies-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'ai': patch
---

Add `generateId` to use-chat params to allow overriding message ID generation
12 changes: 9 additions & 3 deletions packages/core/react/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
JSONValue,
Message,
UseChatOptions,
IdGenerator,
} from '../shared/types';

import { callApi } from '../shared/call-api';
Expand Down Expand Up @@ -88,6 +89,7 @@ const getStreamedResponse = async (
extraMetadataRef: React.MutableRefObject<any>,
messagesRef: React.MutableRefObject<Message[]>,
abortControllerRef: React.MutableRefObject<AbortController | null>,
generateId: IdGenerator,
onFinish?: (message: Message) => void,
onResponse?: (response: Response) => void | Promise<void>,
sendExtraMessageFields?: boolean,
Expand All @@ -111,7 +113,7 @@ const getStreamedResponse = async (
if (typeof api !== 'string') {
// In this case, we are handling a Server Action. No complex mode handling needed.

const replyId = nanoid();
const replyId = generateId();
const createdAt = new Date();
let responseMessage: Message = {
id: replyId,
Expand Down Expand Up @@ -185,6 +187,7 @@ const getStreamedResponse = async (
mutateStreamData([...(existingData || []), ...(data || [])], false);
},
onFinish,
generateId,
});
};

Expand All @@ -201,6 +204,7 @@ export function useChat({
credentials,
headers,
body,
generateId = nanoid,
}: Omit<UseChatOptions, 'api'> & {
api?: string | StreamingReactResponseAction;
} = {}): UseChatHelpers {
Expand Down Expand Up @@ -274,6 +278,7 @@ export function useChat({
extraMetadataRef,
messagesRef,
abortControllerRef,
generateId,
onFinish,
onResponse,
sendExtraMessageFields,
Expand Down Expand Up @@ -317,6 +322,7 @@ export function useChat({
experimental_onFunctionCall,
messagesRef.current,
abortControllerRef.current,
generateId,
],
);

Expand All @@ -326,7 +332,7 @@ export function useChat({
{ options, functions, function_call, data }: ChatRequestOptions = {},
) => {
if (!message.id) {
message.id = nanoid();
message.id = generateId();
}

const chatRequest: ChatRequest = {
Expand All @@ -339,7 +345,7 @@ export function useChat({

return triggerRequest(chatRequest);
},
[triggerRequest],
[triggerRequest, generateId],
);

const reload = useCallback(
Expand Down
7 changes: 5 additions & 2 deletions packages/core/shared/call-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { nanoid } from 'nanoid';
import { parseComplexResponse } from './parse-complex-response';
import { FunctionCall, JSONValue, Message } from './types';
import { FunctionCall, IdGenerator, JSONValue, Message } from './types';
import { COMPLEX_HEADER, createChunkDecoder } from './utils';

export async function callApi({
Expand All @@ -15,6 +15,7 @@ export async function callApi({
onResponse,
onUpdate,
onFinish,
generateId,
}: {
api: string;
messages: Omit<Message, 'id'>[];
Expand All @@ -27,6 +28,7 @@ export async function callApi({
onResponse?: (response: Response) => void | Promise<void>;
onUpdate: (merged: Message[], data: JSONValue[] | undefined) => void;
onFinish?: (message: Message) => void;
generateId: IdGenerator;
}) {
const response = await fetch(api, {
method: 'POST',
Expand Down Expand Up @@ -78,14 +80,15 @@ export async function callApi({
onFinish(prefixMap.text);
}
},
generateId,
});
} else {
const createdAt = new Date();
const decode = createChunkDecoder(false);

// TODO-STREAMDATA: Remove this once Stream Data is not experimental
let streamedResponse = '';
const replyId = nanoid();
const replyId = generateId();
let responseMessage: Message = {
id: replyId,
createdAt,
Expand Down
8 changes: 8 additions & 0 deletions packages/core/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ interface Function {
description?: string;
}

export type IdGenerator = () => string;

/**
* Shared types between the API and UI packages.
*/
Expand Down Expand Up @@ -137,6 +139,12 @@ export type UseChatOptions = {
*/
onError?: (error: Error) => void;

/**
* A way to provide a function that is going to be used for ids for messages.
* If not provided nanoid is used by default.
*/
generateId?: IdGenerator;

/**
* The credentials mode to be used for the fetch request.
* Possible values are: 'omit', 'same-origin', 'include'.
Expand Down
4 changes: 3 additions & 1 deletion packages/core/solid/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export function useChat({
credentials,
headers,
body,
generateId = nanoid,
}: UseChatOptions = {}): UseChatHelpers {
// Generate a unique ID for the chat if not provided.
const chatId = id || `chat-${uniqueId++}`;
Expand Down Expand Up @@ -174,6 +175,7 @@ export function useChat({
mutate(previousMessages.data);
}
},
generateId,
});
},
experimental_onFunctionCall,
Expand Down Expand Up @@ -203,7 +205,7 @@ export function useChat({

const append: UseChatHelpers['append'] = async (message, options) => {
if (!message.id) {
message.id = nanoid();
message.id = generateId();
}
return triggerRequest(
(messages() ?? []).concat(message as Message),
Expand Down
6 changes: 4 additions & 2 deletions packages/core/streams/streaming-react-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
*/

import { parseComplexResponse } from '../shared/parse-complex-response';
import { JSONValue } from '../shared/types';
import { createChunkDecoder } from '../shared/utils';
import { IdGenerator, JSONValue } from '../shared/types';
import { createChunkDecoder, nanoid } from '../shared/utils';
import { experimental_StreamData } from './stream-data';

type UINode = string | JSX.Element | JSX.Element[] | null | undefined;
Expand All @@ -36,6 +36,7 @@ export class experimental_StreamingReactResponse {
data?: JSONValue[] | undefined;
}) => UINode | Promise<UINode>;
data?: experimental_StreamData;
generateId?: IdGenerator;
},
) {
let resolveFunc: (row: ReactResponseRow) => void = () => {};
Expand Down Expand Up @@ -70,6 +71,7 @@ export class experimental_StreamingReactResponse {

lastPayload = payload;
},
generateId: options.generateId ?? nanoid,
onFinish: () => {
// The last payload is resolved twice. This is necessary because we immediately
// push out a payload, but we also need to forward the finish event with a payload.
Expand Down
7 changes: 6 additions & 1 deletion packages/core/svelte/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
JSONValue,
Message,
UseChatOptions,
IdGenerator,
} from '../shared/types';
import { nanoid } from '../shared/utils';
export type { CreateMessage, Message, UseChatOptions };
Expand Down Expand Up @@ -70,6 +71,7 @@ const getStreamedResponse = async (
},
previousMessages: Message[],
abortControllerRef: AbortController | null,
generateId: IdGenerator,
onFinish?: (message: Message) => void,
onResponse?: (response: Response) => void | Promise<void>,
sendExtraMessageFields?: boolean,
Expand Down Expand Up @@ -120,6 +122,7 @@ const getStreamedResponse = async (
mutateStreamData([...(existingData || []), ...(data || [])]);
},
onFinish,
generateId,
});
};

Expand All @@ -140,6 +143,7 @@ export function useChat({
credentials,
headers,
body,
generateId = nanoid,
}: UseChatOptions = {}): UseChatHelpers {
// Generate a unique id for the chat if not provided.
const chatId = id || `chat-${uniqueId++}`;
Expand Down Expand Up @@ -201,6 +205,7 @@ export function useChat({
extraMetadata,
get(messages),
abortController,
generateId,
onFinish,
onResponse,
sendExtraMessageFields,
Expand Down Expand Up @@ -237,7 +242,7 @@ export function useChat({
{ options, functions, function_call }: ChatRequestOptions = {},
) => {
if (!message.id) {
message.id = nanoid();
message.id = generateId();
}

const chatRequest: ChatRequest = {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/vue/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export function useChat({
credentials,
headers,
body,
generateId = nanoid,
}: UseChatOptions = {}): UseChatHelpers {
// Generate a unique ID for the chat if not provided.
const chatId = id || `chat-${uniqueId++}`;
Expand Down Expand Up @@ -173,6 +174,7 @@ export function useChat({
// Restore the previous messages if the request fails.
mutate(previousMessages);
},
generateId,
});
},
experimental_onFunctionCall,
Expand Down Expand Up @@ -202,7 +204,7 @@ export function useChat({

const append: UseChatHelpers['append'] = async (message, options) => {
if (!message.id) {
message.id = nanoid();
message.id = generateId();
}
return triggerRequest(messages.value.concat(message as Message), options);
};
Expand Down

0 comments on commit 3ff8a56

Please sign in to comment.