Observed since 20 Oct, 22:32 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agentkit-sdk
Developer SDK for building personalized voice AI assistants
A developer SDK for building personalized voice AI assistants with memory, learning, and mobile APK generation.
AgentKit is to personal AI assistants what Firebase is to apps — a complete backend + mobile shell that you configure once and deploy in under 30 minutes.
init, serve, build, deploy commands for the full lifecycle# Install
pip install agentkit-sdk
# Create a new agent project
agentkit init my-agent
# Enter the project
cd my-agent
# Add your API keys to .env
# Start the server
python main.py
Use AgentKit like any other Python SDK — import, instantiate, call:
import asyncio
from agentkit import Agent
agent = Agent(
persona="You are a helpful assistant",
llm_provider="gemini",
llm_api_key="your-gemini-key",
llm_model="gemini-2.0-flash",
)
async def main():
# Simple chat
response = await agent.chat("What is Python?")
print(response.text)
# Streaming response
async for token in agent.chat_stream("Tell me a joke"):
print(token, end="", flush=True)
await agent.close()
asyncio.run(main())
from agentkit import Agent
agent = Agent(
persona="You are a helpful assistant with tools",
llm_provider="gemini",
llm_api_key="your-key",
)
@agent.tool
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
return f"25°C and sunny in {city}"
@agent.tool
def calculate(expression: str) -> str:
"""Evaluate a math expression."""
return str(eval(expression))
If you prefer YAML configuration (generated by agentkit init):
from agentkit import Agent
agent = Agent.from_config("agent.config.yaml")
response = await agent.chat("Hello!")
# Or start the server
agent.serve(port=8000)
Process audio through STT → LLM → TTS:
from agentkit import Agent
agent = Agent(
persona="You are a voice assistant",
llm_provider="gemini",
llm_api_key="your-key",
stt_provider="sarvam",
stt_api_key="your-sarvam-key",
tts_provider="sarvam",
tts_api_key="your-sarvam-key",
)
# Process audio
with open("question.wav", "rb") as f:
response = await agent.process_audio(f.read())
print(response.text) # Transcription + answer
# response.audio # Spoken answer as WAV bytes
# Or get audio with text chat
response = await agent.chat("Hello!", include_audio=True)
with open("answer.wav", "wb") as f:
f.write(response.audio)
async with Agent(persona="...", llm_provider="gemini", llm_api_key="key") as agent:
response = await agent.chat("Hello!")
print(response.text)
# Resources automatically cleaned up
| Command | Description |
|---|---|
agentkit init <name> | Interactive project setup — picks providers, generates config |
agentkit serve | Start FastAPI server with playground |
agentkit serve --validate-only | Check config without starting |
agentkit build android | Build Android APK from AgentShell template |
agentkit deploy --platform railway | Deploy to Railway |
agentkit deploy --platform render | Deploy to Render |
agentkit deploy --platform docker | Generate Dockerfile |
When you run agentkit init my-agent, you get:
my-agent/
├── main.py ← Start here — run your agent
├── example.py ← Standalone code usage (no YAML)
├── agent.config.yaml ← Agent configuration
├── tools.py ← Custom tools for your agent
├── .env ← API keys
├── .env.example ← API key template
├── .gitignore
└── memory/ ← Conversation memory storage
The server starts at http://localhost:8000 with:
http://localhost:8000/playground — browser-based test UIws://localhost:8000/ws/voice — real-time voice/text endpointhttp://localhost:8000/health — server status checkPOST /api/chat — text chat endpointagent.config.yaml — generated by agentkit init:
agent:
name: my-agent
persona: "You are a helpful personal assistant..."
language: hinglish # english / hindi / hinglish
voice:
enabled: true
stt:
provider: sarvam # sarvam / deepgram
api_key: ${SARVAM_API_KEY}
tts:
provider: sarvam # sarvam / elevenlabs
voice: meera
api_key: ${SARVAM_API_KEY}
llm:
provider: gemini # gemini / openai
model: gemini-2.0-flash
api_key: ${GEMINI_API_KEY}
temperature: 0.7
memory:
type: markdown # markdown / vector
backend: local # local / qdrant
episodic_window: 20
semantic_top_k: 5
learning:
enabled: true
correction_detection: true
implicit_feedback: true
profile_extraction: true
deployment:
type: self-host
port: 8000
API keys are referenced as ${VAR_NAME} and resolved from your .env file at startup.
Every provider slot (STT, LLM, TTS, Memory) is pluggable. Use a built-in name or a dotted import path to your own class:
# Built-in provider
llm:
provider: gemini
# Custom provider — any class that extends BaseLLM
llm:
provider: my_package.llm.OllamaLLM
api_key: ${OLLAMA_API_KEY}
model: llama3
base_url: http://localhost:11434
Your custom class must extend the appropriate base class (BaseSTT, BaseLLM, BaseTTS, or BaseMemory). All config keys under the provider section are passed as constructor kwargs automatically.
Writing a custom LLM provider:
# my_package/llm.py
from agentkit import BaseLLM, Message
class OllamaLLM(BaseLLM):
def __init__(self, api_key: str, model: str = "llama3", base_url: str = "http://localhost:11434", **kwargs):
self.model = model
self.base_url = base_url
async def chat_stream(self, messages, system, memory_context=""):
# Your streaming implementation
...
async def chat(self, messages, system, memory_context=""):
# Your non-streaming implementation
...
async def close(self):
pass
Registering at runtime (alternative to dotted paths):
from agentkit import registry
from my_package.llm import OllamaLLM
registry.register("llm", "ollama", OllamaLLM)
# Now you can use provider: ollama in config
During agentkit init, select "custom" when prompted for a provider to enter your class path interactively.
| Category | Base Class | Built-in Providers |
|---|---|---|
| STT | BaseSTT | sarvam, deepgram |
| LLM | BaseLLM | gemini, openai |
| TTS | BaseTTS | sarvam, elevenlabs |
| Memory | BaseMemory | markdown, vector |
Add these to your .env file based on your chosen providers:
| Provider | Variable | Get it at |
|---|---|---|
| Sarvam AI | SARVAM_API_KEY | sarvam.ai |
| Gemini | GEMINI_API_KEY | aistudio.google.com |
| OpenAI | OPENAI_API_KEY | platform.openai.com |
| Deepgram | DEEPGRAM_API_KEY | deepgram.com |
| ElevenLabs | ELEVENLABS_API_KEY | elevenlabs.io |
Connect to ws://localhost:8000/ws/voice and exchange JSON messages:
Send text:
{"type": "text", "text": "Hello, what's the weather?"}
Send audio:
{"type": "audio", "data": [/* byte array */]}
Receive responses:
{"type": "audio", "data": "base64-encoded-audio"}
{"type": "text", "text": "The assistant's text response"}
{"type": "done"}
agentkit init → agent.config.yaml + main.py + .env
↓
python main.py / agentkit serve → FastAPI server
├── /ws/voice (WebSocket)
├── /api/chat (REST)
├── /playground (browser UI)
└── /health
↓
STT → LLM → TTS (streaming pipeline)
↕ ↕
Memory Learning
(md/vector) (corrections)
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check src/
MIT
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
fastapiEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | fastapi>=0.115.0 | runtime |
uvicornEnvironment{
"extras": "standard",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | uvicorn[standard]>=0.32.0 | runtime |
websocketsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | websockets>=14.0 | runtime |
pydanticEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pydantic>=2.10.0 | runtime |
pydantic-settingsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pydantic-settings>=2.6.0 | runtime |
python-dotenvEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | python-dotenv>=1.0.0 | runtime |
pyyamlEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pyyaml>=6.0.0 | runtime |
clickEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | click>=8.1.0 | runtime |
httpxEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | httpx>=0.27.0 | runtime |
google-genaiEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | google-genai>=1.0.0 | runtime |
openaiEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | openai>=1.50.0 | runtime |
qdrant-clientEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | qdrant-client>=1.12.0 | runtime |
numpyEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.26.0 | runtime |
tiktokenEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | tiktoken>=0.7.0 | runtime |
aiofilesEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | aiofiles>=24.0.0 | runtime |
tenacityEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | tenacity>=9.0.0 | runtime |
mcpEnvironment{
"extras": "",
"marker": "extra == \"mcp\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | mcp>=1.0.0; extra == "mcp" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pytest>=8.0.0; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio>=0.24.0; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | ruff>=0.8.0; extra == "dev" | runtime |
mypyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.11",
"evaluation": "NOT_EVALUATED"
} | mypy>=1.13.0; extra == "dev" | runtime |
bdist_wheel · 88.92 KB · Python py3
sdist · 76.55 KB · Python source
Observed since 20 Oct, 22:32 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:4e919a2e47a94578a47e888ee19ee74e400ee11ca5fd12490e029bac1dc5dac4