SDK PHP
Instale o SDK oficial em PHP, use no Laravel e trabalhe com chat, busca na web, agentes e busca semântica no HiNow.
Atualizado em 09 de ago. de 2026
O SDK oficial em PHP para a API do HiNow. Funciona em qualquer projeto PHP 8.0 ou mais novo e, no Laravel, se registra sozinho.
A API fala o protocolo da OpenAI, e o SDK segue o mesmo formato. Se você já integrou com a OpenAI, o desenho das chamadas é o que você conhece.
composer require hinow-ai/hinow-aiGuarde a chave em HINOW_API_KEY e o SDK a encontra sozinho. Passar a chave no construtor também funciona, mas evite deixar o valor no código.
export HINOW_API_KEY="hi_sua_chave_aqui"<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
// With no arguments, the client reads the key from HINOW_API_KEY.
$client = new Hinow();
$answer = $client->chat->completions->create([
'model' => 'hinow/himax',
'messages' => [
['role' => 'system', 'content' => 'You answer in English.'],
['role' => 'user', 'content' => 'What is an API? Answer in one paragraph.'],
],
]);
echo $answer['choices'][0]['message']['content'], "\n";
// What it cost, in tokens.
$uso = $answer['usage'];
echo "\ninput: {$uso['prompt_tokens']} · output: {$uso['completion_tokens']}\n";O prefixo hinow/ faz parte do nome do modelo
Mandar himax em vez de hinow/himax devolve 404 model_not_found, e a mensagem não deixa claro que faltou o prefixo. Vale para todos: hinow/himax, hinow/hinova, hinow/higenesis.
O pacote se registra sozinho. Coloque a chave no .env e use a facade, ou receba o cliente por injeção de dependência no construtor do seu serviço.
HINOW_API_KEY=hi_sua_chave_aquiuse Hinow\Facades\Hinow;
$resposta = Hinow::chat()->completions->create([
'model' => 'hinow/himax',
'messages' => [
['role' => 'user', 'content' => $request->input('pergunta')],
],
]);
return $resposta['choices'][0]['message']['content'];use Hinow\Hinow;
class Atendimento
{
public function __construct(private Hinow $hinow)
{
}
public function responder(string $pergunta): string
{
$resposta = $this->hinow->chat->completions->create([
'model' => 'hinow/himax',
'messages' => [['role' => 'user', 'content' => $pergunta]],
]);
return $resposta['choices'][0]['message']['content'];
}
}Para mexer no tempo limite ou no número de tentativas, publique o arquivo de configuração com php artisan vendor:publish --tag=hinow-config.
createStream() devolve um gerador: você percorre com foreach e cada volta traz um pedaço novo. Numa tela de conversa isso muda a percepção de velocidade mais do que trocar de modelo.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
$stream = $client->chat->completions->createStream([
'model' => 'hinow/hinova',
'messages' => [
['role' => 'user', 'content' => 'Write three lines about the sea at dawn.'],
],
]);
foreach ($stream as $chunk) {
// Note the "delta": each chunk carries the new fragment, not the whole answer.
echo $chunk['choices'][0]['delta']['content'] ?? '';
flush();
}
echo "\n";É delta, não message
Na resposta completa o texto vem em message.content. No streaming, cada pedaço traz apenas o trecho novo, em delta.content — juntar tudo é com você.
Responde na hora, sem job para acompanhar, a US$ 0,005 por chamada. O terceiro argumento aceita country e lang para orientar os resultados.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
// Answers on the spot: there is no job to follow.
$search = $client->tools->search('best beaches in northeast Brazil', 'search', [
'country' => 'br',
'lang' => 'pt-br',
]);
foreach ($search['results'] as $item) {
echo "{$item['position']}. {$item['title']}\n";
echo " {$item['url']}\n";
}
printf("\n%d results · US$ %.3f\n", count($search['results']), $search['cost']);type | O que vem em cada resultado |
|---|---|
search, scholar, patents | position, title, url, snippet |
news | os acima mais source, date, image_url |
images | link, image_url, thumbnail_url, width, height |
videos | channel, duration, date, thumbnail_url |
places | address, category, phone, website, rating, coordenadas |
shopping | price, delivery, rating, source |
autocomplete | suggestions, um array de strings — não tem results |
Varre um ou mais sites atrás de e-mails, telefones e perfis sociais, e cada achado vem com a página exata onde apareceu. Como a varredura leva tempo, esta roda como job.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
// The crawl takes a while, so it runs as a job. This method waits for you.
$job = $client->tools->websiteContactsAndWait(['https://www.mongodb.com'], [
'maxDepth' => 2,
'maxLinksPerPage' => 10,
]);
printf(
"status: %s · US$ %.3f%s\n\n",
$job['status'],
$job['cost'],
!empty($job['cached']) ? ' (from cache)' : ''
);
// Each item carries the type, the value and the exact page it was found on.
foreach ($job['result']['items'] as $item) {
$onde = $item['sourceUrl'];
if ($item['type'] === 'email') {
echo "e-mail {$item['value']}\n em {$onde}\n";
} elseif ($item['type'] === 'phone') {
echo "telefone {$item['value']}\n em {$onde}\n";
} else {
echo "{$item['platform']} {$item['url']}\n";
}
}Repetir uma execução idêntica não cobra de novo
O job volta com cached: true quando o resultado veio do cache. Se quiser controlar o ciclo você mesmo, use websiteContacts() e acompanhe com tools->jobs->retrieve($job['job_id']) — repare que o campo é job_id, não id.
Valide o que vier antes de usar
A varredura devolve o que encontrou no HTML da página, sem julgar. Endereços truncados ou repetidos aparecem — trate a lista como matéria-prima e valide antes de gravar no seu banco.
Assistentes, threads e runs no mesmo formato da API da OpenAI. Você declara as funções, o modelo decide quando chamá-las: o run para, você executa e devolve o resultado.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
// Your real function: an array here, just for the example.
function getOrder(string $code): array
{
$orders = [
'A-1001' => ['status' => 'delivered', 'delivered' => '2026-08-02'],
'A-1002' => ['status' => 'in transit', 'estimated' => '2026-08-12'],
];
return $orders[$code] ?? ['error' => 'order not found'];
}
// 1. The assistant: model, instructions and the functions it may call.
$assistant = $client->beta->assistants->create([
'model' => 'hinow/himax',
'name' => 'Support',
'instructions' => 'You answer questions about orders. Check the tool before stating any status.',
'tools' => [[
'type' => 'function',
'function' => [
'name' => 'get_order',
'description' => 'Look up an order by its code.',
'parameters' => [
'type' => 'object',
'properties' => [
'code' => ['type' => 'string', 'description' => 'Order code, e.g. A-1001'],
],
'required' => ['code'],
],
],
]],
]);
// 2. The conversation and the question.
$thread = $client->beta->threads->create();
$client->beta->threads->messages->create($thread['id'], 'Has order A-1002 arrived?');
// 3. Run it and wait.
$run = $client->beta->threads->runs->createAndPoll($thread['id'], $assistant['id']);
// 4. While the model asks for functions, run them and hand the result back.
while ($run['status'] === 'requires_action') {
$outputs = [];
foreach ($run['required_action']['submit_tool_outputs']['tool_calls'] as $call) {
$args = json_decode($call['function']['arguments'], true);
echo "-> the model called {$call['function']['name']}({$args['code']})\n";
$outputs[] = [
'tool_call_id' => $call['id'],
'output' => json_encode(getOrder($args['code'])),
];
}
$client->beta->threads->runs->submitToolOutputs($thread['id'], $run['id'], $outputs);
$run = $client->beta->threads->runs->poll($thread['id'], $run['id']);
}
// 5. The final answer is the last message on the thread.
$messages = $client->beta->threads->messages->list($thread['id'], ['order' => 'desc', 'limit' => 1]);
echo "\n", $messages['data'][0]['content'][0]['text']['value'], "\n";
$client->beta->assistants->delete($assistant['id']);requires_action não é erro
É o run devolvendo o controle para você executar uma função. Por isso o poll() retorna nesse estado em vez de continuar girando: leia o required_action, chame submitToolOutputs() e volte a acompanhar.
Suba arquivos, junte numa base e busque por significado. A indexação é assíncrona — buscar antes de terminar devolve zero resultado, sem erro.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
// 1. Upload the document.
$file = $client->files->create('returns-policy.txt', 'assistants');
echo "file: {$file['id']} ({$file['bytes']} bytes)\n";
// 2. Create the store and attach the file to it.
$base = $client->vectorStores->create(['name' => 'Support base']);
$client->vectorStores->files->create($base['id'], $file['id']);
echo "store: {$base['id']}\n";
// 3. Indexing is asynchronous. Searching before it finishes returns nothing
// resultado, sem error nenhum — por isso vale esperar.
do {
sleep(2);
$state = $client->vectorStores->files->retrieve($base['id'], $file['id']);
echo "indexing: {$state['status']}\n";
} while ($state['status'] === 'in_progress');
// 4. Search by meaning, not by exact word.
$hits = $client->rag->search('How many days do I have to return an item?', [
'rag_id' => $base['id'],
'top_k' => 3,
]);
echo "\n";
foreach ($hits['results'] as $achado) {
printf("%.2f %s\n", $achado['score'], $achado['source']);
echo ' ', str_replace("\n", ' ', mb_substr($achado['text'], 0, 120)), "…\n";
}O filtro por base chama-se rag_id
Passar vector_store_id não gera erro: a busca simplesmente varre todos os documentos da conta em vez da base que você queria. É o tipo de detalhe que faz parecer que a busca está devolvendo lixo.
Cada falha vira uma classe própria, então dá para tratar por tipo em vez de comparar string de mensagem. Todas herdam de Hinow\HinowException, e as que vieram da API carregam status, type e o corpo da resposta.
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
use Hinow\AuthenticationException;
use Hinow\NotFoundException;
use Hinow\RateLimitException;
use Hinow\InvalidRequestException;
use Hinow\ConnectionException;
use Hinow\ApiException;
$client = new Hinow();
try {
$client->chat->completions->create([
'model' => 'himax', // the hinow/ prefix is missing
'messages' => [['role' => 'user', 'content' => 'Olá']],
]);
} catch (AuthenticationException $e) {
echo "Invalid or expired key. Check HINOW_API_KEY.\n";
} catch (NotFoundException $e) {
echo "Not found: {$e->getMessage()}\n";
} catch (InvalidRequestException $e) {
echo "Invalid request: {$e->getMessage()}\n";
} catch (RateLimitException $e) {
echo "Rate limited. The SDK already retried; wait a moment.\n";
} catch (ConnectionException $e) {
// Nothing reached the API, so nothing was charged.
echo "Sem answer da API: {$e->getMessage()}\n";
} catch (ApiException $e) {
// Safety net: any other error the API reported.
echo "Error {$e->status}: {$e->getMessage()}\n";
}| Classe | Quando acontece |
|---|---|
AuthenticationException | 401 — chave ausente, inválida ou revogada |
PermissionException | 403 — a chave não tem acesso a esse recurso |
NotFoundException | 404 — modelo, arquivo ou assistente inexistente |
InvalidRequestException | 400 ou 422 — falta um campo ou um valor está fora da faixa |
RateLimitException | 429 — limite atingido ou saldo esgotado |
ServerException | 5xx — a falha é do lado da API |
ConnectionException | a requisição não chegou; nada foi cobrado |
<?php
require 'vendor/autoload.php';
use Hinow\Hinow;
$client = new Hinow();
// Account credit, in US dollars.
$balance = $client->getBalance();
printf("balance: US$ %.2f\n\n", $balance['balance']);
// One specific model. The id is namespaced: hinow/himax, not himax.
$model = $client->models->retrieve('hinow/himax');
echo "{$model['name']} ({$model['id']})\n";
echo 'categories: ', implode(', ', $model['category']), "\n";| Recurso | Para quê |
|---|---|
chat->completions | Conversa, streaming, chamada de funções, modo JSON |
embeddings | Vetores para busca semântica |
images · audio · video | Geração |
models | Catálogo, preço e recursos de cada modelo |
tools | Busca na web e contatos de site |
files | Upload de documentos |
vectorStores | Bases de conhecimento pesquisáveis |
rag | Busca semântica nos seus documentos |
beta->assistants · beta->threads | Agentes executados no servidor |
getBalance() | Saldo da conta |
use Hinow\Hinow;
$client = new Hinow(
apiKey: getenv('HINOW_API_KEY'), // ou deixe em branco e use a variável
baseUrl: 'https://api.hinow.ai', // ou HINOW_BASE_URL
timeout: 120, // segundos
maxRetries: 2, // repete 429 e 5xx
);Até a 1.0.1, o SDK empacotava temperature, max_tokens, top_p e repetition_penalty dentro de um objeto parameters antes de enviar. A API aceita esse formato e ignora, então essas opções nunca surtiam efeito: pedir max_tokens => 10 devolvia a resposta inteira.
A partir da 2.0 tudo vai no nível raiz, como a API espera. Seu código não muda — mas chamadas que silenciosamente ignoravam um limite passam a respeitá-lo, então revise prompts que dependiam do comportamento antigo.
Duas outras mudanças: os erros agora são tipados, mantendo HinowException como classe base para que os catch existentes continuem valendo; e $client->chat->completions virou propriedade além de método, então ->completions->create() e ->completions()->create() funcionam igual.
Escolhendo entre HiMax, HiNova e HiGenesis
O que cada modelo faz bem, quanto custa e como escrever o prompt para cada um.

