Skip to content

Commit 8d6bdc5

Browse files
committed
fix: prompt tweaks
1 parent 8bced77 commit 8d6bdc5

File tree

1 file changed

+106
-68
lines changed
  • examples/demo-apps/react-native/rnllama/app

1 file changed

+106
-68
lines changed

examples/demo-apps/react-native/rnllama/app/index.tsx

Lines changed: 106 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,83 @@ export default function Index() {
2929
const [tokenizerName, setTokenizerName] = useState('');
3030
const [isInitialized, setIsInitialized] = useState(false);
3131
const [isInitializing, setIsInitializing] = useState(false);
32-
const [history, setHistory] = useState<Array<{input: boolean, text: string}>>([]);
32+
const [history, setHistory] = useState<Array<{ input: boolean, text: string }>>([]);
3333
const scrollViewRef = useRef();
3434

3535
useEffect(() => {
3636
const unsubscribe = LLaMABridge.onToken((token) => {
37-
if (!isStopped) { // Only process tokens if not stopped
38-
setCurrentOutput(prev => prev + token);
39-
if (token === ' ' || /[.,!?]/.test(token)) {
40-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
37+
if (!isStopped) {
38+
// Natural stop
39+
if (token === "<|eot_id|>") {
40+
setIsGenerating(false);
41+
setCurrentOutput(prev => {
42+
if (prev.trim()) {
43+
setHistory(prevHistory => [...prevHistory, { input: false, text: prev.trim() }]);
44+
}
45+
return '';
46+
});
47+
return;
4148
}
49+
50+
// Skip template tokens
51+
if (token === formatPrompt('') ||
52+
token.includes("<|begin_of_text|>") ||
53+
token.includes("<|start_header_id|>") ||
54+
token.includes("<|end_header_id|>") ||
55+
token.includes("assistant")) {
56+
return;
57+
}
58+
59+
// Add token without leading newlines
60+
setCurrentOutput(prev => prev + token.replace(/^\n+/, ''));
4261
}
4362
});
63+
4464
return () => unsubscribe();
45-
}, [isStopped]); // Add isStopped to dependencies
65+
}, [isStopped, currentOutput]);
66+
67+
68+
const formatPrompt = (text: string) => {
69+
return `<|begin_of_text|><|start_header_id|>user<|end_header_id|>${text.trim()}<|eot_id|><|start_header_id|>assistant<|end_header_id|>`;
70+
};
71+
72+
const handleGenerate = async () => {
73+
if (!isInitialized || !prompt.trim()) {
74+
return;
75+
}
76+
77+
setIsStopped(false);
78+
const newPrompt = prompt.trim();
79+
setPrompt('');
80+
setIsGenerating(true);
81+
setCurrentOutput('');
82+
83+
// Add the user message immediately
84+
const userMessage = { input: true, text: newPrompt };
85+
setHistory(prev => [...prev, userMessage]);
86+
87+
try {
88+
const formattedPrompt = formatPrompt(newPrompt);
89+
await LLaMABridge.generate(formattedPrompt, 768);
90+
} catch (error) {
91+
console.error(error);
92+
Alert.alert('Error', 'Generation failed');
93+
setIsGenerating(false);
94+
}
95+
};
96+
97+
const handleStop = () => {
98+
if (!isGenerating) return;
99+
100+
setIsStopped(true);
101+
LLaMABridge.stop();
102+
103+
if (currentOutput) {
104+
setHistory(prev => [...prev, { input: false, text: currentOutput.trim() }]);
105+
}
106+
setCurrentOutput('');
107+
setIsGenerating(false);
108+
};
46109

47110
const selectModel = async () => {
48111
try {
@@ -75,11 +138,23 @@ export default function Index() {
75138
};
76139

77140
const initializeLLaMA = async () => {
141+
// If already initialized, reset everything
142+
if (isInitialized) {
143+
setModelPath('');
144+
setModelName('');
145+
setTokenizerPath('');
146+
setTokenizerName('');
147+
setIsInitialized(false);
148+
setHistory([]);
149+
setCurrentOutput('');
150+
return;
151+
}
152+
78153
if (!modelPath || !tokenizerPath) {
79154
Alert.alert('Error', 'Please select both model and tokenizer files first');
80155
return;
81156
}
82-
157+
83158
setIsInitializing(true);
84159
try {
85160
await LLaMABridge.initialize(modelPath, tokenizerPath);
@@ -88,50 +163,15 @@ export default function Index() {
88163
} catch (error) {
89164
console.error('Failed to initialize LLaMA:', error);
90165
Alert.alert('Error', 'Failed to initialize LLaMA');
166+
setModelPath('');
167+
setModelName('');
168+
setTokenizerPath('');
169+
setTokenizerName('');
91170
} finally {
92171
setIsInitializing(false);
93172
}
94173
};
95174

96-
const handleGenerate = async () => {
97-
if (!isInitialized) {
98-
Alert.alert('Error', 'Please initialize LLaMA first');
99-
return;
100-
}
101-
if (!prompt.trim()) {
102-
return;
103-
}
104-
105-
setIsStopped(false); // Add this line
106-
const newPrompt = prompt.trim();
107-
setPrompt('');
108-
setIsGenerating(true);
109-
setCurrentOutput('');
110-
setHistory(prev => [...prev, { input: true, text: newPrompt }]);
111-
112-
try {
113-
await LLaMABridge.generate(newPrompt, 768);
114-
} catch (error) {
115-
console.error(error);
116-
Alert.alert('Error', 'Generation failed');
117-
} finally {
118-
setIsGenerating(false);
119-
if (currentOutput) {
120-
setHistory(prev => [...prev, { input: false, text: currentOutput }]);
121-
setCurrentOutput('');
122-
}
123-
}
124-
};
125-
126-
const handleStop = () => {
127-
setIsStopped(true);
128-
LLaMABridge.stop();
129-
setIsGenerating(false);
130-
if (currentOutput) {
131-
setHistory(prev => [...prev, { input: false, text: currentOutput }]);
132-
setCurrentOutput('');
133-
}
134-
};
135175

136176
const handleClearHistory = () => {
137177
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
@@ -147,17 +187,17 @@ export default function Index() {
147187

148188
<View style={styles.setupBar}>
149189
<View style={styles.setupControls}>
150-
<TouchableOpacity
151-
style={[styles.setupButton, modelPath ? styles.setupComplete : styles.setupIncomplete]}
190+
<TouchableOpacity
191+
style={[styles.setupButton, modelPath ? styles.setupComplete : styles.setupIncomplete]}
152192
onPress={selectModel}
153193
>
154194
<Ionicons name="cube-outline" size={20} color="#fff" />
155195
<Text style={styles.setupText}>
156196
{modelName ? modelName.substring(0, 15) + '...' : "Select Model"}
157197
</Text>
158198
</TouchableOpacity>
159-
<TouchableOpacity
160-
style={[styles.setupButton, tokenizerPath ? styles.setupComplete : styles.setupIncomplete]}
199+
<TouchableOpacity
200+
style={[styles.setupButton, tokenizerPath ? styles.setupComplete : styles.setupIncomplete]}
161201
onPress={selectTokenizer}
162202
>
163203
<Ionicons name="key-outline" size={20} color="#fff" />
@@ -166,28 +206,28 @@ export default function Index() {
166206
</Text>
167207
</TouchableOpacity>
168208
</View>
169-
<TouchableOpacity
209+
<TouchableOpacity
170210
style={[
171-
styles.initButton,
211+
styles.initButton,
172212
isInitialized ? styles.setupComplete : styles.setupIncomplete,
173213
(!modelPath || !tokenizerPath || isInitializing) && styles.buttonDisabled
174-
]}
214+
]}
175215
onPress={initializeLLaMA}
176216
disabled={!modelPath || !tokenizerPath || isInitializing}
177217
>
178218
{isInitializing ? (
179219
<ActivityIndicator size="small" color="#fff" />
180220
) : (
181-
<Ionicons
182-
name={isInitialized ? "checkmark-circle-outline" : "power-outline"}
183-
size={24}
184-
color="#fff"
221+
<Ionicons
222+
name={isInitialized ? "checkmark-circle-outline" : "power-outline"}
223+
size={24}
224+
color="#fff"
185225
/>
186226
)}
187227
</TouchableOpacity>
188228
</View>
189229

190-
<KeyboardAvoidingView
230+
<KeyboardAvoidingView
191231
behavior={Platform.OS === "ios" ? "padding" : "height"}
192232
style={styles.content}
193233
keyboardVerticalOffset={Platform.OS === "ios" ? 90 : 0}
@@ -205,11 +245,10 @@ export default function Index() {
205245
</Text>
206246
</View>
207247
) : history.length === 0 ? (
208-
<Pressable
248+
<Pressable
209249
style={styles.emptyState}
210250
onLongPress={handleClearHistory}
211251
>
212-
<Ionicons name="chatbubble-outline" size={48} color="#666" />
213252
<Text style={styles.emptyStateText}>Start a conversation</Text>
214253
<Text style={styles.emptyStateHint}>Long press to clear history</Text>
215254
</Pressable>
@@ -247,14 +286,14 @@ export default function Index() {
247286
editable={isInitialized}
248287
/>
249288
<TouchableOpacity
250-
style={[styles.sendButton, !isInitialized && styles.buttonDisabled]}
289+
style={[styles.sendButton, (!isInitialized || (!isGenerating && !prompt.trim())) && styles.buttonDisabled]}
251290
onPress={isGenerating ? handleStop : handleGenerate}
252-
disabled={!isInitialized || (!prompt.trim() && !isGenerating)}
291+
disabled={!isInitialized || (!prompt.trim() && !isGenerating)} // This was backwards
253292
>
254-
<Ionicons
255-
name={isGenerating ? "stop-outline" : "send-outline"}
256-
size={24}
257-
color="#fff"
293+
<Ionicons
294+
name={isGenerating ? "stop-outline" : "send-outline"}
295+
size={24}
296+
color="#fff"
258297
/>
259298
</TouchableOpacity>
260299
</View>
@@ -270,9 +309,9 @@ const styles = StyleSheet.create({
270309
},
271310
header: {
272311
padding: 16,
273-
alignItems: 'center',
274312
borderBottomWidth: 1,
275313
borderBottomColor: '#333',
314+
alignItems: "start",
276315
},
277316
headerTitle: {
278317
fontSize: 24,
@@ -349,7 +388,6 @@ const styles = StyleSheet.create({
349388
color: '#666',
350389
fontSize: 12,
351390
marginTop: 8,
352-
fontStyle: 'italic',
353391
},
354392
initPrompt: {
355393
padding: 20,

0 commit comments

Comments
 (0)