NotiLens — unified SDK + CLI for AI agent notifications
Project description
NotiLens
Send notifications from AI agents and any Python project to NotiLens.
Two ways to use it — pick one or both:
- CLI — for shell scripts, Claude Code hooks, bash pipelines
- SDK — for Python projects, with optional AI framework auto-patching
Installation
pip install notilens
With AI framework auto-patching:
pip install notilens[openai] # OpenAI
pip install notilens[anthropic] # Anthropic
pip install notilens[langchain] # LangChain
pip install notilens[all] # all frameworks
CLI
Use the CLI in shell scripts, Claude Code hooks, or any terminal workflow.
1. Setup (required, one time)
Get your token and secret from the NotiLens dashboard.
notilens init --agent my-agent --token YOUR_TOKEN --secret YOUR_SECRET
This saves credentials to ~/.notilens_config.json. All future commands for this agent read from there — no need to pass token/secret again.
Multiple agents (each agent notifies a different topic):
notilens init --agent scraper --token TOKEN_A --secret SECRET_A
notilens init --agent mailer --token TOKEN_B --secret SECRET_B
2. Commands
Task Lifecycle
# required: --agent
# optional: --task (auto-generated if omitted)
notilens task.start --agent my-agent --task job_001
notilens task.progress "Fetching data" --agent my-agent --task job_001
notilens task.loop "Step 3 of 10" --agent my-agent --task job_001
notilens task.retry --agent my-agent --task job_001
notilens task.stop --agent my-agent --task job_001
notilens task.complete "All done" --agent my-agent --task job_001
notilens task.error "Step 3 failed" --agent my-agent --task job_001
notilens task.fail "Unrecoverable" --agent my-agent --task job_001
notilens task.timeout "Took too long" --agent my-agent --task job_001
notilens task.cancel "User cancelled" --agent my-agent --task job_001
notilens task.terminate "Out of memory" --agent my-agent --task job_001
Input / Human-in-the-loop
notilens input.required "Please confirm the output" --agent my-agent --task job_001
notilens input.approve "Confirmed" --agent my-agent --task job_001
notilens input.reject "Rejected by user" --agent my-agent --task job_001
AI Response
notilens ai.response.generate "Summary generated" --agent my-agent --task job_001
notilens ai.response.fail "Model unavailable" --agent my-agent --task job_001
Custom Events
Works for any project — AI or not:
notilens emit user.registered "New signup" --agent my-agent
notilens emit disk.space.full "Only 2GB left" --agent my-agent
notilens emit order.placed "Order #1234" --agent my-agent
3. Full CLI Example
# Register once
notilens init --agent summarizer --token my_token --secret my_secret
# Run a job
notilens task.start --agent summarizer --task job_42
notilens task.progress "Downloading document" --agent summarizer --task job_42
notilens task.complete "Summary ready" \
--agent summarizer \
--task job_42 \
--open_url https://example.com/summary.pdf \ # optional: link to open
--meta pages=12 # optional: custom key=value
4. Claude Code Hooks Example
Register the agent once:
notilens init --agent claude-code --token YOUR_TOKEN --secret YOUR_SECRET
Then in ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "notilens task.progress \"Using tool: $CLAUDE_TOOL_NAME\" --agent claude-code --task $CLAUDE_SESSION_ID"
}]
}],
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "notilens task.complete \"Session ended\" --agent claude-code --task $CLAUDE_SESSION_ID"
}]
}]
}
}
CLI Options
| Flag | Required | Description |
|---|---|---|
--agent NAME |
Yes | Agent name |
--task ID |
No | Task ID — auto-generated if omitted |
--level |
No | Override level: debug info warning error |
--meta key=value |
No | Custom metadata (repeatable) |
--image_url URL |
No | Attach an image |
--open_url URL |
No | Link to open |
--download_url URL |
No | Link to download |
--tags "tag1,tag2" |
No | Comma-separated tags |
--is_actionable true|false |
No | Override actionable flag |
--confidence 0.0-1.0 |
No | Confidence score |
SDK
Use the SDK in Python projects. Supports manual task lifecycle calls and optional auto-patching of AI frameworks.
1. Setup (required)
import notilens
# required: agent, token, secret
# token/secret can also come from NOTILENS_TOKEN / NOTILENS_SECRET env vars
agent = notilens.init(
agent="my-agent", # required — agent name
token="YOUR_TOKEN", # required — or set NOTILENS_TOKEN env var
secret="YOUR_SECRET" # required — or set NOTILENS_SECRET env var
)
Via environment variables:
export NOTILENS_TOKEN=your_token
export NOTILENS_SECRET=your_secret
agent = notilens.init(agent="my-agent") # reads token+secret from env
All init options:
agent = notilens.init(
agent="my-agent", # required
token="...", # required (or env var)
secret="...", # required (or env var)
patch=False, # optional — auto-patch AI frameworks (default: False)
min_level="info", # optional — minimum event level to send (default: "info")
loop_threshold=10, # optional — AI calls before loop alert (default: 10)
loop_window=60.0, # optional — loop detection window in seconds (default: 60)
call_timeout=30.0, # optional — alert if AI call exceeds N seconds (default: 30)
silent=False, # optional — suppress SDK log output (default: False)
debug=False, # optional — verbose logging (default: False)
)
2. Task Lifecycle
# task_id is optional — auto-generated if omitted
# task_start returns the task_id so you can reference it later
task_id = agent.task_start(task_id="job_001") # task_id optional
agent.task_progress("Step 2 of 5", task_id=task_id) # optional mid-run update
agent.task_loop("Processing item 42", task_id=task_id) # optional loop marker
agent.task_retry(task_id=task_id) # optional retry signal
# Terminal events — pick one to end the task
agent.task_complete("All done", task_id=task_id)
agent.task_fail("Unrecoverable error", task_id=task_id)
agent.task_timeout("Exceeded time limit", task_id=task_id)
agent.task_cancel("User cancelled", task_id=task_id)
agent.task_terminate("OOM", task_id=task_id)
agent.task_stop(task_id=task_id)
# Non-terminal error (task continues)
agent.task_error("Step 3 failed, retrying", task_id=task_id)
3. Input / Human-in-the-loop
agent.input_required("Confirm before proceeding", task_id=task_id)
agent.input_approved("User confirmed", task_id=task_id)
agent.input_rejected("User rejected", task_id=task_id)
4. AI Response
agent.ai_response_generated("Summary ready", task_id=task_id)
agent.ai_response_failed("Model unavailable", task_id=task_id)
5. Custom Events
Works for any project — AI or not:
agent.emit("user.registered", "New signup", meta={"plan": "pro"}) # meta optional
agent.emit("disk.space.full", "Only 2GB left", level="warning") # level optional
agent.emit("order.placed", "Order #1234", meta={"amount": 99.99})
6. Auto-patching AI Frameworks
Add patch=True to init() — no other changes needed. NotiLens will automatically track every AI call.
import notilens
import openai # or anthropic, langchain, crewai, pydantic-ai
agent = notilens.init(
agent="my-agent",
token="YOUR_TOKEN",
secret="YOUR_SECRET",
patch=True, # required to enable auto-patching
call_timeout=30.0, # optional — alert if any AI call takes longer than 30s
loop_threshold=10, # optional — alert if 10+ AI calls happen within loop_window
)
# From here, use OpenAI / Anthropic etc. normally.
# NotiLens fires ai.call.start, ai.call.complete, task.error, task.timeout, task.loop automatically.
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarise this..."}],
)
Multiple agents — only one can own patching:
scraper = notilens.init(agent="scraper", token="TOKEN_A", secret="SECRET_A", patch=True)
mailer = notilens.init(agent="mailer", token="TOKEN_B", secret="SECRET_B")
# patch=True on a second agent raises RuntimeError
7. Full SDK Example
import notilens
import openai
# --- Setup ---
agent = notilens.init(
agent="summarizer",
token="my_token", # or use NOTILENS_TOKEN env var
secret="my_secret", # or use NOTILENS_SECRET env var
patch=True, # auto-track all OpenAI calls
)
# --- Task ---
task_id = agent.task_start() # task_id auto-generated
try:
agent.task_progress("Downloading document", task_id=task_id)
# OpenAI call — ai.call.start + ai.call.complete fire automatically
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarise this document..."}],
)
# Manually signal the AI produced a useful response
agent.ai_response_generated(
response.choices[0].message.content[:80],
task_id=task_id
)
agent.task_complete("Summary done", task_id=task_id)
except Exception as e:
agent.task_fail(str(e), task_id=task_id)
Events Reference
| Event | Default Level | Description |
|---|---|---|
task.started |
info | Task began |
task.progress |
info | Mid-run update |
task.loop |
warning | Loop iteration |
task.retrying |
warning | Retry attempt |
task.completed |
info | Task finished successfully |
task.stopped |
info | Manually stopped |
task.failed |
error | Task failed |
task.error |
error | Non-fatal error |
task.timeout |
error | Exceeded time limit |
task.cancelled |
warning | Task cancelled |
task.terminated |
error | Force-terminated |
ai.call.start |
info | AI framework call began (auto-patch) |
ai.call.complete |
info | AI framework call finished (auto-patch) |
ai.response.generated |
info | AI produced a response |
ai.response.failed |
error | AI failed to respond |
input.required |
warning | Waiting for human input |
input.approved |
info | Input approved |
input.rejected |
warning | Input rejected |
License
MIT — notilens.com
Project details
Release history Release notifications | RSS feed
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 notilens-0.2.1.tar.gz.
File metadata
- Download URL: notilens-0.2.1.tar.gz
- Upload date:
- Size: 18.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
090360745f02d35ac5815e99df695010e2b04930e85b95d3fa7100eb3c686a77
|
|
| MD5 |
4b128d3dd777e8011520b6ad95a815b9
|
|
| BLAKE2b-256 |
f0e3ae868f53f1a2da6e617a1c26a54fc5b711324c47fc7eb49b800830ac76f3
|
File details
Details for the file notilens-0.2.1-py3-none-any.whl.
File metadata
- Download URL: notilens-0.2.1-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54108bf2044ffe2cc3f65a3707d37de14cb1981615f16f78be28e7d74d7fb748
|
|
| MD5 |
936d90e89424c62584061bd844cf5cb5
|
|
| BLAKE2b-256 |
d2891a6869c8981479d0e5bf74c3bf02f0637406ebfd5302d6ee79d48104400c
|