Skip to content

Commit

Permalink
anthropic initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
depombo committed Sep 10, 2024
1 parent 6df353f commit 995a171
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 805 deletions.
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
node_modules
dist
openai-node
scripts/test-runner
dist
68 changes: 23 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
# Open AI React Native Client
# Anthropic React Native Client

The goal of this library is to use [React Native SSE](https://github.com/binaryminds/react-native-sse) and [Expo FileSystem](https://docs.expo.dev/versions/latest/sdk/filesystem/) instead of polyfills to support calling the OpenAI API directly from React Native with streaming and file upload support. The package uses the same types and API as the [OpenAI Node SDK](https://github.com/openai/openai-node) wherever possible.
The goal of this library is to use [React Native SSE](https://github.com/binaryminds/react-native-sse) and [Expo FileSystem](https://docs.expo.dev/versions/latest/sdk/filesystem/) instead of polyfills to support calling the Anthropic API directly from React Native with streaming and file upload support. The package uses the same types and API as the [Anthropic Node SDK](https://github.com/anthropics/anthropic-sdk-typescript) wherever possible.

> [!CAUTION]
> This package is meant to be used with a proxy to OpenAI like the one [Backmesh](https://backmesh.com) provides. The `baseURL` parameter for this OpenAI client is thus mandatory. If you do not use a proxy and set the baseURL to https://api.openai.com/v1, you are basically exposing your Open AI API key on the internet! You should never expose any secrets in the bundle of a web or mobile app. The correct usage of this client is to create an endpoint on a proxy server for communication with Open AI and then use that endpoint with a user generated auth JWT in your app.
> This package is meant to be used with a proxy to Anthropic like the one [Backmesh](https://backmesh.com) provides. The `baseURL` parameter for this Anthropic client is thus mandatory. If you do not use a proxy and set the baseURL to https://api.anthropic.com/v1, you are basically exposing your Anthropic API key on the internet! You should never expose any secrets in the bundle of a web or mobile app. The correct usage of this client is to create an endpoint on a proxy server for communication with Anthropic and then use that endpoint with a user generated auth JWT in your app.
### Contributions and Feature Requests

If you would like to contribute or request a feature, please join our [Discord](https://discord.com/invite/FfYyJfgUUY) and ask questions in the **#openai-react-native** channel or create a pull request or issue.
If you would like to contribute or request a feature, please join our [Discord](https://discord.com/invite/FfYyJfgUUY) and ask questions in the **#oss** channel or create a pull request or issue.

### Setup

Install the package

```bash
npm i openai-react-native
npm i anthropic-react-native
```

And then instantiate the client:

```typescript
import OpenAI from 'openai-react-native';
import Anthropic from 'anthropic-react-native';

const client = new OpenAI({
const client = new Anthropic({
baseURL:
'https://edge.backmesh.com/v1/proxy/PyHU4LvcdsQ4gm2xeniAFhMyuDl2/yWo35DdTROVMT52N0qs4/',
// The backmesh proxy uses your auth provider's JWT to authorize access
Expand All @@ -32,60 +32,38 @@ const client = new OpenAI({

### Usage

The streaming APIs uses an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) from [react-native-sse](https://github.com/binaryminds/react-native-sse) under the hood to provide a required typed `onData` stream event callback and three optional ones: `onDone`, `onOpen` and `onError`.
The streaming APIs uses an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) from [react-native-sse](https://github.com/binaryminds/react-native-sse) under the hood to provide a required typed event callbacks for each of the [Anthropic API Types](https://docs.anthropic.com/en/api/messages-streaming#event-types).

```typescript
client.chat.completions.stream(
client.messages.stream(
{
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Hello, world!' }],
},
(data) => {
console.log(data.choices[0].delta.content);
const content = data.choices[0].delta.content;
if (content) {
setText((prevText) => prevText + content); // Handle the streaming completion data here
}
model: 'claude-3-5-sonnet-20240620',
messages: [{ role: 'user', content: userInput }],
max_tokens: 1024,
},
{
onError: (error) => {
console.error('SSE Error:', error); // Handle any errors here
},
onOpen: () => {
console.log('SSE connection for completion opened.'); // Handle when the connection is opened
onMessageStart: () => {
setUserInput('');
},
onContentBlockDelta: (ev: ContentBlockDeltaEvent) => {
if (ev.delta.type === 'text_delta')
setText((prevText) => prevText + (ev.delta as TextDelta).text);
},
onMessageStop: () => {
console.log('SSE connection for completion closed.'); // Handle when the connection is opened
},
}
);
```

The file upload API is async, requires a `purpose` string and leverages the [Expo File System](https://docs.expo.dev/versions/latest/sdk/filesystem/) so only a filepath needs to be provided.

```typescript
try {
const filePath = FileSystem.documentDirectory + 'data.pdf'; // Adjust the path as needed
const file = await client.files.create(filePath, 'fine-tune');
console.log(file);
} catch (error) {
console.error('File creation error:', error);
}
```

Check the [example](https://github.com/backmesh/openai-react-native/blob/main/sample/app/index.tsx) for more details
Check the [example](https://github.com/backmesh/anthropic-react-native/blob/main/sample/app/index.tsx) for more details

## Coverage

- [x] [Chat Completion](https://platform.openai.com/docs/api-reference/chat)
- [x] [Models](https://beta.openai.com/docs/api-reference/models)
- [x] [Files](https://beta.openai.com/docs/api-reference/files)
- [x] [Moderations](https://beta.openai.com/docs/api-reference/moderations)
- [ ] [Images](https://beta.openai.com/docs/api-reference/images)
- [ ] [Audio](https://platform.openai.com/docs/api-reference/audio)
- [ ] [Embeddings](https://platform.openai.com/docs/api-reference/embeddings)

## Beta Coverage

- [x] [Threads](https://beta.openai.com/docs/api-reference/threads)
- [x] [Assistants](https://platform.openai.com/docs/api-reference/assistants)
- [x] [Messages](https://docs.anthropic.com/en/api/messages)

### License

Expand Down
21 changes: 8 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
{
"name": "openai-react-native",
"version": "0.3.6",
"description": "OpenAI React Native API Client without polyfills",
"name": "anthropic-react-native",
"version": "0.1.0",
"description": "Anthropic React Native API Client without polyfills",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"scripts": {
"build": "tsc",
"prepare": "npm run build",
"test": "./scripts/test"
"prepare": "npm run build"
},
"keywords": [
"openai",
"anthropic",
"react-native",
"ios",
"android"
],
"repository": {
"type": "git",
"url": "git+https://github.com/backmesh/openai-react-native.git"
"url": "git+https://github.com/backmesh/anthropic-react-native.git"
},
"author": "L. Fernando De Pombo <[email protected]> (https://backmesh.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/backmesh/openai-react-native/issues"
"url": "https://github.com/backmesh/anthropic-react-native/issues"
},
"prettier": {
"quoteProps": "consistent",
Expand All @@ -33,16 +32,12 @@
},
"jest": {
"preset": "jest-expo",
"testPathIgnorePatterns": [
"openai-node"
],
"moduleNameMapper": {
"^react-native-sse$": "<rootDir>/node_modules/react-native-sse"
}
},
"dependencies": {
"expo-file-system": "^17.0.1",
"openai": "^4.56.0",
"@anthropic-ai/sdk": "^0.27.3",
"react-native-sse": "^1.2.1"
},
"devDependencies": {
Expand Down
59 changes: 13 additions & 46 deletions sample/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { StyleSheet, Text, TextInput } from 'react-native';
import React, { useState, useEffect } from 'react';
import OpenAI, { ChatCompletion } from 'openai-react-native';
import Anthropic, { ContentBlockDeltaEvent, TextDelta } from 'anthropic-react-native';
import { Button, View } from 'react-native';
import * as FileSystem from 'expo-file-system';


import { initializeApp } from 'firebase/app';
Expand All @@ -18,10 +17,7 @@ const firebaseConfig = {

export default function HomeScreen() {
const [text, setText] = useState<string>('');
const [fileDetails, setFileDetails] = useState<string>('');
const [fileContent, setFileContent] = useState<string>('');
const [files, setFiles] = useState<string>('');
const [client, setClient] = useState<OpenAI | null>(null);
const [client, setClient] = useState<Anthropic | null>(null);
const [userInput, setUserInput] = useState<string>(''); // New state for user input


Expand All @@ -30,8 +26,8 @@ export default function HomeScreen() {
initializeApp(firebaseConfig);
const auth = getAuth();
const creds = await signInAnonymously(auth);
const newClient = new OpenAI({
baseURL: 'https://edge.backmesh.com/v1/proxy/PyHU4LvcdsQ4gm2xeniAFhMyuDl2/K2G8ucCHwh5zN6CLEGeL/v1',
const newClient = new Anthropic({
baseURL: 'https://edge.backmesh.com/v1/proxy/PyHU4LvcdsQ4gm2xeniAFhMyuDl2/5E0H2L4zGG5thXl8FOpA/v1',
apiKey: await creds.user.getIdToken(),
});
setClient(newClient);
Expand All @@ -43,50 +39,29 @@ export default function HomeScreen() {
const startStreaming = async () => {
if (!client) return;
setText('');
client.chat.completions.stream(
client.messages.stream(
{
model: 'gpt-4o-mini',
model: 'claude-3-5-sonnet-20240620',
messages: [{ role: 'user', content: userInput }],
max_tokens: 1024,
},
(data: ChatCompletion) => {
const content = data.choices[0].delta?.content;
if (content) {
setText((prevText) => prevText + content); // Handle the streaming completion data here
}
}, {
{
onError: (error) => {
console.error('SSE Error:', error); // Handle any errors here
},
onOpen: () => {
onMessageStart: () => {
setUserInput('');
console.log('SSE connection for completion opened.'); // Handle when the connection is opened
},
onDone: () => {
onContentBlockDelta: (ev: ContentBlockDeltaEvent) => {
if (ev.delta.type === 'text_delta') setText((prevText) => prevText + (ev.delta as TextDelta).text);
},
onMessageStop: () => {
console.log('SSE connection for completion closed.'); // Handle when the connection is opened
},
}
);
};

const uploadFile = async () => {
if (!client) return;
try {
const filePath = FileSystem.documentDirectory + 'example.txt';
await FileSystem.writeAsStringAsync(filePath, 'Hello, world!');
const file = await client.files.create(
filePath,
'fine-tune',
);
setFileDetails(JSON.stringify(file, null, 4));
// const content = await client.files.content(file.id);
// setFileContent(JSON.stringify(content, null, 4));
const files = await client.files.list();
setFiles(JSON.stringify(files, null, 4));
} catch (err) {
console.error(err);
}
};

return (
<View style={styles.buttonContainer}>
<TextInput
Expand All @@ -98,14 +73,6 @@ export default function HomeScreen() {
<Button title="Start Streaming" onPress={startStreaming} />
<Text style={styles.header}>chat complete streaming:</Text>
<Text>{text}</Text>
<Button title="Upload sample file" onPress={uploadFile} />
<Text style={styles.header}>uploaded file object</Text>
<Text>{fileDetails}</Text>
{/* <Text style={styles.header}>uploaded file contents:</Text>
<Text>{fileContent}</Text> */}
<Text style={styles.header}>all files:</Text>
<Text>{files}</Text>

</View>
);
}
Expand Down
4 changes: 2 additions & 2 deletions sample/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "openai-rn-sample",
"name": "anthropic-rn-sample",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
Expand All @@ -26,7 +26,7 @@
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"firebase": "^10.13.1",
"openai-react-native": "file:..",
"anthropic-react-native": "file:..",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.5",
Expand Down
2 changes: 1 addition & 1 deletion sample/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"@/*": [
"./*",
],
"openai-react-native": ["./node_modules/openai-react-native/dist"]
"anthropic-react-native": ["./node_modules/anthropic-react-native/dist"]
}
},
"include": [
Expand Down
Loading

0 comments on commit 995a171

Please sign in to comment.