Skip to main content

NuCoreAI Platform

Goal

Convert natural language user queries into commands, queries, and programs for any NuCore-enabled platform (currently eisy).

Quick Start

git clone https://github.com/NuCoreAI/nucore-ai.git
cd nucore-ai
python -m venv .venv && source .venv/bin/activate
pip install -e .

Development

pip install -e . installs production dependencies only. To run the test suite, install the dev extras instead:

pip install -e ".[dev]"
# or: pip install -r requirements-dev.txt
pytest

Running the Unified Runtime

The entry point is the unified runtime: one system prompt, one native tool-calling agentic loop, no router, no per-intent directory dispatch. It executes user queries directly against a NuCore backend.

Create a runtime profile JSON first (see src/unified/runtime_config.example.json).

Minimal (no backend)

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --query "Turn on the patio lights"

With NuCore Backend (eisy)

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --backend-api-classpath iox.IoXWrapper \
  --backend-api-base-url https://192.168.6.134 \
  --backend-api-username admin \
  --backend-api-password yourpassword \
  --json-output true

Interactive Mode

Omit --query to enter an interactive prompt loop:

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --backend-api-classpath iox.IoXWrapper \
  --backend-api-base-url https://192.168.6.134 \
  --backend-api-username admin \
  --backend-api-password yourpassword

WebSocket Server Mode

Pass --websocket-port to run as a standalone WebSocket server instead of --query/REPL mode -- no HTTP framework involved (uses the websockets package, already a project dependency). Each connection gets its own session and conversation history; every received message is treated as a query, and the response streams back over the same connection.

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --backend-api-classpath iox.IoXWrapper \
  --backend-api-base-url https://192.168.6.134 \
  --backend-api-username admin \
  --backend-api-password yourpassword \
  --websocket-port 8765

This is a lower-level alternative to eisy_ai's FastAPI-based chat server (a separate sibling project) -- use this mode when a raw ws:// endpoint is all you need, without serving a browser UI.

TLS (wss://)

Add --ssl-certfile/--ssl-keyfile (a PEM cert and private key, given together) to serve wss:// instead of ws:// -- needed for clients that always connect over TLS (e.g. the Eisy UI). Uses Python's standard-library ssl module, no extra dependency.

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --backend-api-classpath iox.IoXWrapper \
  --backend-api-base-url https://192.168.6.134 \
  --backend-api-username admin \
  --backend-api-password yourpassword \
  --websocket-port 8765 \
  --ssl-certfile secrets/cert.pem \
  --ssl-keyfile secrets/key.pem

Generate a self-signed pair for local/dev use with:

openssl req -x509 -newkey rsa:2048 -keyout secrets/key.pem -out secrets/cert.pem \
  -days 825 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

secrets/ is already gitignored. A self-signed cert will trigger a browser warning unless the client is configured to skip verification.

Secrets File

Use --secrets-file to provide API keys as key/value pairs. These values are loaded into a dict and passed to provider dispatch as the environment source.

The file must be valid JSON with a single top-level object. Each property name is a secret name and each value is the string to use for lookup. Do not use shell syntax, comments, or nested structures.

Example secrets.json:

{
  "OPENAI_API_KEY": "...",
  "ANTHROPIC_API_KEY": "...",
  "GEMINI_API_KEY": "...",
  "XAI_API_KEY": "...",
  "LLAMACPP_API_KEY": "..."
}

Format rules:

  • Top level must be a JSON object.
  • Keys should be environment-style secret names such as OPENAI_API_KEY.
  • Values should be strings.
  • Duplicate or alias keys are allowed if you want to point multiple names at the same secret value.

Usage:

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --secrets-file /path/to/secrets.json \
  --query "Turn on the patio lights"

Logging

The runtime supports centralized, flexible logging for both development and production use. When console logging is on (the default), DEBUG/INFO records go to stdout and WARNING/ERROR records go to stderr -- so 2>/dev/null silences errors/warnings while keeping normal output, and piping just stdout to a log aggregator won't miss error-level records mixed in. The optional log file (--log-file/NUCORE_LOG_FILE) always receives every level, regardless of the console split.

Runtime Logging Flags

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --log-level DEBUG \
  --log-file logs/unified-runtime.log

Use JSON logs for ingestion by external tools:

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --log-json \
  --log-file logs/unified-runtime.json.log

Disable console logs (for quiet batch or service environments):

python -m unified.run_unified_runtime \
  --runtime-config src/unified/runtime_config.example.json \
  --no-log-console \
  --log-file logs/unified-runtime.log

Logging Environment Variables

  • NUCORE_LOG_LEVEL (default: INFO)
  • NUCORE_LOG_JSON (true/false, default: false)
  • NUCORE_LOG_FILE (optional file path)
  • NUCORE_LOG_CONSOLE (true/false, default: true)

Logger Usage in Code

from utils import configure_logging, get_logger

configure_logging(level="INFO")
logger = get_logger(__name__)
logger.info("runtime started")

Full CLI Reference

Flag Description
--runtime-config Required path to JSON with top-level nucore_runtime
--secrets-file Optional JSON file of secret key/value pairs passed into provider client key resolution
--query Single query mode; omit for interactive loop
--websocket-port Run as a native WebSocket server on this port instead of --query/REPL mode
--ssl-certfile PEM cert file; with --ssl-keyfile, serves --websocket-port over wss://
--ssl-keyfile PEM private key file; with --ssl-certfile, serves --websocket-port over wss://
--backend-api-classpath Python class path for backend API (e.g. iox.IoXWrapper)
--backend-api-base-url Base URL for backend API (http(s)://host:port, or unix:///path/to/socket for iox.IoXWrapper to connect over a Unix domain socket instead of TCP)
--backend-api-username Backend API username
--backend-api-password Backend API password
--json-output Enable JSON output mode for backend API
--prompt_type Prompt variant to use (e.g. shared-features)
--log-level Logging level override: DEBUG, INFO, WARNING, ERROR, CRITICAL
--log-file Optional rotating log file path
--log-json Emit logs in JSON format
--no-log-console Disable console logging
--stream / --no-stream Force LLM token streaming on/off for every nucore_runtime profile, overriding each profile's own stream setting
--max-iterations Override the agentic loop's max tool-call iterations per query (defaults to runtime config's max_iterations, or 8)
--preferences-dir Directory for this installation's customer preferences (aliases/events); overrides runtime config's preferences_dir -- no default, preferences are unavailable without one
--diagnostic-step Bypass the LLM/agentic loop and call one diagnostic tool directly against a live backend (e.g. get_full_system_config); prints the raw result and exits. Pairs with --diagnostic-params. Manual testing only
--diagnostic-params JSON object of keyword params for --diagnostic-step

Supported Providers

Provider Alias Env Var
Anthropic Claude claude, anthropic ANTHROPIC_API_KEY
OpenAI openai OPENAI_API_KEY
Google Gemini gemini, google GEMINI_API_KEY
xAI Grok grok, xai XAI_API_KEY
llama.cpp (local) llama.cpp, llamacpp LLAMACPP_API_KEY (optional)

Provider and model settings come from the runtime profile file passed to --runtime-config. Profiles use a provider field and do not rely on legacy llm aliases or supported_llms fallback behavior. API keys can be embedded in the profile, supplied via --secrets-file, or read from process environment variables.

Ready-to-copy example profiles: src/unified/runtime_config.example.json (Claude, the default), src/unified/runtime_config.openai.example.json, src/unified/runtime_config.grok.example.json. Copy whichever one you want over your live runtime_config.json (or pass it directly via --runtime-config) and set that provider's env var.

A profile can also set "reasoning_effort" (e.g. "none"/"low"/"medium"/"high"), forwarded to OpenAI-compatible providers only when present. Some reasoning-tier models reject function tools on /v1/chat/completions entirely unless this is explicitly set -- see the OpenAI example profile.

Using a Local (Edge) LLM with llama.cpp

Build llama.cpp

sudo apt install build-essential cmake clang libomp-dev libcurl4-openssl-dev

CPU only

cmake -B build.cpu
cmake --build build.cpu --config release

Nvidia GPU

sudo ubuntu-drivers install
sudo apt install nvidia-cuda-toolkit
cmake -B build.cuda -DGGML_CUDA=on
cmake --build build.cuda --config release

Start the Server

build.cuda/bin/llama-server \
  -m /path/to/model.gguf \
  -c 64000 --port 8013 --host 0.0.0.0 \
  -t 15 --n-gpu-layers 50 --batch-size 8192

Connect the Runtime to llama.cpp

python -m unified.run_unified_runtime \
  --runtime-config /path/to/nucore_runtime.json \
  --backend-api-classpath iox.IoXWrapper \
  --backend-api-base-url https://192.168.6.134 \
  --backend-api-username admin \
  --backend-api-password yourpassword

Runtime profile for llama.cpp (--runtime-config target):

{
  "nucore_runtime": {
    "default": {
      "provider": "llama.cpp",
      "model": "qwen3-instruct",
      "url": "http://192.168.6.113:8013/v1",
      "max_turns": 20,
      "temperature": 0.2,
      "max_tokens": 32000
    }
  }
}

Capabilities

Beyond device/group/routine/variable command-and-control, the unified runtime supports:

  • Diagnostics -- stateless, no session or start call: get_diagnostics_prompt fetches reference material (PLM/device link records, the DEVICE ACTIVITY LOG format and how to read it for past behavior, known fixes) on demand for the current question only -- like consulting DEVICE DATABASE/ROUTINES DATABASE, it never turns into a standing mode and doesn't carry over to a later, unrelated question in the same conversation. run_diagnostic_step then runs one diagnostic step directly against the backend (e.g. checking or starting/stopping/restarting core or plugin services), and run_shell_command runs a shell command on the backend host (e.g. to search the device activity log).
  • Plan -- start_plan/run_plan_step walk a customer through a structured multi-step task such as a new device installation, rather than a single command/response turn.
  • User preferences -- preference_op/list_preferences store per-user aliases (e.g. naming a device or routine) and event subscriptions, persisted across sessions.
  • Plugin management -- list_store_plugins/list_purchased_plugins/list_installed_plugins cover NuCore's plugin marketplace (browse/license/installed state); get_plugin_capabilities/ call_plugin let the model extend its own capabilities with a plugin's tools when no built-in tool covers a request. install_plugin/buy_plugin/delete_plugin don't complete anything themselves -- for security reasons, installing, purchasing, and deleting all happen on the web -- each returns a link for the customer to finish there. Starting/stopping/restarting a plugin's underlying service goes through the diagnostics flow above, not this one.

See src/unified/prompt/definitions.md for the exact tool-selection rules the model follows for each of these, and src/unified/README.md for the tool/handler layout.

Hardware

Tested with eisy.

Further Documentation

  • Unified runtime architecture and tool reference: src/unified/README.md

Metadata

Release files for nucore-ai 2.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nucore-ai 2.1.0
File Size Uploaded
nucore_ai-2.1.0.tar.gz 236.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nucore-ai 2.1.0
File Interpreter ABI Platform
nucore_ai-2.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 523.0 kB

Release files / nucore_ai-2.1.0.tar.gz

Download URL nucore_ai-2.1.0.tar.gz
Size 236.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a965725f9956bddf10c34c0301a49de7052f45064bbd01e5c5674804bcb4e4e7
BLAKE2b-256 checksum
How to use checksums
64a459f17c9b7c0c953b10f6d80c2234d9a812229583d1b0a4cdb2b0d0a11194
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release files / nucore_ai-2.1.0-py3-none-any.whl

Download URL nucore_ai-2.1.0-py3-none-any.whl
Size 286.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7bdd3f9b48beb0e2d07c8177e9f8400eb460a7807d5b264434f2f2c670e01aa3
BLAKE2b-256 checksum
How to use checksums
768ef33db4a4271f36e87561732129123f8bb2f0cf8bfa250360db683b8c8eec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release history Release notifications | RSS feed

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

This release

2.1.0 This release

2 release files

2.0.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page