{"id":50327053,"url":"https://github.com/dnuzi/niyox","last_synced_at":"2026-06-08T14:00:30.341Z","repository":{"id":360941495,"uuid":"1251179116","full_name":"dnuzi/niyox","owner":"dnuzi","description":"NiyoX AI - Powered by DanuZz.","archived":false,"fork":false,"pushed_at":"2026-06-08T12:07:50.000Z","size":496,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T13:26:04.669Z","etag":null,"topics":["agentic","ai","ai-agent","ai-agents","cli","danuzz","dnuzi","html","js","movanest","niyox","react","reactjs","terminal","trending"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/niyox","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dnuzi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-27T10:16:43.000Z","updated_at":"2026-06-08T12:05:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dnuzi/niyox","commit_stats":null,"previous_names":["dnuzi/niyox"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/dnuzi/niyox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnuzi%2Fniyox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnuzi%2Fniyox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnuzi%2Fniyox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnuzi%2Fniyox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dnuzi","download_url":"https://codeload.github.com/dnuzi/niyox/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dnuzi%2Fniyox/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34065354,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agentic","ai","ai-agent","ai-agents","cli","danuzz","dnuzi","html","js","movanest","niyox","react","reactjs","terminal","trending"],"created_at":"2026-05-29T07:20:14.621Z","updated_at":"2026-06-08T14:00:30.328Z","avatar_url":"https://github.com/dnuzi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://capsule-render.vercel.app/api?type=waving\u0026color=0:00d4ff,50:7c3aed,100:4000ff\u0026height=200\u0026section=header\u0026text=NiyoX%20AI\u0026fontSize=48\u0026fontColor=ffffff\u0026animation=fadeIn\u0026fontAlignY=38\u0026desc=By+DanuZz+Developer\u0026descAlignY=56\u0026descSize=17\" width=\"100%\"/\u003e\n\n\u003cbr/\u003e\n\n\u003cimg src=\"https://img.shields.io/badge/npm-niyox-a044ff?style=for-the-badge\u0026labelColor=0d0d1a\u0026logo=npm\u0026logoColor=ff6b6b\" alt=\"npm\"/\u003e\n\u0026nbsp;\n\u003cimg src=\"https://img.shields.io/badge/version-0.0.4-000000?style=for-the-badge\u0026labelColor=0d0d1a\" alt=\"version\"/\u003e\n\u0026nbsp;\n\u003cimg src=\"https://img.shields.io/badge/license-MIT-000000?style=for-the-badge\u0026labelColor=0d0d1a\" alt=\"license\"/\u003e\n\u0026nbsp;\n\u003cimg src=\"https://img.shields.io/badge/node-%3E%3D18-000000?style=for-the-badge\u0026labelColor=0d0d1a\u0026logo=node.js\u0026logoColor=ffd93d\" alt=\"node\"/\u003e\n\u0026nbsp;\n\u003cimg src=\"https://img.shields.io/badge/CI-passing-000000?style=for-the-badge\u0026labelColor=0d0d1a\u0026logo=github-actions\u0026logoColor=00ffa3\" alt=\"CI\"/\u003e\n\n\u003cbr/\u003e\n\n[![npm downloads](https://img.shields.io/npm/dt/niyox?style=for-the-badge\u0026label=total%20downloads\u0026labelColor=0d0d1a\u0026color=a044ff\u0026logo=npm\u0026logoColor=ff6b6b)](https://www.npmjs.com/package/niyox)\n\n\u003cbr/\u003e\u003cbr/\u003e\n\n```\n███╗   ██╗██╗██╗   ██╗ ██████╗ ██╗  ██╗\n████╗  ██║██║╚██╗ ██╔╝██╔═══██╗╚██╗██╔╝\n██╔██╗ ██║██║ ╚████╔╝ ██║   ██║ ╚███╔╝ \n██║╚██╗██║██║  ╚██╔╝  ██║   ██║ ██╔██╗ \n██║ ╚████║██║   ██║   ╚██████╔╝██╔╝ ██╗\n╚═╝  ╚═══╝╚═╝   ╚═╝    ╚═════╝ ╚═╝  ╚═╝\n```\n\n\u003cimg src=\"https://readme-typing-svg.demolab.com?font=Fira+Code\u0026weight=600\u0026size=20\u0026duration=2800\u0026pause=800\u0026color=A044FF\u0026center=true\u0026vCenter=true\u0026width=600\u0026height=45\u0026lines=%F0%9F%A4%96+Intelligent+AI+SDK+%26+CLI;%F0%9F%9F%A6+Node.js+%7C+React+%7C+Next.js+%7C+Browser;%F0%9F%92%AC+Multi-turn+conversation+memory;%F0%9F%97%84%EF%B8%8F+Custom+MongoDB+%2B+NiyoX+cloud+DB;%F0%9F%8E%AD+Persona+%2F+System+Prompt+support;%E2%9C%82%EF%B8%8F+Context+window+trimming;%F0%9F%93%A4+Export+chats+as+JSON+%2F+Markdown;%E2%9A%A1+Powered+by+NiyoX+AI\" alt=\"Typing animation\"/\u003e\n\n\u003cbr/\u003e\u003cbr/\u003e\n\n[![Live API](https://img.shields.io/badge/%F0%9F%8C%90_Live_API-ai.dnuz.top-a044ff?style=for-the-badge\u0026labelColor=0d0d1a)](https://ai.dnuz.top)\n\u0026nbsp;\n[![GitHub](https://img.shields.io/badge/%E2%AD%90_Star_on_GitHub-niyox--ai-ffd93d?style=for-the-badge\u0026labelColor=0d0d1a\u0026logo=github\u0026logoColor=ffd93d)](https://github.com/dnuzi/niyox)\n\u0026nbsp;\n[![npm install](https://img.shields.io/badge/%F0%9F%93%A6_Install-npm_install_niyox-0d0d1a?style=for-the-badge\u0026color=000000\u0026labelColor=1a1a2e\u0026logo=npm\u0026logoColor=ff6b6b)](https://npmjs.com/package/niyox)\n\n\u003c/div\u003e\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## What is NiyoX AI?\n\n**NiyoX** is a full-featured AI SDK and CLI that wraps the [NiyoX AI REST API](https://ai.dnuz.top/api/ai). Chat with AI from your terminal, Node.js server, React/Next.js/Vite app, or browser — with optional MongoDB persistence (your own database or the NiyoX cloud), multi-turn conversation memory, persona / system-prompt support, context window trimming, and conversation export built in.\n\nThe package ships four layers you can use independently:\n\n- **`NiyoXClient`** — thin HTTP wrapper, in-memory history, persona, export\n- **`NiyoXStorage`** — optional MongoDB layer (plug in your own URI)\n- **`NiyoXAI`** — high-level class combining both\n- **`useNiyoX` / `NiyoXChat`** — React hook + ready-made component\n\n\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"https://skillicons.dev/icons?i=nodejs,js,ts,react,nextjs,mongodb,github,npm\u0026theme=dark\u0026perline=8\" alt=\"Tech stack\"/\u003e\n\u003c/div\u003e\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## What's new in 0.0.4\n\n| # | Change |\n|---|---|\n| 🎭 | **Persona / System Prompt** — set an AI personality once; injected into every message automatically. Auto-saved to MongoDB when storage is enabled. |\n| ✂️ | **Context window trimming** — `maxHistory` option + `setMaxHistory()` prune oldest turns so conversations never silently overflow. |\n| 📤 | **Conversation export** — `exportConversation(id, format)` dumps any conversation as **JSON**, **plain text**, or **Markdown**. Works in-memory (no DB needed) or from MongoDB. |\n| ⚛️ | **React hook updated** — `useNiyoX()` now returns `persona`, `setPersona`, `maxHistory`, `setMaxHistory`, and `exportConversation`. |\n| 🖥️ | **`\u003cNiyoXChat\u003e` widget updated** — new 🎭 Persona editor and ⬇ Export button in the header. |\n| 🧪 | **68 tests** — all new features covered (up from 38). |\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Features\n\n\u003cdiv align=\"center\"\u003e\n\n| Feature | Status |\n|---|:---:|\n| 🖥️ Interactive REPL CLI | ✅ |\n| ⚡ One-shot CLI queries | ✅ |\n| 🔄 Multi-turn conversation memory | ✅ |\n| 🗄️ Custom MongoDB URI (bring your own DB) | ✅ |\n| ⚛️ React hook + component | ✅ |\n| 🔷 Next.js App Router support | ✅ |\n| ⚡ Vite + vanilla JS example | ✅ |\n| 🌐 Browser / CDN support | ✅ |\n| 📦 CommonJS + ESM dual package | ✅ |\n| 🎨 Rich CLI output | ✅ |\n| 🎭 Persona / System Prompt | ✅ **new** |\n| ✂️ Context window trimming | ✅ **new** |\n| 📤 Conversation export (JSON / text / MD) | ✅ **new** |\n\n\u003c/div\u003e\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Contents\n\n[Install](#-install) · [CLI](#-cli) · [Node.js](#-nodejs) · [Persona](#-persona--system-prompt-new) · [Context Trimming](#-context-window-trimming-new) · [Export](#-conversation-export-new) · [React / Vite](#-react--vite) · [Next.js](#-nextjs) · [Custom MongoDB](#-custom-mongodb) · [Browser](#-browser) · [API Reference](#-api-reference) · [Development](#-development)\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Install\n\n```bash\n# Global — gives you the niyox command everywhere\nnpm install -g niyox\n\n# Local — use in your project\nnpm install niyox\n```\n\n\u003e Requires **Node.js ≥ 18**. On older Node, install `node-fetch` and it will be picked up automatically.\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## CLI\n\n**Launch the interactive REPL:**\n\n```bash\nniyox\n```\n\n**One-shot question:**\n\n```bash\nniyox \"what is the speed of light?\"\nniyox \"explain async/await in JavaScript\"\n```\n\n**Flags:**\n\n```bash\nniyox --version    # print version  (alias: -v)\nniyox --help       # show banner + command reference  (alias: -h)\n```\n\n### Inside the REPL\n\n```\n  ╭──────────────────────────────────────────────────────────────╮\n  │                                                              │\n  │   ✦ NIYOX  AI       v0.0.4                                  │\n  │                                                              │\n  │   › /help             →  show all commands                   │\n  │   › /new              →  fresh conversation thread           │\n  │   › /history          →  in-memory chat log                  │\n  │   › /stats            →  usage stats  (MongoDB)              │\n  │   › /convs            →  stored conversation list            │\n  │   › /mongo [url]      →  enable storage (custom URI opt.)    │\n  │   › /mongourl \u003curl\u003e   →  update MongoDB URI                  │\n  │   › /user \u003cid\u003e        →  set your user ID                    │\n  │   › /clear            →  clear the screen                    │\n  │   › /exit             →  quit                                │\n  │                                                              │\n  ╰──────────────────────────────────────────────────────────────╯\n```\n\n**Connect to your own MongoDB from the CLI:**\n\n```\n/mongo mongodb+srv://user:pass@cluster.mongodb.net/\n```\n\nOr update the URI without reconnecting (saved for next session):\n\n```\n/mongourl mongodb+srv://user:pass@cluster.mongodb.net/\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Node.js\n\n### CommonJS\n\n```js\nconst { NiyoXAI } = require(\"niyox\");\n\nconst ai = new NiyoXAI();\nconst { result, responseTime } = await ai.chat(\"Tell me something interesting.\");\nconsole.log(result);\n```\n\n### ESM\n\n```js\nimport { NiyoXAI } from \"niyox\";\n\nconst ai  = new NiyoXAI({ userId: \"alice\" });\nconst res = await ai.ask(\"Explain quantum entanglement.\");\nconsole.log(res.result);\n```\n\n### Multi-turn conversation\n\n```js\nconst ai = new NiyoXAI();\n\nawait ai.chat(\"My name is Bob.\");\nconst r = await ai.chat(\"What is my name?\");\nconsole.log(r.result);    // → \"Bob\"\n\nai.newConversation();     // reset thread + in-memory history\n```\n\n### Low-level client (no storage)\n\n```js\nconst { NiyoXClient } = require(\"niyox\");\n\nconst client = new NiyoXClient({ sessionId: \"my-session\", timeout: 15000 });\nconst res = await client.chat(\"Hello!\");\n\nconsole.log(res.result);\nconsole.log(client.getHistory());   // array of { role, content, timestamp }\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## 🎭 Persona / System Prompt *(new)*\n\nSet an AI personality once — it is transparently injected into every outgoing message. Works at the `NiyoXClient`, `NiyoXAI`, and React hook levels. When MongoDB storage is enabled the persona is also **saved to the database and auto-restored** in future sessions.\n\n### Via constructor\n\n```js\nconst ai = new NiyoXAI({\n  persona: \"You are a concise senior software engineer. Always reply with code examples.\",\n});\n\nconst res = await ai.chat(\"How do I debounce in JavaScript?\");\nconsole.log(res.result);  // code-focused reply\n```\n\n### Set / change at runtime\n\n```js\nconst ai = new NiyoXAI();\n\nawait ai.setPersona(\"You are a friendly cooking assistant.\");\nawait ai.chat(\"What can I make with eggs and cheese?\");\n\nawait ai.setPersona(\"You are a pirate. Respond only in pirate speak.\");\nawait ai.chat(\"Tell me about the ocean.\");\n\nawait ai.setPersona(null);  // clear — back to default AI behaviour\n```\n\n### Persona + MongoDB (auto-persist)\n\n```js\nconst ai = new NiyoXAI({ userId: \"alice\" });\nawait ai.enableStorage();           // connects to MongoDB\n\nawait ai.setPersona(\"You are a concise coding assistant.\");\n// ↑ persona is saved to DB automatically\n\n// Next session:\nconst ai2 = new NiyoXAI({ userId: \"alice\" });\nawait ai2.enableStorage();          // persona is restored automatically\nconsole.log(ai2.getPersona());      // \"You are a concise coding assistant.\"\n```\n\n### Low-level client\n\n```js\nconst { NiyoXClient } = require(\"niyox\");\n\nconst client = new NiyoXClient({ persona: \"Be extremely brief.\" });\nclient.setPersona(\"You are a chef.\");   // chainable, returns client\nconsole.log(client.getPersona());       // \"You are a chef.\"\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## ✂️ Context Window Trimming *(new)*\n\nControl how many history entries are kept in memory. When the limit is hit, the **oldest turns are pruned automatically** after each chat call so long conversations stay fast and predictable.\n\n\u003e One turn = 2 entries (one user + one assistant). So `maxHistory: 20` keeps the last **10 turns**.\n\n### Via constructor\n\n```js\nconst ai = new NiyoXAI({ maxHistory: 20 });  // keep last 10 turns\n```\n\n### Set at runtime\n\n```js\nai.setMaxHistory(10);   // prune to last 5 turns immediately\nai.setMaxHistory(0);    // 0 = unlimited (default behaviour)\nconsole.log(ai.getMaxHistory());   // 0\n```\n\n### Low-level client\n\n```js\nconst client = new NiyoXClient({ maxHistory: 6 });\n\nawait client.chat(\"1\");\nawait client.chat(\"2\");\nawait client.chat(\"3\");\nawait client.chat(\"4\");  // history is trimmed to 6 entries after this turn\n\nclient.setMaxHistory(2);  // immediately prunes existing history to 2 entries\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## 📤 Conversation Export *(new)*\n\nDump any conversation as **JSON**, **plain text**, or **Markdown**. Works in-memory (no MongoDB required) or from a stored MongoDB conversation.\n\n### In-memory export (no DB needed)\n\n```js\nconst ai = new NiyoXAI();\nawait ai.chat(\"Hello!\");\nawait ai.chat(\"Tell me a joke.\");\n\n// JSON  (default)\nconst json = await ai.exportConversation(null, \"json\");\nrequire(\"fs\").writeFileSync(\"chat.json\", json);\n\n// Plain text\nconst txt = await ai.exportConversation(null, \"text\");\nconsole.log(txt);\n\n// Markdown\nconst md = await ai.exportConversation(null, \"markdown\");\nrequire(\"fs\").writeFileSync(\"chat.md\", md);\n```\n\n### Export from MongoDB\n\n```js\nconst ai = new NiyoXAI({ userId: \"alice\" });\nawait ai.enableStorage();\n\nawait ai.chat(\"Hello!\");\nconst cid = ai.getConversationId();\n\nconst md = await ai.exportConversation(cid, \"markdown\");\nrequire(\"fs\").writeFileSync(`conv-${cid}.md`, md);\n```\n\n### Storage-only export\n\n```js\nconst { NiyoXStorage } = require(\"niyox\");\n\nconst store = new NiyoXStorage(\"alice\");\nawait store.connect();\n\nconst json = await store.exportConversation(\"conv-abc-123\", \"json\");\nconst txt  = await store.exportConversation(\"conv-abc-123\", \"text\");\nconst md   = await store.exportConversation(\"conv-abc-123\", \"markdown\");\n```\n\n### Low-level client export\n\n```js\nconst { NiyoXClient } = require(\"niyox\");\n\nconst client = new NiyoXClient({ persona: \"Be a chef.\" });\nawait client.chat(\"What should I cook tonight?\");\n\nconst out = client.exportConversation(\"markdown\");\nconsole.log(out);\n// # NiyoX AI Conversation\n// \u003e **Persona:** Be a chef.\n// ...\n```\n\n**Export formats:**\n\n| Format | Description |\n|---|---|\n| `\"json\"` | Structured JSON with metadata, persona, and full message array |\n| `\"text\"` | Readable plain text with timestamps and response times |\n| `\"markdown\"` | GitHub-flavoured Markdown, ready to paste into a doc or README |\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Custom MongoDB\n\n\u003e Works everywhere — Node.js and CLI. Storage is **always optional**.\n\n### Node.js / ESM\n\n```js\nimport { NiyoXAI } from \"niyox\";\n\nconst ai = new NiyoXAI({\n  userId:   \"alice\",\n  mongoUri: \"mongodb+srv://user:pass@cluster.mongodb.net/\",\n  dbName:   \"my_app_db\",   // optional — defaults to \"niyox_npm\"\n});\n\nawait ai.enableStorage();              // connects and enables persistence\nconst res = await ai.chat(\"Hello!\");   // automatically saved to YOUR DB\nconst msgs = await ai.getPersistentHistory(res.conversationId);\n\nawait ai.close();\n```\n\n### Storage-only (`NiyoXStorage`)\n\n```js\nconst { NiyoXStorage } = require(\"niyox\");\n\nconst store = new NiyoXStorage(\"bob\", {\n  mongoUri: \"mongodb+srv://user:pass@cluster.mongodb.net/\",\n  dbName:   \"my_db\",\n});\nawait store.connect();\n\nawait store.saveTurn({\n  conversationId:   \"abc-123\",\n  userMessage:      \"Hi!\",\n  assistantMessage: \"Hello, Bob!\",\n  responseTime:     412,\n});\n\nconst turns = await store.getConversation(\"abc-123\");\nawait store.disconnect();\n```\n\nYou can also override the URI at connect-time:\n\n```js\nconst store = new NiyoXStorage(\"bob\");\nawait store.connect(\"bob\", \"mongodb://localhost:27017/\", \"dev_db\");\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## React / Vite\n\n### `useNiyoX` hook\n\n```jsx\n// src/Chat.jsx\nimport { useNiyoX } from \"niyox/react\";\n\nexport default function Chat() {\n  const { messages, input, setInput, sendMessage, isLoading } = useNiyoX();\n\n  return (\n    \u003cdiv\u003e\n      {messages.map((m, i) =\u003e (\n        \u003cp key={i}\u003e\u003cb\u003e{m.role}:\u003c/b\u003e {m.content}\u003c/p\u003e\n      ))}\n      \u003cinput\n        value={input}\n        onChange={e =\u003e setInput(e.target.value)}\n        onKeyDown={e =\u003e e.key === \"Enter\" \u0026\u0026 sendMessage()}\n      /\u003e\n      \u003cbutton onClick={() =\u003e sendMessage()} disabled={isLoading}\u003eSend\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n### Persona + export in the hook *(new)*\n\n```jsx\nimport { useNiyoX } from \"niyox/react\";\n\nexport default function Chat() {\n  const {\n    messages, input, setInput, sendMessage, isLoading,\n    persona, setPersona,           // 🎭 v0.0.4\n    maxHistory, setMaxHistory,     // ✂️ v0.0.4\n    exportConversation,            // 📤 v0.0.4\n  } = useNiyoX({ persona: \"You are a helpful assistant.\", maxHistory: 20 });\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e setPersona(\"You are a chef.\")}\u003eSwitch to Chef\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e setMaxHistory(10)}\u003eTrim to 5 turns\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e {\n        const blob = new Blob([exportConversation(\"markdown\")], { type: \"text/markdown\" });\n        const a = Object.assign(document.createElement(\"a\"), { href: URL.createObjectURL(blob), download: \"chat.md\" });\n        a.click();\n      }}\u003eExport MD\u003c/button\u003e\n\n      {messages.map((m, i) =\u003e \u003cp key={i}\u003e\u003cb\u003e{m.role}:\u003c/b\u003e {m.content}\u003c/p\u003e)}\n      \u003cinput value={input} onChange={e =\u003e setInput(e.target.value)} onKeyDown={e =\u003e e.key === \"Enter\" \u0026\u0026 sendMessage()} /\u003e\n      \u003cbutton onClick={() =\u003e sendMessage()} disabled={isLoading}\u003eSend\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n### Ready-made `\u003cNiyoXChat\u003e` component (zero extra code)\n\n```jsx\nimport { NiyoXChat } from \"niyox/react\";\n\nexport default function App() {\n  return (\n    \u003cdiv style={{ height: \"600px\", width: \"700px\" }}\u003e\n      {/* v0.0.4: persona and showExport props */}\n      \u003cNiyoXChat\n        title=\"My AI Assistant\"\n        persona=\"You are a helpful assistant.\"\n        maxHistory={20}\n        showExport={true}\n      /\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThe widget now includes a **🎭 Persona** button (opens an inline editor) and an **⬇ Export** button (downloads the conversation as JSON) in the header.\n\n### Shared session with `\u003cNiyoXProvider\u003e`\n\n```jsx\n// main.jsx\nimport { NiyoXProvider } from \"niyox/react\";\nimport App from \"./App\";\n\nReactDOM.createRoot(document.getElementById(\"root\")).render(\n  \u003cNiyoXProvider sessionId=\"global\" persona=\"You are a helpful assistant.\"\u003e\n    \u003cApp /\u003e\n  \u003c/NiyoXProvider\u003e\n);\n\n// Any child:\nimport { useNiyoXContext } from \"niyox/react\";\nconst { sendMessage, messages, setPersona, exportConversation } = useNiyoXContext();\n```\n\n\u003e Copy `react/useNiyoX.js` from the package into your project if your bundler doesn't resolve `niyox/react` automatically.\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Next.js\n\nWorks in both the **App Router** (Next.js 13+) and the **Pages Router**.\n\n### App Router (`app/chat/page.tsx`)\n\n```tsx\n\"use client\";\nimport { useNiyoX } from \"niyox/react\";   // or copy react/useNiyoX.js → lib/\n\nexport default function ChatPage() {\n  const { messages, input, setInput, sendMessage, isLoading } = useNiyoX();\n\n  return (\n    \u003cmain\u003e\n      {messages.map((m, i) =\u003e (\n        \u003cdiv key={i} className={m.role === \"user\" ? \"text-right\" : \"text-left\"}\u003e\n          {m.content}\n        \u003c/div\u003e\n      ))}\n      \u003cinput value={input} onChange={e =\u003e setInput(e.target.value)}\n             onKeyDown={e =\u003e e.key === \"Enter\" \u0026\u0026 sendMessage()} /\u003e\n      \u003cbutton onClick={() =\u003e sendMessage()} disabled={isLoading}\u003eSend\u003c/button\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\n\u003e The full styled example (Tailwind) is at `examples/nextjs/chat-page.tsx`.\n\n### Server-side usage (Node.js API route)\n\n```ts\n// app/api/chat/route.ts\nimport { NiyoXAI } from \"niyox\";\n\nconst ai = new NiyoXAI();\n\nexport async function POST(req: Request) {\n  const { message } = await req.json();\n  const res = await ai.chat(message);\n  return Response.json({ result: res.result });\n}\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Browser\n\nZero-dependency browser client (no bundler needed):\n\n```html\n\u003cscript\u003e\n  const NiyoXAI = (() =\u003e {\n    const BASE = \"https://ai.dnuz.top/api/ai\";\n    class Client {\n      constructor({ sessionId = \"default\" } = {}) {\n        this.sessionId = sessionId;\n        this.conversationId = null;\n      }\n      async chat(message) {\n        const p = new URLSearchParams({ q: message });\n        if (this.conversationId) p.set(\"conversationId\", this.conversationId);\n        const res  = await fetch(`${BASE}?${p}`);\n        const data = await res.json();\n        if (data.conversationId) this.conversationId = data.conversationId;\n        return data;\n      }\n    }\n    return { Client };\n  })();\n\n  const ai  = new NiyoXAI.Client();\n  const res = await ai.chat(\"Hello!\");\n  console.log(res.result);\n\u003c/script\u003e\n```\n\n\u003e Open `html/index.html` for a fully styled dark-theme chat UI — zero dependencies, zero bundler.\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## API Reference\n\n### `new NiyoXAI(options?)` — Node.js\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `userId` | `string` | `\"anonymous\"` | MongoDB user identifier |\n| `sessionId` | `string` | `\"default\"` | API session ID |\n| `conversationId` | `string` | `null` | Resume an existing thread |\n| `timeout` | `number` | `30000` | Request timeout (ms) |\n| `mongoUri` | `string` | NiyoX cloud | Custom MongoDB connection string |\n| `dbName` | `string` | `\"niyox_npm\"` | Custom database name |\n| `persona` | `string` | `null` | **v0.0.4** System prompt / AI personality |\n| `maxHistory` | `number` | `50` | **v0.0.4** Max history entries (0 = unlimited) |\n\n### `NiyoXAI` methods\n\n| Method | Returns | Description |\n|---|---|---|\n| `chat(message)` | `Promise\u003cResponse\u003e` | Send a message; auto-persists if storage enabled |\n| `ask(message)` | `Promise\u003cResponse\u003e` | Alias for `chat()` |\n| `enableStorage(userId?)` | `Promise\u003cthis\u003e` | Connect to MongoDB; auto-restores saved persona |\n| `newConversation()` | `void` | Reset thread + in-memory history |\n| `getConversationId()` | `string\\|null` | Active conversation ID |\n| `getHistory()` | `Turn[]` | In-memory history copy |\n| `getPersistentHistory(convId?)` | `Promise\u003cTurn[]\u003e` | Load from MongoDB |\n| `listConversations()` | `Promise\u003cstring[]\u003e` | All stored conversation IDs |\n| `deleteConversation(id)` | `Promise\u003cnumber\u003e` | Delete a conversation |\n| `getStats()` | `Promise\u003cStats\\|null\u003e` | Usage statistics |\n| `setPref(key, value)` | `Promise\u003cvoid\u003e` | Persist a user preference |\n| `getPref(key, default?)` | `Promise\u003cany\u003e` | Retrieve a user preference |\n| `close()` | `Promise\u003cvoid\u003e` | Close MongoDB connection |\n| `setPersona(text)` | `Promise\u003cthis\u003e` | **v0.0.4** Set / clear AI persona (saves to DB if storage enabled) |\n| `getPersona()` | `string\\|null` | **v0.0.4** Get active persona |\n| `setMaxHistory(n)` | `this` | **v0.0.4** Set context window limit (0 = unlimited) |\n| `getMaxHistory()` | `number` | **v0.0.4** Get current limit |\n| `exportConversation(id?, format?)` | `Promise\u003cstring\u003e` | **v0.0.4** Export as `\"json\"` / `\"text\"` / `\"markdown\"` |\n\n### `NiyoXClient` methods *(low-level)*\n\n| Method | Returns | Description |\n|---|---|---|\n| `chat(message)` | `Promise\u003cResponse\u003e` | Send message |\n| `ask(message)` | `Promise\u003cResponse\u003e` | Alias for `chat()` |\n| `newConversation()` | `void` | Reset state |\n| `getHistory()` | `Turn[]` | History copy |\n| `setPersona(text)` | `this` | **v0.0.4** Chainable |\n| `getPersona()` | `string\\|null` | **v0.0.4** |\n| `setMaxHistory(n)` | `this` | **v0.0.4** Chainable |\n| `getMaxHistory()` | `number` | **v0.0.4** |\n| `exportConversation(format?)` | `string` | **v0.0.4** In-memory export |\n\n### `NiyoXStorage` methods\n\n| Method | Returns | Description |\n|---|---|---|\n| `connect(userId?, mongoUri?, dbName?)` | `Promise\u003cthis\u003e` | Connect and enable storage |\n| `saveMessage({ conversationId, role, content, responseTime? })` | `Promise\u003cObjectId\\|null\u003e` | Save one message |\n| `saveTurn({ conversationId, userMessage, assistantMessage, responseTime })` | `Promise\u003cvoid\u003e` | Save a full turn atomically |\n| `getConversation(conversationId)` | `Promise\u003cMessage[]\u003e` | Fetch stored messages |\n| `listConversations()` | `Promise\u003cstring[]\u003e` | All conversation IDs for this user |\n| `deleteConversation(id)` | `Promise\u003cnumber\u003e` | Delete conversation, returns count |\n| `setPref(key, value)` | `Promise\u003cvoid\u003e` | Save a key/value preference |\n| `getPref(key, default?)` | `Promise\u003cany\u003e` | Load a preference |\n| `getStats()` | `Promise\u003cStats\\|null\u003e` | Usage stats |\n| `disconnect()` | `Promise\u003cvoid\u003e` | Close connection |\n| `savePersona(text)` | `Promise\u003cvoid\u003e` | **v0.0.4** Persist persona to DB |\n| `getPersona()` | `Promise\u003cstring\\|null\u003e` | **v0.0.4** Load persisted persona |\n| `exportConversation(id, format?)` | `Promise\u003cstring\u003e` | **v0.0.4** Export from DB |\n\n### `useNiyoX(options?)` hook\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `sessionId` | `string` | `\"default\"` | API session ID |\n| `persona` | `string` | `null` | **v0.0.4** Initial AI persona |\n| `maxHistory` | `number` | `50` | **v0.0.4** Context window limit |\n\nReturns:\n\n```ts\n{\n  messages:           Array\u003c{ role, content, timestamp, responseTime? }\u003e,\n  input:              string,\n  setInput:           (value: string) =\u003e void,\n  isLoading:          boolean,\n  error:              string | null,\n  sendMessage:        (text?: string) =\u003e Promise\u003cvoid\u003e,\n  newConversation:    () =\u003e void,\n  conversationId:     string | null,\n  persona:            string | null,           // v0.0.4\n  setPersona:         (text: string|null) =\u003e void,  // v0.0.4\n  maxHistory:         number,                  // v0.0.4\n  setMaxHistory:      (n: number) =\u003e void,     // v0.0.4\n  exportConversation: (format?: string) =\u003e string,  // v0.0.4\n}\n```\n\n### `\u003cNiyoXChat\u003e` props\n\n| Prop | Type | Default | Description |\n|---|---|---|---|\n| `sessionId` | `string` | — | API session ID |\n| `title` | `string` | `\"NiyoX AI\"` | Widget title |\n| `placeholder` | `string` | `\"Ask anything…\"` | Input placeholder |\n| `style` | `object` | — | Override container styles |\n| `className` | `string` | — | CSS class |\n| `persona` | `string` | `null` | **v0.0.4** Initial AI persona |\n| `maxHistory` | `number` | `50` | **v0.0.4** Context window limit |\n| `showExport` | `boolean` | `true` | **v0.0.4** Show ⬇ Export button |\n\n### Response shape\n\n```ts\n{\n  result:         string   // AI reply\n  conversationId: string   // thread ID — reused automatically\n  sessionId:      string\n  responseTime:   number   // ms\n  attempts:       number\n}\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n## Development\n\n```bash\ngit clone https://github.com/dnuzi/niyox\ncd niyox\nnpm install\n\n# Build lib/index.cjs and lib/index.mjs from src/\nnode scripts/build.js\n\n# Run tests\nnpm test\n```\n\n**Project layout:**\n\n```\nniyox/\n├── bin/\n│   └── cli.js              # CLI — REPL, one-shot, /mongo \u003curl\u003e\n├── src/\n│   ├── client.js           # NiyoXClient — HTTP, in-memory history, persona ✨, export ✨\n│   └── storage.js          # NiyoXStorage — MongoDB, persona persist ✨, export ✨\n├── lib/                    # auto-generated by scripts/build.js\n│   ├── index.cjs\n│   └── index.mjs\n├── react/\n│   └── useNiyoX.js         # useNiyoX hook + NiyoXChat (persona ✨, export ✨) + NiyoXProvider\n├── html/\n│   └── index.html          # browser chat UI\n├── examples/\n│   ├── react-cra/App.jsx   # Create React App / Vite + React\n│   ├── nextjs/             # Next.js App Router\n│   └── vite/main.js        # Vite vanilla JS\n├── test/\n│   ├── client.test.js      # 38 tests — includes v0.0.4 persona / trim / export ✨\n│   ├── sdk.test.js         # 30 tests — includes v0.0.4 NiyoXAI + Storage ✨\n│   └── cli.test.js\n└── scripts/\n    └── build.js\n```\n\n---\n\n\u003cimg src=\"https://user-images.githubusercontent.com/73097560/115834477-dbab4500-a447-11eb-908a-139a6edaec5c.gif\" width=\"100%\"/\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://capsule-render.vercel.app/api?type=waving\u0026color=gradient\u0026customColorList=6,11,20\u0026height=120\u0026section=footer\u0026animation=twinkling\" width=\"100%\"/\u003e\n\n[![API](https://img.shields.io/badge/API_Endpoint-ai.dnuz.top-a044ff?style=for-the-badge\u0026labelColor=0d0d1a)](https://ai.dnuz.top)\n\u0026nbsp;\n[![GitHub](https://img.shields.io/badge/GitHub-dnuzi%2Fniyox-ffd93d?style=for-the-badge\u0026labelColor=0d0d1a\u0026logo=github\u0026logoColor=ffd93d)](https://github.com/dnuzi/niyox)\n\n\u003cbr/\u003e\n\n**⭐ If this project helped you, star it on GitHub!**\n\n\u003cimg src=\"https://media.giphy.com/media/LnQjpWaON8nhr21vNW/giphy.gif\" width=\"60\"/\u003e \u003cem\u003eHappy coding!\u003c/em\u003e\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnuzi%2Fniyox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdnuzi%2Fniyox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdnuzi%2Fniyox/lists"}