Artificial intelligence


1Introduction

Temma provides a unified data source \Temma\Datasources\Ai to query language models (LLMs) from various providers through a single interface.

This data source supports:

  • Text exchange (prompt / response)
  • System prompts and temperature configuration
  • Multi-turn conversations (conversation history)
  • Input attachments (images, audio, video, PDF)
  • Structured JSON output

Six providers are built in (OpenAI, Claude, Gemini, Mistral, OpenRouter, Ollama), and any OpenAI-compatible service can be used via the bracket syntax.

For the data source reference documentation, see the AI data source page.


2Configuration

2.1DSN format

The connection DSN (Data Source Name) is written as:

ai://PROVIDER/MODEL#API_KEY
  • PROVIDER: provider name (see list below).
  • MODEL: language model identifier. May contain / and : characters.
  • API_KEY: your API key (optional for some providers like Ollama).

2.2Configuration example

In the etc/temma.php file:

<?php

return [
    'application' => [
        'dataSources' => [
            // connect to OpenAI
            'ai' => 'ai://openai/gpt-4o#sk-proj-xxxxxxxxxxxxx',
            // or to Claude
            'ai' => 'ai://claude/claude-sonnet-4-20250514#sk-ant-xxxxxxxxxxxxx',
            // or to a local model via Ollama
            'ai' => 'ai://ollama/llama3:70b',
        ],
    ],
];

3Built-in providers

3.1Summary table

Provider Text Images Audio Video PDF JSON
openai yes yes yes yes
claude yes yes yes yes
gemini yes yes yes yes yes yes
mistral yes yes yes yes yes
openrouter yes yes yes yes yes
ollama yes yes yes

Actual support also depends on the model used. For example, not all OpenAI models support vision.


3.2DSN examples

# OpenAI
ai://openai/gpt-4o#sk-proj-XXX

# Claude (Anthropic)
ai://claude/claude-sonnet-4-20250514#sk-ant-XXX

# Google Gemini
ai://gemini/gemini-2.5-flash#AIza-XXX

# Mistral
ai://mistral/mistral-small-latest#XXX

# OpenRouter (model name contains "/" separating provider from model)
ai://openrouter/openai/gpt-4o#sk-or-XXX
ai://openrouter/anthropic/claude-sonnet-4#sk-or-XXX

# Ollama (local, no API key)
ai://ollama/llama3:70b
ai://ollama/mistral:latest

4OpenAI-compatible services

4.1Bracket syntax (URL)

Many services provide an OpenAI-compatible API. To use them, place the endpoint URL in brackets in the DSN:

ai://[ENDPOINT_URL]/MODEL#API_KEY

Some common services:

Service Endpoint URL
Groq https://api.groq.com/openai/v1/chat/completions
Together AI https://api.together.xyz/v1/chat/completions
Fireworks AI https://api.fireworks.ai/inference/v1/chat/completions
DeepInfra https://api.deepinfra.com/v1/openai/chat/completions

Examples:

# Groq
ai://[https://api.groq.com/openai/v1/chat/completions]/llama-3.3-70b#gsk-XXX

# Together AI (model name with "/")
ai://[https://api.together.xyz/v1/chat/completions]/meta-llama/Meta-Llama-3-70B#XXX

# Self-hosted LiteLLM
ai://[https://my-litellm.internal/v1/chat/completions]/model#key

4.2Custom providers

You can also provide a PHP class name in brackets. This class will be instantiated and used as a provider:

ai://[\App\MyProvider]/model#key

The class must implement the buildPayload() and parseResponse() methods, like the built-in providers (see classes in \Temma\Datasources\Ai\*).


5Usage

5.1Array-like access

Array notation sends a prompt and returns the response as raw text.

$response = $this->ai['What is the capital of France?'];
// $response contains "The capital of France is Paris."

5.2read() method

The read() method returns a raw text response. The second parameter is a default value (scalar or callback) used on error. The third parameter is an options array.

// simple prompt
$response = $this->ai->read('What is the capital of France?');

// with a default value
$response = $this->ai->read('Translate to French: Hello', 'Bonjour');

// with a callback on error
$response = $this->ai->read('Translate to French: Hello', function() {
    return 'Fallback value';
});

// with options
$response = $this->ai->read('Explain photosynthesis', null, [
    'system'      => 'You are a biology teacher.',
    'temperature' => 0.3,
]);

5.3get() method

The get() method automatically activates JSON mode: the LLM is instructed to respond with valid JSON, and the response is automatically decoded into a PHP array.

// request structured data
$data = $this->ai->get('List the 3 largest cities in France with their population');
// $data contains a PHP array, for example:
// [
//     ['city' => 'Paris', 'population' => 2161000],
//     ['city' => 'Marseille', 'population' => 873076],
//     ['city' => 'Lyon', 'population' => 522250],
// ]

// with options
$recipe = $this->ai->get("Create a recipe using: eggs, tomatoes, cheese", null, [
    'system' => "You are a chef. Return JSON with keys: title, difficulty (1-5), ingredients (list), steps (list).",
]);

6Options

The read() and get() methods accept a third parameter $options, an associative array:

  • system: (string) System prompt defining the assistant's behavior.
  • messages: (array) Previous message history for multi-turn conversation (see below).
  • temperature: (float) Sampling temperature, between 0 and 2. Lower values make responses more deterministic, higher values make them more creative.
  • max_tokens: (int) Maximum number of tokens in the response.
  • attachments: (array) Attachments to send with the prompt (see next section).
  • output: (string) Desired output format. Can be an alias ('json', 'csv', 'audio', 'image', 'pdf', 'video', 'html', 'xml', 'wav') or a full MIME type ('application/json', 'audio/ogg', etc.).

Full example:

$response = $this->ai->read('Describe this image in detail', null, [
    'system'      => 'You are an image analysis expert.',
    'temperature' => 0.5,
    'max_tokens'  => 1000,
    'attachments' => ['/path/to/photo.jpg'],
]);

7Attachments

7.1Accepted formats

The attachments option accepts an array of attachments. Each element can be:

  • A string: file path (if file_exists() returns true) or binary content. MIME type is detected automatically.
  • An associative array with the keys:
    • path: file path.
    • data: binary content.
    • mime: (optional) explicit MIME type.

Examples:

// file path (MIME auto-detected)
'attachments' => ['/path/to/photo.jpg']

// binary content (MIME auto-detected)
'attachments' => [$binaryContent]

// binary with explicit MIME
'attachments' => [
    ['data' => $binaryContent, 'mime' => 'image/png'],
]

// file path with explicit MIME
'attachments' => [
    ['path' => '/path/to/document.pdf', 'mime' => 'application/pdf'],
]

// multiple attachments
'attachments' => [
    '/path/to/image1.jpg',
    ['data' => $pdfContent, 'mime' => 'application/pdf'],
]

7.2MIME type

If the MIME type is not explicitly provided, it is automatically detected via finfo. When you already have binary content in memory, it is recommended to provide the MIME type explicitly to avoid unnecessary detection.

Attachment support depends on the provider and model used (see summary table above). If an attachment type is not supported by the provider, it will be ignored.


8JSON output

Two approaches to get structured JSON:

get() method: automatically activates JSON mode. The LLM receives a system instruction asking it to respond with valid JSON. The response is automatically decoded into a PHP array.

$data = $this->ai->get('The 5 most popular programming languages');
// $data is a PHP array

read() method with the output option: allows requesting JSON while using read(). The response remains a raw JSON string (not decoded).

$json = $this->ai->read('The 5 most popular programming languages', null, [
    'output' => 'json',
]);
// $json is a raw JSON string
$data = json_decode($json, true);

9Multi-turn conversation

The messages option allows providing the history of previous exchanges. Each message is an associative array with a user or ai key:

$response = $this->ai->read('And the capital of Italy?', null, [
    'system'   => 'You are a geography assistant.',
    'messages' => [
        ['user' => 'What is the capital of France?'],
        ['ai' => 'The capital of France is Paris.'],
    ],
]);
// $response contains "The capital of Italy is Rome."

History messages can also contain attachments:

$response = $this->ai->read('And this one?', null, [
    'messages' => [
        ['user' => 'Describe this image', 'attachments' => ['/path/to/photo1.jpg']],
        ['ai' => 'It is a ginger cat sitting on a sofa.'],
    ],
    'attachments' => ['/path/to/photo2.jpg'],
]);