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