Ir para o conteúdo

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.

Instalação

terminalbash
composer require hinow-ai/hinow-ai

Guarde 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.

terminalbash
export HINOW_API_KEY="hi_sua_chave_aqui"

A primeira chamada

primeira-chamada.phpphp
<?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.

No Laravel

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.

.envbash
HINOW_API_KEY=hi_sua_chave_aqui

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.

Mostrando a resposta enquanto ela chega

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.

streaming.phpphp
<?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ê.

Busca na web

Responde na hora, sem job para acompanhar, a US$ 0,005 por chamada. O terceiro argumento aceita country e lang para orientar os resultados.

busca-web.phpphp
<?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']);
typeO que vem em cada resultado
search, scholar, patentsposition, title, url, snippet
newsos acima mais source, date, image_url
imageslink, image_url, thumbnail_url, width, height
videoschannel, duration, date, thumbnail_url
placesaddress, category, phone, website, rating, coordenadas
shoppingprice, delivery, rating, source
autocompletesuggestions, um array de strings — não tem results

Contatos de um site

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.

contatos-site.phpphp
<?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.

Agentes

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.

agente.phpphp
<?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.

Documentos e busca semântica

Suba arquivos, junte numa base e busque por significado. A indexação é assíncrona — buscar antes de terminar devolve zero resultado, sem erro.

conhecimento.phpphp
<?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.

Erros

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.

erros.phpphp
<?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";
}
ClasseQuando acontece
AuthenticationException401 — chave ausente, inválida ou revogada
PermissionException403 — a chave não tem acesso a esse recurso
NotFoundException404 — modelo, arquivo ou assistente inexistente
InvalidRequestException400 ou 422 — falta um campo ou um valor está fora da faixa
RateLimitException429 — limite atingido ou saldo esgotado
ServerException5xx — a falha é do lado da API
ConnectionExceptiona requisição não chegou; nada foi cobrado
modelos.phpphp
<?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";

Tudo que o cliente expõe

RecursoPara quê
chat->completionsConversa, streaming, chamada de funções, modo JSON
embeddingsVetores para busca semântica
images · audio · videoGeração
modelsCatálogo, preço e recursos de cada modelo
toolsBusca na web e contatos de site
filesUpload de documentos
vectorStoresBases de conhecimento pesquisáveis
ragBusca semântica nos seus documentos
beta->assistants · beta->threadsAgentes executados no servidor
getBalance()Saldo da conta

Configuração

cliente.phpphp
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
);

Vindo da versão 1.x

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.

Esta página foi útil?