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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Your router updates its own model catalog from a signed feed: new free models, q
- [How it works](#how-it-works)
- [Context Handoff](#context-handoff)
- [Limitations](#limitations)
- [Documentation index](docs/README.md)
- [Contributor guide](CONTRIBUTING.md)
- [Contributing](#contributing)
- [Terms of Service review](#terms-of-service-review)
- [Disclaimer](#disclaimer)
Expand Down Expand Up @@ -164,6 +166,8 @@ Prefer to read before you pipe to bash? [The script is here](https://freellmapi.

On Windows, the easiest path is the desktop **[`.exe` installer from Releases](https://github.com/tashfeenahmed/freellmapi/releases/latest)** (below); the Docker steps work in WSL or any bash shell.

On Android, see the experimental [Termux installation guide](docs/install/android-termux.md). It uses Node's built-in SQLite driver and does not require the Android NDK.

**Or manually with Docker Compose.** It runs the API and dashboard together on port 3001 and persists SQLite in a named volume.

**Prerequisites:** Docker, Docker Compose, OpenSSL.
Expand Down
14 changes: 11 additions & 3 deletions client/src/components/keys/provider-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
DropdownMenuItem,
DropdownMenuCheckboxItem,
} from '@/components/ui/dropdown-menu'
import { ChevronDown, ExternalLink, KeyRound, MoreHorizontal, Pencil, Plus, RefreshCw, Search, Trash2 } from 'lucide-react'
import { ChevronDown, CircleAlert, ExternalLink, KeyRound, MoreHorizontal, Pencil, Plus, RefreshCw, Search, Trash2 } from 'lucide-react'
import type { ApiKey, ApiKeyModel } from '../../../../shared/types'
import { formatSqliteUtcToLocalTime } from '@/lib/utils'
import { useI18n } from '@/i18n'
Expand Down Expand Up @@ -163,7 +163,7 @@ export function ProviderList({ onAddKey }: { onAddKey: () => void }) {
}
}, [editingKeyId])

const healthKeyMap = new Map<number, { status: string; lastCheckedAt: string | null }>()
const healthKeyMap = new Map<number, { status: string; lastCheckedAt: string | null; lastHealthError: string | null }>()
for (const k of healthData?.keys ?? []) healthKeyMap.set(k.id, k)
const statusOf = (k: ApiKey) => healthKeyMap.get(k.id)?.status ?? k.status

Expand Down Expand Up @@ -346,7 +346,9 @@ export function ProviderList({ onAddKey }: { onAddKey: () => void }) {
<div className="rounded-2xl border divide-y bg-card overflow-hidden">
{group.keys.map(k => {
const status = statusOf(k)
const lastChecked = healthKeyMap.get(k.id)?.lastCheckedAt
const health = healthKeyMap.get(k.id)
const lastChecked = health?.lastCheckedAt ?? k.lastCheckedAt
const lastHealthError = health?.lastHealthError ?? k.lastHealthError
const isEditing = editingKeyId === k.id
const customModels = k.models ?? []
const hasCustomModels = customModels.length > 0
Expand Down Expand Up @@ -437,6 +439,12 @@ export function ProviderList({ onAddKey }: { onAddKey: () => void }) {
</ConfirmButton>
</div>
</div>
{lastHealthError && (
<div className="flex items-start gap-2 px-4 pb-3 pl-8 text-xs text-destructive" role="status">
<CircleAlert className="mt-0.5 size-3.5 flex-shrink-0" />
<span className="break-words" title={lastHealthError}>{lastHealthError}</span>
</div>
)}
{hasCustomModels && isExpanded && (
<div className="flex flex-wrap gap-2 border-t bg-muted/20 px-4 py-3 pl-12">
{customModels.map(model => {
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/keys/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const PLATFORMS: { value: Platform; label: string; url: string; keyless?:
{ value: 'zhipu', label: 'Zhipu AI (Z.ai)', url: 'https://z.ai/manage-apikey/apikey-list' },
{ value: 'ollama', label: 'Ollama Cloud', url: 'https://ollama.com/settings/keys' },
{ value: 'kilo', label: 'Kilo Gateway (no key needed)', url: 'https://app.kilo.ai', keyless: true },
{ value: 'pollinations', label: 'Pollinations (no key needed)', url: 'https://pollinations.ai', keyless: true },
{ value: 'pollinations', label: 'Pollinations', url: 'https://enter.pollinations.ai' },
{ value: 'ovh', label: 'OVH AI Endpoints (no key needed)', url: 'https://endpoints.ai.cloud.ovh.net', keyless: true },
{ value: 'llm7', label: 'LLM7 (anon ok)', url: 'https://llm7.io' },
{ value: 'huggingface', label: 'HuggingFace Router', url: 'https://huggingface.co/settings/tokens' },
Expand Down Expand Up @@ -110,6 +110,6 @@ export interface HealthPlatform {

export interface HealthData {
platforms: HealthPlatform[]
keys: { id: number; platform: string; status: string; lastCheckedAt: string | null }[]
keys: { id: number; platform: string; status: string; lastCheckedAt: string | null; lastHealthError: string | null }[]
quotaStates: ProviderQuotaState[]
}
38 changes: 38 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# FreeLLMAPI documentation

This index points to the focused guides in the repository. The root [README](../README.md) remains the product overview and complete API reference.

## Install and deploy

- [Quick start](../README.md#quick-start) — Docker one-liner, manual Docker Compose, and local development.
- [Docker deployment](../docker/README.md) — container configuration and persistent storage.
- [Desktop app](../desktop/README.md) — build and package the Electron application.
- [Android with Termux](install/android-termux.md) — experimental local installation using Node's built-in SQLite driver.

## Configure and operate

- [Declarative startup configuration](../README.md#declarative-startup-config) — configure keys and custom providers from environment variables.
- [Credentials and local data](../README.md#credentials-and-where-your-data-lives) — desktop paths and credential storage.
- [Premium live catalog](../README.md#premium-live-catalog) — catalog update behavior and licensing.
- [Database migrations](../server/src/db/README.md) — create, apply, inspect, and roll back schema migrations.

## Integrate clients

- [OpenAI-compatible clients](../README.md#works-with-openai-compatible-clients) — SDK and tool configuration.
- [Coding agents](../README.md#coding-agents) — Codex CLI, Claude Code, OpenCode, and MCP clients.
- [Using the API](../README.md#using-the-api) — request examples for chat, streaming, tools, images, and audio.
- [Embeddings](../README.md#embeddings) — embedding-model routing and custom endpoints.
- [Anthropic and Claude clients](../README.md#anthropic--claude-clients) — Messages API compatibility.

## Develop and contribute

- [Contributor guide](../CONTRIBUTING.md) — development loop, testing expectations, and contribution policy.
- [How the router works](../README.md#how-it-works) — architecture and fallback behavior.
- [Database migration guide](../server/src/db/README.md) — migration CLI and conventions.

## Website assets in this directory

- [`index.html`](index.html) — project landing page.
- [`install.sh`](install.sh) — Unix Docker bootstrap script.
- [`install.ps1`](install.ps1) — PowerShell bootstrap script.
- [`success.html`](success.html) — post-install success page.
110 changes: 110 additions & 0 deletions docs/install/android-termux.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Android (Termux) installation

> Experimental and community-supported. FreeLLMAPI runs locally on the Android device.

FreeLLMAPI can run in [Termux](https://termux.dev/) without an Android NDK toolchain. On Android, the server uses Node's built-in SQLite driver instead of the native `better-sqlite3` package.

## Requirements

- Android 7 or newer
- About 1 GB of free storage
- [Termux from F-Droid](https://f-droid.org/packages/com.termux/) (the Play Store build is outdated)
- Node.js 22.13 or newer; Node 24 LTS is recommended

Do not mix Termux or add-on packages from different stores.

## Install

Update Termux and install the required packages:

```bash
pkg update
pkg upgrade -y
pkg install -y nodejs-lts git
```

Confirm that Node is new enough for `node:sqlite`:

```bash
node --version
```

Then clone and start FreeLLMAPI:

```bash
git clone https://github.com/tashfeenahmed/freellmapi.git
cd freellmapi
npm install --no-audit --no-fund
npm run dev
```

An installation warning for the optional `better-sqlite3` package is harmless on Android. If npm asks you to approve dependency install scripts, approve the `esbuild` script and run `npm install` again.

Open `http://localhost:5173` in a browser on the phone. The API is available at `http://localhost:3001/v1`.

## Access from another device

The dashboard contains API keys and administrative controls. Only expose it on a trusted network.

```bash
HOST=0.0.0.0 npm run dev:lan
```

Find the phone's local address with Android's Wi-Fi settings or:

```bash
ip addr show wlan0
```

Open `http://PHONE_IP:5173` from another device on the same network. Do not port-forward these development servers directly to the internet; use a private VPN such as Tailscale if remote access is required.

## Keep the process awake

Android may suspend Termux when the screen turns off. Install the Termux:API add-on from the same store as Termux, then run:

```bash
pkg install -y termux-api
termux-wake-lock
```

Release the lock when the server is stopped:

```bash
termux-wake-unlock
```

## Troubleshooting

### `node:sqlite` is unavailable

Upgrade the Termux packages and verify that `node --version` reports 22.13 or newer:

```bash
pkg update
pkg upgrade -y
pkg install -y nodejs-lts
```

### `concurrently: not found`

The dependency installation did not complete. Run `npm install --no-audit --no-fund` again and address the first non-optional error it reports.

### Port already in use

Stop the process using port 3001 or 5173, or configure a different `PORT` in the repository's `.env` file.

### The process stops when the screen turns off

Use `termux-wake-lock` and disable battery optimization for Termux in Android settings.

## Updating

Stop the running development server, then run:

```bash
git pull --ff-only
npm install --no-audit --no-fund
npm run dev
```

When reporting an Android issue, include the Android version, Termux source and version, device architecture, `node --version`, and the complete error output.
Loading
Loading