Skip to main content

model sdk built by the 9th ditrict at tooig

Project description

nineth

nineth is the public Python SDK for the 1984 model API.

This guide is caller-facing and SDK-specific.

If you maintain server internals, use README.md.

Install

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

Quick Start

1) Basic synchronous request

from nineth import NinethClient

with NinethClient(default_model="1984-m3-0424") 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 ...",
  "iterations": 2,
  "usage": {
    "prompt_tokens": 1200,
    "completion_tokens": 310,
    "total_tokens": 1510
  },
  "service_calls": [],
  "service_responses": [],
  "events": []
}

2) Basic asynchronous request

import asyncio
from nineth import AsyncNinethClient

async def main() -> None:
    async with AsyncNinethClient(default_model="1984-m3-0424") as client:
        response = await client.model.request("Summarize crude oil in 5 bullets.")
        print(response["final_response"])

asyncio.run(main())

3) Streaming request

from nineth import NinethClient

with NinethClient(default_model="1984-m3-0424") as client:
    for event in client.model.request("Analyze ETH setup.", stream=True):
        if event["type"] == "model_delta":
            print(event["data"]["text"], end="", flush=True)
        elif event["type"] == "result":
            print("\n---")
            print(event["data"]["final_response"])

Stream event types you should handle:

  • accepted
  • model_delta
  • service_call
  • service_response
  • awaiting_client_services (manual callback mode)
  • result
  • error

Public Surface

Most applications only need:

  • NinethClient
  • AsyncNinethClient
  • client.health()
  • client.model.request(...)

AVAILABLE_MODELS is exported for convenience.

Client Construction

import httpx
from nineth import NinethClient

client = NinethClient(
    base_url="https://weirdpablo--rooster-api.modal.run",
    api_key="...",
    default_model="1984-m3-0424",
    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-Caller": "research-worker-1"},
)

Environment fallbacks:

  • NINETH_API_KEY
  • NINETH_BASE_URL
  • NINETH_DEFAULT_MODEL (or NINETH_MODEL)

Provider Notes

The SDK talks to the Rooster API endpoint. Provider routing happens server-side.

Common server provider modes:

  • Together-backed models (slash-form provider model names)
  • Ollama/cloud models (:cloud / -cloud conventions)
  • OpenRouter models (openrouter/<slug> or latest aliases like ~openai/gpt-latest)

If your server is configured for OpenRouter, ensure OPENROUTER_API exists in the server runtime.

Example SDK request that targets an OpenRouter model slug:

from nineth import NinethClient

with NinethClient(default_model="openrouter/openai/gpt-4o") as client:
    response = client.model.request("Summarize this incident report in 5 bullets.")
    print(response["final_response"])

Model Catalog

Current SDK AVAILABLE_MODELS:

  • 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

Request Arguments (Complete)

client.model.request(...) supports:

Task identity

  • task_input (required)
  • model (optional if client default exists)

Generation controls

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

Loop controls

  • max_iterations
  • continuous

Inputs

  • images: list of base64 strings
  • audio: list of base64 strings or objects {data, mime_type?, filename?}

Runtime controls

  • policy: caller runtime policy text
  • guardrail: ADAM extension text
  • base_system: provider-path control

Memory continuity

  • cache: enable persistent server context
  • session_id: continue a prior cached context

Service controls

  • default_service: False, True, or allowlist list
  • include_service: callback/object or caller-managed list
  • client_service_results: manual callback resume payloads
  • callback_url: global callback URL

Output controls

  • stream
  • response_format: text|json
  • compute
  • verbose (legacy alias: debug)

Messaging transport

  • messaging.email
  • messaging.telegram

Payload Mapping Reference

_build_payload(...) in the SDK maps request arguments into the API payload with the following rules.

Core fields always emitted:

  • task_input
  • model
  • max_iterations
  • show_reasoning
  • continuous (continuous arg or derived as max_iterations > 10)
  • cache
  • base_system
  • default_service (boolean or expanded/merged service list)
  • verbose

Conditionally emitted fields:

  • reasoning -> reasoning_effort
  • temperature -> temperature
  • top_p -> top_p
  • min_p -> min_p
  • top_k -> top_k
  • repetition_penalty -> repetition_penalty
  • presence_penalty -> presence_penalty
  • frequency_penalty -> frequency_penalty
  • seed -> seed
  • include_service -> include_service (deduplicated list/object form)
  • client_service_results -> client_service_results
  • images -> images
  • audio -> audio
  • policy -> policy
  • guardrail -> guardrail
  • messaging -> normalized messaging
  • callback_url -> normalized callback_url
  • response_format="json" -> response_format: "json"
  • compute=True -> compute: true
  • session_id -> process_id

Validation rule:

  • session_id requires cache=True unless you are resuming with client_service_results.

Service auto-enable rule:

  • Email messaging can auto-enable send_email/send_reply when outbound behavior is needed.
  • Telegram messaging auto-enables Telegram send/edit services.

Callback propagation rule:

  • Top-level callback_url is normalized and propagated into inbound email templates missing url.
  • If include_service is list-form and callback_url exists, payload is promoted to object-form with callback + schema.

Cookbook

Recipe 1: Health check

from nineth import NinethClient

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

Typical response:

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

Recipe 2: Per-request model override

with NinethClient(default_model="1984-m2-preview") as client:
    a = client.model.request("fast summary")
    b = client.model.request("deeper review", model="1984-m3-0424")

Recipe 3: Reasoning and sampling

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Build a scenario tree for BTC next week.",
        reasoning="high",
        temperature=0.4,
        top_p=0.9,
        seed=7,
    )

Recipe 4: Request JSON output

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Return a JSON object with keys trend, risk, levels.",
        response_format="json",
    )

print(type(response["final_response"]))  # dict if JSON parse succeeded
print(response.get("raw_response"))      # original string is preserved

Typical JSON-mode response:

{
  "final_response": {
    "trend": "neutral",
    "risk": "medium",
    "levels": ["68000", "70000"]
  },
  "raw_response": "{\"trend\":\"neutral\",...}",
  "iterations": 1
}

Recipe 5: Compute totals

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request("Explain carry trade risk.", compute=True)
    print(response.get("compute"))

Recipe 6: Session continuity (cache + session_id)

from nineth import NinethClient

with NinethClient(default_model="1984-m3-0424") as client:
    first = client.model.request("Remember: my risk budget is medium.", cache=True)
    sid = first["session_id"]

    second = client.model.request(
        "What risk budget did I set?",
        cache=True,
        session_id=sid,
    )
    print(second["final_response"])

Important rule:

  • session_id requires cache=True unless you are explicitly resuming callback steps with client_service_results.

Recipe 7: Built-in services with default_service

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Search web and summarize today's semiconductor headlines.",
        default_service=["browser", "read"],
    )

Notes:

  • default_service=True enables all built-ins.
  • default_service=False disables built-ins.
  • List mode accepts group aliases such as browser, knowledge, computer, workspace, voice, trading, shop.
  • Alias expansion is automatic, and duplicate service names are deduplicated.

Recipe 8: Streaming with service progress

with NinethClient(default_model="1984-m3-0424") as client:
    for event in client.model.request(
        "Research AI gateway patterns and summarize.",
        stream=True,
        default_service=["browser", "read", "deepsearch"],
    ):
        if event["type"] == "model_delta":
            print(event["data"]["text"], end="")
        elif event["type"] == "service_call":
            print("\n[service]", event["data"]["service_name"])
        elif event["type"] == "service_response":
            print("\n[service done]", event["data"].get("service_name"))

Recipe 9: Caller-managed include services (manual resume)

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

with NinethClient(default_model="1984-m3-0424") as client:
    first = client.model.request(
        "Get weather for Lagos and summarize.",
        include_service=[weather_schema],
    )

    if first.get("status") == "awaiting_client_services":
        pending = first["pending_client_calls"]
        # Execute pending client tools yourself.
        manual_results = [
            {
                "call_id": pending[0]["call_id"],
                "service_name": "get_weather",
                "success": True,
                "result": {"location": "Lagos", "forecast": "sunny"},
            }
        ]
        final = client.model.request(
            "continue",
            include_service=[weather_schema],
            client_service_results=manual_results,
            session_id=first["process_id"],
            cache=True,
        )

Recipe 10: SDK-managed callback runtime (include_service object)

weather_schema = {
    "name": "get_weather",
    "description": "Return weather for a city.",
    "parameters": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Get weather for Nairobi and summarize risk impact.",
        include_service={
            "callback": {"url": "https://app.example.com/api/callback"},
            "schema": [weather_schema],
        },
    )

Callback runtime events sent to your callback URL include:

  • service_call
  • interlude (missing caller-side parameters)
  • final_response

Recipe 10b: Callback endpoint contract (request/response)

When include_service callback mode is active, your callback endpoint receives JSON requests with idempotency metadata:

{
    "event_type": "service_call",
    "listener": "nineth_include_service_callback",
    "idempotency_key": "<sha1>",
    "process_id": "proc_abc123",
    "call_id": "call_1",
    "service_name": "get_weather",
    "params": {"location": "Nairobi"},
    "service": {
        "name": "get_weather",
        "description": "Return weather for a city.",
        "parameters": {"type": "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}
    },
    "available_services": [{"name": "get_weather", "description": "..."}]
}

Your callback should return HTTP 200 with a JSON object.

Successful service response example:

{
    "success": true,
    "result": {
        "location": "Nairobi",
        "forecast": "Partly cloudy",
        "temperature_c": 22
    }
}

Interlude response example (ask caller for missing fields):

{
    "success": true,
    "parameters": {
        "location": "Nairobi",
        "units": "metric"
    }
}

Failure response example:

{
    "success": false,
    "error": "Rate limit from upstream weather provider"
}

Notes:

  • The SDK includes X-Idempotency-Key on callback HTTP requests.
  • Callback responses must be JSON objects; non-object JSON is treated as a callback error.
  • The reserved interlude service name is request_include_service_interlude.

Recipe 11: Local schema.py include-service references

include_service supports legacy local references:

  • absolute or relative schema.py path
  • directory containing schema.py
  • shorthand token discoverable from local services/**/schema.py
  • manager class/object references from loaded schema modules

Example:

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Run local weather service.",
        include_service=["./services/weather/schema.py"],
    )

Recipe 12: Messaging (email + telegram)

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Draft and send the update.",
        messaging={
            "email": {
                "address": "ops@example.com",
                "name": "Ops Bot",
                "instruction": "Reply with concise operational summaries.",
            },
            "telegram": {
                "botId": "bot-1",
                "chatId": "12345",
            },
        },
    )

Auto-enable behavior:

  • Telegram messaging config auto-enables Telegram delivery service names.
  • Email messaging auto-enables email send services except inbound-only setup flows.

Recipe 12b: Messaging payload and result events

Example request emphasizing template payloads:

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Handle inbound request and reply.",
        callback_url="https://app.example.com/mailbox-hook",
        messaging={
            "email": {
                "address": "support@example.com",
                "name": "Support Bot",
                "instruction": "Classify and route inbound email.",
                "templates": [
                    {
                        "type": "inbound",
                        "shape": {
                            "subject": "string",
                            "body": "string",
                            "from": "string"
                        }
                    },
                    {
                        "type": "outbound",
                        "recipients": ["customer@example.com"],
                        "messages": [
                            {"subject": "Ticket update", "body": "Issue resolved."}
                        ]
                    }
                ]
            },
            "telegram": {
                "botId": "ops-bot",
                "chatId": "12345"
            }
        },
    )

Typical transport-side effect event fragments inside events:

[
    {
        "type": "mailbox_configured",
        "data": {
            "address": "support@example.com",
            "inbound_uuid": "inb_abc123"
        }
    },
    {
        "type": "service_response",
        "data": {
            "service_name": "send_reply",
            "success": true
        }
    }
]

Inbound UUID behavior:

  • The SDK caches mailbox_configured inbound IDs by sender address per client instance.
  • Subsequent requests can automatically reuse the remembered inbound_uuid for the same address.

Recipe 13: Inbound/outbound email templates with global callback URL

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Configure mailbox templates.",
        callback_url="https://app.example.com/mailbox-hook",
        messaging={
            "email": {
                "address": "helpdesk@example.com",
                "name": "Helpdesk Bot",
                "templates": [
                    {
                        "type": "inbound",
                        "shape": {
                            "subject": "string",
                            "body": "string",
                            "from": "string"
                        },
                        # url omitted -> inherits top-level callback_url
                    },
                    {
                        "type": "outbound",
                        "recipients": ["customer@example.com"],
                        "message": {
                            "subject": "We received your request",
                            "body": "Thanks, we are on it."
                        }
                    },
                ],
            }
        },
    )

Recipe 14: Audio input

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Transcribe and summarize this call.",
        audio=[
            {
                "data": "<base64-audio>",
                "mime_type": "audio/mpeg",
                "filename": "call.mp3",
            }
        ],
    )

Recipe 15: Policy and guardrail

with NinethClient(default_model="1984-m3-0424") as client:
    response = client.model.request(
        "Assess this strategy.",
        policy="Keep output in bullet points with risk-first framing.",
        guardrail="Refuse prohibited trading instructions.",
    )

Interpretation:

  • policy is caller runtime instruction overlay.
  • guardrail augments ADAM in the default SDK/API ingress path.

Recipe 16: Async streaming

import asyncio
from nineth import AsyncNinethClient

async def main() -> None:
    async with AsyncNinethClient(default_model="1984-m3-0424") as client:
        stream = await client.model.request(
            "Stream a quick macro brief.",
            stream=True,
        )
        async for event in stream:
            if event["type"] == "model_delta":
                print(event["data"]["text"], end="")

asyncio.run(main())

Response Shapes

Buffered response

Common keys:

  • final_response
  • iterations
  • usage
  • thinking (when enabled)
  • service_calls
  • service_responses
  • artifacts
  • events
  • compute (when requested)
  • session_id (when cache enabled)

Callback wait response (manual mode)

When waiting for caller-managed services:

{
  "status": "awaiting_client_services",
  "process_id": "proc_123",
  "pending_client_calls": [
    {
      "call_id": "call_1",
      "service_name": "get_weather",
      "params": {"location": "Lagos"}
    }
  ]
}

Stream result event

{
  "type": "result",
  "data": {
    "final_response": "...",
    "iterations": 3,
    "usage": {...}
  }
}

Stream service events

Service execution surfaces in stream mode as readable progress + structured events:

{
    "type": "model_delta",
    "data": {
        "text": "\n> Browsing the web\n",
        "progress": true,
        "synthetic": true
    }
}
{
    "type": "service_call",
    "data": {
        "service_name": "search_web",
        "client_managed": false,
        "call_id": "call_12"
    }
}
{
    "type": "service_response",
    "data": {
        "service_name": "search_web",
        "success": true
    }
}

Error Handling

SDK raises NinethAPIError for API/server failures.

from nineth import NinethClient, NinethAPIError

with NinethClient(default_model="1984-m3-0424") as client:
    try:
        client.model.request("test")
    except NinethAPIError as exc:
        print("request failed:", exc)

Authentication missing raises ValueError before request dispatch.

Practical Patterns

  • Create one long-lived client per worker process to maximize HTTP connection reuse.
  • Use stream_timeout with read=None for long-running SSE sessions.
  • Use response_format="json" only when your prompt explicitly asks for strict JSON.
  • Prefer default_service=[...] over broad True in production to keep service scope tight.
  • For include-service workflows, choose one mode per integration:
    • SDK-managed callback URL mode for autonomous orchestration.
    • caller-managed mode when you need full deterministic control.

Troubleshooting

  • ValueError: Authentication required: set NINETH_API_KEY or pass api_key=.
  • ValueError: A model is required: set client default_model or pass model= per request.
  • session_id requires cache=True: set cache=True when reusing session continuity.
  • callback responses not progressing: verify callback endpoint returns HTTP 200 JSON object.
  • stalled stream with include services: confirm pending calls are resumed via client_service_results (manual mode) or callback endpoint handling (managed mode).

Versioning and Compatibility

  • Public SDK API is centered on NinethClient, AsyncNinethClient, AVAILABLE_MODELS, and NinethAPIError.
  • Legacy aliases (system_prompt, debug, services, service_names) remain compatibility surfaces but should be considered migration paths, not preferred new usage.

Maintainer Link

For server architecture and internal operations, see README.md.

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.51.tar.gz (31.3 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.51-py3-none-any.whl (32.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nineth-0.6.51.tar.gz
  • Upload date:
  • Size: 31.3 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.51.tar.gz
Algorithm Hash digest
SHA256 b9d2d8ce9bf7f6da7f7945f7581e3193d6f036ae45d808959cc20308de7e2dc6
MD5 97d6dd940fa90221c4699a1e34fe8547
BLAKE2b-256 472c922e83f54a046929b275d99e6aafcce84a5e3248cf9a34ff88f1941be09d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nineth-0.6.51-py3-none-any.whl
  • Upload date:
  • Size: 32.1 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.51-py3-none-any.whl
Algorithm Hash digest
SHA256 6cd62b9671fca6d4af12c609ff7b8fa7e4684c4899a7cdebe3afc894d40d9927
MD5 1084f42db70072e1f2653fe5e244affb
BLAKE2b-256 eed586d7f404def9f5a803807f2ae3d0ed3fca28ed817c8896949772fe66cd51

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