SDK TypeScript
Instale o SDK oficial em TypeScript e use chat, busca na web, agentes e busca semântica no HiNow.
Atualizado em 09 de ago. de 2026
O SDK oficial em TypeScript para a API do HiNow. Sem dependências, feito para Node 18 ou mais novo, e também roda em Deno e Bun.
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.
npm install hinow-aipnpm add hinow-aiyarn add hinow-aiGuarde a chave em HINOW_API_KEY e o SDK a encontra sozinho. Passar apiKey no construtor também funciona, mas evite deixar o valor no código.
export HINOW_API_KEY="hi_sua_chave_aqui"import { Hinow } from 'hinow-ai';
// The key comes from the HINOW_API_KEY environment variable when you pass nothing.
const client = new Hinow({ apiKey: process.env.HINOW_API_KEY });
const response = await client.chat.completions.create({
model: 'hinow/higenesis',
messages: [{ role: 'user', content: 'Explain what an embedding is in one sentence.' }],
max_tokens: 120,
temperature: 0,
});
console.log(response.choices[0].message.content);
console.log('tokens:', response.usage.total_tokens);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.
Com stream: true o retorno vira um iterável assíncrono. Numa tela de conversa isso muda a percepção de velocidade mais do que trocar de modelo.
import { Hinow } from 'hinow-ai';
const client = new Hinow();
const stream = await client.chat.completions.create({
model: 'hinow/hinova',
messages: [{ role: 'user', content: 'List three uses of an LLM, one per line.' }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
}Responde na hora, sem job para acompanhar, a US$ 0,005 por chamada. O tipo do retorno acompanha o type, então o compilador só deixa você acessar os campos que aquele tipo realmente devolve.
import { Hinow } from 'hinow-ai';
const client = new Hinow();
// The return type follows `type`: `news` gives you date and source,
// em `places` tem telefone e coordenadas, em `autocomplete` tem suggestions.
const web = await client.tools.search({
query: 'plataforma de IA brasileira',
country: 'br',
lang: 'pt-br',
});
console.log(`${web.total_results} results · US$ ${web.cost}`);
for (const r of web.results.slice(0, 3)) {
console.log(`${r.position}. ${r.title}`);
console.log(` ${r.url}`);
}
const news = await client.tools.search({ query: 'artificial intelligence', type: 'news' });
console.log(`\n${news.results[0].source} — ${news.results[0].date}`);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, devolvendo a página onde cada contato apareceu. Esta roda como job, porque a varredura leva tempo.
import { Hinow } from 'hinow-ai';
const client = new Hinow();
// The crawl runs as a job. This method waits and throws if the job fails,
// so `result` always exists when it returns.
const job = await client.tools.websiteContactsAndWait(
{ websites: ['https://teclia.com'], maxDepth: 1, maxLinksPerPage: 5 },
{ onPoll: (j) => console.log('…', j.status) },
);
console.log(`cost US$ ${job.cost} · cached: ${job.cached}`);
for (const c of job.result!.items) {
console.log(`${c.type}: ${c.value} (${c.sourceUrl})`);
}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 tools.websiteContacts() e acompanhe com tools.jobs.retrieve(job.job_id) — repare que o campo é job_id, não id.
Assistentes, threads e runs no mesmo formato da API da OpenAI. O modelo decide quando chamar as suas funções; o run para, você executa e devolve o resultado.
import { Hinow } from 'hinow-ai';
const client = new Hinow();
const assistant = await client.beta.assistants.create({
model: 'hinow/hinova',
name: 'Order support',
instructions: 'You look up order status. Always use the tool. Answer in one sentence.',
tools: [{
type: 'function',
function: {
name: 'get_order',
description: 'Lookup um order pelo identificador.',
parameters: {
type: 'object',
properties: { order_id: { type: 'string', description: 'Identificador, ex.: "A-1001".' } },
required: ['order_id'],
},
},
}],
});
const thread = await client.beta.threads.create();
await client.beta.threads.messages.create(thread.id, {
role: 'user',
content: 'What is the status of order A-1001?',
});
let run = await client.beta.threads.runs.createAndPoll(thread.id, { assistant_id: assistant.id });
// `requires_action` is not an error: it is the run handing control back to you.
if (run.status === 'requires_action') {
const calls = run.required_action!.submit_tool_outputs.tool_calls;
run = await client.beta.threads.runs.submitToolOutputs(thread.id, run.id, {
tool_outputs: calls.map((call) => ({
tool_call_id: call.id,
output: JSON.stringify({ id: 'A-1001', status: 'delivered', total: 349.9 }),
})),
});
run = await client.beta.threads.runs.poll(thread.id, run.id);
}
const messages = await client.beta.threads.messages.list(thread.id, { order: 'desc', limit: 1 });
console.log(messages.data[0].content[0].text?.value);
await client.beta.assistants.del(assistant.id);
await client.beta.threads.del(thread.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 num vector store e busque por significado. A indexação é assíncrona — buscar antes de terminar devolve zero resultado, sem erro.
import { Hinow } from 'hinow-ai';
const client = new Hinow();
const text = 'Free shipping on orders over $200. Standard delivery takes 5 business days.';
const file = await client.files.create({
file: new Blob([text], { type: 'text/plain' }),
filename: 'shipping-policy.txt',
purpose: 'assistants',
});
const store = await client.vectorStores.create({ name: 'Políticas' });
let anexo = await client.vectorStores.files.create(store.id, { file_id: file.id });
// Indexing is asynchronous: the attachment comes back as `in_progress`. Searching before
// it finishes returns nothing, with no error at all.
while (anexo.status === 'in_progress') {
await new Promise((r) => setTimeout(r, 1000));
anexo = await client.vectorStores.files.retrieve(store.id, file.id);
}
// The parameter that narrows the search to one store is `rag_id`. Passing
// `vector_store_id` raises no error: the search sweeps the whole account.
const hits = await client.rag.search({
query: 'qual o prazo de entrega?',
rag_id: store.id,
top_k: 2,
});
for (const h of hits.results) {
console.log(`${h.score.toFixed(2)} ${h.source}: ${h.text.slice(0, 60)}`);
}
await client.vectorStores.del(store.id);
await client.files.del(file.id);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 o RAG está devolvendo lixo.
Cada falha vira uma classe própria, então dá para tratar por tipo em vez de comparar string de mensagem.
import { Hinow, AuthenticationError, RateLimitError, InsufficientBalanceError } from 'hinow-ai';
const client = new Hinow({ apiKey: 'hi_invalid_key' });
try {
await client.getBalance();
} catch (error) {
if (error instanceof AuthenticationError) {
console.log('invalid or revoked key:', error.message);
} else if (error instanceof RateLimitError) {
console.log('rate limited; wait and try again');
} else if (error instanceof InsufficientBalanceError) {
console.log('out of credit');
} else {
throw error;
}
}| 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 |
const client = new Hinow({
apiKey: process.env.HINOW_API_KEY, // ou deixe em branco e use a variável
baseURL: 'https://api.hinow.ai', // ou HINOW_BASE_URL
timeout: 120_000, // milissegundos
maxRetries: 3,
});Até a 1.0.7, o SDK empacotava temperature, max_tokens, top_p e response_format 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.
Escolhendo entre HiMax, HiNova e HiGenesis
O que cada modelo faz bem, quanto custa e como escrever o prompt para cada um.

