SDK Python
Instale o SDK oficial em Python e use chat, busca na web, agentes e busca semântica no HiNow.
Atualizado em 09 de ago. de 2026
O SDK oficial em Python para a API do HiNow. Requer Python 3.8 ou mais novo e usa httpx por baixo, com cliente síncrono e assíncrono.
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.
pip install hinow-aiuv add hinow-aipoetry 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 os
from hinow_ai import Hinow
# The key comes from HINOW_API_KEY when you pass nothing.
client = Hinow(api_key=os.environ["HINOW_API_KEY"])
response = client.chat.completions.create(
model="hinow/higenesis",
messages=[{"role": "user", "content": "Explain what an embedding is in one sentence."}],
max_tokens=120,
temperature=0,
)
print(response.choices[0].message.content)
print("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 gerador. Cada pedaço chega em chunk.choices[0].delta.content, tipado igual à resposta não-stream.
from hinow_ai import Hinow
client = Hinow()
for chunk in client.chat.completions.create(
model="hinow/hinova",
messages=[{"role": "user", "content": "List three uses of an LLM, one per line."}],
stream=True,
):
print(chunk.choices[0].delta.content or "", end="", flush=True)
print()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.
from hinow_ai import Hinow
client = Hinow()
# The shape of the answer follows `type`: news carries date and source,
# places carries phone and coordinates, autocomplete carries suggestions.
web = client.tools.search("plataforma de IA brasileira", country="br", lang="pt-br")
print(f"{web['total_results']} results · US$ {web['cost']}")
for r in web["results"][:3]:
print(f"{r['position']}. {r['title']}")
print(f" {r['url']}")
news = client.tools.search("artificial intelligence", type="news", country="br")
print(f"\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.
from hinow_ai import Hinow
client = Hinow()
# The crawl runs as a job. This method waits and raises if it fails,
# so "result" always exists when it returns.
job = client.tools.website_contacts_and_wait(
["https://teclia.com"],
max_depth=1,
max_links_per_page=5,
on_poll=lambda j: print("…", j["status"]),
)
print(f"cost US$ {job['cost']} · cached: {job['cached']}")
for contact in job["result"]["items"]:
print(f"{contact['type']}: {contact['value']} ({contact['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.website_contacts() 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 json
from hinow_ai import Hinow
client = Hinow()
assistant = 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": 'ex.: "A-1001"'}},
"required": ["order_id"],
},
},
}],
)
thread = client.beta.threads.create()
client.beta.threads.messages.create(thread["id"], content="What is the status of order A-1001?")
run = client.beta.threads.runs.create_and_poll(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":
chamadas = run["required_action"]["submit_tool_outputs"]["tool_calls"]
run = client.beta.threads.runs.submit_tool_outputs(
thread["id"], run["id"],
tool_outputs=[
{"tool_call_id": c["id"], "output": json.dumps({"id": "A-1001", "status": "delivered"})}
for c in chamadas
],
)
run = client.beta.threads.runs.poll(thread["id"], run["id"])
messages = client.beta.threads.messages.list(thread["id"], order="desc", limit=1)
print(messages["data"][0]["content"][0]["text"]["value"])
client.beta.assistants.delete(assistant["id"])
client.beta.threads.delete(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 submit_tool_outputs 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 time
from hinow_ai import Hinow
client = Hinow()
text = b"Free shipping on orders over $200. Standard delivery takes 5 business days."
file = client.files.create(text, filename="shipping-policy.txt", purpose="assistants")
base = client.vector_stores.create(name="Politicas")
anexo = client.vector_stores.files.create(base["id"], file_id=file["id"])
# Indexing is asynchronous. Searching before it finishes returns nothing,
# with no error to say why.
while anexo["status"] == "in_progress":
time.sleep(1)
anexo = client.vector_stores.files.retrieve(base["id"], file["id"])
# The per-store filter is called rag_id. Passing vector_store_id raises no error:
# the search sweeps every document on the account.
hits = client.rag.search("qual o prazo de entrega?", rag_id=base["id"], top_k=2)
for a in hits["results"]:
print(f"{a['score']:.2f} {a['source']}: {a['text'][:60]}")
client.vector_stores.delete(base["id"])
client.files.delete(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.
from hinow_ai import Hinow, AuthenticationError, RateLimitError, InsufficientBalanceError
client = Hinow(api_key="hi_invalid_key")
try:
client.get_balance()
except AuthenticationError as e:
print("invalid or revoked key:", e)
except RateLimitError:
print("rate limited; wait and try again")
except InsufficientBalanceError:
print("out of credit")| Recurso | Para quê |
|---|---|
client.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 |
vector_stores | Bases de conhecimento pesquisáveis |
rag | Busca semântica nos seus documentos |
beta.assistants · beta.threads | Agentes executados no servidor |
get_balance() | 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.
Duas outras correções da mesma versão: o upload de arquivo não funcionava, porque o cliente fixava Content-Type: application/json e isso quebrava o multipart; e os pedaços do streaming agora são tipados, então é chunk.choices[0].delta.content em vez de acesso por dicionário.
Escolhendo entre HiMax, HiNova e HiGenesis
O que cada modelo faz bem, quanto custa e como escrever o prompt para cada um.

