NotiLens — unified SDK + CLI for AI agent notifications
Project description
NotiLens
Unified SDK + CLI for sending notifications from AI agents and any Python project to NotiLens.
Installation
pip install notilens
With framework auto-patching:
pip install notilens[openai]
pip install notilens[anthropic]
pip install notilens[all] # all frameworks
Quick Start
CLI (hooks, shell scripts)
# Register your agent
notilens init --agent my-agent --token YOUR_TOKEN --secret YOUR_SECRET
# Use in scripts / Claude Code hooks
notilens task.start --agent my-agent --task job_001
notilens task.complete "All done" --agent my-agent --task job_001
Python SDK
import notilens
agent = notilens.init(agent="my-agent", token="YOUR_TOKEN", secret="YOUR_SECRET")
agent.task_start(task_id="job_001")
agent.task_complete("All done", task_id="job_001")
Python SDK with auto-patching
import notilens
agent = notilens.init(
agent="my-agent",
token="YOUR_TOKEN",
secret="YOUR_SECRET",
patch=True, # auto-instruments OpenAI, Anthropic, LangChain, CrewAI, etc.
)
# Use OpenAI, Anthropic etc. normally — events fire automatically
Setup
Get your token and secret from the NotiLens dashboard.
CLI
notilens init --agent AGENT --token TOKEN --secret SECRET
SDK (env vars)
export NOTILENS_TOKEN=your_token
export NOTILENS_SECRET=your_secret
agent = notilens.init(agent="my-agent") # reads from env
Multiple agents
notilens init --agent scraper --token TOKEN_A --secret SECRET_A
notilens init --agent mailer --token TOKEN_B --secret SECRET_B
scraper = notilens.init(agent="scraper", token="TOKEN_A", secret="SECRET_A")
mailer = notilens.init(agent="mailer", token="TOKEN_B", secret="SECRET_B", patch=True)
Only one agent can own patch=True — raises if more than one tries.
CLI Reference
Task Lifecycle
notilens task.start --agent AGENT [--task ID]
notilens task.progress "msg" --agent AGENT --task ID
notilens task.loop "msg" --agent AGENT --task ID
notilens task.retry --agent AGENT --task ID
notilens task.stop --agent AGENT --task ID
notilens task.complete "msg" --agent AGENT --task ID
notilens task.error "msg" --agent AGENT --task ID
notilens task.fail "msg" --agent AGENT --task ID
notilens task.timeout "msg" --agent AGENT --task ID
notilens task.cancel "msg" --agent AGENT --task ID
notilens task.terminate "msg" --agent AGENT --task ID
Input / Human-in-the-loop
notilens input.required "Please confirm" --agent AGENT --task ID
notilens input.approve "Approved" --agent AGENT --task ID
notilens input.reject "Rejected" --agent AGENT --task ID
AI Response
notilens ai.response.generate "Summary ready" --agent AGENT --task ID
notilens ai.response.fail "Model error" --agent AGENT --task ID
Generic Event
Emit any custom event — works for non-AI projects too:
notilens emit user.registered "New signup" --agent AGENT
notilens emit disk.space.full "Only 2GB left" --agent AGENT
notilens emit order.placed "Order #1234" --agent AGENT
Agent Management
notilens agents # list configured agents
notilens remove-agent AGENT # remove an agent
notilens version
SDK Reference
notilens.init()
agent = notilens.init(
agent="my-agent", # agent name (required)
token="...", # NotiLens token (or NOTILENS_TOKEN env var)
secret="...", # NotiLens secret (or NOTILENS_SECRET env var)
patch=False, # auto-patch AI frameworks
min_level="info", # minimum event level to send
loop_threshold=10, # AI calls within loop_window before loop alert
loop_window=60.0, # time window in seconds for loop detection
call_timeout=30.0, # alert if AI call exceeds this many seconds
silent=False, # suppress SDK log output
debug=False, # enable verbose logging
)
Task Lifecycle
task_id = agent.task_start(task_id="job_001") # task_id auto-generated if omitted
agent.task_progress("Step 2 of 5", task_id=task_id)
agent.task_loop("Processing item 42", task_id=task_id)
agent.task_retry(task_id=task_id)
agent.task_stop(task_id=task_id)
agent.task_complete("Done", task_id=task_id)
agent.task_error("Step failed", task_id=task_id)
agent.task_fail("Unrecoverable", task_id=task_id)
agent.task_timeout("Exceeded limit", task_id=task_id)
agent.task_cancel("User cancelled", task_id=task_id)
agent.task_terminate("OOM", task_id=task_id)
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)
AI Response
agent.ai_response_generated("Summary ready", task_id=task_id)
agent.ai_response_failed("Model unavailable", task_id=task_id)
Generic Emit
agent.emit("user.registered", "New signup", meta={"plan": "pro"})
agent.emit("disk.space.full", "Only 2GB left", level="warning")
agent.emit("order.placed", "Order #1234", meta={"amount": 99.99})
CLI Options
| Flag | Description |
|---|---|
--agent NAME |
Agent name (required) |
--task ID |
Task ID (auto-generated if omitted) |
--level |
Override level: debug info warning error |
--meta key=value |
Custom metadata (repeatable) |
--image_url URL |
Attach an image |
--open_url URL |
Link to open |
--download_url URL |
Link to download |
--tags "tag1,tag2" |
Comma-separated tags |
--is_actionable true|false |
Override actionable flag |
--confidence 0.0-1.0 |
Confidence score |
Events
| Event | 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 |
ai.call.complete |
info | AI framework call finished |
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 |
Full Example
# Register
notilens init --agent summarizer --token my_token --secret my_secret
# Run a job
notilens task.start --agent summarizer --task job_42
notilens task.progress "Fetching document" --agent summarizer --task job_42
notilens task.complete "Summary ready" --agent summarizer --task job_42 \
--open_url https://example.com/summary.pdf \
--meta pages=12
import notilens
import openai
agent = notilens.init(
agent="summarizer",
token="my_token",
secret="my_secret",
patch=True,
)
task_id = agent.task_start()
try:
# OpenAI call tracked automatically
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarise this document..."}],
)
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)
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.0.tar.gz.
File metadata
- Download URL: notilens-0.2.0.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de36b626f685cb072bffce6ed7bac28e608da2193bf5ad5aedbb1303271f6761
|
|
| MD5 |
a13acf2238a990892c97666bf6a09f85
|
|
| BLAKE2b-256 |
312bacc161be387f6757ee8a052d123e04f0808518dfb8b9498c604632b97dfc
|
File details
Details for the file notilens-0.2.0-py3-none-any.whl.
File metadata
- Download URL: notilens-0.2.0-py3-none-any.whl
- Upload date:
- Size: 20.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 |
047ede0a070cf095a4163f323b9b49e5cc391f449e8ab7c5710407c21a674045
|
|
| MD5 |
52d30fc3b5ba8bb15f3398ecacc26e68
|
|
| BLAKE2b-256 |
dc4d05fdde9df153f67be5eec9ff6c2d9f46cec33df1289d03239a4ccc5f3f90
|