PHP code example of laybot / ai-sdk
1. Go to this page and download the library: Download laybot/ai-sdk library . Choose the download type require .
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
laybot / ai-sdk example snippets
use LayBot\Ai\AiClient;
$ai = new AiClient([
'default_provider' => 'ark',
'providers' => [
'ark' => [
'api_key' => getenv('ARK_API_KEY'),
'options' => [
'log_bodies' => false,
],
],
'deepseek' => [
'api_key' => getenv(
'DEEPSEEK_API_KEY'
),
'options' => [
'log_bodies' => false,
],
],
'volc-speech' => [
'api_key' => getenv(
'VOLC_SPEECH_API_KEY'
),
'options' => [
'tts_resource_id' => getenv(
'VOLC_TTS_RESOURCE_ID'
),
'tts_model' => getenv(
'VOLC_TTS_MODEL'
),
'auc_resource_id' => getenv(
'VOLC_AUC_RESOURCE_ID'
) ?: 'volc.seedasr.auc',
'sauc_resource_id' => getenv(
'VOLC_SAUC_RESOURCE_ID'
) ?: 'volc.seedasr.sauc.duration',
'log_bodies' => false,
],
],
],
]);
[
'driver' => 'openai',
'base_uri' => 'https://api.example.com',
'api_key' => 'server-side-key',
'headers' => [
'X-App-Name' => 'xiaoyi-ai',
],
'sensitive_headers' => [
'X-Private-Token',
],
'endpoints' => [
'chat' => '/v1/chat/completions',
],
'options' => [
'
$ai = new AiClient([
'providers' => [
'primary-chat' => [
'driver' => 'openai',
'base_uri' =>
'https://internal-gateway.example.com',
'api_key' => getenv('PRIMARY_CHAT_KEY'),
],
],
]);
$result = $ai
->provider('primary-chat')
->chat()
->create([...]);
use LayBot\Ai\Vendor\Ark;
$ark = new Ark([
'api_key' => getenv('ARK_API_KEY'),
]);
$result = $ark->chat()->create([
'model' => getenv('ARK_CHAT_MODEL'),
'messages' => [
[
'role' => 'system',
'content' => '你是一名专业的教学助手。',
],
[
'role' => 'user',
'content' => '为初二学生讲解浮力定律。',
],
],
'stream' => false,
'max_completion_tokens' => 1024,
]);
echo $result->text;
echo $result->reasoningText;
echo $result->usage->totalTokens;
echo $result->providerRequestId;
use LayBot\Ai\Vendor\DeepSeek;
$deepSeek = new DeepSeek([
'api_key' => getenv('DEEPSEEK_API_KEY'),
]);
$result = $deepSeek->chat()->create([
'model' => getenv('DEEPSEEK_CHAT_MODEL'),
'messages' => [
[
'role' => 'user',
'content' => '请解释什么是二叉搜索树。',
],
],
'stream' => false,
'max_tokens' => 1024,
]);
echo $result->text;
$result = $ai
->chat('ark')
->completions(
request: [
'model' => getenv('ARK_CHAT_MODEL'),
'messages' => [
[
'role' => 'user',
'content' => '你好',
],
],
'stream' => true,
],
callbacks: [
'on_start' => static function (): void {
echo '[START]';
},
'on_text_delta' =>
static function (string $text): void {
echo $text;
},
'on_reasoning_delta' =>
static function (string $text): void {
// 按业务要求决定是否展示。
},
'on_usage' =>
static function ($usage): void {
echo $usage->totalTokens;
},
'on_finish' =>
static function (?string $reason): void {
echo $reason ?? 'unknown';
},
'on_complete' =>
static function ($result): void {
// 最终归并结果。
},
'on_error' =>
static function (\Throwable $error): void {
report($error);
},
]
);
use LayBot\Ai\DTO\StreamEvent;
use LayBot\Ai\Enum\StreamEventType;
$result = $ai
->chat('ark')
->stream(
request: [
'model' => getenv('ARK_CHAT_MODEL'),
'messages' => [
[
'role' => 'user',
'content' => '请分步骤讲解勾股定理。',
],
],
'stream' => true,
'max_completion_tokens' => 2048,
],
onEvent: static function (
StreamEvent $event
): void {
match ($event->type) {
StreamEventType::TEXT_DELTA =>
print($event->textDelta),
StreamEventType::REASONING_DELTA =>
null,
StreamEventType::USAGE =>
null,
StreamEventType::FINISH =>
print(PHP_EOL . '[DONE]'),
default => null,
};
},
options: [
'connect_timeout' => 10,
'request_timeout' => 0,
'idle_timeout' => 120,
]
);
echo $result->text;
$result->text;
$result->reasoningText;
$result->encryptedReasoning;
$result->model;
$result->providerRequestId;
$result->finishReason;
$result->usage;
$result->durationMs;
$result->firstTokenDurationMs;
$result->receivedAnyDelta;
$result->completed;
$result->possiblyCharged;
$result->toolCalls;
use LayBot\Ai\DTO\ChatStreamResult;
use LayBot\Ai\DTO\StreamEvent;
use LayBot\Ai\Enum\StreamEventType;
$handle = $ai
->chat('ark')
->streamAsync(
request: [
'model' => getenv('ARK_CHAT_MODEL'),
'messages' => [
[
'role' => 'user',
'content' => '请讲解牛顿第二定律。',
],
],
'stream' => true,
'max_completion_tokens' => 2048,
],
onEvent: static function (
StreamEvent $event
): void {
if (
$event->type
=== StreamEventType::TEXT_DELTA
) {
// 推送给App或浏览器WebSocket。
echo $event->textDelta;
}
},
onComplete: static function (
ChatStreamResult $result
): void {
echo 'Completed: ', $result->text;
},
onError: static function (
\Throwable $error
): void {
echo $error->getMessage();
},
options: [
'connect_timeout' => 10,
'request_timeout' => 0,
'idle_timeout' => 120,
]
);
$handle->cancel('user stopped generation');
Workerman\Worker::runAll();
use LayBot\Ai\DTO\ChatRequest;
$request = new ChatRequest(
model: 'runtime-model',
messages: [
[
'role' => 'user',
'content' => '你好',
],
],
parameters: [
'temperature' => 0.7,
'max_completion_tokens' => 1024,
'tools' => [],
]
);
$result = $ai
->chat('ark')
->create($request);
$result = $ai
->chat('deepseek')
->create([
'model' => getenv('DEEPSEEK_CHAT_MODEL'),
'messages' => [
[
'role' => 'user',
'content' => '查询北京天气。',
],
],
'tools' => [
[
'type' => 'function',
'function' => [
'name' => 'get_weather',
'description' => '查询城市天气',
'parameters' => [
'type' => 'object',
'properties' => [
'city' => [
'type' => 'string',
],
],
'
use LayBot\Ai\Vendor\Ark;
$ark = new Ark([
'api_key' => getenv('ARK_API_KEY'),
]);
$result = $ark->responses()->create(
[
'model' => getenv('ARK_RESPONSES_MODEL'),
'input' => [
[
'role' => 'user',
'content' => [
[
'type' => 'input_text',
'text' => '请简要解释光合作用。',
],
],
],
],
'max_output_tokens' => 2048,
],
[
'connect_timeout' => 15,
'request_timeout' => 120,
]
);
echo $result->status;
echo $result->text;
echo $result->usage->totalTokens;
$result = $ark->responses()->create([
'model' => getenv('ARK_RESPONSES_MODEL'),
'input' => [
[
'role' => 'user',
'content' => [
[
'type' => 'input_image',
'image_url' =>
'https://example.com/image.png',
'detail' => 'high',
],
[
'type' => 'input_text',
'text' => '请描述图片内容。',
],
],
],
],
]);
$result = $ark->responses()->create(
[
'model' => getenv('ARK_RESPONSES_MODEL'),
'input' => [
[
'role' => 'user',
'content' => [
[
'type' => 'input_file',
'file_url' =>
'https://example.com/document.pdf',
],
[
'type' => 'input_text',
'text' => '请概括PDF主题。',
],
],
],
],
/*
* 推理模型需要同时为Reasoning和可见正文
* 预留输出预算。
*/
'max_output_tokens' => 4096,
],
[
'connect_timeout' => 15,
'request_timeout' => 120,
]
);
$uploaded = $ark
->files()
->upload(
__DIR__ . '/document.pdf',
'user_data'
);
$fileId = $uploaded->id();
if ($fileId === null) {
throw new RuntimeException(
'Ark upload response has no file ID'
);
}
$file = $ark->files()->retrieve($fileId);
$result = $ark->responses()->create(
[
'model' => getenv('ARK_RESPONSES_MODEL'),
'input' => [
[
'role' => 'user',
'content' => [
[
'type' => 'input_file',
'file_id' => $fileId,
],
[
'type' => 'input_text',
'text' => '请概括上传的文件。',
],
],
],
],
'max_output_tokens' => 4096,
],
[
'connect_timeout' => 15,
'request_timeout' => 120,
]
);
$result = $ark->responses()->stream(
request: [
'model' => getenv('ARK_RESPONSES_MODEL'),
'input' => '请介绍LayBot。',
],
onEvent: static function (
\LayBot\Ai\DTO\StreamEvent $event
): void {
if (
$event->type
=== \LayBot\Ai\Enum\StreamEventType::TEXT_DELTA
) {
echo $event->textDelta;
}
},
options: [
'request_timeout' => 0,
'idle_timeout' => 120,
]
);
$handle = $ark->responses()->streamAsync(
request: [...],
onEvent: $onEvent,
onComplete: $onComplete,
onError: $onError,
options: [
'request_timeout' => 0,
'idle_timeout' => 120,
]
);
use LayBot\Ai\Vendor\VolcSpeech;
$speech = new VolcSpeech([
'api_key' => getenv(
'VOLC_SPEECH_API_KEY'
),
'options' => [
'tts_resource_id' => getenv(
'VOLC_TTS_RESOURCE_ID'
),
'tts_model' => getenv(
'VOLC_TTS_MODEL'
),
'auc_resource_id' => getenv(
'VOLC_AUC_RESOURCE_ID'
) ?: 'volc.seedasr.auc',
'sauc_resource_id' => getenv(
'VOLC_SAUC_RESOURCE_ID'
) ?: 'volc.seedasr.sauc.duration',
],
]);
use LayBot\Ai\DTO\Tts\TtsStreamEvent;
use LayBot\Ai\Enum\TtsStreamEventType;
$audio = '';
$result = $speech
->tts()
->sse()
->streamSse(
request: [
'text' => '你好,这是一段语音合成测试。',
'speaker' => getenv(
'VOLC_TTS_SPEAKER'
),
'audio' => [
'format' => 'mp3',
'sample_rate' => 24000,
'bit_rate' => 64000,
],
'additions' => [
'tone_fidelity' => true,
],
],
onEvent: static function (
TtsStreamEvent $event
) use (&$audio): void {
if (
$event->type
=== TtsStreamEventType::AUDIO_CHUNK
&& $event->audio !== null
) {
$audio .= $event->audio->audio;
}
},
options: [
'connect_timeout' => 10,
'request_timeout' => 0,
'idle_timeout' => 90,
]
);
if (!$result->completed) {
throw new RuntimeException(
'TTS did not complete'
);
}
file_put_contents(
__DIR__ . '/speech.mp3',
$audio
);
$result->provider;
$result->status;
$result->clientRequestId;
$result->providerRequestId;
$result->providerLogId;
$result->usage->textWords;
$result->audioBytes;
$result->audioChunks;
$result->durationMs;
$result->completed;
$result->cancelled;
$result->sentences;
$result = $speech
->tts()
->chunked()
->streamChunked(
request: [
'text' => '你好',
'speaker' => getenv(
'VOLC_TTS_SPEAKER'
),
],
onEvent: static function (
TtsStreamEvent $event
): void {
if (
$event->audio !== null
) {
// 消费二进制音频。
}
}
);
$voice = $speech
->tts()
->voice()
->get(
speakerId: getenv(
'VOLC_TTS_SPEAKER'
)
);
if (!$voice->availableForSynthesis()) {
throw new RuntimeException(sprintf(
'voice status is %d',
$voice->status
));
}
if ($voice->supportsClone20()) {
echo 'Clone 2.0 is available';
}
use LayBot\Ai\Contract\BidirectionalTtsConnectionInterface;
use LayBot\Ai\Contract\BidirectionalTtsSessionInterface;
use LayBot\Ai\DTO\Tts\TtsResult;
use LayBot\Ai\DTO\Tts\TtsStreamEvent;
use LayBot\Ai\Enum\TtsStreamEventType;
use LayBot\Ai\Provider\VolcSpeech\Tts\BidirectionalTtsListener;
use LayBot\Request\DTO\WebSocketCloseInfo;
$listener = new class extends BidirectionalTtsListener {
public function onConnectionReady(
BidirectionalTtsConnectionInterface $connection
): void {
$connection->startSession([
'speaker' => getenv(
'VOLC_TTS_SPEAKER'
),
'audio' => [
'format' => 'pcm',
'sample_rate' => 24000,
'bit_rate' => null,
],
]);
}
public function onSessionStarted(
BidirectionalTtsConnectionInterface $connection,
BidirectionalTtsSessionInterface $session
): void {
$session->appendText('你好,');
$session->appendText('这是双向流式语音。');
$session->finish();
}
public function onEvent(
BidirectionalTtsConnectionInterface $connection,
BidirectionalTtsSessionInterface $session,
TtsStreamEvent $event
): void {
if (
$event->type
=== TtsStreamEventType::AUDIO_CHUNK
&& $event->audio !== null
) {
$pcm = $event->audio->audio;
// 立即转发给App或写入播放器。
}
}
public function onSessionCompleted(
BidirectionalTtsConnectionInterface $connection,
BidirectionalTtsSessionInterface $session,
TtsResult $result
): void {
if ($result->completed) {
$connection->finish();
} else {
$connection->cancel(
'TTS Session did not complete'
);
}
}
public function onError(
BidirectionalTtsConnectionInterface $connection,
\Throwable $error
): void {
report($error);
$connection->cancel(
'TTS failed'
);
}
public function onClose(
BidirectionalTtsConnectionInterface $connection,
WebSocketCloseInfo $info
): void {
// 清理业务资源。
}
};
$connection = $speech
->tts()
->bidirectional()
->connectAsync(
request: [
'connect_timeout' => 10,
'idle_timeout' => 180,
'ping_interval' => 30,
'close_timeout' => 5,
],
listener: $listener
);
$submit = $speech
->asr()
->auc()
->submit([
'audio' => [
'url' =>
'https://example.com/audio.mp3',
'format' => 'mp3',
'codec' => 'raw',
'rate' => 24000,
'bits' => 16,
'channel' => 1,
],
'request' => [
'model_name' => 'bigmodel',
'enable_itn' => true,
'enable_punc' => true,
'show_utterances' => true,
],
]);
$taskId = $submit->taskId;
do {
usleep(1_500_000);
$result = $speech
->asr()
->auc()
->query(
taskId: $taskId,
resourceId: $submit->resourceId
);
} while (!$result->state->terminal());
if (!$result->successful()) {
throw new RuntimeException(
$result->message
?? 'AUC recognition failed'
);
}
echo $result->text;
foreach ($result->utterances as $utterance) {
echo $utterance->text;
}
Workerman\Timer
use LayBot\Ai\Contract\RealtimeAsrSessionInterface;
use LayBot\Ai\DTO\Asr\RealtimeAsrResult;
use LayBot\Ai\Provider\VolcSpeech\Asr\RealtimeAsrListener;
use LayBot\Request\DTO\WebSocketCloseInfo;
$listener = new class extends RealtimeAsrListener {
public function onReady(
RealtimeAsrSessionInterface $session
): void {
/*
* 初始化确认已经收到。
* 现在可以开始发送PCM音频。
*/
}
public function onResult(
RealtimeAsrSessionInterface $session,
RealtimeAsrResult $result
): void {
echo $result->text;
}
public function onComplete(
RealtimeAsrSessionInterface $session,
RealtimeAsrResult $result
): void {
echo 'Final: ', $result->text;
}
public function onBufferFull(
RealtimeAsrSessionInterface $session
): void {
// 暂停麦克风或上游音频生产。
}
public function onBufferDrain(
RealtimeAsrSessionInterface $session
): void {
// 恢复音频生产。
}
public function onError(
RealtimeAsrSessionInterface $session,
\Throwable $error
): void {
report($error);
}
public function onClose(
RealtimeAsrSessionInterface $session,
WebSocketCloseInfo $info
): void {
// 清理录音与业务状态。
}
};
$session = $speech
->asr()
->realtime()
->connectAsync(
request: [
'endpoint' => 'async',
'audio' => [
'format' => 'pcm',
'codec' => 'raw',
'rate' => 16000,
'bits' => 16,
'channel' => 1,
],
'request' => [
'model_name' => 'bigmodel',
'enable_itn' => true,
'enable_punc' => true,
'show_utterances' => true,
'enable_nonstream' => false,
],
],
listener: $listener,
options: [
'connect_timeout' => 10,
'idle_timeout' => 60,
'ping_interval' => 20,
'close_timeout' => 5,
]
);
$session->sendAudio($pcmChunk);
$session->sendAudio(
$lastPcmChunk,
last: true
);
$session->finish($lastPcmChunk);
[
'connect_timeout' => 10,
'request_timeout' => 0,
'idle_timeout' => 120,
]
[
'connect_timeout' => 15,
'request_timeout' => 120,
]
[
'connect_timeout' => 10,
'idle_timeout' => 180,
'ping_interval' => 30,
'close_timeout' => 5,
]
'retry' => false
$result = $speech
->asr()
->auc()
->query(
taskId: $taskId,
resourceId: $resourceId,
options: [
'idempotent' => true,
'retry' => [
'max_attempts' => 3,
],
]
);
$error->retryable();
$error->partialOutputReceived();
$error->possiblyCharged();
$result = $ai
->chat('ark')
->create(
request: [...],
options: [
'base_uri' =>
'https://internal-gateway.example.com',
'headers' => [
'X-Tenant-Id' => 'tenant-1001',
],
'connect_timeout' => 5,
'request_timeout' => 60,
'proxy' =>
'http://127.0.0.1:7890',
]
);
$result = $ai
->chat('openai')
->create(
request: [
'model' => 'model-name',
'messages' => [...],
],
options: [
'endpoint' =>
'/v1/chat/completions',
]
);
$tenantChat = $ai
->provider(
'ark',
[
'api_key' => $tenantApiKey,
]
)
->chat();
final class AiService
{
public function __construct(
private readonly \LayBot\Ai\AiClient $ai
) {
}
}
use LayBot\Ai\Exception\ProviderException;
use LayBot\Ai\Exception\ValidationException;
try {
$result = $ai
->chat('ark')
->create([...]);
} catch (ValidationException $error) {
// 请求参数错误。
} catch (ProviderException $error) {
echo $error->provider();
echo $error->providerCode();
echo $error->requestId();
echo $error->httpStatus();
if ($error->possiblyCharged()) {
// 不要直接重复生成。
}
if (
$error->retryable()
&& !$error->partialOutputReceived()
) {
// 由业务幂等策略决定是否重试。
}
}
[
'sensitive_headers' => [
'X-Gateway-Credential',
'X-Tenant-Secret',
],
'options' => [
'log_bodies' => false,
],
]
$capabilities = $ai
->provider('ark')
->capabilities('new-model');
$ai = new \LayBot\Ai\AiClient([
'capabilities' => [
'ark' => [
'custom-model' => [
'supports_vision' => true,
'supports_tools' => true,
'supports_reasoning' => true,
'max_context_tokens' => 32768,
],
],
],
'providers' => [
'ark' => [
'api_key' => getenv('ARK_API_KEY'),
],
],
]);
use LayBot\Ai\Vendor\LayBot;
$laybot = new LayBot([
'api_key' => getenv('LAYBOT_API_KEY'),
]);
$result = $laybot->chat()->create([
'model' => 'LB-Cosmos',
'messages' => [
[
'role' => 'user',
'content' =>
'为初二学生讲解浮力定律。',
],
],
'edu_features' => [
'difficulty' => 'middle_school',
'tiered' => true,
],
]);
$laybot->files();
$laybot->batches();
$laybot->embeddings();
$laybot->fineTuning();
$laybot->images();
$laybot->audio();
$laybot->portal();
$laybot->api();
$result = $ai
->chat('deepseek')
->create([...]);
text
PHP 8.4
当前稳定版Webman
Workerman 5.2+
laybot/request-sdk 2.0.6+
Linux
text
查询已有音色状态
判断音色是否可合成
判断speaker_status是否包含复刻2.0模型类型
bash
composer validate --strict
composer dump-autoload \
--optimize \
--strict-psr
php tools/lint.php
composer test
composer analyse
text
17 tests
93 assertions
0 failures
0 errors
0 skipped
bash
composer validate --strict
composer dump-autoload --optimize --strict-psr
php tools/lint.php
composer test
composer analyse
composer audit
git diff --check