> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom config

Presets are the recommended way to configure Polyglot. They live in YAML files, keep secrets in
environment variables, and let you switch providers without touching application code. However,
there are situations where presets are not sufficient -- when configuration values are dynamic,
generated at runtime, or sourced from your own application's settings. In these cases, you can
build `LLMConfig` or `EmbeddingsConfig` objects directly.

## The Configuration Files

Polyglot ships with YAML preset files organized in two directories:

* **`config/llm/presets/`** -- one file per inference provider (e.g. `openai.yaml`, `anthropic.yaml`)
* **`config/embed/presets/`** -- one file per embeddings provider

A typical preset file looks like this:

```yaml theme={null}
# config/llm/presets/openai.yaml
driver: openai
apiUrl: 'https://api.openai.com/v1'
apiKey: '${OPENAI_API_KEY}'
endpoint: /chat/completions
metadata:
  organization: ''
  project: ''
model: gpt-4.1-nano
maxTokens: 1024
contextLength: 1000000
maxOutputLength: 16384
# @doctest id="21c8"
```

Environment variable references like `${OPENAI_API_KEY}` are resolved automatically at load
time. Polyglot searches for preset files in the following directories, in order:

1. `config/llm/presets/` (your project root)
2. `packages/polyglot/resources/config/llm/presets/` (monorepo layout)
3. `vendor/cognesy/instructor-php/packages/polyglot/resources/config/llm/presets/`
4. `vendor/cognesy/instructor-polyglot/resources/config/llm/presets/`

The first directory that exists wins. You can override the search path by passing a `$basePath`
argument to `Inference::using()`.

## Configuration Parameters

Each LLM configuration includes these parameters:

| Parameter         | Type     | Description                                                                  |
| ----------------- | -------- | ---------------------------------------------------------------------------- |
| `driver`          | `string` | The protocol driver to use (e.g. `openai`, `anthropic`, `openai-compatible`) |
| `apiUrl`          | `string` | Base URL for the provider's API                                              |
| `apiKey`          | `string` | API key for authentication                                                   |
| `endpoint`        | `string` | The API endpoint path (e.g. `/chat/completions`)                             |
| `queryParams`     | `array`  | Optional query string parameters appended to the URL                         |
| `metadata`        | `array`  | Provider-specific settings (organization, API version, etc.)                 |
| `model`           | `string` | Default model name                                                           |
| `maxTokens`       | `int`    | Default maximum tokens for responses                                         |
| `contextLength`   | `int`    | Maximum context window supported by the model                                |
| `maxOutputLength` | `int`    | Maximum output length supported by the model                                 |
| `options`         | `array`  | Default request options passed to every call                                 |
| `pricing`         | `array`  | Optional token pricing information for cost tracking                         |

Embeddings configurations use a similar structure with `dimensions` and `maxInputs` instead of
the token-related fields.

## Runtime Configuration with LLMConfig

When you need to build a configuration programmatically, use the `LLMConfig` class:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Config\LLMConfig;
use Cognesy\Polyglot\Inference\Inference;

$config = new LLMConfig(
    driver: 'openai',
    apiUrl: 'https://api.openai.com/v1',
    apiKey: (string) getenv('OPENAI_API_KEY'),
    endpoint: '/chat/completions',
    model: 'gpt-4.1-nano',
    maxTokens: 2048,
    contextLength: 1000000,
    maxOutputLength: 16384,
);

$text = Inference::fromConfig($config)
    ->withMessages(Messages::fromString('Say hello.'))
    ->get();
// @doctest id="9d93"
```

Note: `Messages` is imported from `Cognesy\Messages\Messages`. You can create messages from a
string with `Messages::fromString()` or from an array of role/content pairs with
`Messages::fromArray()`.

This is useful when your application stores provider credentials in a database, rotates API
keys at runtime, or needs to construct configurations for providers not included in the
bundled presets.

### Creating Configurations from Arrays

You can also create an `LLMConfig` from an associative array, which is convenient when loading
configuration from a database or external source:

```php theme={null}
<?php

use Cognesy\Polyglot\Inference\Config\LLMConfig;

$config = LLMConfig::fromArray([
    'driver' => 'openai',
    'apiUrl' => 'https://api.openai.com/v1',
    'apiKey' => $apiKeyFromDatabase,
    'endpoint' => '/chat/completions',
    'model' => 'gpt-4.1-nano',
    'maxTokens' => 1024,
]);
// @doctest id="b63b"
```

### Overriding Configuration Values

If you need to modify an existing configuration, use `withOverrides()` to create a new
instance with specific values changed:

```php theme={null}
<?php

use Cognesy\Polyglot\Inference\Config\LLMConfig;

$baseConfig = LLMConfig::fromPreset('openai');

// Create a variant with a different model and higher token limit
$highCapConfig = $baseConfig->withOverrides([
    'model' => 'gpt-4.1',
    'maxTokens' => 4096,
]);
// @doctest id="4f26"
```

## Embeddings Configuration

The embeddings equivalent follows the same pattern:

```php theme={null}
<?php

use Cognesy\Polyglot\Embeddings\Config\EmbeddingsConfig;
use Cognesy\Polyglot\Embeddings\Embeddings;

$embeddings = Embeddings::fromConfig(new EmbeddingsConfig(
    driver: 'openai',
    apiUrl: 'https://api.openai.com/v1',
    apiKey: (string) getenv('OPENAI_API_KEY'),
    endpoint: '/embeddings',
    model: 'text-embedding-3-small',
    dimensions: 1536,
    maxInputs: 2048,
));
// @doctest id="5400"
```

The embeddings configuration parameters are:

| Parameter    | Type     | Description                                                   |
| ------------ | -------- | ------------------------------------------------------------- |
| `driver`     | `string` | The driver to use (e.g. `openai`, `cohere`, `gemini`, `jina`) |
| `apiUrl`     | `string` | Base URL for the provider's API                               |
| `apiKey`     | `string` | API key for authentication                                    |
| `endpoint`   | `string` | The API endpoint path (e.g. `/embeddings`)                    |
| `model`      | `string` | The embedding model name                                      |
| `dimensions` | `int`    | The dimensionality of the embedding vectors                   |
| `maxInputs`  | `int`    | Maximum number of inputs per batch request                    |
| `metadata`   | `array`  | Provider-specific settings                                    |

## DSN Input

Both `LLMConfig` and `EmbeddingsConfig` support a lightweight DSN (Data Source Name) format
for quick inline configuration:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Config\LLMConfig;
use Cognesy\Polyglot\Inference\Inference;

$config = LLMConfig::fromDsn('openai://api.openai.com/v1?model=gpt-4.1-nano&apiKey=sk-...');

$response = Inference::fromConfig($config)
    ->withMessages(Messages::fromString('Hello!'))
    ->get();
// @doctest id="4bb8"
```

The DSN format encodes the driver as the scheme, the host and path as the API URL, and
query parameters for the remaining configuration values.

## Managing API Keys

API keys should never be committed to your codebase. Polyglot preset files use environment
variable references (`${OPENAI_API_KEY}`) that are resolved at load time. Store your keys in
a `.env` file:

```bash theme={null}
OPENAI_API_KEY=sk-your-key-here
ANTHROPIC_API_KEY=sk-ant-your-key-here
GEMINI_API_KEY=your-key-here
MISTRAL_API_KEY=your-key-here
# @doctest id="684b"
```

Load them with a package like `vlucas/phpdotenv`, or rely on your framework's built-in
environment handling (Laravel loads `.env` automatically).

> **Security Tip:** The `apiKey` parameter on `LLMConfig` is marked with PHP's
> `#[SensitiveParameter]` attribute, which prevents it from appearing in stack traces.
> Polyglot also redacts sensitive values from event payloads and debug output.

## Provider-Specific Options

Different providers support unique request parameters. You can pass these through the
`options` parameter on each request:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Inference;

// OpenAI-specific options
$response = Inference::using('openai')
    ->with(
        messages: Messages::fromString('Generate a creative story.'),
        options: [
            'temperature' => 0.8,
            'top_p' => 0.95,
            'frequency_penalty' => 0.5,
            'presence_penalty' => 0.5,
            'stop' => ["\n\n", "THE END"],
        ],
    )
    ->get();
// @doctest id="184c"
```

For Anthropic, the available options differ:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Inference;

// Anthropic-specific options
$response = Inference::using('anthropic')
    ->with(
        messages: Messages::fromString('Generate a creative story.'),
        options: [
            'temperature' => 0.7,
            'top_p' => 0.9,
            'top_k' => 40,
        ],
    )
    ->get();
// @doctest id="f961"
```

Polyglot passes these options through to the provider's API without modification, so consult
each provider's documentation for the full list of supported parameters.

You can also set default options in the preset YAML file so they apply to every request made
with that preset:

```yaml theme={null}
# config/llm/presets/creative-openai.yaml
driver: openai
apiUrl: 'https://api.openai.com/v1'
apiKey: '${OPENAI_API_KEY}'
endpoint: /chat/completions
model: gpt-4.1
maxTokens: 2048
options:
  temperature: 0.9
  top_p: 0.95
# @doctest id="fedb"
```

## Environment-Based Configuration

You can create custom presets for different deployment environments. For example, use a local
Ollama instance in development and a cloud provider in production:

```yaml theme={null}
# config/llm/presets/dev-local.yaml
driver: openai-compatible
apiUrl: 'http://localhost:11434/v1'
apiKey: ''
endpoint: /chat/completions
model: llama3
maxTokens: 1024
# @doctest id="363c"
```

Then select the preset based on your application's environment:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Inference;

$preset = getenv('APP_ENV') === 'production' ? 'openai' : 'dev-local';

$response = Inference::using($preset)
    ->withMessages(Messages::fromString('Hello!'))
    ->get();
// @doctest id="8f01"
```

This pattern keeps your application code completely environment-agnostic. The only thing that
changes between environments is which preset name is selected.

## Creating Custom Preset Files

To add a new provider or a custom configuration, create a new YAML file in your project's
`config/llm/presets/` directory:

```yaml theme={null}
# config/llm/presets/my-proxy.yaml
driver: openai-compatible
apiUrl: 'https://my-proxy.example.com/v1'
apiKey: '${MY_PROXY_API_KEY}'
endpoint: /chat/completions
model: gpt-4.1-nano
maxTokens: 2048
contextLength: 128000
maxOutputLength: 16384
# @doctest id="4428"
```

Then reference it by name:

```php theme={null}
<?php

use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\Inference;

$response = Inference::using('my-proxy')
    ->withMessages(Messages::fromString('Hello from my proxy!'))
    ->get();
// @doctest id="5cfd"
```

Polyglot will find your custom preset file before falling back to the bundled presets, so you
can override any built-in preset by creating a file with the same name in your project's
configuration directory.
