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: Added File Support #26

Merged
merged 54 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4713d02
feat: rag-engine impl. - with examples, no tests yet
amirai21 Nov 25, 2024
ceaa9e9
feat: rag-engine impl. - with examples, no tests yet
amirai21 Nov 25, 2024
657fa5e
feat: makeFormDataRequest - change type and disable lint
amirai21 Nov 25, 2024
d68e935
feat: remove log
amirai21 Nov 25, 2024
329eb3e
feat: add upload file object override
amirai21 Nov 26, 2024
6131ee8
feat: add upload file object override
amirai21 Nov 26, 2024
54a96b1
feat: FilePathOrFileObject
amirai21 Nov 26, 2024
48f2a8d
feat: support node path and file object\ and browser file object
amirai21 Nov 27, 2024
c055ca2
feat: support node path and file object\ and browser file object
amirai21 Nov 27, 2024
02b40df
feat: functioning browser upload
amirai21 Nov 28, 2024
b71e598
feat: reorganized
amirai21 Nov 28, 2024
a93d32c
feat: reorganized
amirai21 Nov 28, 2024
eac5e6e
feat: wip
amirai21 Nov 28, 2024
a628702
feat: wip
amirai21 Dec 1, 2024
6ff4df7
feat: basic unit tests for rag engine
amirai21 Dec 1, 2024
ab469be
feat: wip
amirai21 Dec 2, 2024
f438185
feat: wip
amirai21 Dec 2, 2024
382e69f
feat: wip
amirai21 Dec 2, 2024
7ca7640
feat: add file path check before opening + rag examples improved
amirai21 Dec 4, 2024
9705d0b
feat: fix tests
amirai21 Dec 4, 2024
c88bf02
feat: reorg imports
amirai21 Dec 4, 2024
e3b0810
feat: convert upload to non async
amirai21 Dec 4, 2024
29997e0
feat: node fetch casting
amirai21 Dec 4, 2024
90eb449
feat: disable examples for non node env
amirai21 Dec 4, 2024
8914f61
feat: disable examples for non node env
amirai21 Dec 4, 2024
585e278
feat: log
amirai21 Dec 4, 2024
18b46a0
feat: swap condition
amirai21 Dec 4, 2024
79e616b
test: Trying integration test
asafgardin Dec 4, 2024
5888d67
fix: Added log
asafgardin Dec 4, 2024
e251c1b
fix: Run sync
asafgardin Dec 4, 2024
9cccbc9
fix: Added logs
asafgardin Dec 4, 2024
9de0844
fix: Added logs of env
asafgardin Dec 4, 2024
4e7f794
fix: Added logs of env
asafgardin Dec 4, 2024
8ff04de
fix: Added more logs
asafgardin Dec 4, 2024
e2129a6
fix: Checked env
asafgardin Dec 4, 2024
ac11d5c
ci: Added form-data to bundle
asafgardin Dec 4, 2024
49d82c6
ci: Added form-data to bundle
asafgardin Dec 4, 2024
1b5b314
fix: Node file checks
asafgardin Dec 4, 2024
e3b31ac
fix: Node file checks
asafgardin Dec 4, 2024
670fc69
fix: check type
asafgardin Dec 4, 2024
09be7a4
fix: Moved to factory
asafgardin Dec 4, 2024
d24dc65
fix: ignore ts
asafgardin Dec 4, 2024
af96613
fix: Import of runtime
asafgardin Dec 4, 2024
bf6ad2a
refactor: Renamed methods
asafgardin Dec 5, 2024
0e4019e
Merge pull request #24 from AI21Labs/library-upload
Josephasafg Dec 5, 2024
e70ecbc
chore(release): 1.1.0-rc.1 [skip ci]
semantic-release-bot Dec 5, 2024
4baa92d
feat: files readme
amirai21 Dec 5, 2024
04b35e5
feat: files readme
amirai21 Dec 5, 2024
3059f8d
feat: files readme
amirai21 Dec 5, 2024
c0e1b1e
docs: readme
amirai21 Dec 5, 2024
ed0ef97
docs: Fixed badge tests url
asafgardin Dec 5, 2024
3b8a073
docs: Fixed text
asafgardin Dec 5, 2024
f087f36
docs: Added env text
asafgardin Dec 5, 2024
43b0954
docs: Moved description
asafgardin Dec 5, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Install dependencies
run: |
npm install
npm install ai21
npm run build

- name: Run Integration Tests
env:
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.txt
103 changes: 99 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
# AI21 API Client
<h1 align="center">
<a href="https://github.com/AI21Labs/ai21-typescript">AI21 Labs TypeScript SDK</a>
</h1>

<p align="center">
<a href="https://github.com/AI21Labs/ai21-typescript/actions/workflows/unittests.yml"><img src="https://github.com/AI21Labs/ai21-typescript/actions/workflows/unittests.yml/badge.svg?branch=main" alt="Test"></a>
<a href="https://github.com/AI21Labs/ai21-typescript/actions/workflows/integration-tests.yml"><img src="https://github.com/AI21Labs/ai21-typescript/actions/workflows/integration-tests.yml/badge.svg?branch=main" alt="Integration Tests"></a>
<a href="https://www.npmjs.com/package/ai21" target="_blank"><img src="https://img.shields.io/npm/v/ai21?color=%2334D058&label=npm%20package" alt="Package version"></a>
<a href="https://nodejs.org/" target="_blank"><img src="https://img.shields.io/badge/node->=18.0.0-brightgreen" alt="Supported Node.js versions"></a>
<a href="https://github.com/semantic-release/semantic-release" target="_blank"><img src="https://img.shields.io/badge/semantic--release-typescript-e10079?logo=semantic-release" alt="Semantic Release Support"></a>
<a href="https://opensource.org/licenses/Apache-2.0" target="_blank"><img src="https://img.shields.io/badge/License-Apache_2.0-blue.svg" alt="License"></a>
</p>

The AI21 API Client is a TypeScript library that provides a convenient interface for interacting with the AI21 API. It abstracts away the low-level details of making API requests and handling responses, allowing developers to focus on building their applications.

- [Installation](#Installation) 💿
- [Examples](#examples-tldr) 🗂️
- [AI21 Official Documentation](#Documentation)
- [Chat](#Chat)
- [Conversational RAG (Beta)](#Conversational-RAG)
- [Files](#Files)


## Environment Support

This client supports both Node.js and browser environments:

- **Node.js**: Works out of the box with Node.js >=18.0.0
- **Browser**: Requires explicit opt-in by setting `dangerouslyAllowBrowser: true` in the client options

```typescript
// Browser usage example
const client = new AI21({
apiKey: process.env.AI21_API_KEY, // or pass it in directly
dangerouslyAllowBrowser: true // Required for browser environments
});
```

> ⚠️ **Security Notice**: Using this client in the browser could expose your API key to end users. Only enable `dangerouslyAllowBrowser` if you understand the security implications and have implemented appropriate security measures.

## Installation

You can install the AI21 API Client using npm or yarn:
Expand All @@ -16,10 +52,25 @@ or
yarn add ai21
```

## Usage
## Examples (tl;dr)

If you want to quickly get a glance how to use the AI21 Typescript SDK and jump straight to business, you can check out the examples. Take a look at our models and see them in action! Several examples and demonstrations have been put together to show our models' functionality and capabilities.

### [Check out the Examples](examples/)

Feel free to dive in, experiment, and adapt these examples to suit your needs. We believe they'll help you get up and running quickly.

## Documentation

The full documentation for the REST API can be found on [docs.ai21.com](https://docs.ai21.com/).


## Chat

To use the AI21 API Client, you'll need to have an API key. You can obtain an API key by signing up for an account on the AI21 website.

The `AI21` class provides a `chat` property that gives you access to the Chat API. You can use this to generate text, complete prompts, and more.

Here's an example of how to use the `AI21` class to interact with the API:

```typescript
Expand All @@ -44,7 +95,7 @@ The client supports streaming responses for real-time processing. Here are examp
#### Using Async Iterator

```typescript
const streamResponse = await ai21.chat.completions.create({
const streamResponse = await client.chat.completions.create({
model: 'jamba-1.5-mini',
messages: [{ role: 'user', content: 'Write a story about a space cat' }],
stream: true,
Expand All @@ -54,8 +105,51 @@ for await (const chunk of streamResponse) {
console.log(chunk.choices[0]?.delta?.content || '');
}
```
---
### Files


The `AI21` class provides a `files` property that gives you access to the Files API. You can use it to upload, retrieve, update, list, and delete files.


```typescript
import { AI21 } from 'ai21';

const client = new AI21({
apiKey: process.env.AI21_API_KEY, // or pass it in directly
});

const fileUploadResponse = await client.files.create({
file: './articles/article1.pdf',
labels: ['science', 'biology'],
path: 'virtual-path/to/science-articles',
});


const file = await client.files.get(fileUploadResponse.fileId);

```

---
### Conversational-RAG


The `AI21` class provides a `conversationalRag` property that gives you access to the Conversational RAG API. You can use it to ask questions that are answered based on the files you uploaded.


```typescript
import { AI21 } from 'ai21';

const client = new AI21({
apiKey: process.env.AI21_API_KEY, // or pass it in directly
});

const convRagResponse = await client.conversationalRag.create({
messages: [{ role: 'user', content: 'This question presumes that the answer can be found within the uploaded files.' }],
});

```

The `AI21` class provides a `chat` property that gives you access to the Chat API. You can use this to generate text, complete prompts, and more.

## Configuration

Expand All @@ -65,6 +159,7 @@ The `AI21` class accepts several configuration options, which you can pass in wh
- `apiKey`: Your AI21 API Key
- `maxRetries`: The maximum number of retries for failed requests (default: `3`)
- `timeout`: The request timeout in seconds
- `dangerouslyAllowBrowser`: Set to `true` to allow the client to be used in a browser environment.

## API Reference

Expand Down
1 change: 1 addition & 0 deletions examples/studio/conversational-rag/files/meerkat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The meerkat (Suricata suricatta) or suricate is a small mongoose found in southern Africa. It is characterised by a broad head, large eyes, a pointed snout, long legs, a thin tapering tail, and a brindled coat pattern. The head-and-body length is around 24–35 cm (9.4–13.8 in), and the weight is typically between 0.62 and 0.97 kg (1.4 and 2.1 lb). The coat is light grey to yellowish-brown with alternate, poorly-defined light and dark bands on the back. Meerkats have foreclaws adapted for digging and have the ability to thermoregulate to survive in their harsh, dry habitat. Three subspecies are recognised.
139 changes: 139 additions & 0 deletions examples/studio/conversational-rag/rag-engine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { AI21, FileResponse, UploadFileResponse } from 'ai21';
import path from 'path';
import fs from 'fs';

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function waitForFileProcessing(
client: AI21,
fileId: string,
timeout: number = 30000,
interval: number = 1000,
) {
const startTime = Date.now();

while (Date.now() - startTime < timeout) {
const file: FileResponse = await client.files.get(fileId);
if (file.status !== 'PROCESSING') {
return file;
}
await sleep(interval);
}

throw new Error(`File processing timed out after ${timeout}ms`);
}

async function uploadGetUpdateDelete(fileInput, path) {
const client = new AI21({ apiKey: process.env.AI21_API_KEY });
try {
console.log(`Starting upload for file:`, typeof fileInput);
const uploadFileResponse: UploadFileResponse = await client.files.create({
file: fileInput,
path: path,
});
console.log(`✓ Upload completed. File ID: ${uploadFileResponse.fileId}`);

console.log('Waiting for file processing...');
let file: FileResponse = await waitForFileProcessing(client, uploadFileResponse.fileId);
console.log(`✓ File processing completed with status: ${file.status}`);

if (file.status === 'PROCESSED') {
console.log('Starting file update...');
await client.files.update({
fileId: uploadFileResponse.fileId,
labels: ['test99'],
publicUrl: 'https://www.miri.com',
});
file = await client.files.get(uploadFileResponse.fileId);
console.log('✓ File update completed');
} else {
console.log(`⚠ File processing failed with status ${file.status}`);
return; // Exit early if processing failed
}

console.log('Starting file deletion...');
await client.files.delete(uploadFileResponse.fileId);
console.log('✓ File deletion completed');

// Add buffer time between operations
await sleep(2000);
} catch (error) {
console.error('❌ Error in uploadGetUpdateDelete:', error);
throw error;
}
}

async function listFiles() {
const client = new AI21({ apiKey: process.env.AI21_API_KEY });
const files = await client.files.list({ limit: 4 });
console.log(`Listed files: ${files}`);
}

const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';

const createNodeFile = (content: Buffer, filename: string, type: string) => {
if (process.platform === 'linux') {
console.log('Running on Linux (GitHub Actions)');
// Special handling for Linux (GitHub Actions)
return {
name: filename,
type: type,
buffer: content,
[Symbol.toStringTag]: 'File',
};
} else {
console.log('Running on other platforms');
// Regular handling for other platforms
return new File([content], filename, { type });
}
};

if (isBrowser) {
console.log('Cannot run upload examples in Browser environment');
} else {
/* Log environment details */
console.log('=== Environment Information ===');
console.log(`Node.js Version: ${process.version}`);
console.log(`Platform: ${process.platform}`);
console.log(`Architecture: ${process.arch}`);
console.log(`Process ID: ${process.pid}`);
console.log(`Current Working Directory: ${process.cwd()}`);
console.log('===========================\n');

/* Run all operations sequentially */
(async () => {
try {
console.log('=== Starting first operation ===');
// First operation - upload file from path
const filePath = path.resolve(process.cwd(), 'examples/studio/conversational-rag/files', 'meerkat.txt');
if (!fs.existsSync(filePath)) {
throw new Error(`File not found: ${filePath}`);
} else {
console.log(`File found: ${filePath}`);
}

await uploadGetUpdateDelete(filePath, Date.now().toString());
console.log('=== First operation completed ===\n');
await sleep(2000);

console.log('=== Starting second operation ===');
// Second operation - upload file from File instance
const fileContent = Buffer.from(
'Opossums are members of the marsupial order Didelphimorphia endemic to the Americas.',
);
const dummyFile = createNodeFile(fileContent, 'example.txt', 'text/plain');
await uploadGetUpdateDelete(dummyFile, Date.now().toString());
console.log('=== Second operation completed ===\n');
await sleep(2000);

console.log('=== Starting file listing ===');
await listFiles();
console.log('=== File listing completed ===');
} catch (error) {
console.error('❌ Main execution error:', error);
process.exit(1); // Exit with error code if something fails
}
})();
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "ai21",
"version": "1.0.3",
"engines": {
"node": ">=18.0.0"
},
"version": "1.1.0-rc.1",
"description": "AI21 TypeScript SDK",
"main": "./dist/bundle.cjs.js",
"types": "./dist/index.d.ts",
Expand Down
4 changes: 3 additions & 1 deletion src/AI21.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Chat } from './resources/chat';
import { APIClient } from './APIClient';
import { Headers } from './types';
import * as Runtime from './runtime';
import { ConversationalRag } from './resources/rag/conversationalRag';
import { ConversationalRag } from './resources/rag/conversational-rag';
import { Files } from './resources';

export interface ClientOptions {
baseURL?: string | undefined;
Expand Down Expand Up @@ -67,6 +68,7 @@ export class AI21 extends APIClient {
// Resources
chat: Chat = new Chat(this);
conversationalRag: ConversationalRag = new ConversationalRag(this);
files: Files = new Files(this);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected override authHeaders(_: Types.FinalRequestOptions): Types.Headers {
Expand Down
Loading
Loading