Skip to content

Commit

Permalink
feat (examples): add streamObject example with streamableValue (#1787)
Browse files Browse the repository at this point in the history
Co-authored-by: Max Leiter <max.leiter@vercel.com>
  • Loading branch information
lgrammel and MaxLeiter authored Jun 3, 2024
1 parent 41a86e7 commit b441c11
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 2 deletions.
4 changes: 2 additions & 2 deletions examples/next-openai/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });

export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
title: 'Vercel AI SDK - Next.js OpenAI Examples',
description: 'Examples of using the Vercel AI SDK with Next.js and OpenAI.',
};

export default function RootLayout({
Expand Down
26 changes: 26 additions & 0 deletions examples/next-openai/app/stream-object/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use server';

import { openai } from '@ai-sdk/openai';
import { streamObject } from 'ai';
import { createStreamableValue } from 'ai/rsc';
import { PartialNotification, notificationSchema } from './schema';

export async function generateNotifications(context: string) {
const notificationsStream = createStreamableValue<PartialNotification>();

streamObject({
model: openai('gpt-4-turbo'),
prompt: `Generate 3 notifications for a messages app in this context: ${context}`,
schema: notificationSchema,
})
.then(async ({ partialObjectStream }) => {
for await (const partialObject of partialObjectStream) {
notificationsStream.update(partialObject);
}
})
.finally(() => {
notificationsStream.done();
});

return notificationsStream.value;
}
68 changes: 68 additions & 0 deletions examples/next-openai/app/stream-object/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { StreamableValue, useStreamableValue } from 'ai/rsc';
import { useState } from 'react';
import { generateNotifications } from './actions';
import { PartialNotification } from './schema';

// Force the page to be dynamic and allow streaming responses up to 30 seconds
export const dynamic = 'force-dynamic';
export const maxDuration = 30;

// page component with a button to generate notifications
export default function Page() {
const [notificationStream, setNotificationStream] =
useState<StreamableValue<PartialNotification> | null>(null);

return (
<div className="flex flex-col items-center min-h-screen p-4 m-4">
<button
className="px-4 py-2 mt-4 text-white bg-blue-500 rounded-md"
onClick={async () => {
setNotificationStream(
await generateNotifications('Messages during finals week.'),
);
}}
>
Generate notifications
</button>

{notificationStream && (
<NotificationsView notificationStream={notificationStream} />
)}
</div>
);
}

// separate component to display notifications that received the streamable value:
function NotificationsView({
notificationStream,
}: {
notificationStream: StreamableValue<PartialNotification>;
}) {
const [data, pending, error] = useStreamableValue(notificationStream);

return (
<div className="flex flex-col gap-4 mt-4">
{data?.notifications?.map((notification, index) => (
<div
className="flex items-start gap-4 p-4 bg-gray-100 rounded-md dark:bg-gray-800"
key={index}
>
<div className="flex-1 space-y-1">
<div className="flex items-center justify-between">
<p className="font-medium">{notification?.name}</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
{notification?.minutesAgo}
{notification?.minutesAgo != null ? ' minutes ago' : ''}
</p>
</div>
<p className="text-gray-700 dark:text-gray-300">
{notification?.message}
</p>
</div>
</div>
))}
</div>
);
}
18 changes: 18 additions & 0 deletions examples/next-openai/app/stream-object/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DeepPartial } from 'ai';
import { z } from 'zod';

// define a schema for the notifications
export const notificationSchema = z.object({
notifications: z.array(
z.object({
name: z.string().describe('Name of a fictional person.'),
message: z.string().describe('Message. Do not use emojis or links.'),
minutesAgo: z.number(),
}),
),
});

// define a type for the partial notifications during generation
export type PartialNotification = DeepPartial<
z.infer<typeof notificationSchema>
>;

0 comments on commit b441c11

Please sign in to comment.