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 | 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'],
]);