Skip to content

Commit 5cecaa3

Browse files
committed
Change how chat entries are modifed from descendents
- Add ability to hint at the intent of a query - If the intent is 'text', we won't try to query for image intent/keywords - Added ability to modify a response from the ChatHistory context - Used entry modifying for both filling in the query result _and_ for modifying intent (i.e. deciding something is not an image) Fixes #50
1 parent 9a5bfa9 commit 5cecaa3

File tree

4 files changed

+49
-31
lines changed

4 files changed

+49
-31
lines changed

src/AiQuery.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,36 @@ import { SettingsContext } from './Settings';
1616
// Component that drives the queries to OpenAi to respond to a prompt
1717
type AiSectionWithQueryProps = {
1818
prompt: string;
19+
intent?: string;
1920
id: number;
20-
onResponse: ({prompt, response, contentType} : { prompt: string, response: string, contentType: ChatContent} ) => void;
21+
onResponse: ({prompt, intent, response, contentType} : { prompt: string, intent?: string, response: string, contentType: ChatContent} ) => void;
2122
};
22-
function AiSectionWithQuery({prompt, id, onResponse}: AiSectionWithQueryProps): JSX.Element {
23+
function AiSectionWithQuery({prompt, intent, id, onResponse}: AiSectionWithQueryProps): JSX.Element {
2324
const settingsContext = React.useContext(SettingsContext);
2425
const chatScroll = React.useContext(ChatScrollContext);
2526
const chatHistory = React.useContext(ChatHistoryContext);
2627
const [isLoading, setIsLoading] = React.useState(true);
27-
const [queryResult, setQueryResult] = React.useState<string | undefined>(undefined);
28-
const [error, setError] = React.useState<string | undefined>(undefined);
2928
const [isRequestForImage, setIsRequestForImage] = React.useState<boolean | undefined>(undefined);
3029
const [imagePrompt, setImagePrompt] = React.useState<string | undefined>(undefined);
3130

3231
// First determine the intent of the prompt
3332
const imageIntentSentinel = "[IMAGE]";
3433
React.useEffect(() => {
3534
setIsLoading(true);
36-
setError(undefined);
3735
setIsRequestForImage(undefined);
3836
setImagePrompt(undefined);
37+
if (intent === 'text') {
38+
setIsRequestForImage(false);
39+
return;
40+
}
3941
CallOpenAi({
4042
api: OpenAiApi.Completion,
4143
apiKey: settingsContext.apiKey,
4244
instructions: `You are an intuitive assistant helping the user with a project. Your only job is need to determine the primary intent of the user's last prompt.
4345
If and only if you are absolutely certain the user's primary intent is to see an image, respond with exactly the string "${imageIntentSentinel}". Otherwise, respond with your description of their intent.`,
4446
identifier: "INTENT:",
4547
prompt: prompt,
46-
onError: (error) => {
48+
onError: () => {
4749
setIsRequestForImage(false);
4850
},
4951
onResult: (result) => {
@@ -79,7 +81,7 @@ Where items enclosed in brackets ([]) would be replaced with an appropriate sugg
7981
Respond with the image prompt string in the required format. Do not respond conversationally.`,
8082
identifier: "KEYWORDS:",
8183
prompt: prompt,
82-
onError: (error) => {
84+
onError: () => {
8385
setIsRequestForImage(false);
8486
},
8587
onResult: (result) => {
@@ -103,14 +105,12 @@ Respond with the image prompt string in the required format. Do not respond conv
103105
filter((entry) => { return entry.text !== undefined && entry.id < id; }).
104106
map((entry) => { return {role: entry.type == ChatSource.Human ? "user" : "assistant", "content": entry.text ?? ""} }),
105107
onError: (error) => {
106-
setError(error);
107108
onResponse({
108109
prompt: prompt,
109110
response: error ?? "",
110111
contentType: ChatContent.Error});
111112
},
112113
onResult: (result) => {
113-
setQueryResult(result);
114114
onResponse({
115115
prompt: prompt,
116116
response: result ?? "",
@@ -129,14 +129,12 @@ Respond with the image prompt string in the required format. Do not respond conv
129129
identifier: "IMAGE-ANSWER:",
130130
prompt: imagePrompt,
131131
onError: (error) => {
132-
setError(error);
133132
onResponse({
134133
prompt: imagePrompt,
135134
response: error ?? "",
136135
contentType: ChatContent.Error});
137136
},
138137
onResult: (result) => {
139-
setQueryResult(result);
140138
onResponse({
141139
prompt: imagePrompt,
142140
response: result ?? "",

src/AiResponse.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
} from './Controls';
1515
import {
1616
ChatElement,
17-
ChatContent
17+
ChatContent,
18+
ChatHistoryContext
1819
} from './Chat';
1920
import { StylesContext } from './Styles';
2021
import { FeedbackContext } from './Feedback';
@@ -38,7 +39,7 @@ function AiImageResponse({imageUrl, prompt, rejectImage}: AiImageResponseProps):
3839
<View style={{alignSelf: 'flex-end', alignItems: 'flex-end'}}>
3940
<Button
4041
title="I didn't want to see an image"
41-
onPress={() => {rejectImage()}}/>
42+
onPress={() => rejectImage()}/>
4243
<Button
4344
title="Show me more"
4445
onPress={() => console.log("Not yet implemented")}/>
@@ -143,9 +144,11 @@ function AiSection({children, isLoading, contentShownOnHover}: AiSectionProps):
143144
}
144145

145146
type AiSectionContentProps = {
147+
id: number,
146148
content: ChatElement;
147149
}
148-
function AiSectionContent({content}: AiSectionContentProps): JSX.Element {
150+
function AiSectionContent({id, content}: AiSectionContentProps): JSX.Element {
151+
const chatHistory = React.useContext(ChatHistoryContext);
149152
return (
150153
<AiSection>
151154
{(() => {
@@ -156,7 +159,7 @@ function AiSectionContent({content}: AiSectionContentProps): JSX.Element {
156159
return <AiImageResponse
157160
imageUrl={content.text}
158161
prompt={content.prompt}
159-
rejectImage={() => console.log("Not yet implemented")}/>; // TODO: This would need to reset back to the text prompt
162+
rejectImage={() => chatHistory.modifyResponse(id, {intent: 'text', text: undefined})}/>;
160163
default:
161164
case ChatContent.Text:
162165
return <AiTextResponse text={content.text}/>

src/AppContent.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import { handleAIResponse } from './ChatScript';
1111
type AutomatedChatSessionProps = {
1212
entries: ChatElement[];
1313
appendEntry: (entry: ChatElement | ChatElement[]) => void;
14-
modifyEntryText: (index: number, text: string, contentType: ChatContent, prompt: string) => void;
14+
modifyEntry: (id: number, delta: any) => void;
1515
clearConversation: () => void;
1616
};
17-
function AutomatedChatSession({entries, appendEntry, modifyEntryText, clearConversation}: AutomatedChatSessionProps): JSX.Element {
17+
function AutomatedChatSession({entries, appendEntry, modifyEntry, clearConversation}: AutomatedChatSessionProps): JSX.Element {
1818
const styles = React.useContext(StylesContext);
1919
const settings = React.useContext(SettingsContext);
2020

@@ -122,8 +122,8 @@ function AutomatedChatSession({entries, appendEntry, modifyEntryText, clearConve
122122
entries={entries}
123123
humanText={humanText}
124124
onPrompt={(text) => onPrompt(text, chatScriptIndex)}
125-
onResponse={({prompt, response, contentType, entryId}) => modifyEntryText(entryId, response, contentType, prompt)}
126-
regenerateResponse={() => setChatScriptIndex(chatScriptIndex - 1)}
125+
onResponse={({prompt, response, contentType, entryId}) => modifyEntry(entryId, {text: response, contentType: contentType, prompt: prompt})}
126+
modifyResponse={(entryId, delta) => modifyEntry(entryId, delta)}
127127
clearConversation={() => {
128128
setChatScriptIndex(0);
129129
clearConversation();
@@ -145,14 +145,19 @@ function ChatSession(): JSX.Element {
145145
setEntries(modifiedEntries);
146146
}, [entries]);
147147

148-
const modifyEntryText = React.useCallback((index: number, text: string, contentType: ChatContent, prompt: string) => {
148+
const modifyEntry = React.useCallback((index: number, delta: any) => {
149149
let modifiedEntries = [...entries];
150150
if (index >= entries.length) {
151151
console.error(`Index ${index} is out of bounds`);
152152
} else {
153-
modifiedEntries[index].prompt = prompt;
154-
modifiedEntries[index].text = text;
155-
modifiedEntries[index].contentType = contentType;
153+
let entry = modifiedEntries[index];
154+
155+
if (delta.hasOwnProperty('text')) entry.text = delta.text;
156+
if (delta.hasOwnProperty('contentType')) entry.contentType = delta.contentType;
157+
if (delta.hasOwnProperty('prompt')) entry.prompt = delta.prompt;
158+
if (delta.hasOwnProperty('intent')) entry.intent = delta.intent;
159+
160+
modifiedEntries[index] = entry;
156161
setEntries(modifiedEntries);
157162
}
158163
}, [entries]);
@@ -163,7 +168,7 @@ function ChatSession(): JSX.Element {
163168
<AutomatedChatSession
164169
entries={entries}
165170
appendEntry={appendEntry}
166-
modifyEntryText={modifyEntryText}
171+
modifyEntry={modifyEntry}
167172
clearConversation={clearConversation}/>
168173
);
169174
}

src/Chat.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from 'react';
22
import {
33
Button,
44
ScrollView,
5-
Text,
65
TextInput,
76
View,
87
} from 'react-native';
@@ -30,6 +29,7 @@ type ChatElement = {
3029
id: number;
3130
type: ChatSource;
3231
contentType: ChatContent;
32+
intent?: string;
3333
prompt?: string;
3434
text?: string;
3535
content?: JSX.Element;
@@ -38,7 +38,11 @@ type ChatElement = {
3838
// Context for read-only access to the chat log
3939
const ChatHistoryContext = React.createContext<{
4040
entries: ChatElement[];
41-
}>({entries: []});
41+
modifyResponse: (id: number, delta?: any) => void;
42+
}>({
43+
entries: [],
44+
modifyResponse: () => {},
45+
});
4246

4347
// Context for being able to drive the chat scroller
4448
const ChatScrollContext = React.createContext<{
@@ -94,10 +98,10 @@ type ChatProps = {
9498
humanText? : string;
9599
onPrompt: (prompt: string) => void;
96100
onResponse: ({prompt, response, contentType, entryId} : { prompt: string, response: string, contentType: ChatContent, entryId: number} ) => void;
97-
regenerateResponse: () => void;
101+
modifyResponse: (id: number, delta?: any) => void;
98102
clearConversation: () => void;
99103
};
100-
function Chat({entries, humanText, onPrompt, onResponse, regenerateResponse, clearConversation}: ChatProps): JSX.Element {
104+
function Chat({entries, humanText, onPrompt, onResponse, modifyResponse, clearConversation}: ChatProps): JSX.Element {
101105
const styles = React.useContext(StylesContext);
102106
const [showFeedbackPopup, setShowFeedbackPopup] = React.useState(false);
103107
const [showSettingsPopup, setShowSettingsPopup] = React.useState(false);
@@ -120,7 +124,7 @@ function Chat({entries, humanText, onPrompt, onResponse, regenerateResponse, cle
120124

121125
return (
122126
<FeedbackContext.Provider value={feedbackContext}>
123-
<ChatHistoryContext.Provider value={{entries: entries}}>
127+
<ChatHistoryContext.Provider value={{entries: entries, modifyResponse: modifyResponse}}>
124128
<ChatScrollContext.Provider value={{scrollToEnd: scrollToEnd}}>
125129
<View style={styles.appContent}>
126130
<ScrollView
@@ -141,17 +145,25 @@ function Chat({entries, humanText, onPrompt, onResponse, regenerateResponse, cle
141145
entry.content :
142146
// Otherwise, either render the completed query or start a query to get the resolved text
143147
entry.text ?
144-
<AiSectionContent content={entry}/> :
148+
<AiSectionContent
149+
id={entry.id}
150+
content={entry}/> :
145151
<AiSectionWithQuery
146152
id={entry.id}
147153
prompt={entry.prompt ?? ""}
154+
intent={entry.intent}
148155
onResponse={({prompt, response, contentType}) => onResponse({prompt: prompt, response: response, contentType: contentType, entryId: entry.id})}/>
149156
}
150157
</View>
151158
))}
152159
{(entries.length > 0) &&
153160
<View style={{alignSelf: 'center'}}>
154-
<Button title="🔁 Regenerate response" onPress={() => regenerateResponse()}/>
161+
<Button
162+
title="🔁 Regenerate response"
163+
onPress={() => {
164+
// Clear the response for the last entry
165+
modifyResponse(entries.length - 1, {text: undefined});
166+
}}/>
155167
</View>
156168
}
157169
</View>

0 commit comments

Comments
 (0)