Skip to content
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
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ Releases all resources associated with the model. Automatically calls `stop()` f

**`getModels(): Promise<CactusModel[]>`**

Fetches available models from the database and checks their download status. Results are cached in memory after the first call and subsequent calls return the cached results.
Fetches available models from the database and checks their download status.

### useCactusLM Hook

Expand All @@ -603,7 +603,7 @@ The `useCactusLM` hook manages a `CactusLM` instance with reactive state. When m
- `stop(): Promise<void>` - Stops ongoing generation. Clears any errors.
- `reset(): Promise<void>` - Resets the model's internal state, clearing cached context. Also clears the `completion` state.
- `destroy(): Promise<void>` - Releases all resources associated with the model. Clears the `completion` state. Automatically called when the component unmounts.
- `getModels(): Promise<CactusModel[]>` - Fetches available models from the database and checks their download status. Results are cached in memory and reused on subsequent calls.
- `getModels(): Promise<CactusModel[]>` - Fetches available models from the database and checks their download status.

### CactusSTT Class

Expand Down Expand Up @@ -662,9 +662,9 @@ Resets the model's internal state. Automatically calls `stop()` first.

Releases all resources associated with the model. Automatically calls `stop()` first. Safe to call even if the model is not initialized.

**`getModels(): Promise<CactusModel[]>`**
**`getModels(): Promise<CactusSTTModel[]>`**

Fetches available models from the database and checks their download status. Results are cached in memory after the first call and subsequent calls return the cached results.
Fetches available STT models from the database and checks their download status.

### useCactusSTT Hook

Expand All @@ -689,7 +689,7 @@ The `useCactusSTT` hook manages a `CactusSTT` instance with reactive state. When
- `stop(): Promise<void>` - Stops ongoing generation. Clears any errors.
- `reset(): Promise<void>` - Resets the model's internal state. Also clears the `transcription` state.
- `destroy(): Promise<void>` - Releases all resources associated with the model. Clears the `transcription` state. Automatically called when the component unmounts.
- `getModels(): Promise<CactusModel[]>` - Fetches available models from the database and checks their download status. Results are cached in memory and reused on subsequent calls.
- `getModels(): Promise<CactusSTTModel[]>` - Fetches available STT models from the database and checks their download status.

## Type Definitions

Expand Down Expand Up @@ -826,6 +826,19 @@ interface CactusModel {
downloadUrl: string;
supportsToolCalling: boolean;
supportsVision: boolean;
supportsCompletion: boolean;
createdAt: Date;
isDownloaded: boolean;
}
```

### CactusSTTModel

```typescript
interface CactusSTTModel {
slug: string;
sizeMb: number;
downloadUrl: string;
createdAt: Date;
isDownloaded: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,7 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
}

private fun cactusFile(): File {
val documentsDir =
context.getExternalFilesDir(android.os.Environment.DIRECTORY_DOCUMENTS) ?: context.filesDir
val cactusDir = File(documentsDir, "cactus")
val cactusDir = File(context.filesDir, "cactus")

if (!cactusDir.exists()) {
cactusDir.mkdirs()
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PODS:
- boost (1.84.0)
- Cactus (1.2.0):
- Cactus (1.2.1):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -2643,7 +2643,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
Cactus: 8853f351fa4c1ef40bad6d2b0152f503b713dc3e
Cactus: 9eae9838fd1c7be78375534369f7b07123ae645c
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6
FBLazyVector: b8f1312d48447cca7b4abc21ed155db14742bd03
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cactus-react-native",
"version": "1.2.0",
"version": "1.2.1",
"description": "Run AI models locally on mobile devices",
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
Expand Down
66 changes: 66 additions & 0 deletions src/api/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DeviceInfo } from '../specs/CactusDeviceInfo.nitro';
import type { LogRecord } from '../telemetry/Telemetry';
import { packageVersion } from '../constants/packageVersion';
import type { CactusModel } from '../types/CactusModel';
import type { CactusSTTModel } from '../types/CactusSTTModel';

interface CactusModelResponse {
name: string;
Expand All @@ -12,6 +13,14 @@ interface CactusModelResponse {
download_url: string;
supports_tool_calling: boolean;
supports_vision: boolean;
supports_completion: boolean;
created_at: Date;
}

interface CactusSTTModelResponse {
slug: string;
download_url: string;
size_mb: number;
created_at: Date;
}

Expand Down Expand Up @@ -76,6 +85,38 @@ export class Database {
downloadUrl: model.download_url,
supportsToolCalling: model.supports_tool_calling,
supportsVision: model.supports_vision,
supportsCompletion: model.supports_completion,
createdAt: model.created_at,
isDownloaded: false,
};
}

public static async getSTTModel(slug: string): Promise<CactusSTTModel> {
const response = await fetch(
`${this.url}/rest/v1/whisper?slug=eq.${slug}&select=*`,
{
headers: {
'apikey': this.key,
'Authorization': `Bearer ${this.key}`,
'Accept-Profile': 'cactus',
},
}
);

if (!response.ok) {
throw new Error('Getting STT model failed');
}

const [model] = (await response.json()) as CactusSTTModelResponse[];

if (!model) {
throw new Error(`STT model with slug "${slug}" not found`);
}

return {
slug: model.slug,
downloadUrl: model.download_url,
sizeMb: model.size_mb,
createdAt: model.created_at,
isDownloaded: false,
};
Expand Down Expand Up @@ -103,6 +144,31 @@ export class Database {
downloadUrl: model.download_url,
supportsToolCalling: model.supports_tool_calling,
supportsVision: model.supports_vision,
supportsCompletion: model.supports_completion,
createdAt: model.created_at,
isDownloaded: false,
}));
}

public static async getSTTModels(): Promise<CactusSTTModel[]> {
const response = await fetch(`${this.url}/rest/v1/whisper?select=*`, {
headers: {
'apikey': this.key,
'Authorization': `Bearer ${this.key}`,
'Accept-Profile': 'cactus',
},
});

if (!response.ok) {
throw new Error('Getting STT models failed');
}

const models = (await response.json()) as CactusSTTModelResponse[];

return models.map((model) => ({
slug: model.slug,
downloadUrl: model.download_url,
sizeMb: model.size_mb,
createdAt: model.created_at,
isDownloaded: false,
}));
Expand Down
6 changes: 0 additions & 6 deletions src/classes/CactusLM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ export class CactusLM {
private static readonly defaultCompleteMode = 'local';
private static readonly defaultEmbedBufferSize = 2048;

private static cactusModelsCache: CactusModel[] | null = null;

constructor({ model, contextSize, corpusDir }: CactusLMParams = {}) {
Telemetry.init(CactusConfig.telemetryToken);

Expand Down Expand Up @@ -226,14 +224,10 @@ export class CactusLM {
}

public async getModels(): Promise<CactusModel[]> {
if (CactusLM.cactusModelsCache) {
return CactusLM.cactusModelsCache;
}
const models = await Database.getModels();
for (const model of models) {
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
}
CactusLM.cactusModelsCache = models;
return models;
}
}
15 changes: 5 additions & 10 deletions src/classes/CactusSTT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import type {
CactusSTTAudioEmbedParams,
CactusSTTAudioEmbedResult,
} from '../types/CactusSTT';
import type { CactusModel } from '../types/CactusModel';
import { Telemetry } from '../telemetry/Telemetry';
import { CactusConfig } from '../config/CactusConfig';
import { Database } from '../api/Database';
import { getErrorMessage } from '../utils/error';
import type { CactusSTTModel } from '../types/CactusSTTModel';

export class CactusSTT {
private readonly cactus = new Cactus();
Expand All @@ -32,8 +32,6 @@ export class CactusSTT {
};
private static readonly defaultEmbedBufferSize = 4096;

private static cactusModelsCache: CactusModel[] | null = null;

constructor({ model, contextSize }: CactusSTTParams = {}) {
Telemetry.init(CactusConfig.telemetryToken);

Expand All @@ -55,9 +53,10 @@ export class CactusSTT {

this.isDownloading = true;
try {
const model = await Database.getSTTModel(this.model);
await CactusFileSystem.downloadModel(
this.model,
`https://vlqqczxwyaodtcdmdmlw.supabase.co/storage/v1/object/public/voice-models/${this.model}.zip`,
model.downloadUrl,
onProgress
);
} finally {
Expand Down Expand Up @@ -174,15 +173,11 @@ export class CactusSTT {
this.isInitialized = false;
}

public async getModels(): Promise<CactusModel[]> {
if (CactusSTT.cactusModelsCache) {
return CactusSTT.cactusModelsCache;
}
const models = await Database.getModels();
public async getModels(): Promise<CactusSTTModel[]> {
const models = await Database.getSTTModels();
for (const model of models) {
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
}
CactusSTT.cactusModelsCache = models;
return models;
}
}
2 changes: 1 addition & 1 deletion src/constants/packageVersion.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const packageVersion = '1.2.0';
export const packageVersion = '1.2.1';
4 changes: 2 additions & 2 deletions src/hooks/useCactusSTT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
CactusSTTAudioEmbedParams,
CactusSTTAudioEmbedResult,
} from '../types/CactusSTT';
import type { CactusModel } from '../types/CactusModel';
import type { CactusSTTModel } from '../types/CactusSTTModel';

export const useCactusSTT = ({
model = 'whisper-small',
Expand Down Expand Up @@ -254,7 +254,7 @@ export const useCactusSTT = ({
}
}, [cactusSTT]);

const getModels = useCallback(async (): Promise<CactusModel[]> => {
const getModels = useCallback(async (): Promise<CactusSTTModel[]> => {
setError(null);
try {
return await cactusSTT.getModels();
Expand Down
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { useCactusSTT } from './hooks/useCactusSTT';

// Types
export type { CactusModel } from './types/CactusModel';
export type { CactusSTTModel } from './types/CactusSTTModel';
export type {
CactusLMParams,
CactusLMDownloadParams,
Expand Down
14 changes: 11 additions & 3 deletions src/native/Cactus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@ export class Cactus {
callback?: (token: string, tokenId: number) => void
): Promise<CactusLMCompleteResult> {
const messagesInternal: Message[] = [];
for (const message of messages) {
if (!message.images) {
messagesInternal.push(message);
for (let i = 0; i < messages.length; i++) {
const message = messages[i]!;
const isLastMessage = i === messages.length - 1;

if (!message.images || !isLastMessage) {
messagesInternal.push({
...message,
images: undefined,
});
continue;
}

const resizedImages: string[] = [];
for (const imagePath of message.images) {
const resizedImage = await CactusImage.resize(
Expand All @@ -47,6 +54,7 @@ export class Cactus {
);
resizedImages.push(resizedImage);
}

messagesInternal.push({ ...message, images: resizedImages });
}

Expand Down
1 change: 1 addition & 0 deletions src/types/CactusModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface CactusModel {
downloadUrl: string;
supportsToolCalling: boolean;
supportsVision: boolean;
supportsCompletion: boolean;
createdAt: Date;

// Local
Expand Down
10 changes: 10 additions & 0 deletions src/types/CactusSTTModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface CactusSTTModel {
// API
slug: string;
sizeMb: number;
downloadUrl: string;
createdAt: Date;

// Local
isDownloaded: boolean;
}
Loading