Skip to content

Latest commit

 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

πŸ€– NIRA β€” Personal AI Assistant

NIRA is a privacy-first personal AI assistant that bridges natural-language conversation with real tools and a workspace for your work. It runs a FastAPI backend and a polished, installable web UI (PWA) so it feels like a native desktop app. NIRA can talk β€” with natural, sentence-level streaming voice β€” and it can act: browse the web, run terminal commands, read/write files, check the weather, and search the internet, all orchestrated by an LLM through OpenRouter.

Built by Robel Biruk β€” pharmacy student and software developer passionate about AI, automation and human-centered interfaces.


✨ Features

πŸ’¬ Conversational Core

  • Multi-step tool orchestration β€” NIRA automatically decides which tools to call and chains them to complete a request.
  • Streaming responses β€” live Server-Sent Events (SSE) token/state streaming, with a visible "thinking β†’ executing β†’ speaking" core ring.
  • Conversation memory β€” every chat is saved as a named session. Memory is local-first (browser localStorage, so it always survives reloads) and synced to Firebase Firestore per anonymous user for cross-device access.
  • Inline session management β€” rename a session by clicking its title, delete via a confirmation popover (no browser alerts).
  • Personalized greeting β€” NIRA asks your name once (first-run modal) and greets you by time of day ("Hello you" for new users, "Hello {name}" after).
  • Automatic model fallback β€” when the active model hits a rate limit, NIRA seamlessly switches to another available free model.

πŸŽ™οΈ Voice

  • Speech-to-text β€” tap the core / mic to dictate; your speech is transcribed and sent as a message.
  • Natural text-to-speech β€” replies are spoken with sentence-level streaming via the browser's Web Speech API (keyless, no external dependency), prefetching the next sentence so there is no robotic pause between phrases. Tap the core to interrupt speech.
  • Voice toggle in the activity panel; mic state shown on the core ring.

πŸ“ Projects (workspace)

  • Group everything related to one goal into a Project: chats, memories, notes and research.
  • Create projects with a name, emoji icon and description.
  • Each project card shows πŸ’¬ chats / 🧠 memories / πŸ”¬ research counts + last active.
  • Tag the current chat to a project from the Project dropdown in Chat, so it appears under that project.
  • Projects persist locally and sync to Firebase (under users/{uid}/projects).

πŸ› οΈ Built-in Tools

Tool Description Example
open_browser Open a URL in your default browser "Open GitHub"
run_terminal_command Execute a shell command "What's my IP?"
read_file / write_file Read or create/modify files "Create a notes file"
list_directory List a directory's contents "What's in Documents?"
get_weather Current weather + forecast for a city (Open-Meteo with wttr.in fallback) "Weather in Paris?"
web_search Web search (Tavily, key from TAVILY_API_KEY env) "Find Python tutorials"
browser Browsing surface driven by the browser tool "Summarize this page"

Tools can also be invoked directly with slash commands (e.g. /tools, /clear, /model, /help) which bypass the LLM and call the backend.

πŸ–₯️ App Surfaces (UI pages)

  • Chat β€” the main conversation with the holographic AI core ring.
  • Memory β€” all saved sessions; resume, inline-rename, or delete.
  • Projects β€” workspace: create projects, see conversations/memories/notes/research grouped by project.
  • Browser β€” web browsing surface driven by the browser tool.
  • Research β€” research/summarization workflow.
  • About β€” project info: version, live statistics, creator, roadmap, license, contributing, and a scrolling footer marquee.
  • Settings β€” switch models, add/remove AI providers (OpenRouter + custom providers), and set per-tool API keys (Google, GitHub, Spotify, Tavily, …).

πŸ†• What's New

A snapshot of the capabilities added in the latest releases β€” smarter research, secure account connections, and a transparent "thinking" layer.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       NIRA β€” New Capabilities                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ ─
β”‚  🌐 Web        β”‚  Smart research agent β€” reads real pages, not just    β”‚
β”‚                β”‚  snippet dumps. Streams its steps, then answers.       β”‚
β”‚  πŸ”— OAuth      β”‚  One-click Connect for Google (Gmail), GitHub,         β”‚
β”‚                β”‚  Spotify β€” per-user tokens, nothing shared.            β”‚
β”‚  πŸ’‘ Reasoning  β”‚  Live "Thinking…" stream shown before the answer.      β”‚
β”‚  🧠 Memory     β”‚  Local-first + Firebase sync, fully private.           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🌐 Web β€” Smart Research

The web tool runs an autonomous fetch-based research loop: it searches, opens the most relevant result, reads the page, and synthesises a sourced answer β€” all within a 512 MB cloud footprint (no headless Chromium required).

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   search    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   pick + fetch   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Query β”‚ ──────────► β”‚  Search  β”‚ ───────────────► β”‚  Content  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚  (Tavily β”‚                  β”‚   Page    β”‚
       β–²                 β”‚  β†’ DDG β†’ β”‚                  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
       β”‚                 β”‚   Wiki)  β”‚                        β”‚ read
       β”‚   answer +      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β–Ό
       β”‚   source                                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       └─────────────────────────────────────────────│  Summarise β”‚
                                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   Output:  [web Β· fetch Β· N steps]  +  answer  +  πŸ”— source link
  • Resilient search chain β€” Tavily (datacenter-friendly) β†’ DuckDuckGo HTML β†’ Wikipedia fallback, so it always returns something useful.
  • Never loops β€” a step-limit safety net summarises the best page it found instead of failing.
  • Honest β€” cites the page it read; says so when a source doesn't fully cover the question.

πŸ”— OAuth Tool Connections

Connect real accounts from Settings β†’ Tool Connections β€” each user's tokens are isolated (no shared credentials), and every tool returns actionable errors (exact console fix) instead of raw exceptions.

   User ──Connect──► OAuth Consent ──token──► Encrypted per-user store
                                                      β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β–Ό                 β–Ό               β–Ό                 β–Ό
                 πŸ“§ Gmail          πŸ™ GitHub       🎧 Spotify      πŸ”‘ API keys
              (read/summarise)   (search/repos)   (search/play)   (Tavily, …)
Connection Unlocks Notes
Google gmail β€” list / read / summarise mail Add yourself as a Test User if the app is unverified
GitHub repo & code search Fine-grained token scopes
Spotify search & playback control Auto token via Client Credentials
API keys Tavily and other keyed tools Env vars win over Settings

πŸ’‘ Live Reasoning ("Thinking-first")

With a reasoning-capable model (e.g. deepseek/deepseek-r1:free, qwen/qwq-32b:free), NIRA shows its work before the answer β€” the wait becomes a live, pulsing "πŸ’‘ Thinking…" stream that quietly collapses once the reply begins.

   β”Œβ”€β”€ while reasoning ──┐          β”Œβ”€β”€ once answering ──┐
   β”‚  πŸ’‘ Thinking…       β”‚  ──────► β”‚  β–Έ πŸ’‘ Reasoning     β”‚  (collapsed)
   β”‚  Β· streams live     β”‚  answer  β”‚  ────────────────── β”‚
   β”‚  Β· gently pulses    β”‚  starts  β”‚  Answer leads ↴     β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Captured from native reasoning fields and inline <think> tags.
  • Purely additive β€” non-reasoning models simply don't show the block.

πŸ–ΌοΈ Favicons

NIRA ships a favicon set (not a single file) so every device picks the right resolution. They live in ui/public/ and are referenced from ui/index.html:

File Size Used for
favicon-16.png 16Γ—16 Small browser-tab icons, bookmark bars
favicon-32.png 32Γ—32 Standard desktop tab / taskbar
favicon-70.png 70Γ—70 Windows tiles / medium shortcuts
favicon-96.png 96Γ—96 High-DPI tabs, Android home-screen, apple-touch-icon

The browser's <link rel="icon" sizes="…"> + srcSet/sizes attributes let it choose the closest size for the current device pixel ratio β€” e.g. a 2Γ— Retina tab uses favicon-32.png (rendered at 16 CSS px), a 3Γ— phone uses favicon-96.png. The same set is reused in manifest.webmanifest (maskable + any-purpose) so the installed PWA icon looks crisp on phones and tablets. To change the logo, replace all four PNGs at the sizes above (keep the names) β€” no code change needed.

πŸ“¦ Progressive Web App (PWA)

  • Installable: "Install app" / "Add to Home Screen" from any modern browser.
  • Ships a manifest.webmanifest (display: standalone, orientation: any) and a service worker (sw.js) that caches the production bundle for offline use.
  • The service worker is registered only in production builds (dev keeps HMR clean).

πŸ”Œ Extensibility

  • Model selection at runtime; any OpenRouter-compatible model works.
  • Custom providers β€” add your own OpenAI-compatible endpoints in Settings.
  • Tool API keys β€” supply keys for external tools (e.g. Tavily) via the TAVILY_API_KEY env var or Settings; environment variables take priority.
  • Feature toggles β€” enable/disable capabilities from the activity panel (persisted server-side).
  • Adding a tool is a matter of subclassing Tool in tools/ and registering it.

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          NIRA Architecture                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Web UI    β”‚    β”‚  FastAPI    β”‚    β”‚     Assistant       β”‚  β”‚
β”‚  β”‚   (React)   │◄──►│  Backend    │◄──►│   (Core Brain)     β”‚  β”‚
β”‚  β”‚  (PWA/Vite) β”‚    β”‚  app.py     β”‚    β”‚  core/assistant.py β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                                   β”‚              β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚                    β”‚            AI Client (OpenRouter)        β”‚  β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                                   β”‚                              β”‚
β”‚           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚           β”‚                       β”‚                       β”‚      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Memory        β”‚    β”‚   Tool Manager   β”‚    β”‚  Runtime    β”‚   β”‚
β”‚  β”‚ (localStorage + β”‚    β”‚  (router/registry)β”‚   β”‚  (State)    β”‚   β”‚
β”‚  β”‚   Firestore)    β”‚    β”‚                   β”‚   β”‚             β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚           β”‚                       β”‚                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚  Preferences    β”‚    β”‚  Tool Registry   β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                        β”‚                          β”‚
β”‚        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
β”‚        β”‚  Browser   β”‚  β”‚  Terminal  β”‚  β”‚  Weather    β”‚  ...      β”‚
β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

User Request β†’ FastAPI Endpoint β†’ Assistant β†’ AI Model + Tools β†’ Response
                ↑                                    ↓
            Streaming (SSE)                    Memory / Speech
                ↑                                    ↓
            Web UI (PWA) β†β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Memory storage: chat sessions and the user's name are stored local-first in localStorage (guaranteed to work, survives reloads) and mirrored to Firebase Firestore (users/{uid}/sessions, users/{uid}/projects) when an anonymous account is available. Anonymous auth + per-uid Firestore rules keep data private.


πŸš€ Quick Start

Prerequisites

  • Python 3.11+
  • Node.js 18+
  • Git

1. Clone

git clone https://github.com/Robibiruk/Nira-AI-Assistant.git
cd Nira-AI-Assistant

2. Backend

python -m venv .venv
# Windows
.\.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate

pip install -r requirements.txt

3. API key

Get a free key at OpenRouter and set it:

# PowerShell
$env:OPENROUTER_API_KEY="sk-or-..."
# macOS / Linux
export OPENROUTER_API_KEY="sk-or-..."

Or put it in config/settings.yaml under openrouter.api_key (see config/settings.yaml.example).

4. Run the backend

uvicorn app:app --reload

Backend serves on http://127.0.0.1:8000 (and serves the built UI in production).

5. Frontend (dev)

cd ui
npm install
npm run dev      # http://127.0.0.1:5173 (proxies API to :8000)

6. Production build + serve

cd ui
npm run build    # outputs ui/dist
cd ..
uvicorn app:app --reload   # serves the built PWA at http://127.0.0.1:8000

Install as an app (PWA)

In a supported browser, open the running app and choose Install app / Add to Home Screen. The service worker caches the bundle so NIRA works offline.


πŸ“‘ API Endpoints

Core

Method Endpoint Description
GET /health Health check
POST /chat Non-streaming chat (final reply)
POST /chat/stream Streaming chat (SSE: meta β†’ state β†’ tool_result β†’ message)
POST /tools/run Run a tool directly (slash commands)

Config & State

Method Endpoint Description
GET /models List available models
POST /models/select Switch active model
GET /providers List configured providers
GET/POST /providers/custom List / add / remove custom providers
GET/POST /tools/keys List / set per-tool API keys
GET/POST /features List / toggle feature flags
GET /status Runtime status readout

Tools & Weather

Method Endpoint Description
GET /desktop Installed apps, running windows, browser tabs (desktop builds)
POST /desktop/action Focus / close / launch a desktop app
POST /speak Text-to-speech synthesis (browser TTS on the client)

Sessions/name are managed client-side (localStorage + Firebase), so there are no /sessions server endpoints β€” the backend is stateless for chat history.


βš™οΈ Configuration

config/settings.yaml (copy config/settings.yaml.example β†’ config/settings.yaml):

openrouter:
  api_key: sk-or-...        # or set OPENROUTER_API_KEY env var
model: poolside/laguna-m.1:free
voice: true                 # enable voice (speech-to-text + TTS)
temperature: 0.7
tools:
  enabled: []               # empty = all tools enabled

Environment Variables

Variable Description Default
OPENROUTER_API_KEY OpenRouter API key (required) β€”
TAVILY_API_KEY Tavily web-search key (optional; env wins over config) β€”
SPOTIFY_CLIENT_ID Spotify app Client ID β€” enables automatic token fetch (Client Credentials) so Spotify works without manual tokens and survives restarts β€”
SPOTIFY_CLIENT_SECRET Spotify app Client Secret (paired with SPOTIFY_CLIENT_ID) β€”
SPOTIFY_API_KEY Optional: a manually-pasted Spotify access token (overrides the above). Short-lived β€” refresh when requests 401 β€”
UVICORN_HOST FastAPI host 127.0.0.1
UVICORN_PORT FastAPI port 8000

Frontend env (Vercel / ui/.env)

Variable Description
VITE_API_BASE Backend base URL (e.g. https://nira-ai-backend.onrender.com)
VITE_FIREBASE_API_KEY Firebase web API key (the only Firebase value wired to env; others are hardcoded in firebase.js)

πŸ“¦ Project Structure

Nira-AI-Assistant/
β”œβ”€β”€ app.py                  # FastAPI entry point (also serves built UI)
β”œβ”€β”€ config.py               # Configuration loader
β”œβ”€β”€ requirements.txt        # Python dependencies
β”œβ”€β”€ ai/                     # AI client + model registry
β”‚   β”œβ”€β”€ openrouter.py       # OpenRouter client
β”‚   β”œβ”€β”€ provider.py         # Provider abstraction
β”‚   β”œβ”€β”€ providers.py        # Built-in + custom providers
β”‚   └── models.py           # Model ids / defaults
β”œβ”€β”€ core/                   # Assistant brain
β”‚   β”œβ”€β”€ assistant.py        # Orchestration (stateless; gets history from client)
β”‚   β”œβ”€β”€ planner.py          # Multi-step planning
β”‚   β”œβ”€β”€ prompts.py          # System prompt / personality
β”‚   β”œβ”€β”€ runtime.py          # Runtime state
β”‚   └── router.py           # Tool manager + registry
β”œβ”€β”€ tools/                  # Built-in tools
β”‚   β”œβ”€β”€ base.py             # Tool contract
β”‚   β”œβ”€β”€ browser.py          # Browser
β”‚   β”œβ”€β”€ files.py            # File read/write
β”‚   β”œβ”€β”€ search.py           # Web search (Tavily)
β”‚   β”œβ”€β”€ terminal.py         # Terminal
β”‚   β”œβ”€β”€ weather.py          # Weather (Open-Meteo + wttr.in fallback)
β”‚   └── _keys.py            # Per-tool key resolution (env > config)
β”œβ”€β”€ ui/                     # Vite + React PWA frontend
β”‚   β”œβ”€β”€ public/             # manifest.webmanifest, sw.js, icons, Me.jpg, fontawesome/
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ App.jsx         # App shell + routing
β”‚   β”‚   β”œβ”€β”€ main.jsx        # Entry + SW registration
β”‚   β”‚   β”œβ”€β”€ index.css       # Global styles
β”‚   β”‚   β”œβ”€β”€ api.js          # API client
β”‚   β”‚   β”œβ”€β”€ firebase.js     # Firebase init + per-uid Firestore helpers
β”‚   β”‚   β”œβ”€β”€ memoryStore.js  # Local-first localStorage persistence
β”‚   β”‚   β”œβ”€β”€ utils.js         # Shared helpers (date formatting)
β”‚   β”‚   β”œβ”€β”€ hooks/          # useNira, useVoice
β”‚   β”‚   └── components/     # Chat, Memory, Projects, Browser, Research,
β”‚   β”‚                       #   About, Settings, Sidebars, …
β”‚   β”œβ”€β”€ package.json
β”‚   └── vite.config.js
β”œβ”€β”€ config/
β”‚   └── settings.yaml       # User config (git-ignored)
└── firestore.rules         # Per-uid Firestore security rules

🧭 Roadmap

Completed

  • Smart Chat
  • Browser
  • File Tools
  • Research
  • Voice Assistant
  • Projects (workspace)
  • Local-first memory + Firebase sync

Upcoming

  • Plugin Marketplace
  • Mobile App
  • Multi-Agent System
  • Smart Home Integration
  • File/image attachments inside Projects (Firebase Storage ready)

🌟 Supported Models

NIRA works with any free, tool-capable OpenRouter model. Verified (as of July 2026):

Model ID Context
Laguna M.1 poolside/laguna-m.1:free 128,000
Llama 3.3 70B meta-llama/llama-3.3-70b-instruct:free 128,000
Gemma 4 31B google/gemma-4-31b-it:free 128,000
GPT-OSS 120B openai/gpt-oss-120b:free 128,000
Qwen3 Coder qwen/qwen3-coder:free 128,000

Automatic fallback switches models on rate-limit.


πŸ”§ Customization

Add a tool

  1. tools/my_tool.py subclassing Tool (from tools.base):
    from .base import Tool
    class MyTool(Tool):
        name = "my_tool"
        description = "What it does."
        parameters = {"q": {"type": "string", "description": "Query"}}
        required = ["q"]
        def run(self, q: str) -> str:
            return f"Result: {q}"
    my_tool = MyTool()
  2. Export it in tools/__init__.py and add to ALL_TOOLS in core/router.py.

Custom system prompt

Edit core/prompts.py.

Custom providers

Add an OpenAI-compatible endpoint in Settings β†’ Providers (no code needed).


πŸ›‘οΈ Security

  • Terminal execution runs arbitrary shell commands β€” only enable for trusted local use; restrict via tools.enabled.
  • API keys are never committed (see .gitignore); use env vars or config/settings.yaml (git-ignored).
  • File tools can read/write any file on your system.
  • Memory is private β€” chat history lives in your browser (localStorage) and your own Firebase account (anonymous auth, per-uid Firestore rules). No shared server database.

πŸ“„ License & Contributing

NIRA is open source under the MIT License β€” see LICENSE.md. Contributions are welcome β€” see CONTRIBUTING.md.


πŸ”— Connect

Made with πŸ’™ by Robel Biruk β€” Β© 2026 Nira AI.

About

Locally-hosted, privacy-first AI assistant with a FastAPI backend and a React PWA frontend. NIRA chats with streaming voice, orchestrates tools (browser, terminal, files, weather, search), and can control your desktop β€” all through any free tool-capable OpenRouter model.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages