Skip to content

Commit

Permalink
Provide option to omit genUI (#322)
Browse files Browse the repository at this point in the history
Previously, the Sidekick would always emit GenUI components (e.g.
`<Card>`). This can present a challenge for integrators, because using
this output requires you to do MDX compilation.

Now, we introduce three options:
* GenUI
* Plain Markdown
* Plain text

The latter option offers the lowest barrier to entry for integrators.

Also adds option to disable recommended next steps
  • Loading branch information
NickHeiner committed Sep 21, 2023
1 parent 095764c commit 956ce57
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/ai-jsx/package.json
Expand Up @@ -4,7 +4,7 @@
"repository": "fixie-ai/ai-jsx",
"bugs": "https://github.com/fixie-ai/ai-jsx/issues",
"homepage": "https://ai-jsx.com",
"version": "0.18.0",
"version": "0.18.1",
"volta": {
"extends": "../../package.json"
},
Expand Down
24 changes: 15 additions & 9 deletions packages/ai-jsx/src/batteries/sidekick/platform/gen-ui.tsx
@@ -1,5 +1,8 @@
import { SidekickProps } from './sidekick.js';

// prettier-ignore
export const mdxUsageExamples = <>
export function MdxUsageExamples({includeNextStepsRecommendations}: Pick<SidekickProps, 'includeNextStepsRecommendations'>) {
return <>
Respond concisely, using MDX formatting to make your response
more readable and structured.

Expand Down Expand Up @@ -29,15 +32,17 @@ export const mdxUsageExamples = <>
When you show users links to knowledge base, use the {'<Citation />'} component. Its props are:
{' interface CitationProps { title: string; href: string } '}

You may suggest follow-up ideas to the user, if they fall within the scope of
what you are able to do.
{includeNextStepsRecommendations && <>
You may suggest follow-up ideas to the user, if they fall within the scope of
what you are able to do.

To give the user a canned reply to respond to you with, use the {'<NextStepsButton />'} component. Here is the interface for its props:
{`
interface NextStepsButtonProps {
prompt: string
}
`}
To give the user a canned reply to respond to you with, use the {'<NextStepsButton />'} component. Here is the interface for its props:
{`
interface NextStepsButtonProps {
prompt: string
}
`}
</>}

When you emit MDX, be sure to use the proper quote type so quote characters in the string do not break the syntax. For instance:

Expand All @@ -55,3 +60,4 @@ export const mdxUsageExamples = <>
Example 1: The handlebars template language looks like: \`\{'{'}\{'{'}foo\{'}'}\{'}'}\`
Example 2: The handlebars template language looks like: `{'{{'}foo{'}}'}`
</>;
}
45 changes: 41 additions & 4 deletions packages/ai-jsx/src/batteries/sidekick/platform/sidekick.tsx
Expand Up @@ -5,6 +5,7 @@ import { OpenAI } from '../../../lib/openai.js';
import { UseToolsProps } from '../../use-tools.js';
import * as AI from '../../../index.js';
import { ConversationHistory, ShowConversation } from '../../../core/conversation.js';
import { MergeExclusive } from 'type-fest';

export type OpenAIChatModel = Exclude<Parameters<typeof OpenAI>[0]['chatModel'], undefined>;
export type ModelProvider = 'openai';
Expand Down Expand Up @@ -38,19 +39,53 @@ export function ModelProvider({
}
}

export interface SidekickProps {
interface UniversalSidekickProps {
tools?: UseToolsProps['tools'];
systemMessage?: AI.Node;
finalSystemMessageBeforeResponse?: AI.Node;
genUIExamples?: AI.Node;
genUIComponentNames?: string[];

/**
* The role the model should take, like "a customer service agent for Help Scout".
* The role the model should take, like "a customer service agent for my_company_name".
*/
role: string;
}

type OutputFormatSidekickProps = MergeExclusive<
{
/**
* Pass `text/gen-ui`, or omit this field, to get a Gen UI response.
* To render this, you'll need an MDX compiler that's aware of the Gen UI components.
*/
outputFormat?: 'text/mdx' | undefined;

genUIExamples?: AI.Node;
genUIComponentNames?: string[];

/**
* If true, the Sidekick will emit next steps recommendations, such as:
*
* ...and that's some detail about your most recent order.
*
* <NextStepsButton prompt='What other orders are there?' />
* <NextStepsButton prompt='How do I cancel the order?' />
* <NextStepsButton prompt='How do I resubmit the order?' />
*
* Defaults to true.
*/
includeNextStepsRecommendations?: boolean;
},
{
/**
* Pass `text/markdown` to get a Markdown response. To render this, you'll need a Markdown compiler.
*
* Pass `text/plain` to get a plain text response.
*/
outputFormat: 'text/markdown' | 'text/plain';
}
>;

export type SidekickProps = UniversalSidekickProps & OutputFormatSidekickProps;

export function Sidekick(props: SidekickProps) {
return (
<ModelProvider model="gpt-4-32k" modelProvider="openai">
Expand All @@ -64,6 +99,8 @@ export function Sidekick(props: SidekickProps) {
timeZone="America/Los_Angeles"
timeZoneOffset="420"
role={props.role}
includeNextStepsRecommendations={props.includeNextStepsRecommendations ?? true}
outputFormat={props.outputFormat ?? 'text/mdx'}
userProvidedGenUIUsageExamples={props.genUIExamples}
userProvidedGenUIComponentNames={props.genUIComponentNames}
/>
Expand Down
34 changes: 23 additions & 11 deletions packages/ai-jsx/src/batteries/sidekick/platform/system-message.tsx
@@ -1,10 +1,12 @@
import { SystemMessage } from '../../../core/conversation.js';
import { MdxSystemMessage } from '../../../react/jit-ui/mdx.js';
import { Prompt } from '../../prompts.js';
import { mdxUsageExamples } from './gen-ui.js';
import { MdxUsageExamples } from './gen-ui.js';
import { Node } from '../../../index.js';
import { SidekickProps } from './sidekick.js';

export interface SidekickSystemMessageProps {
export interface SidekickSystemMessageProps
extends Pick<SidekickProps, 'outputFormat' | 'includeNextStepsRecommendations'> {
timeZone: string;
timeZoneOffset: string;
userProvidedGenUIUsageExamples?: Node;
Expand All @@ -17,6 +19,8 @@ export function SidekickSystemMessage({
timeZoneOffset,
userProvidedGenUIUsageExamples,
userProvidedGenUIComponentNames,
includeNextStepsRecommendations,
outputFormat,
role,
}: SidekickSystemMessageProps) {
const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
Expand All @@ -39,6 +43,11 @@ export function SidekickSystemMessage({
</SystemMessage>
);

const baseComponentNames = ['Card', 'Citation'];
if (includeNextStepsRecommendations) {
baseComponentNames.push('NextStepsButton');
}

return (
<>
{dateTimeSystemMessage}
Expand All @@ -51,15 +60,18 @@ export function SidekickSystemMessage({
If the user asks something that is impossible, tell them **up-front** it is impossible. You can still write
relevant helpful comments, but do not lead the user to think something can be done when it cannot be.
</SystemMessage>
<MdxSystemMessage
componentNames={['Card', 'Citation', 'NextStepsButton', ...(userProvidedGenUIComponentNames ?? [])]}
usageExamples={
<>
{mdxUsageExamples}
{userProvidedGenUIUsageExamples}
</>
}
/>
{outputFormat === 'text/markdown' && <SystemMessage>Respond with Markdown.</SystemMessage>}
{outputFormat === 'text/mdx' && (
<MdxSystemMessage
componentNames={[...baseComponentNames, ...(userProvidedGenUIComponentNames ?? [])]}
usageExamples={
<>
<MdxUsageExamples includeNextStepsRecommendations={includeNextStepsRecommendations} />
{userProvidedGenUIUsageExamples}
</>
}
/>
)}
</>
);
}
10 changes: 8 additions & 2 deletions packages/docs/docs/changelog.md
@@ -1,13 +1,19 @@
# Changelog

## 0.18.0
## 0.18.1

- Modified `Sidekick` to add the following options:
- `outputFormat`: `text/mdx`, `text/markdown`, `text/plain`
- `includeNextStepsRecommendations`: `boolean`

## [0.18.0](https://github.com/fixie-ai/ai-jsx/commit/36b9f02c866df9df761017fd9f8785d876d15ab8)

- Added components for Automatic Speech Recognition (ASR) in `lib/asr/asr.tsx`.
- Addec components for Text-to-Speech (TTS) in `lib/tts/tts.tsx`.
- ASR providers include Deepgram, Speechmatics, Assembly AI, Rev AI, Soniox, and Gladia.
- TTS providers include Google Cloud, AWS, Azure, and ElevenLabs.

## 0.17.4
## [0.17.4](https://github.com/fixie-ai/ai-jsx/commit/b002c00d3926e03769438b01443c1bb715ade496)

- Fixed a bug where passing an empty `functionDefinitions` prop to `<OpenAIChatModel>` would cause an error.

Expand Down
1 change: 1 addition & 0 deletions packages/examples/package.json
Expand Up @@ -36,6 +36,7 @@
"demo:context": "yarn build && node dist/src/context.js",
"demo:multi-model": "yarn build && node dist/src/multi-model.js",
"demo:opentelemetry": "yarn build && node dist/src/opentelemetry.js",
"demo:sidekick-output-format": "yarn build && node dist/src/sidekick-output-format.js",
"demo:wandb": "yarn build && node dist/src/wandb.js",
"demo:helloworld": "yarn build && node dist/src/helloworld.js",
"demo:inline-chat": "yarn build && node dist/src/inline-chat.js",
Expand Down
63 changes: 63 additions & 0 deletions packages/examples/src/sidekick-output-format.tsx
@@ -0,0 +1,63 @@
import * as AI from 'ai-jsx';
import { Sidekick, SidekickProps } from 'ai-jsx/sidekick';
import { ConversationHistoryContext, SystemMessage, UserMessage } from 'ai-jsx/core/conversation';

const mySystemMessage = (
<SystemMessage>
You can do anything a flight assistant can do, including looking up user information and booking new flights. Here
is information about the user's upcoming flight:
{`{
"flightNumber": "UA 123",
"departureTime": "2021-10-31T12:00:00Z",
"arrivalTime": "2021-10-31T13:00:00Z",
"departureAirport": "SFO",
"arrivalAirport": "LAX"
}`}
</SystemMessage>
);

function MySidekick({
contentType,
includeNextStepsRecommendations,
}: {
contentType: SidekickProps['outputFormat'];
includeNextStepsRecommendations?: boolean;
}) {
return (
// I don't feel like making the types line up.
// @ts-expect-error
<Sidekick
role="Flight assistant"
outputFormat={contentType}
systemMessage={mySystemMessage}
includeNextStepsRecommendations={includeNextStepsRecommendations}
/>
);
}

const conversation = [
<UserMessage>Tell me about my flight. Also, give me a list of cities near my destination.</UserMessage>,
];

console.log(
await AI.createRenderContext().render(
<>
<ConversationHistoryContext.Provider value={conversation}>
{/* Unfortunately, I couldn't get this to actually output next steps buttons, but I confirmed that the prompt
is being passed.
*/}
GenUI output:{'\n'}
<MySidekick contentType="text/mdx" />
{'\n\n'}
GenUI output no next steps:{'\n'}
<MySidekick contentType="text/mdx" includeNextStepsRecommendations={false} />
{'\n\n'}
Markdown output:{'\n'}
<MySidekick contentType="text/markdown" />
{'\n\n'}
Plain text output:{'\n'}
<MySidekick contentType="text/plain" />
</ConversationHistoryContext.Provider>
</>
)
);

3 comments on commit 956ce57

@vercel
Copy link

@vercel vercel bot commented on 956ce57 Sep 21, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

ai-jsx-docs – ./packages/docs

ai-jsx-docs.vercel.app
docs.ai-jsx.com
ai-jsx-docs-fixie-ai.vercel.app
ai-jsx-docs-git-main-fixie-ai.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 956ce57 Sep 21, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

ai-jsx-tutorial-nextjs – ./packages/tutorial-nextjs

ai-jsx-tutorial-nextjs-git-main-fixie-ai.vercel.app
ai-jsx-tutorial-nextjs-fixie-ai.vercel.app
ai-jsx-tutorial-nextjs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 956ce57 Sep 21, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

ai-jsx-nextjs-demo – ./packages/nextjs-demo

ai-jsx-nextjs-demo-fixie-ai.vercel.app
ai-jsx-nextjs-demo.vercel.app
ai-jsx-nextjs-demo-git-main-fixie-ai.vercel.app

Please sign in to comment.