Ir para o conteúdo

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.

Instalação

terminalbash
npm install hinow-ai

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

terminalbash
export HINOW_API_KEY="hi_sua_chave_aqui"

A primeira chamada

primeira-chamada.tstypescript
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.

Mostrando a resposta enquanto ela chega

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.

streaming.tstypescript
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 ?? '');
}

Busca na web

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.

busca-web.tstypescript
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}`);
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, devolvendo a página onde cada contato apareceu. Esta roda como job, porque a varredura leva tempo.

contatos-site.tstypescript
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.

Agentes

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.

agente.tstypescript
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.

Documentos e busca semântica

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

conhecimento.tstypescript
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.

Erros

Cada falha vira uma classe própria, então dá para tratar por tipo em vez de comparar string de mensagem.

erros.tstypescript
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;
  }
}

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.tstypescript
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,
});

Vindo da versão 1.x

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.

Esta página foi útil?