NormaHub knowledge base

OpenAI SDK via NormaHub

Keep the familiar client interface: only the base URL, key, and model ID change. Routes, timeouts, retries, and safety are below.

1. Three settings to move

Compatibility means matching the API format, not the behavior of every model. So migration is replacing three values with verification, not blindly swapping the address. Start with a short text request without tools or large context and record the status, the actual model, and X-Request-Id.

base_url: https://api.normahub.cc/v1
api_key:  YOUR_API_KEY          # from the dashboard, server-side only
model:    MODEL_ID              # exact ID from the catalog or /v1/models

Take the model ID from the current catalog or from GET /v1/models with your key. IDs from third-party READMEs may be renamed, disabled, or closed to your key — no prompt fixes the catalog. Russian version of this guide: OpenAI SDK через NormaHub.

2. Picking the route

One base_url serves several routes, but each route's availability is set by the chosen model. Chat Completions is the messages dialog route. Responses API is a separate input-based route, available where declared. Anthropic-compatible clients use the /v1/messages route — pick it by client format, not by model name.

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.normahub.cc/v1",
)

# Chat Completions — the classic dialog route
chat = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Hello"}],
)

# Responses API — where the model declares it
resp = client.responses.create(
    model="MODEL_ID",
    input="Hello",
)

Do not mix protocols blindly: a Claude or GPT entry in the catalog does not mean every model accepts tools, streaming, or the same response shape. Verify each model's endpoint and capabilities before switching — see the API reference for details.

3. Timeouts and retries

A production client differs from a README example in two things: every request has a timeout, and retries are bounded. The SDK retries transient 429s and 5xx itself, but you set the attempt count and the operation deadline. An interactive UI is often better off failing fast with a clear message than holding the user in a retry queue.

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.normahub.cc/v1",
    timeout=30.0,       # seconds per request instead of waiting forever
    max_retries=2,      # the SDK retries only transient failures
)

response = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=512,     # caps both the answer and the bill
)

If the connection drops after the request is sent, the outcome is unknown: upstream may already have processed the operation. Do not repeat non-idempotent actions without your own idempotency key. Full rules are in the errors guide.

4. Beyond the OpenAI SDK

The same base_url works with any OpenAI-compatible client: LangChain, LiteLLM, Cline, and CLI agents take the address and key in their own configuration. One rule for all: the key on the server or in the process environment, the model ID from the catalog, the first request small. Tool-specific examples are in the OpenCode guide and the Claude guide.

5. Security

Call NormaHub from a backend or a local AI agent. Never pass the secret key into browser JavaScript, mobile bundles, public logs, CI artifacts, or docker history. Split keys by environment and integration with their own limits: revoking one secret must not stop the whole product. Create and revoke keys in the dashboard.

Частые вопросы

What exactly changes when moving from direct OpenAI?

Three lines: base_url to https://api.normahub.cc/v1, api_key to the NormaHub key, and model to an ID from the current catalog. Logic, SDK, and request structure stay untouched.

Chat Completions or Responses API?

Chat Completions is the universal dialog route. Use the Responses API where the chosen model declares it in the catalog. Verify the route before switching: not every model supports every endpoint, tools, and streaming.

Why doesn't the SDK retry every error itself?

The SDK retries only transient failures (429, 5xx). Errors 400, 401, 402, 403, and 413 need the request, key, balance, or payload size fixed — retrying them is pointless.

How do I keep the key out of logs?

Call the API only from a backend or a local agent, never put the key in NEXT_PUBLIC_/VITE_ variables or the query string. Log time, endpoint, model, status, and X-Request-Id — that is enough for diagnosis.