AI
object
%AI
/phlo/resources/AI/AI.phlo
const
AI :: engines
line 10
AI::engines 是一个将 AI 引擎标识符映射到其各自名称的结构,便于在 Phlo 环境中进行引用和集成。
['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
从缓冲区解码服务器发送的事件(SSE),提取以'data:'开头的数据行,并将其作为解码的JSON对象数组返回。
$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 $eventsstatic
AI :: sseStreamJSONWithTool (string $url, array $headers, array $payload, \Closure $extractText, \Closure $extractTool, ?\Closure $cb = null, ?\Closure $toolCb = null):obj
line 31
从服务器发送事件(SSE)端点流式传输JSON数据,处理传入事件并根据提供的回调提取文本和工具信息。
$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
此函数初始化一个cURL会话以发送HTTP请求,允许使用GET和POST方法,并可选地对请求体进行JSON编码和自定义头部。
$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 $resstatic
AI :: resolve (...$args):array
line 75
根据提供的参数解析要使用的AI引擎,如果没有指定特定引擎,则默认为'OpenAI'。
$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
此函数初始化与AI引擎的聊天会话,在返回聊天实例之前解析引擎和参数。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->chat(...$args)method
%AI -> stream (...$args):Generator
line 87
此函数解析指定的引擎和参数,然后使用Phlo框架启动流处理。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->stream(...$args)method
%AI -> stream_with_tool (...$args):obj
line 91
此函数解析引擎和参数,然后使用Phlo中指定的工具流式传输数据。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->stream_with_tool(...$args)method
%AI -> embedding (...$args)
line 95
此函数解析引擎和参数,然后使用提供的参数调用指定引擎的embedding方法。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->embedding(...$args)method
%AI -> vision (...$args)
line 99
此函数将指定的参数解析为引擎,然后使用这些参数调用解析后的引擎上的 vision 方法。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->vision(...$args)method
%AI -> transcribe (...$args)
line 103
使用指定的AI引擎和参数转录音频输入。
[$engine, $args] = static::resolve(...$args)
return phlo($engine)->transcribe(...$args)object
%Claude
/phlo/resources/AI/Claude.phlo
const
Claude :: model
line 10
为指定资源创建模型实例,允许与底层数据结构进行交互。
'claude-sonnet-4-5-20250929'static
Claude :: context (...$args)
line 12
通过设置消息和系统参数来初始化Claude的上下文,确保用户和助手消息被正确格式化和存储。
$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 $argsstatic
Claude :: tool ($tool):array
line 22
这构建了一个关联数组,包含工具的名称和描述,以及其输入模式,定义了工具的属性和必需参数。
[
'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
使用指定的模型为给定输入生成嵌入,默认为'text-embedding-3-small'。
%OpenAI->embedding($input, $model)method
%Claude -> vision ($text, $image, $stream = false, ...$args)
line 34
此函数将文本和图像发送到视觉模型,选项是流式传输响应或以聊天格式返回。
$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
此函数与Claude API交互以发送聊天请求并处理响应,返回一个包含答案、模型信息、令牌使用情况和交互过程中使用的任何工具的对象。
$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 $returnmethod
%Claude -> parseSSE (string $url, array $headers, array $payload):Generator
line 58
建立与指定URL的服务器推送事件(SSE)连接,发送JSON有效负载和头信息,并从流中生成接收到的数据,适当地处理错误和连接状态。
$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
使用指定的参数从Claude API流式传输消息,包括模型和令牌,同时设置必要的身份验证头。
%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
使用指定的URI和参数向Claude API发送请求,处理使用令牌或API密钥的身份验证,并返回解码的JSON响应。
$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 $resobject
%DeepSeek
/phlo/resources/AI/DeepSeek.phlo
const
DeepSeek :: model
line 10
DeepSeek::model 是一个组件,便于在 DeepSeek 框架内创建和管理模型,从而实现高效的数据处理和交互。
'deepseek-chat'static
DeepSeek :: context (...$args)
line 12
DeepSeek::$context 初始化 'messages' 数组,并根据提供的参数填充系统、助手和用户消息。
$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 $argsstatic
DeepSeek :: tool ($tool):array
line 20
在DeepSeek中定义一个工具,包括其名称、描述和参数,强制对提供的参数进行严格验证。
[
'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
使用指定的模型为给定输入生成嵌入,默认模型为'text-embedding-3-small'。
%OpenAI->embedding($input, $model)method
%DeepSeek -> chat (...$args)
line 37
此函数向模型发送聊天完成请求,并返回一个包含响应的对象,包括答案、使用的模型、完成原因和令牌使用详情。
$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 $returnmethod
%DeepSeek -> parseSSE (string $url, array $headers, array $payload):Generator
line 46
建立与指定URL的服务器推送事件(SSE)连接,发送JSON负载和头部,并在完成之前逐步输出流中的解析数据。
$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
通过设置适当的参数并返回解析的服务器发送事件响应,启用DeepSeek API的流式传输。
%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
使用指定的URI和可选参数向DeepSeek API发送请求,返回解码的JSON响应。如果发生错误,它会触发一个包含响应详细信息的错误消息。
$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 $resobject
%Gemini
/phlo/resources/AI/Gemini.phlo
const
Gemini :: model
line 10
在Gemini框架中创建模型,允许定义和操作数据结构。
'gemini-2.0-flash'const
Gemini :: endpoint
line 11
定义一个用于访问Gemini API的端点,允许与给定URL处的指定模型进行交互。
'https://generativelanguage.googleapis.com/v1beta/models/'static
Gemini :: context (...$args)
line 13
Gemini::$context 处理输入参数以构建对话的结构化上下文,将系统、助手和用户消息组织到内容数组中。
$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 $argsstatic
Gemini :: tool ($tool):array
line 24
Gemini::$tool 获取工具的名称和描述,以及其参数,包括类型、属性和必需的参数。
[
'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
使用指定的模型为给定的输入文本生成嵌入,默认为'text-embedding-004'。
$this->request($model.':embedContent', POST: ['content' => ['parts' => [['text' => $input]]]])->embedding->valuesmethod
%Gemini -> vision ($text, $image, $stream = false, ...$args)
line 36
通过结合文本和图像生成视觉响应,并提供可选的实时输出流功能。
$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
根据指定的模型和参数生成内容,返回一个对象,其中包括生成的答案、使用的模型、结束原因和令牌使用详情。
$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 $returnmethod
%Gemini -> parseSSE (string $url, array $headers, array $payload):Generator
line 59
建立与指定URL的服务器推送事件(SSE)连接,发送JSON负载和头部,并生成从流中接收到的数据。
$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
通过服务器推送事件(SSE)从指定模型流式传输实时数据。它通过设置流上下文并包含必要的凭据来准备请求。
%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
向指定路径的Gemini API发送请求,带有可选参数,处理错误并返回解码的JSON响应。
$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 $resobject
%OpenAI
/phlo/resources/AI/OpenAI.phlo
const
OpenAI :: model
line 10
此函数使用指定的标识符初始化OpenAI模型,在这种情况下为'gpt-4o-mini'。
'gpt-4o-mini'const
OpenAI :: voices
line 11
OpenAI::voices 是可用于 Phlo 应用程序的预定义语音选项列表,包括 'alloy'、'echo'、'fable'、'onyx'、'nova' 和 'shimmer'。
['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer']static
OpenAI :: context (...$args):array
line 12
此函数在OpenAI的上下文中初始化'messages'数组,并根据输入参数添加系统、助手和用户消息。
$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 $argsstatic
OpenAI :: tool ($tool):array
line 19
在OpenAI中定义一个具有指定参数的函数,包括类型、枚举和描述,同时强制执行参数的严格验证。
[
'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
此函数与OpenAI API交互,根据提供的参数生成聊天完成,返回一个包含响应详细信息的对象,例如生成的答案、使用的模型和令牌使用统计信息。
$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 $returnmethod
%OpenAI -> embedding ($input, $model = 'text-embedding-3-small')
line 43
使用OpenAI指定的模型为给定输入生成嵌入。
$this->request('embeddings', POST: ['input' => $input, 'model' => $model])->data[0]->embeddingmethod
%OpenAI -> parseSSE (string $url, array $headers, array $payload):Generator
line 44
建立与指定 URL 的服务器推送事件 (SSE) 连接,发送 JSON 有效负载和头信息,并在完成之前逐步返回从流中接收到的数据。
$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
从OpenAI API流式传输聊天完成的响应,使用提供的模型和身份验证令牌。
$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
使用指定模型对给定文件中的音频进行转录,返回持续时间、语言和转录文本等详细信息。
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
此函数将文本和图像发送到OpenAI视觉模型,选项是流式传输响应或以聊天格式返回。
$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
使用指定的URI和可选参数向OpenAI API发送请求,返回解码的JSON响应或请求失败时的错误消息。
$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 $resFunctions
function
answer($question, ...$options)
/phlo/resources/AI/answer.phlo line 10
生成对给定问题的简洁回答,或者通过从提供的选项中选择,或者通过形成直接的回应。输出严格限制为答案,没有额外的文本或标点符号。
$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