SDK 가이드
PHP
PHP에서 openai-php/client를 api14와 함께 쓰는 방법입니다. 팩토리 설정, 스트리밍, 도구 호출, 구조화 출력, 이미지 입력, 음성, 오류 처리와 백오프를 적용한 재시도를 다룹니다. 임베딩 사용법과 지원 기능 목록도 있습니다.
openai-php/client를 api14와 함께 사용하세요. 팩토리로 클라이언트를 만들고, base URI를 api14로 설정하고, 모델 id는 provider/model 형식으로 사용합니다.
설치
composer require openai-php/client guzzlehttp/guzzle
export API14_API_KEY="sk-api14-..."
설정
<?php
require __DIR__ . '/vendor/autoload.php';
$client = OpenAI::factory()
->withApiKey(getenv('API14_API_KEY'))
->withBaseUri(getenv('API14_BASE_URL') ?: 'https://api14.com/api/v1')
->withHttpClient(new \GuzzleHttp\Client(['timeout' => 120, 'connect_timeout' => 5]))
->make();
키는 소스가 아니라 환경(또는 프레임워크의 .env)에 보관하세요. Guzzle의 timeout은 초 단위이며 요청 전체에 적용됩니다. 긴 추론이나 스트리밍 호출에서는 값을 늘리세요.
채팅
$result = $client->chat()->create([
'model' => 'openai/gpt-5-mini',
'messages' => [
['role' => 'user', 'content' => 'Explain vector databases in two sentences.'],
],
]);
echo $result->choices[0]->message->content;
스트리밍
$stream = $client->chat()->createStreamed([
'model' => 'anthropic/claude-sonnet-4.5',
'messages' => [['role' => 'user', 'content' => 'Write a haiku about APIs.']],
]);
foreach ($stream as $response) {
echo $response->choices[0]->delta->content;
}
스트리밍을 참고하세요.
도구 호출
$tools = [[
'type' => 'function',
'function' => [
'name' => 'get_weather',
'description' => 'Current weather for a city',
'parameters' => [
'type' => 'object',
'properties' => ['city' => ['type' => 'string']],
'required' => ['city'],
],
],
]];
$messages = [['role' => 'user', 'content' => "What's the weather in Istanbul?"]];
while (true) {
$response = $client->chat()->create(['model' => 'openai/gpt-5-mini', 'messages' => $messages, 'tools' => $tools]);
$message = $response->choices[0]->message;
if (empty($message->toolCalls)) {
echo $message->content;
break;
}
$messages[] = $message->toArray();
foreach ($message->toolCalls as $call) {
$args = json_decode($call->function->arguments, true);
$messages[] = [
'role' => 'tool',
'tool_call_id' => $call->id,
'content' => json_encode(getWeather($args['city'])), // 직접 만든 함수
];
}
}
도구 호출을 참고하세요.
구조화 출력
$result = $client->chat()->create([
'model' => 'openai/gpt-5-mini',
'messages' => [['role' => 'user', 'content' => 'Extract: Ada Lovelace, born 1815 in London.']],
'response_format' => [
'type' => 'json_schema',
'json_schema' => [
'name' => 'person',
'strict' => true,
'schema' => [
'type' => 'object',
'properties' => [
'name' => ['type' => 'string'],
'born' => ['type' => 'integer'],
'city' => ['type' => 'string'],
],
'required' => ['name', 'born', 'city'],
'additionalProperties' => false,
],
],
],
]);
$person = json_decode($result->choices[0]->message->content, true, flags: JSON_THROW_ON_ERROR);
구조화 출력을 참고하세요.
이미지 입력
$image = base64_encode(file_get_contents('photo.png'));
$result = $client->chat()->create([
'model' => 'google/gemini-2.5-flash',
'messages' => [[
'role' => 'user',
'content' => [
['type' => 'text', 'text' => 'What is in this picture?'],
['type' => 'image_url', 'image_url' => ['url' => "data:image/png;base64,{$image}"]],
],
]],
]);
비전 및 파일을 참고하세요.
임베딩
$result = $client->embeddings()->create([
'model' => 'openai/text-embedding-3-small',
'input' => ['The quick brown fox', 'Lorem ipsum'],
]);
echo count($result->embeddings[0]->embedding);
음성
$audio = $client->audio()->speech([
'model' => 'openai/gpt-4o-mini-tts',
'input' => 'Hello from api14!',
'voice' => 'alloy',
]);
file_put_contents('speech.mp3', $audio);
오류
게이트웨이 오류는 OpenAI 형식을 사용하므로, 클라이언트는 상태 코드와 머신 코드가 담긴 OpenAI\Exceptions\ErrorException을 발생시킵니다.
use OpenAI\Exceptions\ErrorException;
try {
$client->chat()->create([
'model' => 'openai/gpt-5-mini',
'messages' => [['role' => 'user', 'content' => 'Hi']],
]);
} catch (ErrorException $e) {
match ($e->getStatusCode()) {
402 => error_log('Balance too low, add funds: ' . $e->getErrorCode()), // insufficient_balance
429 => error_log('Rate limited, retry in 60s: ' . $e->getErrorCode()), // rate_limited
default => throw $e,
};
}
게이트웨이는 rate_limited와 함께 Retry-After: 60을 보냅니다. 속도 제한과 오류를 참고하세요.
백오프를 적용한 재시도
openai-php/client는 스스로 재시도하지 않습니다. rate_limited와 5xx만 재시도하고 나머지는 포기하는 작은 루프로 호출을 감싸세요.
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;
function withRetry(callable $fn, int $attempts = 5): mixed
{
for ($i = 0; ; $i++) {
try {
return $fn();
} catch (ErrorException $e) {
$retryable = $e->getErrorCode() === 'rate_limited' || $e->getStatusCode() >= 500;
if (!$retryable || $i + 1 >= $attempts) throw $e;
} catch (TransporterException $e) { // 네트워크 오류 또는 타임아웃
if ($i + 1 >= $attempts) throw $e;
}
usleep((min(60, 2 ** $i) * 1000 + random_int(0, 1000)) * 1000); // 백오프 + 지터
}
}
$result = withRetry(fn () => $client->chat()->create([
'model' => 'openai/gpt-5-mini',
'messages' => [['role' => 'user', 'content' => 'Hi']],
]));
daily_limit_reached와 insufficient_balance는 몇 초 기다려도 해결되지 않으므로 재시도하지 않습니다.
모든 기능
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api14.com/api/v1",
apiKey: process.env.API14_API_KEY,
});
const result = await client.chat.completions.create({
"model": "openai/gpt-5-mini",
"messages": [
{
"role": "user",
"content": "Explain vector databases in two sentences."
}
]
});
console.log(result.choices[0].message.content);