Skip to content

Commit 5074643

Browse files
authored
Merge pull request #2 from unstubbable/structured-input
Model image selection as assistant state
2 parents ce93c13 + 2ba82a9 commit 5074643

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed

src/app/ai-state.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
export type AIStateItem =
2+
| {
3+
readonly role: 'user' | 'assistant' | 'system';
4+
readonly content: string;
5+
}
6+
| {
7+
readonly role: 'function';
8+
readonly content: string;
9+
readonly name: string;
10+
};
11+
12+
export type UserInput =
13+
| {
14+
readonly action: 'message';
15+
readonly content: string;
16+
}
17+
| {
18+
readonly action: 'select-image';
19+
readonly url: string;
20+
};
21+
22+
export function fromUserInput(userInput: UserInput): AIStateItem {
23+
switch (userInput.action) {
24+
case `select-image`:
25+
return {
26+
role: `assistant`,
27+
content: `[user wants to know more about the image ${userInput.url}. keep it short.]`,
28+
};
29+
case `message`:
30+
return {
31+
role: `user`,
32+
content: userInput.content,
33+
};
34+
}
35+
}

src/app/ai.tsx

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import {createAI} from 'ai/rsc';
22
import type * as React from 'react';
3+
import type {AIStateItem} from './ai-state.js';
34
import {submitUserMessage} from './submit-user-message.js';
45

5-
export type AIStateItem =
6-
| {
7-
readonly role: 'user' | 'assistant' | 'system';
8-
readonly content: string;
9-
}
10-
| {
11-
readonly role: 'function';
12-
readonly content: string;
13-
readonly name: string;
14-
};
15-
166
export interface UIStateItem {
177
readonly id: number;
188
readonly role: 'user' | 'assistant' | 'error';

src/app/chat.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ export function Chat({children}: React.PropsWithChildren): React.ReactNode {
5757
]);
5858

5959
try {
60-
const message = await submitUserMessage(userInput);
60+
const message = await submitUserMessage({
61+
action: `message`,
62+
content: userInput,
63+
});
6164

6265
setMessages((prevMessages) => [...prevMessages, message]);
6366
} catch (error) {

src/app/image-selector.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ export function ImageSelector({
2727
document.body.scrollIntoView({block: `end`, behavior: `smooth`});
2828

2929
try {
30-
const message = await submitUserMessage(
31-
`Tell me more about image ${url}, keep it short.`,
32-
);
30+
const message = await submitUserMessage({action: `select-image`, url});
3331

3432
setMessages((prevMessages) => [
3533
...prevMessages.filter(({id}) => id !== optimisticMessageId),

src/app/submit-user-message.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {getMutableAIState, render} from 'ai/rsc';
44
import {OpenAI} from 'openai';
55
import * as React from 'react';
66
import {z} from 'zod';
7+
import {type UserInput, fromUserInput} from './ai-state.js';
78
import type {AI, UIStateItem} from './ai.js';
89
import {imageSearchParams, searchImages} from './google-image-search.js';
910
import {ImageSelector} from './image-selector.js';
@@ -16,11 +17,11 @@ const openai = new OpenAI({apiKey: process.env.OPENAI_API_KEY});
1617

1718
// eslint-disable-next-line @typescript-eslint/require-await
1819
export async function submitUserMessage(
19-
userInput: string,
20+
userInput: UserInput,
2021
): Promise<UIStateItem> {
2122
const aiState = getMutableAIState<typeof AI>();
2223

23-
aiState.update([...aiState.get(), {role: `user`, content: userInput}]);
24+
aiState.update([...aiState.get(), fromUserInput(userInput)]);
2425

2526
let lastTextContent: string | undefined;
2627

@@ -41,6 +42,8 @@ export async function submitUserMessage(
4142
Never ask the user whether they want to see images of the discussed subject, always show them unprompted.
4243
4344
Before showing images of a certain artist it might make sense to introduce them to the user first, with a couple of words.
45+
46+
The user can also select an image if they want to know more about it.
4447
`,
4548
},
4649
...aiState.get(),
@@ -62,7 +65,7 @@ export async function submitUserMessage(
6265
6366
If the images to search for are distinctly different from each other (e.g. two different, titled paintings by the same artist), split it the search up into multiple search parameter sets.
6467
65-
Select three images overall, unless the user asks for more or less.
68+
Select three images overall, unless the user asks for more or less. When searching for a specific artwork, select only one image.
6669
6770
Never call the function more than once in a message, instead use multiple searches within the same function call.
6871
`,

0 commit comments

Comments
 (0)