8000 docs: list backups, HTTPS, the graph as files, every clipper and the Costs page as built by adeelahmad · Pull Request #312 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 50 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ what their person may read.
Everything below is built and merged. Work that is still in a pull request is listed under [In progress](#in-progress).

- **Connect your agents.** Lens is an MCP server (`/mcp`): agents search, read transcripts and notes, cite exact
moments, find similar content, explore the graph with Cypher or SPARQL, suggest topics and graph changes, write
notes and make podcasts. They sign in with OAuth and see only what their person may read. The web app runs on the
same REST API, so an agent can do what a person can. ([MCP](docs/mcp.md), [API](docs/api.md),
[authentication](docs/authentication.md))
moments, find similar content, explore the graph with Cypher or SPARQL or browse it as folders and files, suggest
topics and graph changes, write notes and make podcasts. They sign in with OAuth and see only what their person may
read. The web app runs on the same REST API, so an agent can do what a person can. ([MCP](docs/mcp.md),
[API](docs/api.md), [the graph as files](docs/file-tree.md), [authentication](docs/authentication.md))
- **Ask, and get cited answers.** A chat assistant on every page, and by voice, searches your archive by words (and by
meaning, once turned on), reads it, cites it and acts on it with tools: changes wait for your approval. It reads
answers aloud with a speech provider, macOS `say` or StyleTTS2 on this machine (opt-in), and `/clear` and
Expand All @@ -76,9 +76,9 @@ Everything below is built and merged. Work that is still in a pull request is li
at a time, from a link list or a bookmarks file, by API or by asking the assistant; and import videos and podcasts
by their address (off by default). ([Sensors](docs/sensors.md), [processing](docs/processing.md),
[import webhooks](docs/api.md#import-webhooks))
- **Clip from the browser.** The Lens web clipper for Chrome and Edge turns a page, a selection or a part you pick
into Markdown, lets you look it over, and sends it to a namespace. New releases reach installed copies through the
browser's store or a self-hosted update file. ([Web clipper](docs/web-clipper.md))
- **Clip from the browser.** The Lens web clipper for Chrome, Edge, Firefox and Safari turns a page, a selection or a
part you pick into Markdown, lets you look it over, and sends it to a namespace. New releases reach installed copies
through the browser's store or a self-hosted update file. ([Web clipper](docs/web-clipper.md))
- **Understand it.** Transcription and speaker recognition, text on screen and in scans, faces (off by default),
entities and topics in a knowledge graph you can explore, query with Cypher or SPARQL, and see as it was at any
version. Find what is most like a passage, a resource, a topic or an entity, and, with anytopdf, photos by what
Expand All @@ -92,17 +92,24 @@ Everything below is built and merged. Work that is still in a pull request is li
notifications to webhooks, Slack, Discord and Matterbridge. ([Processing](docs/processing.md#workflows-and-the-canvas),
[notifications](docs/notifications.md))
- **See what it costs.** An activity ledger of every model call, with reports, charts and budgets that cap spending.
Routine choices go to small decision models, and the LLM only when it adds value.
([Activity and costs](docs/activity.md), [budgets](docs/budgets.md))
- **Keep it up to date.** Lens checks for a new release once a day and tells admins by email or a notification. It
backs the database up and upgrades itself through a small agent on the machine, from the API; nothing installs
unless an admin says so. ([Updates and backups](docs/updates.md))
A meter in the top bar shows spend against the budget, and the Costs page breaks it down by day, model and what
cost most. Routine choices go to small decision models, and the LLM only when it adds value.
([Activity and costs](docs/activity.md), [budgets](docs/budgets.md#the-meter-and-the-costs-page))
- **Keep it up to date and backed up.** Lens checks for a new release once a day and tells admins by email or a
notification, and upgrades itself through a small agent on the machine, from the API; nothing installs unless an
admin says so. Settings › Backups takes, lists, downloads and restores backups, takes them on any schedule, and can
copy each one, encrypted, to a storage connection. ([Updates and backups](docs/updates.md),
[backups](docs/database.md#backups))
- **Watch it run.** OpenTelemetry traces and metrics, and a Prometheus `/metrics` endpoint with a sample Grafana
dashboard. Both are off until you turn them on, and send nothing anywhere else. ([Telemetry](docs/telemetry.md),
[Prometheus](docs/prometheus.md))
- **Stay private.** Passkeys instead of passwords, encryption at rest with a key per namespace, roles per namespace,
and an audit log. Reach it from anywhere through a Cloudflare tunnel with nothing opened on your router.
([Encryption](docs/encryption.md), [access](docs/access.md), [remote access](docs/remote-access.md))
- **Stay private and safe.** Passkeys instead of passwords, encryption at rest with a key per namespace, roles per
namespace, and an audit log. What the assistant reads (recordings, documents, web pages, tool results) reaches the
model marked as data, not instructions. Serve Lens over HTTPS with certificates from Let's Encrypt or ZeroSSL that
renew on their own, or reach it from anywhere through a Cloudflare tunnel with nothing opened on your router.
([Encryption](docs/encryption.md), [access](docs/access.md),
[prompt injection](docs/assistant.md#third-party-content-and-prompt-injection), [HTTPS](docs/https.md),
[remote access](docs/remote-access.md))
- **Use open standards.** RDF and Dublin Core for every record, OAuth for apps and OpenID Connect sign-in, MCP, MQTT,
IIIF for publishing, OpenTelemetry and Prometheus for monitoring, and an optional Fedora mirror.
([Linked data](docs/rdf.md), [IIIF](docs/iiif.md), [Fedora](docs/fedora.md))
Expand All @@ -120,26 +127,33 @@ These are open pull requests, not yet part of Lens:

- **Chat channels:** a built-in Telegram bot on a shared channel layer
([#293](https://github.com/adeelahmad/lense/pull/293)) and a Slack app over Socket Mode
([#287](https://github.com/adeelahmad/lense/pull/287)), without Matterbridge.
- **Costs:** a budget meter in the top bar and a Costs page ([#275](https://github.com/adeelahmad/lense/pull/275)),
and an overall AI budget with soft and hard limits asked for in setup
([#281](https://github.com/adeelahmad/lense/pull/281)).
- **Upgrades and backups in the web app:** an upgrade page with live steps that rolls back when the new version doesn't
start ([#262](https://github.com/adeelahmad/lense/pull/262)), and restore, download links and scheduled backups
([#263](https://github.com/adeelahmad/lense/pull/263)).
- **Running it:** HTTPS with certificates from Let's Encrypt or ZeroSSL
([#289](https://github.com/adeelahmad/lense/pull/289)), structured logs with a log viewer and syslog output
([#287](https://github.com/adeelahmad/lense/pull/287)), without Matterbridge; and the assistant's own email address,
over IMAP and SMTP ([#314](https://github.com/adeelahmad/lense/pull/314)).
- **The assistant in your browser:** the side panel in the Lens extension where you ask the assistant and it uses your
browser ([#322](https://github.com/adeelahmad/lense/pull/322)); the server side is built
([guide](docs/browser-assistant.md)).
- **Assistant safety:** asking before sending data out once an answer has read third-party text
([#309](https://github.com/adeelahmad/lense/pull/309)), and running MCP servers that are programs (stdio) in a sandbox
([#304](https://github.com/adeelahmad/lense/pull/304)).
- **Search:** a Similar tab, and similar content for text selected anywhere
([#307](https://github.com/adeelahmad/lense/pull/307)); NSFW pictures classified by a local model, per namespace
([#306](https://github.com/adeelahmad/lense/pull/306)).
- **Costs and setup:** an overall AI budget with soft and hard limits, asked for in setup
([#281](https://github.com/adeelahmad/lense/pull/281)); fewer clicks in setup, Chat, Search and Notes
([#315](https://github.com/adeelahmad/lense/pull/315)).
- **Running it:** an upgrade page with live steps that rolls back when the new version doesn't start
([#262](https://github.com/adeelahmad/lense/pull/262)), HTTPS set up in Settings
([#326](https://github.com/adeelahmad/lense/pull/326)), structured logs with a log viewer and syslog output
([#284](https://github.com/adeelahmad/lense/pull/284)), built-in usage analytics
([#274](https://github.com/adeelahmad/lense/pull/274)), and Prometheus and Grafana as an opt-in service
([#297](https://github.com/adeelahmad/lense/pull/297)).
- **People:** email invitations, and removing someone for good ([#286](https://github.com/adeelahmad/lense/pull/286)).
- **Lens.app for macOS:** a menu-bar app that carries microsandbox ([#280](https://github.com/adeelahmad/lense/pull/280)).
- **The web clipper** for Firefox ([#282](https://github.com/adeelahmad/lense/pull/282)) and Safari
([#283](https://github.com/adeelahmad/lense/pull/283)).
- **Files:** assets shown as folders and files by namespace and collection
([#290](https://github.com/adeelahmad/lense/pull/290)) and shared read-only over NFS
([#295](https://github.com/adeelahmad/lense/pull/295)); items called assets unless they are audio or video
([#273](https://github.com/adeelahmad/lense/pull/273)).
- **Lens.app for macOS:** a menu-bar app that carries microsandbox
([#280](https://github.com/adeelahmad/lense/pull/280)).
- **The graph as files, shared:** read-only over S3 and WebDAV ([#324](https://github.com/adeelahmad/lense/pull/324)),
SMB ([#305](https://github.com/adeelahmad/lense/pull/305)) and NFS
([#295](https://github.com/adeelahmad/lense/pull/295)); assets and people rather than recordings and speakers, unless
it is audio ([#273](https://github.com/adeelahmad/lense/pull/273)).
- **Reading documents** with an Apache Tika server ([#292](https://github.com/adeelahmad/lense/pull/292)).

## Stack
Expand All @@ -158,14 +172,16 @@ The guides are on the [docs site](https://adeelahmad.github.io/lense/) and in th

- **Start:** [Get started](docs/get-started.md), [deployment](docs/deployment.md),
[microsandbox](docs/microsandbox.md), [configuration](docs/configuration.md), [components](docs/components.md),
[updates and backups](docs/updates.md), [remote access](docs/remote-access.md)
[updates and backups](docs/updates.md), [remote access](docs/remote-access.md), [HTTPS](docs/https.md)
- **Agents and apps:** [MCP server](docs/mcp.md), [API](docs/api.md), [authentication](docs/authentication.md),
[access](docs/access.md)
- **Use:** [chat and batch runs](docs/assistant.md), [chat rooms](docs/chat-rooms.md), [podcasts](docs/podcasts.md),
[notes](docs/notes.md), [web clipper](docs/web-clipper.md), [sensors](docs/sensors.md),
[notes](docs/notes.md), [web clipper](docs/web-clipper.md),
[the assistant in your browser](docs/browser-assistant.md), [sensors](docs/sensors.md),
[processing](docs/processing.md), [video](docs/video.md), [notifications](docs/notifications.md)
- **Knowledge:** [graph and Cypher](docs/graph.md), [graph history](docs/graph-history.md), [topics](docs/topics.md),
[linked data (RDF)](docs/rdf.md), [IIIF](docs/iiif.md), [Fedora](docs/fedora.md)
[the graph as files](docs/file-tree.md), [linked data (RDF)](docs/rdf.md), [IIIF](docs/iiif.md),
[Fedora](docs/fedora.md)
- **Models and cost:** [local models](docs/local-models.md), [speech providers](docs/speech-providers.md),
[activity and costs](docs/activity.md), [budgets](docs/budgets.md)
- **Operate:** [encryption](docs/encryption.md), [storage](docs/storage.md), [database](docs/database.md),
Expand Down
4 changes: 2 additions & 2 deletions docs/updates.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Updates and backups

Lens tells admins when a new release is out, backs the database up, and installs the release from the web app, so
upgrading never needs a terminal. Status: **built** (the check, backups and upgrades from the API and the agent);
the web app's banner and Settings page come in their own change.
upgrading never needs a terminal. Status: **built** (the check, upgrades from the API and the agent, and backups in
Settings › Backups); the web app's upgrade page is **in progress**.

## The check

Expand Down
2 changes: 1 addition & 1 deletion docs/web-clipper.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Web clipper

**Status: built (Chrome, Edge and Firefox); Safari in progress.** Every browser's build comes from the same code;
**Status: built (Chrome, Edge, Firefox and Safari).** Every browser's build comes from the same code;
each gets a target in `extensions/browser/targets/`.

The Lens web clipper is a browser extension that turns what you're reading into Markdown and sends it to your Lens,
Expand Down
5 changes: 3 additions & 2 deletions fastapi_backend/app/domain/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,9 +633,10 @@ def _plain(v):


def _retryable(err):
"""SurrealDB's optimistic transactions fail with a write conflict when two touch the same record at once."""
"""SurrealDB's optimistic transactions fail with a write conflict when two touch the same record at once, and ask
for a retry when a transaction's snapshot is older than the commit oracle's GC window (a busy server)."""
m = str(err)
return "can be retried" in m or "Transaction conflict" in m or "Write conflict" in m
return "can be retried" in m or "Transaction conflict" in m or "Write conflict" in m or "retry required" in m


def _lost(err):
Expand Down
67 changes: 67 additions & 0 deletions fastapi_backend/tests/domain/test_store_retry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""SurrealDB asks for some transactions to be run again (a write conflict, a snapshot older than the commit oracle's
GC window on a busy server); the store runs them again instead of failing.""&quo 8BD0 t;

from __future__ import annotations

import pytest

from app.domain import store

GC = "There was a problem with a transaction: Transaction retry required: snapshot is older than the commit oracle's GC window"


class _Conn:
"""A stand-in server connection: its first `failures` answers are SurrealDB's retry error, then OK."""

socket = None

def __init__(self, failures):
self.failures, self.calls = failures, 0

def query_raw(self, sql, v):
self.calls += 1
if self.calls <= self.failures:
return {"result": [{"status": "ERR", "result": GC}]}
return {"result": [{"status": "OK", "result": []}]}

def query(self, sql, v):
self.calls += 1
if self.calls <= self.failures:
raise RuntimeError(GC)
return [1]


def _db(conn):
d = store.DB.__new__(store.DB)
d.url, d.embedded = "ws://test", False
d._pool = store.queue.LifoQueue()
d._all = [conn]
d._pool.put(conn)
return d


@pytest.fixture(autouse=True)
def _no_wait(monkeypatch):
monkeypatch.setattr(store, "_backoff", lambda attempt: None)


def test_a_snapshot_older_than_the_gc_window_is_retried():
assert store._retryable(GC)
c = _Conn(failures=2)
_db(c).run(["DEFINE INDEX IF NOT EXISTS field_space ON field FIELDS space"], transaction=False)
assert c.calls == 3
c = _Conn(failures=1)
assert _db(c).values("SELECT 1") == [1]
assert c.calls == 2


def test_other_errors_still_fail_at_once():
class _Bad(_Conn):
def query_raw(self, sql, v):
self.calls += 1
return {"result": [{"status": "ERR", "result": "Parse error: unexpected token"}]}

c = _Bad(failures=0)
with pytest.raises(RuntimeError, match="Parse error"):
_db(c).run(["SELEC 1"], transaction=False)
455F assert c.calls == 1
Loading
0