AI

object

%AI

/phlo/resources/AI/AI.phlo
version 1.0
creator q-ai.nl
summary Unified AI facade with engine auto-detect
package ai
frontend false
backend true
requires @OpenAI? @Claude? @Gemini? @DeepSeek?
tags ai facade llm streaming tools embeddings
const

AI :: engines

line 10
AI::engines is a mapping of AI engine identifiers to their respective names, allowing for easy reference and integration within the Phlo environment.
['claude' => 'Claude', 'gpt' => 'OpenAI', 'chatgpt' => 'OpenAI', 'o1' => 'OpenAI', 'o3' => 'OpenAI', 'o4' => 'OpenAI', 'deepseek' => 'DeepSeek', 'gemini' => 'Gemini']
static

AI :: sseDecodeEvents (string &$buffer, string $chunk):array

line 11
Decodes Server-Sent Events (SSE) from a buffer, extracting data lines that start with 'data:' and returning them as an array of decoded JSON objects.
$buffer .= strtr($chunk, ["\r\n" => lf, "\r" => lf])
$events = []
while (($pos = strpos($buffer, lf.lf)) !== false){
	$raw = substr($buffer, 0, $pos)
	$buffer = substr($buffer, $pos + 2)
	$dataLines = []
	foreach (explode(lf, $raw) AS $line){
		$line = ltrim($line)
		if (!str_starts_with($line, 'data:')) continue
		$dataLines[] = ltrim(substr($line, 5))
	}
	if (!$dataLines) continue
	$payload = trim(implode(lf, $dataLines))
	if ($payload === '[DONE]' || $payload === void) continue
	$decoded = json_decode($payload)
	$decoded && $events[] = $decoded
}
return $events
static

AI :: sseStreamJSONWithTool (string $url, array $headers, array $payload, \Closure $extractText, \Closure $extractTool, ?\Closure $cb = null, ?\Closure $toolCb = null):obj

line 31
Streams JSON data from a server-sent events (SSE) endpoint, processing incoming events and extracting text and tool information based on provided callbacks.
$req = %req
if (!$cb){
	$req->cli || %res->header('Content-Type', 'text/event-stream')
	$cb = fn($text) => last(print($text), $req->cli || [@ob_flush(), flush()], $text)
}
$answer = void
$toolJson = void
$buffer = void
$curl = curl_init($url)
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST')
curl_setopt($curl, CURLOPT_POSTFIELDS, $json)
curl_setopt($curl, CURLOPT_HTTPHEADER, [...$headers, 'Content-Type: application/json', 'Content-Length: '.strlen($json)])
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5)
curl_setopt($curl, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($extractText, $extractTool, $cb, $toolCb, &$buffer, &$answer, &$toolJson){
	foreach (static::sseDecodeEvents($buffer, $data) AS $event){
		$text = $extractText($event)
		(is_null($text) || $text === void || $text === false) || $answer .= $cb((string)$text, $event)
		$tool = $extractTool($event)
		if (!(is_null($tool) || $tool === void || $tool === false)){
			$toolJson .= (string)$tool
			if ($toolCb) $toolCb((string)$tool)
		}
	}
	return strlen($data)
})
$res = curl_exec($curl)
$res === false && error('HTTP error: '.curl_error($curl))
return new obj(answer: $answer, tool_json: $toolJson)
static

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

line 62
This function initializes a cURL session to send HTTP requests, allowing for both GET and POST methods, with optional JSON encoding for the request body and custom headers.
$curl = curl_init($url)
if ($post !== null){
	curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST')
	if ($json && !is_string($post)) $post = json_encode($post, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
	if ($json) $headers[] = 'Content-Type: application/json'
	curl_setopt($curl, CURLOPT_POSTFIELDS, $post)
}
curl_setopt_array($curl, [CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 300, CURLOPT_ENCODING => ''])
$res = curl_exec($curl)
if ($res === false) throw new \RuntimeException('HTTP error: '.curl_error($curl))
return $res
static

AI :: resolve (...$args):array

line 75
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'] ??= %app->model ?? void
if ($via) $via = static::engines[strtolower($via)] ?? $via
elseif (isset($args['model'])) $via = static::engines[strtolower(explode('-', $args['model'])[0])] ?? void
return [$via ?: 'OpenAI', $args]
method

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

line 83
This function initializes a chat session with an AI engine, resolving the engine and arguments before returning the chat instance.
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->chat(...$args)
method

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

line 87
This function resolves the specified engine and arguments, then initiates a streaming process using the Phlo framework.
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->stream(...$args)
method

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

line 91
This function resolves the engine and arguments, then streams data using the specified tool in Phlo.
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->stream_with_tool(...$args)
method

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

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

%AI -> vision (...$args)

line 99
This function resolves the specified arguments to an engine and then invokes the vision method on the resolved engine with those arguments.
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->vision(...$args)
method

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

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

%Claude

/phlo/resources/AI/Claude.phlo
version 1.1
creator q-ai.nl
summary Anthropic Claude API
package ai
frontend false
backend true
requires creds:Claude @AI
tags ai claude anthropic chat vision embeddings
const

Claude :: model

line 10
Creates a model instance for the specified resource, allowing interaction with the underlying data structure.
'claude-sonnet-4-5-20250929'
static

Claude :: context (...$args)

line 12
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 22
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')

line 32
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)

line 34
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)

line 41
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 58
Establishes a Server-Sent Events (SSE) connection to a specified URL, sending a JSON payload and headers, and yields data received from the stream, handling errors and connection status appropriately.
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode("\r\n", [...$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(' ', $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, "\r\n")
	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 90
Streams messages from the Claude API using specified arguments, including model and token, while setting up the necessary headers for authentication.
%app->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 103
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
version 1.1
creator q-ai.nl
summary DeepSeek API
package ai
frontend false
backend true
requires creds:DeepSeek @AI
tags ai deepseek chat embeddings
const

DeepSeek :: model

line 10
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'
static

DeepSeek :: context (...$args)

line 12
DeepSeek::$context initializes the 'messages' array and populates it with system, assistant, and user messages based on the provided 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

DeepSeek :: tool ($tool):array

line 20
Defines a tool in DeepSeek with its name, description, and parameters, enforcing strict validation of the provided 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

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

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

%DeepSeek -> chat (...$args)

line 37
This function sends a chat completion request to a model and returns an object containing the response, including the answer, model used, finish reason, and token usage details.
$args['model'] ??= static::model
$args = static::context(...$args)
$res = $this->request('chat/completions', 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

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

line 46
Establishes a Server-Sent Events (SSE) connection to a specified URL, sending a JSON payload and headers, and yields parsed data from the stream until completion.
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode("\r\n", [...$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(' ', $http_response_header[0] ?? 'HTTP/1.1 200')[1]
if ($status >= 400){
	$err = json_decode((string)stream_get_contents($stream))
	fclose($stream)
	error('DeepSeek error '.$status.': '.($err->error->message ?? 'unknown'))
}
$finish = null
$usage = null
while (!feof($stream)){
	$line = fgets($stream)
	if ($line === false) break
	$line = rtrim($line, "\r\n")
	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('DeepSeek 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

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

line 77
Enables streaming for the DeepSeek API by setting the appropriate parameters and returning a parsed Server-Sent Events response.
%app->streaming = true
$args['model'] ??= static::model
unset($args['cb'])
$args = static::context(...$args)
$args['stream'] = true
return $this->parseSSE('https://api.deepseek.com/v1/chat/completions', ['Authorization: Bearer '.%creds->DeepSeek], $args)
method

%DeepSeek -> request ($uri, $JSON = true, ...$args)

line 86
Sends a request to the DeepSeek API using the specified URI and optional parameters, returning the decoded JSON response. If an error occurs, it triggers an error message with details from the response.
$res = json_decode(AI::http("https://api.deepseek.com/v1/$uri", ['Authorization: Bearer '.%creds->DeepSeek], $JSON, $args['POST'] ?? null))
if (isset($res->error)) error('DeepSeek Request error: '.$res->error->message)
return $res
object

%Gemini

/phlo/resources/AI/Gemini.phlo
version 1.1
creator q-ai.nl
summary Google Gemini API
package ai
frontend false
backend true
requires creds:Gemini @AI
tags ai gemini google chat vision embeddings
const

Gemini :: model

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

Gemini :: endpoint

line 11
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)

line 13
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 24
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')

line 34
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)

line 36
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)

line 44
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 59
Establishes a Server-Sent Events (SSE) connection to a specified URL, sending a JSON payload and headers, and yields data received from the stream.
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode("\r\n", [...$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(' ', $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, "\r\n")
	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 90
Streams real-time data from the specified model using Server-Sent Events (SSE). It prepares the request by setting the streaming context and includes necessary credentials.
%app->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 98
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

%OpenAI

/phlo/resources/AI/OpenAI.phlo
version 1.0
creator q-ai.nl
summary Basic OpenAI functions
package ai
frontend false
backend true
requires creds:OpenAI @AI
tags ai openai llm chat embeddings audio vision
const

OpenAI :: model

line 10
This function initializes the OpenAI model with the specified identifier, in this case, 'gpt-4o-mini'.
'gpt-4o-mini'
const

OpenAI :: voices

line 11
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 12
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 19
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 33
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')

line 43
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 44
Establishes a Server-Sent Events (SSE) connection to the specified URL, sending a JSON payload and headers, and yields data received from the stream until completion.
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
$ctx = stream_context_create(['http' => ['method' => 'POST', 'header' => implode("\r\n", [...$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(' ', $http_response_header[0] ?? 'HTTP/1.1 200')[1]
if ($status >= 400){
	$err = json_decode((string)stream_get_contents($stream))
	fclose($stream)
	error('OpenAI error '.$status.': '.($err->error->message ?? 'unknown'))
}
$finish = null
$usage = null
while (!feof($stream)){
	$line = fgets($stream)
	if ($line === false) break
	$line = rtrim($line, "\r\n")
	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('OpenAI 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 75
Streams responses from the OpenAI API for chat completions, using the provided model and authentication token.
$args['model'] ??= static::model
$token = $args['token'] ?? null
unset($args['token'], $args['cb'])
$args = static::context(...$args)
$args['stream'] = true
$bearer = $token ?? %creds->OpenAI
return $this->parseSSE('https://api.openai.com/v1/chat/completions', ['Authorization: Bearer '.$bearer], $args)
method

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

line 84
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 95
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 101
Sends a request to the OpenAI API using the specified URI and optional parameters, returning the decoded JSON response or an error message if the request fails.
$bearer = $token ?? %creds->OpenAI
$res = json_decode(AI::http("https://api.openai.com/v1/$uri", ['Authorization: Bearer '.$bearer], $JSON, $args['POST'] ?? null))
if (isset($res->error)) error('OpenAI Request error: '.$res->error->message)
return $res

Functions

function

answer($question, ...$options)

/phlo/resources/AI/answer.phlo line 10
Generates a concise answer to a given question, either by selecting from provided options or by formulating a direct response. The output is strictly limited to the answer with no additional text or punctuation.
	$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 = %OpenAI->chat(user: $prompt, temperature: .1)->answer
	return $answer === dash ? null : $answer

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