Skip to main content

NotiLens — send alerts to NotiLens from Python scripts, apps, and AI agents

Project description

NotiLens

Send alerts to NotiLens from Python scripts, apps, and AI agents.

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


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 --name my-app --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 sources (each notifies a different topic):

notilens init --name scraper --token TOKEN_A --secret SECRET_A
notilens init --name mailer  --token TOKEN_B --secret SECRET_B

2. Notify

The simplest way to send a notification — no task or run context needed:

notilens notify order.placed    "Order #1234"      --name my-app
notilens notify disk.space.full "Only 2GB left"    --name my-app --level warning
notilens notify report.ready    "Report is ready"  --name my-app --download_url https://example.com/report.pdf

3. Commands

--task is a semantic label (e.g. email, report). Each task.start creates an isolated run internally — concurrent executions of the same label never conflict.

Task Lifecycle

notilens queue    --name my-app --task email
notilens start    --name my-app --task email
notilens progress "Fetching data"  --name my-app --task email
notilens loop     "Step 3 of 10"   --name my-app --task email
notilens retry    --name my-app --task email
notilens pause    "Rate limited"   --name my-app --task email
notilens resume   "Resuming"       --name my-app --task email
notilens wait     "Awaiting tool"  --name my-app --task email
notilens stop     --name my-app --task email
notilens complete "All done"       --name my-app --task email
notilens error    "Step 3 failed"  --name my-app --task email
notilens fail     "Unrecoverable"  --name my-app --task email
notilens timeout  "Took too long"  --name my-app --task email
notilens cancel   "User cancelled" --name my-app --task email
notilens terminate "Out of memory" --name my-app --task email

task.start prints the internal run_id to stdout. You can capture it if needed — but for sequential scripts, just use --task LABEL and the SDK handles the rest automatically.

Input / Human-in-the-loop

notilens input.required "Please confirm the output" --name my-app --task email
notilens input.approve  "Confirmed"                 --name my-app --task email
notilens input.reject   "Rejected by user"          --name my-app --task email

Output Events

notilens output.generate "Report ready"     --name my-app --task email
notilens output.fail     "Model unavailable" --name my-app --task email

Metrics

Pass any key=value pairs — numeric values accumulate across calls:

notilens metric tokens=512 cost=0.003 --name my-app --task email
notilens metric records=1500          --name my-app --task email

# Reset one metric
notilens metric.reset tokens --name my-app --task email

# Reset all metrics
notilens metric.reset --name my-app --task email

Custom Events

notilens track user.registered "New signup"      --name my-app
notilens track disk.space.full "Only 2GB left"   --name my-app
notilens track order.placed    "Order #1234"      --name my-app

4. Full CLI Example

# Register once
notilens init --name summarizer --token my_token --secret my_secret

# Run a job
notilens start --name summarizer --task report

notilens metric tokens=1024 --name summarizer --task report
notilens metric cost=0.004  --name summarizer --task report

notilens complete "Summary ready" \
  --name summarizer \
  --task report \
  --open_url https://example.com/summary.pdf \
  --meta pages=12

5. Claude Code Hooks Example

Register the agent once:

notilens init --name claude-code --token YOUR_TOKEN --secret YOUR_SECRET

Then in ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "",
      "hooks": [{
        "type": "command",
        "command": "notilens progress \"Using tool: $CLAUDE_TOOL_NAME\" --name claude-code --task $CLAUDE_SESSION_ID"
      }]
    }],
    "Stop": [{
      "matcher": "",
      "hooks": [{
        "type": "command",
        "command": "notilens complete \"Session ended\" --name claude-code --task $CLAUDE_SESSION_ID"
      }]
    }]
  }
}

CLI Options

Flag Required Description
--name NAME Yes Name identifying the source (agent, app, service, etc.)
--task LABEL For task commands Task label (semantic name, e.g. email, report)
--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
--force_send true|false No Bypass ML filtering. Default true for notify, fail, timeout, terminate, input.required, output.*


SDK

Use the SDK in Python projects. Supports manual task lifecycle calls and optional auto-patching of AI frameworks.

1. Setup (required)

import notilens

# token/secret can also come from NOTILENS_TOKEN / NOTILENS_SECRET env vars
nl = notilens.init(
    name="my-app",    # required — name identifying this source
    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
nl = notilens.init(name="my-app")  # reads token+secret from env

All init options:

nl = notilens.init(
    name="my-app",      # required
    token="...",           # required (or env var)
    secret="...",          # required (or env var)
    patch=False,           # optional — auto-patch AI frameworks (default: False)
    state_ttl=86400,       # optional — orphaned state TTL in seconds (default: 86400 / 24h)
    min_level="info",      # optional — minimum event level to send (default: "info")
    call_timeout=30.0,     # optional — alert if AI call exceeds N seconds (default: 30)
    debug=False,           # optional — verbose logging (default: False)
)

2. Notify

The simplest way to send a notification — no task or run context needed:

nl.notify("order.placed", "Order #1234")
nl.notify("disk.space.full", "Only 2GB left", level="warning")
nl.notify("report.ready", "Your report is ready",
    download_url="https://example.com/report.pdf",
    meta={"pages": 12},
    tags="report,weekly",
)
# force_send=True by default — pass False to route through ML
nl.notify("low.priority", "FYI only", force_send=False)

Also available on a run, without any run state attached:

run.notify("deploy.done", "Deployed to production",
    open_url="https://example.com/deploy/123",
)

3. Task Lifecycle

nl.task(label) creates a Run — an isolated execution context with its own state. Multiple concurrent runs of the same label never conflict.

run = nl.task("email")     # create a run for the "email" task
run.queue()                    # optional — pre-start signal
run.start()                    # begin the run

run.progress("Fetching data")  # mid-run update
run.loop("Processing item 42") # loop iteration marker
run.retry()                    # retry signal

# Pause / resume / wait (non-terminal)
run.pause("Rate limited")
run.resume("Resuming work")
run.wait("Waiting for tool response")

run.stop()                     # non-terminal stop

# Non-terminal error (run continues)
run.error("Step 3 failed, retrying")

# Terminal events — pick one to end the run
run.complete("All done")
run.fail("Unrecoverable error")
run.timeout("Exceeded time limit")
run.cancel("User cancelled")
run.terminate("OOM")

4. Input / Human-in-the-loop

run.input_required("Confirm before proceeding")
run.input_approved("User confirmed")
run.input_rejected("User rejected")

5. Output Events

run.output_generated("Summary ready")
run.output_failed("Model unavailable")

6. Metrics

Track any numeric or string values per run — accumulated automatically and included in every notification.

run.metric("tokens", 350)    # set
run.metric("tokens", 210)    # now 560 (numeric values accumulate)
run.metric("cost", 0.0012)
run.metric("records", 1500)
run.metric("model", "gpt-4") # strings are replaced, not accumulated

run.reset_metrics("tokens")  # reset one metric
run.reset_metrics()           # reset all metrics

Automatic Timing

NotiLens automatically tracks task timing. These fields are included in every notification's meta payload when non-zero:

Field Description
total_duration_ms Wall-clock time since start
queue_ms Time between queue and start
pause_ms Cumulative time spent paused
wait_ms Cumulative time spent waiting
active_ms Active time (total − pause − wait)

force_send

By default NotiLens routes notifications through ML-based filtering and relevance ranking. Set force_send=True to bypass ML and deliver immediately to the user.

Events/methods that default to force_send=True (high-signal events):

CLI command SDK method Default
notilens notify notify() True
notilens fail fail() True
notilens timeout timeout() True
notilens terminate terminate() True
notilens input.required input_required() True
notilens output.generate output_generated() True
notilens output.fail output_failed() True
Everything else Everything else False

All commands and methods accept force_send as an overridable parameter:

CLI:

# Override to go through ML
notilens fail "Error" --name my-app --task email --force_send false

# Override to bypass ML
notilens progress "Critical step" --name my-app --task email --force_send true

SDK:

# Override a default-True method to go through ML
run.fail("Unrecoverable error", force_send=False)

# Override a default-False method to bypass ML
run.progress("Critical checkpoint", force_send=True)

7. Custom Events

run.track("user.registered", "New signup", meta={"plan": "pro"})  # meta optional
run.track("disk.space.full", "Only 2GB left", level="warning")    # level optional
run.track("order.placed", "Order #1234", meta={"amount": 99.99})

8. 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

nl = notilens.init(
    name="my-app",
    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
)

# 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(name="scraper", token="TOKEN_A", secret="SECRET_A", patch=True)
mailer  = notilens.init(name="mailer",  token="TOKEN_B", secret="SECRET_B")
# patch=True on a second agent raises RuntimeError

9. Full SDK Example

import notilens

nl = notilens.init("summarizer", token="my_token", secret="my_secret")
run   = nl.task("report")
run.start()

try:
    run.progress("Fetching PDF")

    result = llm.complete(prompt)
    run.metric("tokens", result.usage.total_tokens)
    run.metric("cost", result.usage.cost)

    run.output_generated("Summary ready")
    run.complete("All done")

except Exception as e:
    run.fail(str(e))


Events Reference

Event Default Type Description
task.queued info Task queued
task.started info Task began
task.progress info Mid-run update
task.loop warning Loop iteration
task.retry warning Retry attempt
task.completed success Task finished successfully
task.stopped info Manually stopped
task.failed urgent Task failed
task.error urgent Non-fatal error
task.timeout urgent Exceeded time limit
task.cancelled warning Task cancelled
task.terminated urgent Force-terminated
task.paused warning Task paused
task.resumed info Task resumed
task.waiting warning Waiting for external response
output.generated success Output produced (AI response, report, file, etc.)
output.failed urgent Output generation failed
input.required warning Waiting for human input
input.approved success Input approved
input.rejected warning Input rejected

Requirements

  • Python >= 3.9

License

MIT — notilens.com

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

notilens-0.5.5.tar.gz (29.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

notilens-0.5.5-py3-none-any.whl (25.7 kB view details)

Uploaded Python 3

File details

Details for the file notilens-0.5.5.tar.gz.

File metadata

  • Download URL: notilens-0.5.5.tar.gz
  • Upload date:
  • Size: 29.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for notilens-0.5.5.tar.gz
Algorithm Hash digest
SHA256 9b6beddbdd9d6356f21c5973f721a0fe8cc16a364a9105626d2f236d0b34b032
MD5 c4c12c3ae49cc384a07be58965b72cbd
BLAKE2b-256 60a20c544d9576b662483a979cc1bd50311c92ed976f66315d88855c767f47cc

See more details on using hashes here.

File details

Details for the file notilens-0.5.5-py3-none-any.whl.

File metadata

  • Download URL: notilens-0.5.5-py3-none-any.whl
  • Upload date:
  • Size: 25.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for notilens-0.5.5-py3-none-any.whl
Algorithm Hash digest
SHA256 76452d87952db4822cf478cd424e00eff2c3b57b9218fd14a76a14b67bb49e07
MD5 56f8545d4c3211c73c6226e07e35639f
BLAKE2b-256 3ff3909381d0e266aba9cd9c879f5fa6d7dc9c234854477f442187c02855921f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page