AI

object

%AI

/phlo/resources/AI/AI.phlo

Unified AI facade with engine auto-detect

One door for every engine: %AI->chat reads the engine from the model name, so gpt goes to OpenAI, claude to Claude and gemini to Gemini, while via names one outright. Every engine answers in the same shape, with answer, model, finish and a token count, so swapping models is a one-word change. A model no rule matches falls back to OpenAI, and each engine still needs credentials of its own.

aifacadellmstreamingtoolsembeddings
prop

%AI -> model:string

line 11
This item defines an AI model with the identifier 'gpt-5.4-mini'.
'gpt-5.4-mini'
const

AI :: engines

line 12
Defines a mapping of AI engine identifiers to their respective names, allowing for easy reference and integration within Phlo applications.
['claude' => 'Claude', 'gpt' => 'OpenAI', 'chatgpt' => 'OpenAI', 'o1' => 'OpenAI', 'o3' => 'OpenAI', 'o4' => 'OpenAI', 'deepseek' => 'DeepSeek', 'gemini' => 'Gemini', 'grok' => 'Grok']
static

AI :: http (string $url, array $headers, bool $json = true, mixed $post = null):string

line 13
Sends an HTTP request to the specified URL with optional headers, JSON payload, and POST data, allowing for a customizable timeout period.
HTTP($url, $headers, $json, $post, timeout: 300)
method

%AI -> resolve (...$args):array

line 14
Resolves the AI engine to be used based on the provided arguments, defaulting to 'OpenAI' if no specific engine is specified.
$via = $args['via'] ?? void
unset($args['via'])
$args['model'] ??= $this->model
if ($via) $via = static::engines[strtolower($via)] ?? $via
elseif (isset($args['model'])) $via = static::engines[strtolower(explode(dash, $args['model'])[0])] ?? void
return [$via ?: 'OpenAI', $args]
method

%AI -> chat (...$args):obj

line 22
This method initiates a chat session using the specified AI engine and arguments.
[$engine, $args] = $this->resolve(...$args)
return phlo($engine)->chat(...$args)
method

%AI -> stream (...$args):Generator

line 26
Streams data from an AI engine using the provided arguments.
[$engine, $args] = $this->resolve(...$args)
return phlo($engine)->stream(...$args)
method

%AI -> embedding (...$args):array

line 30
This function resolves the engine and arguments, then calls the embedding method on the specified engine with the provided arguments.
[$engine, $args] = $this->resolve(...$args)
return phlo($engine)->embedding(...$args)
method

%AI -> vision (...$args):obj|Generator

line 34
This function resolves the AI engine and arguments, then invokes the vision method of the specified engine with the provided arguments.
[$engine, $args] = $this->resolve(...$args)
return phlo($engine)->vision(...$args)
method

%AI -> transcribe (...$args):obj

line 38
Transcribes audio input using the specified AI engine and arguments.
[$engine, $args] = $this->resolve(...$args)
return phlo($engine)->transcribe(...$args)
object

%Claude

/phlo/resources/AI/Claude.phlo

Anthropic Claude API

Anthropic has no embedding endpoint, so embedding() quietly goes out through OpenAI and needs that key too. system travels as its own field here rather than as a first message, which context() settles, so the same call works on either engine. vision() fetches an image URL itself and sends it inline as base64, so a large photo becomes a large request.

aiclaudeanthropicchatvisionembeddings
const

Claude :: model

line 11
Defines a model using the 'claude-opus-4-8' source, which can be utilized for various AI tasks within Phlo.
'claude-opus-4-8'
static

Claude :: context (...$args):array

line 13
Initializes the context for Claude by setting up messages and system parameters, ensuring that user and assistant messages are properly formatted and stored.
$args['messages'] ??= []
if (isset($args['system'])){
	$args['system'] = [['type' => 'text', 'text' => $args['system']]]
}
if (isset($args['assistant']) && array_push($args['messages'], ['role' => 'assistant', 'content' => $args['assistant']])) unset($args['assistant'])
if (isset($args['user']) && array_push($args['messages'], ['role' => 'user', 'content' => $args['user']])) unset($args['user'])
return $args
static

Claude :: tool ($tool):array

line 23
This constructs an associative array containing the name and description of a tool, along with its input schema, which defines the properties and required arguments for the tool.
[
	'name' => $tool->name,
	'description' => $tool->desc,
	'input_schema' => [
		'type' => 'object',
		'properties' => loop($tool->args, fn($data, $arg) => array_filter($data, fn($key) => in_array($key, ['type', 'enum', 'description']), ARRAY_FILTER_USE_KEY)),
		'required' => array_keys($tool->args),
	],
]
method

%Claude -> embedding ($input, $model = 'text-embedding-3-small'):array

line 33
Generates an embedding for the given input using the specified model, defaulting to 'text-embedding-3-small'.
%OpenAI->embedding($input, $model)
method

%Claude -> vision ($text, $image, $stream = false, ...$args):obj|Generator

line 35
This function sends a text and an image to a vision model, optionally streaming the response or returning it in a chat format.
$data = is_string($image) && str_starts_with($image, 'http') ? file_get_contents($image) : $image
$messages = [['role' => 'user', 'content' => [['type' => 'text', 'text' => $text], ['type' => 'image', 'source' => ['type' => 'base64', 'media_type' => 'image/jpeg', 'data' => base64_encode($data)]]]]]
if ($stream) return $this->stream(...$args, messages: $messages)
else return $this->chat(...$args, messages: $messages)
method

%Claude -> chat (...$args):obj

line 42
This function interacts with the Claude API to send a chat request and process the response, returning an object containing the answer, model information, token usage, and any tools used during the interaction.
$args['model'] ??= static::model
$args['max_tokens'] ??= 4096
$token = $args['token'] ?? null
unset($args['token'])
$args = static::context(...$args)
$res = $this->request('messages', token: $token, POST: $args)
$return = new obj(answer: void, model: $res->model, finish: $res->stop_reason, tokens: ($res->usage->input_tokens ?? 0) + ($res->usage->output_tokens ?? 0), tokens_in: $res->usage->input_tokens ?? 0, tokens_out: $res->usage->output_tokens ?? 0)
$tools = []
foreach ($res->content AS $block){
	if ($block->type === 'text') $return->answer = $block->text
	elseif ($block->type === 'tool_use') $tools[] = new obj(name: $block->name, args: (array)$block->input)
}
if ($tools) $return->tools = $tools
return $return
method

%Claude -> parseSSE (string $url, array $headers, array $payload):Generator

line 59
$json = json_encode($payload, jsonFlat)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode(nl, [...$headers, 'Content-Type: application/json', 'Content-Length: '.strlen($json)]), 'content' => $json, 'timeout' => 300, 'ignore_errors' => true]])
$stream = fopen($url, 'r', false, $ctx)
$stream || error('SSE connection failed')
$status = (int)explode(space, $http_response_header[0] ?? 'HTTP/1.1 200')[1]
if ($status >= 400){
	$err = json_decode((string)stream_get_contents($stream))
	fclose($stream)
	error('Claude error '.$status.': '.($err->error->message ?? 'unknown'))
}
$finish = null
$usage = null
while (!feof($stream)){
	$line = fgets($stream)
	if ($line === false) break
	$line = rtrim($line, nl)
	if (!str_starts_with($line, 'data:')) continue
	$data = ltrim(substr($line, 5))
	if ($data === void) continue
	$p = json_decode($data)
	if (!$p) continue
	if (isset($p->error)) error('Claude stream error: '.$p->error->message)
	if (($p->type ?? null) === 'message_delta'){
		$finish = $p->delta->stop_reason ?? $finish
		if (isset($p->usage)) $usage = $p->usage
	}
	if (($p->type ?? null) === 'content_block_delta' && ($p->delta->type ?? null) === 'text_delta' && isset($p->delta->text)) yield obj(text: $p->delta->text)
}
fclose($stream)
yield obj(done: true, finish: $finish, tokens_in: $usage?->input_tokens, tokens_out: $usage?->output_tokens)
method

%Claude -> stream (...$args):Generator

line 91
Streams messages from the Claude API using Server-Sent Events (SSE), allowing for real-time interaction with the model.
%res->streaming = true
$args['model'] ??= static::model
$args['max_tokens'] ??= 4096
$token = $args['token'] ?? null
unset($args['token'], $args['cb'])
$args = static::context(...$args)
$args['stream'] = true
if ($token) $headers = ['anthropic-version: 2023-06-01', 'anthropic-beta: oauth-2025-04-20', 'Authorization: Bearer '.$token]
else $headers = ['anthropic-version: 2023-06-01', 'x-api-key: '.%creds->Claude]
return $this->parseSSE('https://api.anthropic.com/v1/messages', $headers, $args)
method

%Claude -> request ($uri, ...$args)

line 104
Sends a request to the Claude API using the specified URI and arguments, handling authentication with a token or API key, and returns the decoded JSON response.
$token = $args['token'] ?? null
if ($token) $headers = ['anthropic-version: 2023-06-01', 'anthropic-beta: oauth-2025-04-20', 'Authorization: Bearer '.$token]
else $headers = ['anthropic-version: 2023-06-01', 'x-api-key: '.%creds->Claude]
$res = json_decode(AI::http("https://api.anthropic.com/v1/$uri", $headers, true, $args['POST'] ?? null))
if (isset($res->error)) error('Claude Request error: '.$res->error->message)
return $res
object

%DeepSeek

/phlo/resources/AI/DeepSeek.phlo

DeepSeek API (OpenAI-compatible, extends OpenAI)

OpenAI-compatible, so it inherits the whole OpenAI resource and only swaps endpoint, model and credentials; anything that works there works here unless DeepSeek itself lacks it. Embeddings are such a gap: they are routed to OpenAI, so that key has to be present as well.

aideepseekchatembeddings
const

DeepSeek :: model

line 12
DeepSeek::model is a component that facilitates the creation and management of models within the DeepSeek framework, allowing for efficient data handling and interaction.
'deepseek-chat'
const

DeepSeek :: endpoint

line 13
Defines the endpoint URL for accessing the DeepSeek API version 1.
'https://api.deepseek.com/v1/'
const

DeepSeek :: cred

line 14
DeepSeek::cred retrieves credentials for accessing resources within the DeepSeek framework.
'DeepSeek'
const

DeepSeek :: label

line 15
DeepSeek::label is used to define a label for a DeepSeek resource, allowing for better organization and identification within the system.
'DeepSeek'
method

%DeepSeek -> embedding ($input, $model = 'text-embedding-3-small'):array

line 17
Generates an embedding for the given input using the specified model, defaulting to 'text-embedding-3-small'.
%OpenAI->embedding($input, $model)
object

%Gemini

/phlo/resources/AI/Gemini.phlo

Google Gemini API

Google puts the model in the path rather than in the body, so a wrong model name reads as a wrong URL. Its embeddings come from text-embedding-004 with a different vector length than OpenAI's, so a collection filled by one engine cannot be searched with the other.

aigeminigooglechatvisionembeddings
const

Gemini :: model

line 11
Creates a model in the Gemini framework, allowing for the definition and manipulation of data structures.
'gemini-2.0-flash'
const

Gemini :: endpoint

line 12
Defines an endpoint for accessing the Gemini API, allowing interaction with the specified model at the given URL.
'https://generativelanguage.googleapis.com/v1beta/models/'
static

Gemini :: context (...$args):array

line 14
Gemini::$context processes input arguments to construct a structured context for a conversation, organizing system, assistant, and user messages into a contents array.
$args['contents'] ??= []
if (isset($args['system'])){
	$args['systemInstruction'] = ['parts' => [['text' => $args['system']]]]
	unset($args['system'])
}
if (isset($args['assistant']) && array_push($args['contents'], ['role' => 'model', 'parts' => [['text' => $args['assistant']]]])) unset($args['assistant'])
if (isset($args['user']) && array_push($args['contents'], ['role' => 'user', 'parts' => [['text' => $args['user']]]])) unset($args['user'])
return $args
static

Gemini :: tool ($tool):array

line 25
Gemini::$tool retrieves the name and description of a tool, along with its parameters, which include type, properties, and required arguments.
[
	'name' => $tool->name,
	'description' => $tool->desc,
	'parameters' => [
		'type' => 'object',
		'properties' => loop($tool->args, fn($data, $arg) => array_filter($data, fn($key) => in_array($key, ['type', 'enum', 'description']), ARRAY_FILTER_USE_KEY)),
		'required' => array_keys($tool->args),
	],
]
method

%Gemini -> embedding ($input, $model = 'text-embedding-004'):array

line 35
Generates embeddings for the given input text using the specified model, defaulting to 'text-embedding-004'.
$this->request($model.':embedContent', POST: ['content' => ['parts' => [['text' => $input]]]])->embedding->values
method

%Gemini -> vision ($text, $image, $stream = false, ...$args):obj|Generator

line 37
Generates a vision response by combining text and an image, with an optional streaming feature for real-time output.
$model = $args['model'] ?? static::model
unset($args['model'])
$contents = [['role' => 'user', 'parts' => [['text' => $text], ['inline_data' => ['mime_type' => 'image/jpeg', 'data' => base64_encode(is_string($image) && str_starts_with($image, 'http') ? file_get_contents($image) : $image)]]]]]
if ($stream) return $this->stream(...$args, model: $model, contents: $contents)
else return $this->chat(...$args, model: $model, contents: $contents)
method

%Gemini -> chat (...$args):obj

line 45
Generates content based on the specified model and arguments, returning an object that includes the generated answer, model used, finish reason, and token usage details.
$model = $args['model'] ?? static::model
unset($args['model'])
$args = static::context(...$args)
$res = $this->request($model.':generateContent', POST: $args)
$return = new obj(answer: void, model: $model, finish: $res->candidates[0]->finishReason ?? void, tokens: ($res->usageMetadata->promptTokenCount ?? 0) + ($res->usageMetadata->candidatesTokenCount ?? 0), tokens_in: $res->usageMetadata->promptTokenCount ?? 0, tokens_out: $res->usageMetadata->candidatesTokenCount ?? 0)
$tools = []
foreach ($res->candidates[0]->content->parts ?? [] AS $part){
	if (isset($part->text)) $return->answer = $part->text
	elseif (isset($part->functionCall)) $tools[] = new obj(name: $part->functionCall->name, args: (array)$part->functionCall->args)
}
if ($tools) $return->tools = $tools
return $return
method

%Gemini -> parseSSE (string $url, array $headers, array $payload):Generator

line 60
$json = json_encode($payload, jsonFlat)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode(nl, [...$headers, 'Content-Type: application/json', 'Content-Length: '.strlen($json)]), 'content' => $json, 'timeout' => 300, 'ignore_errors' => true]])
$stream = fopen($url, 'r', false, $ctx)
$stream || error('SSE connection failed')
$status = (int)explode(space, $http_response_header[0] ?? 'HTTP/1.1 200')[1]
if ($status >= 400){
	$err = json_decode((string)stream_get_contents($stream))
	fclose($stream)
	error('Gemini error '.$status.': '.($err->error->message ?? 'unknown'))
}
$finish = null
$usage = null
while (!feof($stream)){
	$line = fgets($stream)
	if ($line === false) break
	$line = rtrim($line, nl)
	if (!str_starts_with($line, 'data:')) continue
	$data = ltrim(substr($line, 5))
	if ($data === void) continue
	$p = json_decode($data)
	if (!$p) continue
	if (isset($p->error)) error('Gemini stream error: '.$p->error->message)
	if (isset($p->usageMetadata)) $usage = $p->usageMetadata
	if ($p->candidates[0]->finishReason ?? null) $finish = $p->candidates[0]->finishReason
	$text = $p->candidates[0]->content->parts[0]->text ?? null
	if (!is_null($text)) yield obj(text: $text)
}
fclose($stream)
yield obj(done: true, finish: $finish, tokens_in: $usage?->promptTokenCount, tokens_out: $usage?->candidatesTokenCount)
method

%Gemini -> stream (...$args):Generator

line 91
Enables streaming for the specified model in Gemini, preparing the necessary arguments and returning a parsed Server-Sent Events response.
%res->streaming = true
$model = $args['model'] ?? static::model
unset($args['model'], $args['cb'])
$args = static::context(...$args)
return $this->parseSSE(static::endpoint.$model.':streamGenerateContent?alt=sse', ['x-goog-api-key: '.%creds->Gemini], $args)
method

%Gemini -> request ($path, ...$args)

line 99
Sends a request to the Gemini API at the specified path with optional arguments, handling errors and returning the decoded JSON response.
$res = json_decode(AI::http(static::endpoint.$path, ['x-goog-api-key: '.%creds->Gemini], true, $args['POST'] ?? null))
if (isset($res->error)) error('Gemini Request error: '.$res->error->message)
return $res
object

%Grok

/phlo/resources/AI/Grok.phlo

xAI Grok API (OpenAI-compatible, extends OpenAI)

OpenAI-compatible, so it inherits the whole OpenAI resource and only swaps endpoint, model and credentials. Embeddings are not part of the deal and go out through OpenAI, so that key has to be present as well.

aigrokxaichatvisionembeddings
const

Grok :: model

line 12
Grok::model is a component in the Grok framework that facilitates the creation and management of data models, allowing for easy interaction with databases and ORM functionality.
'grok-4'
const

Grok :: endpoint

line 13
Defines an endpoint for making API requests to the specified URL.
'https://api.x.ai/v1/'
const

Grok :: cred

line 14
Grok::cred is a module that provides credential management functionalities within the Grok framework.
'Grok'
const

Grok :: label

line 15
Creates a label for a Grok pattern, allowing for better organization and identification of patterns in your code.
'Grok'
method

%Grok -> embedding ($input, $model = 'text-embedding-3-small'):array

line 17
Generates an embedding for the given input using the specified model, defaulting to 'text-embedding-3-small'.
%OpenAI->embedding($input, $model)
object

%OpenAI

/phlo/resources/AI/OpenAI.phlo

Basic OpenAI functions

Write a conversation as system, user and assistant instead of assembling messages yourself; context() folds them into the right order. Each answer carries tokens_in and tokens_out, so a run can be metered or capped without reading the raw response. A refused request is raised as an error rather than returned, which is the opposite of how the connectors behave, so wrap a call you cannot afford to lose. Pass token to use a key other than the configured one.

aiopenaillmchatembeddingsaudiovision
const

OpenAI :: model

line 11
This item specifies the OpenAI model to be used, in this case, 'gpt-5.4-mini'.
'gpt-5.4-mini'
const

OpenAI :: endpoint

line 12
Defines the endpoint for accessing the OpenAI API, specifically the base URL for API requests.
'https://api.openai.com/v1/'
const

OpenAI :: cred

line 13
This item represents the OpenAI credential used for authentication and access to OpenAI services.
'OpenAI'
const

OpenAI :: label

line 14
This function is used to label data using OpenAI's models for various applications.
'OpenAI'
const

OpenAI :: voices

line 15
OpenAI::voices is a predefined list of voice options available for use in Phlo applications, including 'alloy', 'echo', 'fable', 'onyx', 'nova', and 'shimmer'.
['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']
static

OpenAI :: context (...$args):array

line 16
This function initializes the 'messages' array in the context of OpenAI, adding system, assistant, and user messages as specified in the input arguments.
$args['messages'] ??= []
if (isset($args['system']) && array_unshift($args['messages'], ['role' => 'system', 'content' => $args['system']])) unset($args['system'])
if (isset($args['assistant']) && array_push($args['messages'], ['role' => 'assistant', 'content' => $args['assistant']])) unset($args['assistant'])
if (isset($args['user']) && array_push($args['messages'], ['role' => 'user', 'content' => $args['user']])) unset($args['user'])
return $args
static

OpenAI :: tool ($tool):array

line 23
Defines a function in OpenAI with specified parameters, including type, enum, and description, while enforcing strict validation of the arguments.
[
	'type' => 'function',
	'function' => [
		'name' => $tool->name,
		'description' => $tool->desc,
		'parameters' => [
			'type' => 'object',
			'properties' => loop($tool->args, fn($data, $arg) => array_filter($data, fn($key) => in_array($key, ['type', 'enum', 'desc']), ARRAY_FILTER_USE_KEY)),
			'additionalProperties' => false,
			'required' => array_keys($tool->args),
		],
		'strict' => true,
	],
]
method

%OpenAI -> chat (...$args):obj

line 37
This function interacts with the OpenAI API to generate chat completions based on the provided arguments, returning an object containing the response details such as the generated answer, model used, and token usage statistics.
$args['model'] ??= static::model
$token = $args['token'] ?? null
unset($args['token'])
$args = static::context(...$args)
$res = $this->request('chat/completions', token: $token, POST: $args)
$return = new obj(answer: $res->choices[0]->message->content, model: $res->model, finish: $res->choices[0]->finish_reason, tokens: $res->usage->total_tokens, tokens_in: $res->usage->prompt_tokens, tokens_out: $res->usage->completion_tokens)
if (isset($res->choices[0]->message->tool_calls)) $return->tools = array_map(fn($tool) => new obj(name: $tool->function->name, args: json_decode($tool->function->arguments, true)), (array)$res->choices[0]->message->tool_calls)
return $return
method

%OpenAI -> embedding ($input, $model = 'text-embedding-3-small'):array

line 47
Generates an embedding for the given input using the specified model from OpenAI.
$this->request('embeddings', POST: ['input' => $input, 'model' => $model])->data[0]->embedding
method

%OpenAI -> parseSSE (string $url, array $headers, array $payload):Generator

line 48
$json = json_encode($payload, jsonFlat)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode(nl, [...$headers, 'Content-Type: application/json', 'Content-Length: '.strlen($json)]), 'content' => $json, 'timeout' => 300, 'ignore_errors' => true]])
$stream = fopen($url, 'r', false, $ctx)
$stream || error('SSE connection failed')
$status = (int)explode(space, $http_response_header[0] ?? 'HTTP/1.1 200')[1]
if ($status >= 400){
	$err = json_decode((string)stream_get_contents($stream))
	fclose($stream)
	error(static::label.' error '.$status.': '.($err->error->message ?? 'unknown'))
}
$finish = null
$usage = null
while (!feof($stream)){
	$line = fgets($stream)
	if ($line === false) break
	$line = rtrim($line, nl)
	if (!str_starts_with($line, 'data:')) continue
	$data = ltrim(substr($line, 5))
	if ($data === '[DONE]' || $data === void) continue
	$p = json_decode($data)
	if (!$p) continue
	if (isset($p->error)) error(static::label.' stream error: '.$p->error->message)
	if (isset($p->usage)) $usage = $p->usage
	$text = $p->choices[0]->delta->content ?? null
	if (!is_null($text)) yield obj(text: $text)
	if ($p->choices[0]->finish_reason ?? null) $finish = $p->choices[0]->finish_reason
}
fclose($stream)
yield obj(done: true, finish: $finish, tokens_in: $usage?->prompt_tokens, tokens_out: $usage?->completion_tokens)
method

%OpenAI -> stream (...$args):Generator

line 79
Streams responses from the OpenAI API using Server-Sent Events (SSE), allowing for real-time interaction with the model.
%res->streaming = true
$args['model'] ??= static::model
$token = $args['token'] ?? null
unset($args['token'], $args['cb'])
$args = static::context(...$args)
$args['stream'] = true
$bearer = $token ?? %creds->{static::cred};
return $this->parseSSE(static::endpoint.'chat/completions', ['Authorization: Bearer '.$bearer], $args)
method

%OpenAI -> transcribe ($file, $model = 'whisper-1', ...$args):obj

line 89
Transcribes audio from a given file using the specified model, returning details such as duration, language, and transcribed text.
if (is_string($file)) $file = new CURLFile($file)
elseif (is_a($file, 'file')) $file = $file->curl
$res = $this->request('audio/transcriptions', false, POST: [...$args, 'model' => $model, 'file' => $file, 'response_format' => 'verbose_json'])
return obj (
	model: $model,
	duration: $res->duration,
	lang: $res->language,
	text: $res->text,
)
method

%OpenAI -> vision ($text, $image, $stream = false, ...$args):obj

line 100
This function sends a text and an image to the OpenAI vision model, optionally streaming the response or returning it in a chat format.
$args['model'] ??= static::model
$messages = [['role' => 'user', 'content' => [['type' => 'text', 'text' => $text], ['type' => 'image_url', 'image_url' => ['url' => $image]]]]]
if ($stream) return $this->stream(...$args, messages: $messages)
else return $this->chat(...$args, messages: $messages)
method

%OpenAI -> request ($uri, $JSON = true, $token = null, ...$args)

line 106
Sends a request to the OpenAI API at the specified URI, optionally including a JSON payload and an authorization token, and returns the decoded response.
$bearer = $token ?? %creds->{static::cred};
$res = json_decode(AI::http(static::endpoint.$uri, ['Authorization: Bearer '.$bearer], $JSON, $args['POST'] ?? null))
if (isset($res->error)) error(static::label.' Request error: '.$res->error->message)
return $res

Functions

function

answer($question, ...$options):?string

/phlo/resources/AI/answer.phlo line 11

Simple AI answering helper

For a question with one short answer rather than a conversation: answer('...') gives a bare line and answer('...', 'yes', 'no') forces the reply to be exactly one of the options. Nothing fitting gives null instead of an invented answer, so treat null as a real outcome. It runs at temperature .1, so the same question tends to give the same answer.

aianswerquestionllm
Generates a short, direct answer to a given question, either from specified options or by providing a concise response without additional text.
	$prompt = 'You are an AI answer machine. '
	$prompt .= 'You give short, direct answers without repeating the subject. '
	$prompt .= 'You add no explanation, no extra text, and no quotation marks. '
	$prompt .= 'Always answer in the same language as the question.'.lf
	if ($options){
		$prompt .= 'The user asks a question. You choose exactly one of the options below as the answer. '
		$prompt .= 'Your answer matches exactly one of the options, with no extra words or punctuation. '
		$prompt .= 'If none of the options apply, answer with "-".'.lf.lf
		$prompt .= 'Question:'.lf.$question.lf.lf
		$prompt .= 'Options:'.lf
		$prompt .= implode(lf, $options)
	}
	else {
		$prompt .= 'Give a short and precise answer to the question. '
		$prompt .= 'No introduction, no explanation, no list, and no final period.'.lf.lf
		$prompt .= 'Question:'.lf.$question
	}
	$answer = %AI->chat(user: $prompt, temperature: .1)->answer
	return $answer === dash ? null : $answer

Last updated on 23 August 2026

We use essential cookies to make this site work. With your permission we also use analytics to improve the site.