# How to Configure FluentRead to Use Your Own AI API Keys: A Complete Guide

> Learn how to configure FluentRead with your own AI API keys. This guide explains setting custom keys via the UI or programmatically for enhanced privacy and control in your application.

- Repository: [ThinkStu/fluentread](https://github.com/bistutu/fluentread)
- Tags: how-to-guide
- Published: 2026-02-26

---

**FluentRead stores AI API credentials in a local `Config` object with a `token` map that persists to browser storage, allowing you to configure custom keys through the settings UI or programmatically via the `config.token` interface.**

FluentRead is an open-source browser extension that provides intelligent translation services using various AI providers. If you want to configure FluentRead to use your own AI API keys instead of default credentials, you need to understand how the extension manages authentication tokens across its architecture.

## How FluentRead Stores API Credentials

The extension centralizes all configuration in a `Config` class defined in [`entrypoints/utils/model.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/model.ts). This class declares a `token` property as an `IMapping` type to hold string tokens for every service requiring authentication.

```typescript
// entrypoints/utils/model.ts (lines 21-27)
export class Config {
  service: string;
  token: IMapping;  // Stores API keys for each service
  // ... other properties
}

```

When the extension initializes, [`entrypoints/utils/config.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/config.ts) loads persisted settings from the browser's local storage and hydrates the exported `config` instance.

```typescript
// entrypoints/utils/config.ts (lines 16-28)
const stored = await storage.getItem('local:config');
if (stored) {
  const parsed = JSON.parse(stored as string);
  Object.assign(config, parsed);  // Merges stored tokens into config
}

```

This architecture ensures your API keys remain in local browser storage and never transit to external servers except when making direct API calls to your chosen AI provider.

## Setting Up Your AI API Keys in FluentRead

### Using the Settings UI

The primary interface for entering credentials resides in [`components/Main.vue`](https://github.com/bistutu/fluentread/blob/main/components/Main.vue). The UI binds an `<el-input>` element directly to `config.token[config.service]`, creating a two-way data binding that updates the configuration object as you type.

```vue
<!-- components/Main.vue (lines 92-104) -->
<el-row v-show="compute.showToken" class="margin-bottom margin-left-2em">
  <el-col :span="12" class="lightblue rounded-corner">
    <span class="popup-text popup-vertical-left">访问令牌</span>
  </el-col>
  <el-col :span="12">
    <el-input 
      v-model="config.token[config.service]" 
      type="password" 
      show-password 
      placeholder="请输入API访问令牌" />
  </el-col>
</el-row>

```

To configure your key through the UI:

1. Open the FluentRead extension panel in your browser.
2. Select your desired AI service from the dropdown (e.g., OpenAI, Azure OpenAI, Gemini).
3. Locate the **"访问令牌"** (Access Token) field.
4. Paste your API key into the password input.
5. The extension automatically persists the value to `local:config` storage.

### Programmatic Configuration

For advanced users who want to inject credentials via userscripts or extension popups, you can manipulate the `config` object directly:

```typescript
import { config } from '@/entrypoints/utils/config';
import { services } from '@/entrypoints/utils/option';

// Configure OpenAI API key programmatically
config.token[services.openai] = 'sk-your-openai-api-key';

// Persist immediately to local storage
await storage.setItem('local:config', JSON.stringify(config));

```

This approach is useful when distributing FluentRead in managed environments where you pre-configure keys for users.

## Validating API Keys Before Translation

Before dispatching any translation request, FluentRead validates that required credentials exist. The `checkConfig()` function in [`entrypoints/utils/check.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/check.ts) verifies token presence for services marked as requiring authentication.

```typescript
// entrypoints/utils/check.ts (lines 10-18)
export function checkConfig(): boolean {
  if (servicesType.isUseToken(config.service) && !config.token[config.service]) {
    // Validation fails - token required but not provided
    return false;
  }
  // Additional checks...
  return true;
}

```

The `servicesType.isUseToken()` method checks membership in a `Set` defined in [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts) that enumerates all token-requiring services.

```typescript
// entrypoints/utils/option.ts (lines 69-78)
export const servicesType = {
  useToken: new Set([
    services.openai,
    services.azureOpenai,
    services.gemini,
    services.anthropic,
    services.deepseek,
    services.moonshot,
    services.siliconflow,
    services.glm,
    services.groq,
    services.custom
  ])
};

```

If validation fails, FluentRead prompts the user to enter their API key before proceeding with the translation.

## Adding Custom AI Services

You can extend FluentRead to support additional AI providers that require API keys by modifying three components:

First, register the new service in [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts):

```typescript
// 1. Add to services enum
export const services = {
  // ... existing services
  myCustomAi: 'myCustomAi',
};

// 2. Add to token-requiring set
export const servicesType = {
  useToken: new Set([
    ...servicesType.useToken,
    services.myCustomAi
  ])
};

```

Next, implement the API client that reads the token from `config.token`:

```typescript
// Custom service implementation example
import { config } from '@/entrypoints/utils/config';
import { services } from '@/entrypoints/utils/option';

export async function translateWithCustomAI(text: string) {
  const apiKey = config.token[services.myCustomAi];
  
  const response = await fetch('https://api.myservice.com/v1/chat', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      model: 'gpt-4',
      messages: [{ role: 'user', content: text }]
    })
  });
  
  return response.json();
}

```

The existing UI in [`components/Main.vue`](https://github.com/bistutu/fluentread/blob/main/components/Main.vue) automatically supports your new service because the token input binds to `config.token[config.service]` dynamically.

## Key Files and Architecture Reference

| File | Role | Location |
|------|------|----------|
| [`entrypoints/utils/model.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/model.ts) | Declares the `Config` class and the `token` map property. | [model.ts](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/model.ts) |
| [`entrypoints/utils/config.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/config.ts) | Loads and persists the `Config` instance from local browser storage. | [config.ts](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/config.ts) |
| [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts) | Defines available services and identifies which require tokens via `servicesType.useToken`. | [option.ts](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts) |
| [`entrypoints/utils/check.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/check.ts) | Validates token presence before translation requests via `checkConfig()`. | [check.ts](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/check.ts) |
| [`components/Main.vue`](https://github.com/bistutu/fluentread/blob/main/components/Main.vue) | Provides the settings UI where users input API keys bound to `config.token`. | [Main.vue](https://github.com/bistutu/fluentread/blob/main/components/Main.vue) |

## Summary

- **FluentRead uses a centralized `Config` class** with a `token` map to store API keys for each AI service in [`entrypoints/utils/model.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/model.ts).
- **Credentials persist in browser local storage** via [`entrypoints/utils/config.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/config.ts), ensuring keys remain private to your machine.
- **The settings UI** in [`components/Main.vue`](https://github.com/bistutu/fluentread/blob/main/components/Main.vue) binds directly to `config.token[config.service]`, providing real-time updates and automatic persistence.
- **Validation occurs before translation** through `checkConfig()` in [`entrypoints/utils/check.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/check.ts), which verifies tokens exist for services listed in `servicesType.useToken`.
- **You can extend support** for new AI providers by adding entries to [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts) and implementing the corresponding API client.

## Frequently Asked Questions

### Where does FluentRead store my API keys?

FluentRead stores your API keys in the browser's local storage under the key `local:config`. The `Config` class in [`entrypoints/utils/model.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/model.ts) defines a `token` property that maps service names to their corresponding API keys. When you enter a key in the settings UI, it writes directly to `config.token[config.service]` and persists via `storage.setItem('local:config', JSON.stringify(config))`, ensuring your credentials never leave your local machine except when making direct API calls to your chosen AI provider.

### Can I use multiple AI providers simultaneously with different API keys?

Yes, FluentRead supports multiple AI providers simultaneously through the `token` map structure. Each service defined in [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts)—such as `services.openai`, `services.gemini`, or `services.anthropic`—maintains its own entry in `config.token`. You can configure a unique API key for each provider through the settings panel, and FluentRead will use the corresponding token based on whichever `config.service` is currently selected for translation.

### How do I add a custom AI service that requires an API key?

To add a custom AI service, modify three files in the FluentRead source. First, add your service identifier to the `services` object in [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts). Second, add the service to the `servicesType.useToken` Set to indicate it requires authentication. Third, implement the API client in a new service file, reading the key via `config.token[services.yourService]`. The existing UI in [`components/Main.vue`](https://github.com/bistutu/fluentread/blob/main/components/Main.vue) will automatically display the token input field for your new service because it dynamically binds to `config.token[config.service]`.

### What happens if my API key is invalid or missing?

Before sending any translation request, FluentRead executes `checkConfig()` in [`entrypoints/utils/check.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/check.ts) to validate the configuration. If the selected service is listed in `servicesType.useToken` (defined in [`entrypoints/utils/option.ts`](https://github.com/bistutu/fluentread/blob/main/entrypoints/utils/option.ts)) and `config.token[config.service]` is empty or undefined, the validation fails and FluentRead prompts you to enter your API key. The translation request will not proceed until a valid token is provided, preventing unnecessary API errors and ensuring you know exactly which service requires authentication.