Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(example): add chat ui #1

Merged
merged 5 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added example/assets/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions example/ios/AiExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
7699B88040F8A987B510C191 /* libPods-AiExample-AiExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-AiExample-AiExampleTests.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
AC91505B2C4C37D100E4348A /* bundle in Resources */ = {isa = PBXBuildFile; fileRef = AC91505A2C4C37D100E4348A /* bundle */; };
FA9143FAE05E547962661C6F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1158F81499343192EE2DB0E3 /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -46,6 +47,8 @@
5DCACB8F33CDC322A6C60F78 /* libPods-AiExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AiExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AiExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
89C6BE57DB24E9ADA2F236DE /* Pods-AiExample-AiExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AiExample-AiExampleTests.release.xcconfig"; path = "Target Support Files/Pods-AiExample-AiExampleTests/Pods-AiExample-AiExampleTests.release.xcconfig"; sourceTree = "<group>"; };
AC9150592C4C347200E4348A /* AiExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = AiExample.entitlements; path = AiExample/AiExample.entitlements; sourceTree = "<group>"; };
AC91505A2C4C37D100E4348A /* bundle */ = {isa = PBXFileReference; lastKnownFileType = folder; name = bundle; path = dist/bundle; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -89,6 +92,7 @@
13B07FAE1A68108700A75B9A /* AiExample */ = {
isa = PBXGroup;
children = (
AC9150592C4C347200E4348A /* AiExample.entitlements */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
13B07FB51A68108700A75B9A /* Images.xcassets */,
Expand Down Expand Up @@ -121,6 +125,7 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
AC91505A2C4C37D100E4348A /* bundle */,
13B07FAE1A68108700A75B9A /* AiExample */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* AiExampleTests */,
Expand Down Expand Up @@ -246,6 +251,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AC91505B2C4C37D100E4348A /* bundle in Resources */,
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
FA9143FAE05E547962661C6F /* PrivacyInfo.xcprivacy in Resources */,
Expand Down Expand Up @@ -471,6 +477,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = AiExample/AiExample.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = ZK8L4ATDPD;
ENABLE_BITCODE = NO;
Expand Down Expand Up @@ -557,6 +564,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = AiExample/AiExample.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = ZK8L4ATDPD;
INFOPLIST_FILE = AiExample/Info.plist;
Expand Down
8 changes: 8 additions & 0 deletions example/ios/AiExample/AiExample.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.kernel.increased-memory-limit</key>
<true/>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
ENV['RCT_NEW_ARCH_ENABLED'] = '0'

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
Expand Down
20 changes: 16 additions & 4 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,10 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-get-random-values (1.11.0):
- React-Core
- react-native-netinfo (11.3.2):
- React-Core
- React-nativeconfig (0.74.2)
- React-NativeModulesApple (0.74.2):
- glog
Expand Down Expand Up @@ -1244,6 +1248,8 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- react-native-ai (from `../..`)
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
Expand Down Expand Up @@ -1337,6 +1343,10 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
react-native-ai:
:path: "../.."
react-native-get-random-values:
:path: "../node_modules/react-native-get-random-values"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
React-nativeconfig:
:path: "../node_modules/react-native/ReactCommon"
React-NativeModulesApple:
Expand Down Expand Up @@ -1417,15 +1427,17 @@ SPEC CHECKSUMS:
React-jsitracing: 0fa7f78d8fdda794667cb2e6f19c874c1cf31d7e
React-logger: 29fa3e048f5f67fe396bc08af7606426d9bd7b5d
React-Mapbuffer: bf56147c9775491e53122a94c423ac201417e326
react-native-ai: 4c526ef3a4ab811772b4ef0a3300f7d6b26cd0b4
react-native-ai: e1592baf0559a8b64e5f38dd2c0f2758ccea2d25
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-netinfo: 076df4f9b07f6670acf4ce9a75aac8d34c2e2ccc
React-nativeconfig: 9f223cd321823afdecf59ed00861ab2d69ee0fc1
React-NativeModulesApple: ff7efaff7098639db5631236cfd91d60abff04c0
React-perflogger: 32ed45d9cee02cf6639acae34251590dccd30994
React-RCTActionSheet: 19f967ddaea258182b56ef11437133b056ba2adf
React-RCTAnimation: d7f4137fc44a08bba465267ea7cb1dbdb7c4ec87
React-RCTAppDelegate: dca95e1a6194f7ae06c2b5f1d5f891c61af00ec8
React-RCTAppDelegate: 2b3f4d8009796af209a0d496e73276b743acee08
React-RCTBlob: c6c3e1e0251700b7bea036b893913f22e2b9cb47
React-RCTFabric: a7874c54aea18f64677446efc5f839ec4fa5e931
React-RCTFabric: 93a3ea55169d19294f07092013c1c9ea7a015c9b
React-RCTImage: 40528ab74a4fef0f0e2ee797a074b26d120b6cc6
React-RCTLinking: 385b5beb96749aae9ae1606746e883e1c9f8a6a7
React-RCTNetwork: ffc9f05bd8fa5b3bce562199ba41235ad0af645c
Expand All @@ -1444,6 +1456,6 @@ SPEC CHECKSUMS:
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Yoga: 2f71ecf38d934aecb366e686278102a51679c308

PODFILE CHECKSUM: bbc2c796a007c2b3b597f12669100724c622d914
PODFILE CHECKSUM: c1ce3fcc38763d2aab0f2e5c56e464d324db8386

COCOAPODS: 1.15.2
5 changes: 5 additions & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
"start": "react-native start"
},
"dependencies": {
"@react-native-community/netinfo": "^11.3.2",
"@types/uuid": "^10.0.0",
"ai": "^3.2.15",
"react": "18.2.0",
"react-native": "0.74.2",
"react-native-get-random-values": "^1.11.0",
"react-native-gifted-chat": "^2.4.0",
"text-encoding": "^0.7.0",
"uuid": "^10.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions example/polyfills.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import 'react-native-get-random-values';

// @ts-ignore
import { polyfillGlobal } from 'react-native/Libraries/Utilities/PolyfillFunctions';
const webStreamPolyfills = require('web-streams-polyfill/ponyfill/es6');

polyfillGlobal('TextEncoder', () => require('text-encoding').TextEncoder);
polyfillGlobal('TextDecoder', () => require('text-encoding').TextDecoder);
polyfillGlobal('ReadableStream', () => webStreamPolyfills.ReadableStream);
polyfillGlobal('TransformStream', () => webStreamPolyfills.TransformStream);
polyfillGlobal('WritableStream', () => webStreamPolyfills.WritableStream);
polyfillGlobal('TextEncoderStream', () => webStreamPolyfills.TextEncoderStream);
79 changes: 58 additions & 21 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,69 @@
import React from 'react';
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import { doGenerate } from 'react-native-ai';

export default function App() {
const askQuestion = async () => {
try {
const data = await doGenerate('ai', 'whats react native');
console.log(data);
} catch (e) {
console.error(e);
}
import React, { useState } from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import { GiftedChat, type IMessage } from 'react-native-gifted-chat';
import { getModel } from 'react-native-ai';
import { generateText } from 'ai';
import { v4 as uuid } from 'uuid';
import NetworkInfo from './NetworkInfo';

const modelId = 'Phi-3-mini-4k-instruct-q4f16_1-MLC';

const aiBot = {
_id: 2,
name: 'AI Chat Bot',
avatar: require('./../assets/avatar.png'),
};

export default function Example() {
const [messages, setMessages] = useState<IMessage[]>([
{
_id: uuid(),
text: 'Hello! How can I help you today?',
createdAt: new Date(),
user: aiBot,
},
]);

const onSendMessage = async (prompt: string) => {
const { text } = await generateText({
model: getModel(modelId),
prompt,
});

setMessages((previousMessages) =>
GiftedChat.append(previousMessages, {
// @ts-ignore
_id: uuid(),
text,
createdAt: new Date(),
user: aiBot,
})
);
};

return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={askQuestion}>
<Text>Ask a question</Text>
</TouchableOpacity>
</View>
<SafeAreaView style={styles.container}>
<NetworkInfo />
<GiftedChat
messages={messages}
onSend={(newMessage) => {
setMessages((previousMessages) =>
GiftedChat.append(previousMessages, newMessage)
);

onSendMessage(newMessage[0]!.text);
}}
user={{
_id: 1,
}}
/>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'darkblue',
backgroundColor: '#fff',
},
button: { width: 200, height: 200, backgroundColor: 'red' },
});
56 changes: 56 additions & 0 deletions example/src/NetworkInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useNetInfo } from '@react-native-community/netinfo';

const NetworkInfo = () => {
const netInfo = useNetInfo();

const getStatusColor = () => {
if (netInfo.isConnected) return styles.connected;
return styles.disconnected;
};

return (
<View style={styles.container}>
<View style={[styles.statusIndicator, getStatusColor()]} />
<Text style={styles.statusText}>
{netInfo.isConnected ? 'Connected ✅' : 'Disconnected ❌'}
</Text>
</View>
);
};

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
backgroundColor: '#f0f0f0',
borderRadius: 8,
},
statusIndicator: {
width: 12,
height: 12,
borderRadius: 6,
marginRight: 10,
},
connected: {
backgroundColor: '#4CAF50',
},
disconnected: {
backgroundColor: '#F44336',
},
textContainer: {
flex: 1,
},
statusText: {
fontSize: 16,
fontWeight: 'bold',
},
detailText: {
fontSize: 14,
color: '#666',
},
});

export default NetworkInfo;
5 changes: 3 additions & 2 deletions ios/Ai.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#import <React/RCTEventEmitter.h>

#ifdef RCT_NEW_ARCH_ENABLED
#import "RNAiSpec.h"

@interface Ai : NSObject <NativeAiSpec>
@interface Ai : RCTEventEmitter <NativeAiSpec>
#else
#import <React/RCTBridgeModule.h>

@interface Ai : NSObject <RCTBridgeModule>
@interface Ai : RCTEventEmitter <RCTBridgeModule>
#endif

@end
Loading
Loading