A self-hosted, provider-agnostic voice assistant that delivers structured executive briefings via voice or text from 10 LLM providers.
Project description
Àkàndé
A self-hosted, provider-agnostic voice assistant that delivers structured executive briefings from any of ten LLM providers — including fully private local inference via Ollama and LM Studio.
Contents
Getting started
- Install — PyPI, source, system dependencies
- Quick Start — voice briefing in ten lines
- Use as a library — programmatic API
Surface
- Provider configuration — ten providers behind one env var
- Profiles and modes —
AKANDE_PROFILEandAKANDE_MODE - Interaction modes — TUI, classic CLI, web server, MCP
- Subcommands —
data,verify-audit,mcp,install-local,skill - Skills — briefing, web search, weather, finance + consent policy
- Model Context Protocol — serve and consume MCP
Operational
- Compliance — EU AI Act Article 50 controls
- Troubleshooting
- Trust — test count, coverage, security gates
- Development
- License
Install
Prerequisites
| Dependency | Why | Ubuntu / Debian | macOS |
|---|---|---|---|
| Python 3.10+ | Runtime | sudo apt install python3.12 python3.12-venv |
brew install python@3.12 |
portaudio (optional) |
Microphone capture ([mic] extra) |
sudo apt install portaudio19-dev |
brew install portaudio |
ffmpeg |
Audio decoding | sudo apt install ffmpeg |
brew install ffmpeg |
From PyPI
# Core install — provider SDKs and mic capture are optional extras.
pip install akande
# Full kit: every provider + microphone + MCP.
pip install "akande[all,mic,mcp]"
From source
git clone https://github.com/sebastienrousseau/akande
cd akande
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" # bundles every test-relevant extra
Extras index
| Extra | Pulls in | Enables |
|---|---|---|
mic |
pyaudio |
Microphone capture (requires PortAudio system headers) |
anthropic / google / mistral / cohere / huggingface / groq |
the matching SDK | The corresponding LLM_PROVIDER value |
offline-tts |
pyttsx4 |
Offline TTS fallback |
tts-local |
kokoro-onnx |
Local Kokoro-82M TTS (AKANDE_TTS=kokoro) |
watermark |
audioseal, torch |
AudioSeal voice watermarking (Article 50 §2) |
redact |
presidio-analyzer |
Higher-recall PII redaction in the cache |
memory |
mem0ai |
Long-term memory façade |
mcp |
mcp |
Run / consume Model Context Protocol servers |
redis |
redis |
Distributed rate limiter for the web server |
all |
every provider SDK + pyttsx4 |
Provider-agnostic deployments |
dev |
testing + lint + audit + every provider SDK + mcp |
Local development |
Quick Start
# 1. Install the core + the mic extra.
pip install "akande[mic]"
# 2. Point at a provider.
export LLM_PROVIDER=openai
export OPENAI_API_KEY=sk-your-key-here
# 3. Launch the TUI.
akande
The TUI accepts spoken or typed questions and renders the briefing as it streams in. PDF and CSV artefacts are written to a date-keyed output directory on every answered question.
Use as a library
"""Ask Àkàndé a question programmatically."""
import asyncio
from akande.akande import Akande
from akande.providers import get_provider
async def main() -> None:
# 1. Pick any of the ten configured providers by name.
provider = get_provider("openai") # honours $OPENAI_API_KEY
akande = Akande(openai_service=provider)
# 2. Ask a question. The four-section briefing comes back as plain text.
question = "What is quantitative easing?"
response = await akande.openai_service.generate_response(
user_prompt=question,
system_prompt="You are an executive briefing assistant.",
model="gpt-4o-mini",
)
# 3. Print the structured briefing. `choices[0].message.content` follows
# the OpenAI-shaped response envelope used by every provider.
print(response.choices[0].message.content)
if __name__ == "__main__":
asyncio.run(main())
Provider configuration
Set LLM_PROVIDER in your environment (or .env file). Each provider reads
its own credentials from environment variables.
| Provider | LLM_PROVIDER |
Required env vars | Install | Default model |
|---|---|---|---|---|
| OpenAI | openai |
OPENAI_API_KEY |
(included) | gpt-3.5-turbo¹ |
| Anthropic | anthropic |
ANTHROPIC_API_KEY |
pip install akande[anthropic] |
claude-3-haiku-20240307 |
| Google Gemini | google |
GOOGLE_API_KEY |
pip install akande[google] |
gemini-pro |
| Mistral | mistral |
MISTRAL_API_KEY |
pip install akande[mistral] |
mistral-small-latest |
| Cohere | cohere |
COHERE_API_KEY |
pip install akande[cohere] |
command-r |
| Hugging Face | huggingface |
HUGGINGFACE_API_KEY |
pip install akande[huggingface] |
mistralai/Mistral-7B-Instruct-v0.2 |
| Groq | groq |
GROQ_API_KEY |
pip install akande[groq] |
llama3-8b-8192 |
| Azure OpenAI | azure_openai |
AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT |
(included) | gpt-35-turbo |
| Ollama | ollama |
OLLAMA_HOST (optional) |
(included) | llama3 |
| LM Studio | lmstudio |
LMSTUDIO_HOST (optional) |
(included) | local-model |
¹ Override per-call with the
modelargument or globally withOPENAI_DEFAULT_MODEL.
Install every provider SDK at once with pip install akande[all].
Profiles and modes
Àkàndé exposes two orthogonal sovereignty switches.
AKANDE_PROFILE selects the compliance posture:
| Profile | EU residency | Audio watermark | Audit signing | Telemetry opt-in |
|---|---|---|---|---|
local (default) |
— | off | off | off |
eu |
enforced | on | on | off |
strict |
enforced | on | on | off |
internal |
— | on | on | on (opt-in only) |
AKANDE_MODE selects the network posture:
| Mode | Provider gate | Cache writes |
|---|---|---|
online (default) |
any provider | enabled |
offline |
ollama or lmstudio only |
enabled |
# EU-residency-aware cloud setup
export AKANDE_PROFILE=eu AKANDE_MODE=online
# Fully air-gapped local stack
export AKANDE_PROFILE=strict AKANDE_MODE=offline LLM_PROVIDER=ollama
Interaction modes
| Mode | How to launch | What you get |
|---|---|---|
| TUI (default) | akande |
Textual chat UI with streaming, voice toggle, history, export |
| Classic CLI | akande --classic |
Numbered menu: voice, text, server, quit |
| Web server | akande → start server (or library Akande.start_server()) |
CherryPy server at http://127.0.0.1:8080 with SSE briefing endpoint |
| MCP server | akande mcp serve |
Expose Àkàndé as MCP tools (Claude Desktop, Cursor, Continue) |
| Library | from akande.akande import Akande |
Programmatic embedding |
Subcommands
akande --help # top-level help
akande --version # installed version
# GDPR data subject controls (export / delete)
akande data export --user alice --output alice.json
akande data delete --user alice --yes
# Audit verification — Ed25519-signed briefing sidecars
akande verify-audit path/to/briefing.audit.json
akande verify-pdf path/to/briefing.pdf
akande verify-watermark path/to/briefing.mp3 --threshold 0.5
# Model Context Protocol
akande mcp serve # stdio MCP server (Claude Desktop ready)
akande mcp serve --http # streamable HTTP transport
akande mcp list # list configured upstream servers
akande mcp list <server> # introspect a server's tools
# One-shot fully-offline bootstrap
akande install-local --model llama3.1 --env-path .env
# Skill management
akande skill list
akande skill enable web_search
akande skill consent web_search
akande skill revoke web_search
Skills
Skills are specialised handlers the router picks over a generic LLM call.
Five ship in the box; third-party skills register via the
akande.skills entry-point group.
| Skill | Match | Consent required | Offline-safe |
|---|---|---|---|
briefing |
default | no | yes |
web_search |
search, look up …, find … |
yes | no |
weather |
weather in …, forecast … |
no | no |
finance |
price of …, ticker … |
no | no |
policy (gate) |
always — enforces consent | n/a | yes |
"""Register a third-party skill via the entry-point group."""
# pyproject.toml
# [project.entry-points."akande.skills"]
# my_skill = "my_package.skill:MySkill"
from akande.skills.base import Skill, SkillMeta, Intent, SkillContext, SkillResult
class MySkill(Skill):
@property
def meta(self) -> SkillMeta:
return SkillMeta(
name="my_skill",
description="One-line description of what this skill does.",
requires_consent=True,
)
def match(self, text: str) -> Intent | None:
if text.lower().startswith("my-skill:"):
return Intent(name="my_skill", raw_text=text)
return None
def handle(self, intent: Intent, ctx: SkillContext) -> SkillResult:
return SkillResult(content=f"Handled: {intent.raw_text}")
Model Context Protocol
Àkàndé can serve and consume MCP. The server exposes the briefing,
audit, and skill surface as MCP tools; the client introspects upstream
servers configured in ~/.akande/mcp.json.
# Serve over stdio for Claude Desktop / Cursor / Continue.
akande mcp serve
# Or streamable HTTP for HTTP-only hosts.
akande mcp serve --http
Claude Desktop drop-in (claude_desktop_config.json):
{
"mcpServers": {
"akande": {
"command": "akande",
"args": ["mcp", "serve"]
}
}
}
Compliance
Àkàndé ships the controls required by EU AI Act Article 50 (in force
2026-08-02) out of the box when AKANDE_PROFILE=eu (or strict):
- AI disclosure — every briefing carries a machine-readable disclosure
block (
akande.disclosure) - AudioSeal watermark — synthesised audio is watermarked when the
[watermark]extra is installed; absence is logged but never blocks - Ed25519-signed audit sidecars — every PDF + CSV is paired with a
.audit.jsonsigned at write time;akande verify-auditre-verifies - GDPR data export / delete —
akande data export|deleteagainst the SQLite conversation store - Consent log — voice-cloning prompts require explicit consent recorded in the audit chain
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
Could not find PyAudio |
PortAudio system headers missing | Ubuntu: sudo apt install portaudio19-dev. macOS: brew install portaudio. Then pip install akande[mic]. |
ffmpeg not found |
ffmpeg not installed | Ubuntu: sudo apt install ffmpeg. macOS: brew install ffmpeg. |
| Microphone not detected | OS permissions | Grant microphone access in system settings. |
ModuleNotFoundError: No module named 'anthropic' |
Provider SDK not installed | pip install akande[anthropic] (or the relevant provider extra). |
Invalid or missing OPENAI_API_KEY |
Key not set or malformed | Ensure your environment or .env contains a valid sk- prefixed key. |
AKANDE_MODE=offline forbids provider openai |
Offline mode allows only local providers | Set LLM_PROVIDER=ollama or LLM_PROVIDER=lmstudio, or switch back to AKANDE_MODE=online. |
Trust
- 785 tests + 95 % line coverage in CI on every push and pull request, on Python 3.10 / 3.11 / 3.12
- Quality gates: ruff (lint + format), mypy (strict islands on the provider surface), bandit (SAST), pip-audit (vulnerable-deps scan) — all blocking
- Fresh-install regression matrix (Ubuntu × 3.10/3.11/3.12 + macOS × 3.12) reproduces the user install path on every push
- Security posture documented in SECURITY.md: CSP nonces, custom-header CSRF, per-IP rate limiting (in-memory or Redis), CSV-formula injection prevention, filename sanitisation, IP hashing in logs
Development
git clone https://github.com/sebastienrousseau/akande
cd akande
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Quality gates (mirror CI)
ruff check . && ruff format --check .
mypy akande
pytest -q # uses the [pytest] cov gate (95 %)
bandit -r akande
pip-audit
# Fresh-install regression on this machine
./scripts/regression.sh
See CONTRIBUTING.md for the full development loop.
License
Dual-licensed under the Apache License, Version 2.0 and the MIT License. See LICENSE-APACHE and LICENSE-MIT for the full text. You may pick whichever fits your project.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file akande-0.0.6.tar.gz.
File metadata
- Download URL: akande-0.0.6.tar.gz
- Upload date:
- Size: 191.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d56bae8702a536c852012bb3661c47ffba9a24bd0b4b09bfb7b90460fbbd1136
|
|
| MD5 |
3262b64a3919962a5f932f18cd823a8e
|
|
| BLAKE2b-256 |
c712ebb1cd6fa72c35dc514f5718c6970eb19d143129d901821b2ef416600f90
|
Provenance
The following attestation bundles were made for akande-0.0.6.tar.gz:
Publisher:
publish.yml on sebastienrousseau/akande
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
akande-0.0.6.tar.gz -
Subject digest:
d56bae8702a536c852012bb3661c47ffba9a24bd0b4b09bfb7b90460fbbd1136 - Sigstore transparency entry: 1822363214
- Sigstore integration time:
-
Permalink:
sebastienrousseau/akande@4db07e93822e763a04753186fe07e3d7d85e87de -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4db07e93822e763a04753186fe07e3d7d85e87de -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file akande-0.0.6-py3-none-any.whl.
File metadata
- Download URL: akande-0.0.6-py3-none-any.whl
- Upload date:
- Size: 162.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04843c9aa10c5bf52f3754d2c327287057138501131dad6cb0ce8395aba6a24d
|
|
| MD5 |
c62aae211bcff6350b75a0244c238c81
|
|
| BLAKE2b-256 |
e5db733f355c9b01a4612bc109944d5375b60a19f659f7ea0bb3b425aa7ec282
|
Provenance
The following attestation bundles were made for akande-0.0.6-py3-none-any.whl:
Publisher:
publish.yml on sebastienrousseau/akande
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
akande-0.0.6-py3-none-any.whl -
Subject digest:
04843c9aa10c5bf52f3754d2c327287057138501131dad6cb0ce8395aba6a24d - Sigstore transparency entry: 1822363234
- Sigstore integration time:
-
Permalink:
sebastienrousseau/akande@4db07e93822e763a04753186fe07e3d7d85e87de -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4db07e93822e763a04753186fe07e3d7d85e87de -
Trigger Event:
workflow_dispatch
-
Statement type: