Skip to main content

model SDK built by the 9th district at Tooig

Project description

nineth

nineth is the Python SDK for the 1984 model API.

This document is caller-facing and SDK-focused. It is designed as a cookbook: start simple, then layer in advanced runtime controls.

Install

pip install nineth
export NINETH_API_KEY="your-api-key"

Optional env vars:

export NINETH_BASE_URL="https://weirdpablo--rooster-api.modal.run"
export NINETH_DEFAULT_MODEL="1984-m3-0317"

Public SDK Surface

Most apps only need these public objects:

  • NinethClient (sync)
  • AsyncNinethClient (async)
  • client.health()
  • client.model.request(...)
  • AVAILABLE_MODELS
  • NinethAPIError

Models

Current SDK model list:

  • 1984-m0-brute
  • 1984-m0-sm
  • 1984-m1-unified
  • 1984-m2-light
  • 1984-m2-preview
  • 1984-m3-0317
  • 1984-m3-0404
  • 1984-m3-0421
  • 1984-m3-0424
  • 1984-c0-0427

1) Create a Client Once and Reuse It

Sync client

import httpx
from nineth import NinethClient

client = NinethClient(
    base_url="https://weirdpablo--rooster-api.modal.run",
    api_key="nt_live_xxx",
    default_model="1984-m3-0317",
    timeout=httpx.Timeout(300.0, connect=10.0),
    stream_timeout=httpx.Timeout(connect=10.0, read=None, write=60.0, pool=60.0),
    headers={"X-App-Name": "my-app"},
)

# use client.model.request(...) repeatedly
client.close()

Async client

import asyncio
from nineth import AsyncNinethClient

async def main() -> None:
    async with AsyncNinethClient(
        api_key="nt_live_xxx",
        default_model="1984-m3-0317",
    ) as client:
        response = await client.model.request("Say hello in one short sentence.")
        print(response["final_response"])

asyncio.run(main())

2) Health Check

from nineth import NinethClient

with NinethClient() as client:
    print(client.health())

Typical response:

{
  "status": "ok",
  "timestamp": "2026-05-24T12:00:00+00:00"
}

3) Basic Buffered Request

from nineth import NinethClient

with NinethClient(default_model="1984-m3-0317") as client:
    response = client.model.request("Give me a concise BTC market brief.")
    print(response["final_response"])

Typical response shape:

{
  "final_response": "BTC is range-bound with elevated event risk.",
  "iterations": 1,
  "thinking": [],
  "service_calls": [],
  "service_responses": [],
  "artifacts": [],
  "usage": {
    "prompt_tokens": 123,
    "completion_tokens": 41,
    "total_tokens": 164
  },
  "events": []
}

4) Streaming Request

from nineth import NinethClient

with NinethClient(default_model="1984-m3-0317") as client:
    for event in client.model.request("Summarize oil market headlines.", stream=True):
        if event["type"] == "model_delta":
            print(event["data"]["text"], end="", flush=True)
        elif event["type"] == "result":
            print("\n\nFinal:", event["data"]["final_response"])

Typical stream event sequence:

{"type": "accepted", "data": {"ok": true}}
{"type": "model_delta", "data": {"text": "Crude prices"}}
{"type": "model_delta", "data": {"text": " rose on..."}}
{"type": "result", "data": {"final_response": "Crude prices rose on...", "iterations": 1}}

5) Generation Controls

Use these to tune model behavior per request:

  • reasoning: disabled | low | medium | high
  • show_reasoning: bool
  • temperature
  • top_p, min_p, top_k
  • repetition_penalty, presence_penalty, frequency_penalty
  • seed

Example:

response = client.model.request(
    "Compare two risk scenarios in 5 bullets.",
    reasoning="high",
    show_reasoning=False,
    temperature=0.2,
    top_p=0.9,
    min_p=0.01,
    top_k=40,
    repetition_penalty=1.05,
    presence_penalty=0.1,
    frequency_penalty=0.1,
    seed=1337,
)

6) Loop Controls

  • max_iterations caps model turns.
  • continuous controls idle-loop persistence.
response = client.model.request(
    "Monitor and update only when new data arrives.",
    max_iterations=20,
    continuous=True,
)

7) Cache and Session Reuse

Use cache=True to persist and receive session_id (mapped from process_id).

first = client.model.request(
    "Remember my preferred output format: CSV.",
    cache=True,
)
sid = first["session_id"]

second = client.model.request(
    "Now summarize open tasks.",
    cache=True,
    session_id=sid,
)

Important:

  • session_id requires cache=True unless resuming callback with client_service_results.

8) policy vs guardrail

  • policy: caller runtime policy text for the request.
  • guardrail: ADAM extension text for default SDK/API ingress path.
response = client.model.request(
    "Draft an operations note.",
    policy="Keep response under 120 words and use plain language.",
    guardrail="Never include secrets or credentials.",
)

9) base_system Provider Path

base_system defaults to True.

Set False only when you explicitly want runtime default provider fallback.

response = client.model.request(
    "Run with runtime provider fallback.",
    base_system=False,
)

10) Built-in Services with default_service

default_service is the main built-in service switch:

  • False: built-ins disabled
  • True: built-ins enabled
  • list[str]: built-in allowlist

It also supports alias groups in SDK payload shaping, including browser, knowledge, computer, workspace, voice, trading, and shop.

response = client.model.request(
    "Search for the latest shipping regulations and summarize.",
    default_service=["browser", "search_knowledge"],
)

11) include_service (Local and Callback)

include_service supports three practical patterns:

  1. Local schema.py path (SDK executes locally)
  2. Inline schema dict list
  3. Callback block with URL and schema list

A) Local schema path

response = client.model.request(
    "Get weather for Lagos using local service.",
    include_service=["./services/weather/schema.py"],
)

B) Inline schema

weather_schema = {
    "name": "get_weather",
    "description": "Return weather forecast.",
    "parameters": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
        "additionalProperties": False,
    },
}

response = client.model.request(
    "Use inline service schema for weather.",
    include_service=[weather_schema],
)

C) Callback URL block

response = client.model.request(
    "Call my hosted weather callback.",
    include_service={
        "callback": {"url": "https://my-app.example.com/include-service-callback"},
        "schema": [weather_schema],
    },
)

When callback flow pauses, server can return awaiting_client_services with pending_client_calls.

12) callback_url as Global Callback

callback_url can be reused across callback-capable surfaces and can be inherited by inbound email templates that omit template url.

response = client.model.request(
    "Register inbound mailbox setup.",
    callback_url="https://my-app.example.com/rooster-callback",
    include_service=[weather_schema],
)

13) Manual Callback Resume with client_service_results

For caller-managed continuation:

paused = client.model.request(
    "Pause on client services.",
    include_service=[weather_schema],
)

if paused.get("status") == "awaiting_client_services":
    resumed = client.model.request(
        "Resume after local service execution.",
        include_service=[weather_schema],
        client_service_results=[
            {
                "call_id": "client_1_1",
                "service_name": "get_weather",
                "success": True,
                "result": {"location": "Lagos", "forecast": "sunny"},
            }
        ],
        session_id=paused.get("process_id"),
    )

14) response_format and compute

response_format="json" parses final_response when valid JSON and preserves original raw_response.

response = client.model.request(
    "Return JSON with keys trend and confidence.",
    response_format="json",
    compute=True,
)

print(response["final_response"])  # parsed object if valid JSON
print(response.get("raw_response"))
print(response.get("compute"))

Typical parsed response:

{
  "final_response": {
    "trend": "neutral",
    "confidence": 0.64
  },
  "raw_response": "{\"trend\":\"neutral\",\"confidence\":0.64}",
  "compute": 241
}

15) Messaging Configuration

messaging attaches transport defaults for email and telegram.

response = client.model.request(
    "Send a concise status update.",
    messaging={
        "email": {
            "address": "ops@resident.tooig.com",
            "name": "Ops Bot",
            "instruction": "Always ask for ticket id before escalation.",
            "reasoning_effort": "high",
        },
        "telegram": {
            "botId": "bot-1",
            "chatId": "12345678",
            "reasoning_effort": "disabled",
        },
    },
)

Auto-enable behavior:

  • telegram messaging enables telegram send/edit services.
  • email messaging enables email send services when outbound-capable.
  • inbound-only template registration does not auto-enable email send services.

16) Template-Scoped Email Payloads

Canonical message payload location:

  • messaging.email.templates[*].message
  • messaging.email.templates[*].messages

Inbound template example:

response = client.model.request(
    "Register inbound mailbox template.",
    messaging={
        "email": {
            "address": "agent@resident.tooig.com",
            "instruction": "Handle review requests.",
            "templates": [
                {
                    "type": "inbound",
                    "name": "review_request",
                    "standalone": True,
                    "messages": [
                        {
                            "from": "applicant@example.com",
                            "to": "agent@resident.tooig.com",
                            "subject": "Application follow-up",
                            "body": "Could you share my status?",
                        }
                    ],
                }
            ],
        }
    },
    callback_url="https://my-app.example.com/inbound-callback",
)

Outbound template example:

response = client.model.request(
    "Send outbound applicant decision.",
    messaging={
        "email": {
            "address": "agent@resident.tooig.com",
            "templates": [
                {
                    "type": "outbound",
                    "name": "decision_email",
                    "messages": [
                        {
                            "to": "applicant@example.com",
                            "subject": "Application Decision",
                            "body": "Thanks for your time. We would like to proceed.",
                            "cc": ["hr@example.com"],
                            "bcc": ["audit@example.com"],
                        }
                    ],
                }
            ],
        }
    },
)

17) Audio and Images

response = client.model.request(
    "Transcribe audio and summarize key action items.",
    audio=[
        {
            "data": "<base64-audio>",
            "mime_type": "audio/mpeg",
            "filename": "meeting.mp3",
        }
    ],
)
response = client.model.request(
    "Describe this chart.",
    images=["<base64-image>"],
)

18) Streaming with Service Progress

When services are called, stream can include synthetic progress lines and service call/response events.

for event in client.model.request("Research and summarize.", stream=True, default_service=["browser"]):
    et = event["type"]
    if et == "model_delta":
        print(event["data"]["text"], end="")
    elif et == "service_call":
        print("\nservice call:", event["data"]) 
    elif et == "service_response":
        print("\nservice response:", event["data"]) 

19) Error Handling

All API and stream errors are raised as NinethAPIError.

from nineth import NinethAPIError

try:
    response = client.model.request("Run protected operation.")
except NinethAPIError as exc:
    print("nineth error:", str(exc))

SDK behavior includes sanitizing provider/URL fragments from surfaced errors and masking some model subscription-availability patterns.

20) End-to-End Example: Cached Session + Services + JSON Output

from nineth import NinethClient

weather_schema = {
    "name": "get_weather",
    "description": "Return weather forecast.",
    "parameters": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
        "additionalProperties": False,
    },
}

with NinethClient(api_key="nt_live_xxx", default_model="1984-m3-0421") as client:
    step1 = client.model.request(
        "Create a daily logistics summary format and remember it.",
        cache=True,
        default_service=["browser"],
    )

    sid = step1.get("session_id")

    step2 = client.model.request(
        "Use today weather in Lagos and return JSON with fields summary and risk_level.",
        cache=True,
        session_id=sid,
        include_service=[weather_schema],
        response_format="json",
        compute=True,
        reasoning="medium",
    )

    print(step2["final_response"])
    print("tokens:", step2.get("compute"))

Troubleshooting

Common issues and fixes:

  1. ValueError: Authentication required Set api_key or NINETH_API_KEY.

  2. ValueError: A model is required Pass model=... or set default_model/NINETH_DEFAULT_MODEL.

  3. ValueError: session_id requires cache=True When reusing session_id, set cache=True.

  4. include_service callback conflicts Do not define a user service named request_include_service_interlude when callback URL mode is used.

  5. Stream appears to stop at awaiting_client_services Resume manually with client_service_results, or use callback URL mode to auto-resume.

Quick Reference: request(...) Arguments

Task identity:

  • task_input
  • model

Execution mode:

  • stream
  • max_iterations
  • continuous

Generation controls:

  • reasoning
  • show_reasoning
  • temperature
  • top_p
  • min_p
  • top_k
  • repetition_penalty
  • presence_penalty
  • frequency_penalty
  • seed

Runtime controls:

  • policy
  • guardrail
  • base_system
  • default_service
  • include_service
  • callback_url
  • client_service_results

Continuity:

  • cache
  • session_id

Inputs:

  • images
  • audio

Output shaping:

  • response_format
  • compute
  • verbose
  • debug (legacy alias)

Transport:

  • messaging

Legacy alias compatibility:

  • policy also accepts system_prompt
  • verbose may be influenced by debug

Final Note

If you are integrating nineth in production:

  • Create one long-lived client per process.
  • Reuse cached sessions explicitly when continuity matters.
  • Keep default_service strict and intentional.
  • Treat include_service schemas as request-scoped contracts.
  • Prefer callback_url mode for distributed service execution.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nineth-0.6.48.tar.gz (29.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nineth-0.6.48-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

File details

Details for the file nineth-0.6.48.tar.gz.

File metadata

  • Download URL: nineth-0.6.48.tar.gz
  • Upload date:
  • Size: 29.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nineth-0.6.48.tar.gz
Algorithm Hash digest
SHA256 91fd454889080a4c68c87bf5081d2fdeeb810dbf71ed5eebe9c14fb9a966d673
MD5 5f344dcafedd21429b66fd9726b8dffc
BLAKE2b-256 905350924a0046c362b258c32005f835eb0bc160e3a257f0e2ad3f595e956722

See more details on using hashes here.

File details

Details for the file nineth-0.6.48-py3-none-any.whl.

File metadata

  • Download URL: nineth-0.6.48-py3-none-any.whl
  • Upload date:
  • Size: 30.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nineth-0.6.48-py3-none-any.whl
Algorithm Hash digest
SHA256 67f3f08a3d8d228877d1e89a479263fb81e526df77bd31d3678be5feea8b9ce1
MD5 b529b5b99a211feca4027ea462114d24
BLAKE2b-256 b63947c1ee51abaf731351335c16503ffae5fa0195627b6d62ca1dc6cea2f5ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page