memtrails 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- memtrails/__init__.py +74 -0
- memtrails/_aio.py +23 -0
- memtrails/_fs.py +44 -0
- memtrails/_http.py +25 -0
- memtrails/_js.py +830 -0
- memtrails/core/__init__.py +1 -0
- memtrails/core/consolidate.py +977 -0
- memtrails/core/embedder.py +214 -0
- memtrails/core/episodes.py +78 -0
- memtrails/core/extract.py +665 -0
- memtrails/core/gather.py +209 -0
- memtrails/core/graph.py +239 -0
- memtrails/core/local_embedder.py +178 -0
- memtrails/core/model.py +105 -0
- memtrails/core/paths.py +15 -0
- memtrails/core/profiles.py +218 -0
- memtrails/core/qdrant_binary.py +359 -0
- memtrails/core/qdrant_client.py +421 -0
- memtrails/core/qdrant_process.py +457 -0
- memtrails/core/rebuild.py +201 -0
- memtrails/core/recall.py +352 -0
- memtrails/core/recall_log.py +142 -0
- memtrails/core/redact.py +176 -0
- memtrails/core/reranker.py +116 -0
- memtrails/core/room.py +93 -0
- memtrails/core/segment.py +57 -0
- memtrails/core/sleep.py +267 -0
- memtrails/core/store.py +424 -0
- memtrails/core/text.py +162 -0
- memtrails/core/words.py +187 -0
- memtrails/core/writer.py +291 -0
- memtrails/importers/__init__.py +60 -0
- memtrails/importers/files.py +333 -0
- memtrails/importers/git.py +193 -0
- memtrails/importers/jsonl.py +94 -0
- memtrails/importers/mbox.py +494 -0
- memtrails/importers/slack.py +236 -0
- memtrails/importers/text.py +651 -0
- memtrails/llm.py +192 -0
- memtrails/memory.py +794 -0
- memtrails/py.typed +0 -0
- memtrails/records.py +269 -0
- memtrails/tools/__init__.py +1 -0
- memtrails/tools/agent_tools.py +409 -0
- memtrails-0.4.1.dist-info/METADATA +237 -0
- memtrails-0.4.1.dist-info/RECORD +48 -0
- memtrails-0.4.1.dist-info/WHEEL +4 -0
- memtrails-0.4.1.dist-info/licenses/LICENSE +21 -0
memtrails/__init__.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
memtrails — retrieval and exploration for any text: e-mail, chat, notes, documents, news, commits, conversations.
|
|
3
|
+
|
|
4
|
+
add records, conversations, or a stream from an importer — searchable at once, no model asked
|
|
5
|
+
search dense + keyword, fused; reordered word by word on this machine; dates first when asked
|
|
6
|
+
room the best sentences of what was found, for a small context
|
|
7
|
+
open one item whole, with the records it was learned from
|
|
8
|
+
explore the concept graph from a topic or items to what search does not find
|
|
9
|
+
consolidate (with a language model) typed knowledge, dated observations, kept episodes
|
|
10
|
+
sleep (with a language model) merge, supersede, link, profiles
|
|
11
|
+
gather (with a language model) every instance a counting or listing question needs
|
|
12
|
+
tools memory_search / memory_open / memory_explore for an agent
|
|
13
|
+
|
|
14
|
+
A native port of the TypeScript library (npm ``memtrails``): the same algorithms, prompts and files.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .core.embedder import EmbedRoute
|
|
18
|
+
from .core.gather import Gathered
|
|
19
|
+
from .core.model import MODEL_REPO, MODEL_REVISION, default_model_dir, ensure_model
|
|
20
|
+
from .core.paths import cache_dir
|
|
21
|
+
from .core.qdrant_binary import QDRANT_VERSION, ensure_qdrant
|
|
22
|
+
from .core.recall import Explored, RecalledItem, RecordRef
|
|
23
|
+
from .core.reranker import RerankRoute
|
|
24
|
+
from .core.room import sentence_room, sentence_spans
|
|
25
|
+
from .core.words import WORD_LIMIT, PackedWords, WordStore, WordVectors, max_sim, pack_words
|
|
26
|
+
from .llm import Ask, LlmRequest, LlmUsage, OpenAiAsk, openai_ask
|
|
27
|
+
from .memory import EmbeddingOptions, Memory, QdrantOptions, SearchOptions, open_memory
|
|
28
|
+
from .records import DEFAULT_COLLECTION, Conversation, MemoryRecord, record_uid, time_of
|
|
29
|
+
from .tools.agent_tools import AgentTool, build_agent_tools, search_text
|
|
30
|
+
|
|
31
|
+
__version__ = "0.4.1"
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"DEFAULT_COLLECTION",
|
|
35
|
+
"MODEL_REPO",
|
|
36
|
+
"MODEL_REVISION",
|
|
37
|
+
"QDRANT_VERSION",
|
|
38
|
+
"WORD_LIMIT",
|
|
39
|
+
"AgentTool",
|
|
40
|
+
"Ask",
|
|
41
|
+
"Conversation",
|
|
42
|
+
"EmbedRoute",
|
|
43
|
+
"EmbeddingOptions",
|
|
44
|
+
"Explored",
|
|
45
|
+
"Gathered",
|
|
46
|
+
"LlmRequest",
|
|
47
|
+
"LlmUsage",
|
|
48
|
+
"Memory",
|
|
49
|
+
"MemoryRecord",
|
|
50
|
+
"OpenAiAsk",
|
|
51
|
+
"PackedWords",
|
|
52
|
+
"QdrantOptions",
|
|
53
|
+
"RecalledItem",
|
|
54
|
+
"RecordRef",
|
|
55
|
+
"RerankRoute",
|
|
56
|
+
"SearchOptions",
|
|
57
|
+
"WordStore",
|
|
58
|
+
"WordVectors",
|
|
59
|
+
"__version__",
|
|
60
|
+
"build_agent_tools",
|
|
61
|
+
"cache_dir",
|
|
62
|
+
"default_model_dir",
|
|
63
|
+
"ensure_model",
|
|
64
|
+
"ensure_qdrant",
|
|
65
|
+
"max_sim",
|
|
66
|
+
"open_memory",
|
|
67
|
+
"openai_ask",
|
|
68
|
+
"pack_words",
|
|
69
|
+
"record_uid",
|
|
70
|
+
"search_text",
|
|
71
|
+
"sentence_room",
|
|
72
|
+
"sentence_spans",
|
|
73
|
+
"time_of",
|
|
74
|
+
]
|
memtrails/_aio.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""asyncio pieces the port needs on every Python it supports (3.10 has neither TaskGroup nor asyncio.timeout)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import Awaitable
|
|
7
|
+
from typing import TypeVar
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def gather_or_cancel(*aws: Awaitable[T]) -> list[T]:
|
|
13
|
+
"""``asyncio.gather``, but the first that raises cancels the others before it is raised, and they have ended when it
|
|
14
|
+
is: asks the model is paid for are not left running for an answer no one reads (a cancelled caller cancels them
|
|
15
|
+
too, as gather does)."""
|
|
16
|
+
tasks: list[asyncio.Future[T]] = [asyncio.ensure_future(a) for a in aws]
|
|
17
|
+
try:
|
|
18
|
+
return list(await asyncio.gather(*tasks))
|
|
19
|
+
except BaseException:
|
|
20
|
+
for t in tasks:
|
|
21
|
+
t.cancel()
|
|
22
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
23
|
+
raise
|
memtrails/_fs.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Writes that land whole or raise: ``os.write`` may write fewer bytes than it was given (a full disk, a signal), and a
|
|
2
|
+
short write left unnoticed tears a line, or leaves the offsets an index holds pointing past the file's end."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import errno
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def write_all(fd: int, data: bytes) -> None:
|
|
11
|
+
"""Every byte of ``data`` to ``fd``, or OSError."""
|
|
12
|
+
view = memoryview(data)
|
|
13
|
+
while view:
|
|
14
|
+
n = os.write(fd, view)
|
|
15
|
+
if n <= 0:
|
|
16
|
+
raise OSError(errno.EIO, "nothing was written")
|
|
17
|
+
view = view[n:]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def append_all(path: str, data: bytes) -> None:
|
|
21
|
+
"""``data`` appended to ``path`` (made private when it is new), whole — or OSError, and the file cut back to where
|
|
22
|
+
it ended before, so what is appended next starts on a line of its own."""
|
|
23
|
+
fd = os.open(path, os.O_WRONLY | os.O_APPEND | os.O_CREAT, 0o600)
|
|
24
|
+
try:
|
|
25
|
+
start = os.fstat(fd).st_size
|
|
26
|
+
try:
|
|
27
|
+
write_all(fd, data)
|
|
28
|
+
except OSError:
|
|
29
|
+
try:
|
|
30
|
+
os.ftruncate(fd, start)
|
|
31
|
+
except OSError:
|
|
32
|
+
pass # what was cut short stays: a load ends or drops a torn last line
|
|
33
|
+
raise
|
|
34
|
+
finally:
|
|
35
|
+
os.close(fd)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def write_file(path: str, data: bytes) -> None:
|
|
39
|
+
"""``path`` replaced by ``data`` (private), whole, or OSError."""
|
|
40
|
+
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
41
|
+
try:
|
|
42
|
+
write_all(fd, data)
|
|
43
|
+
finally:
|
|
44
|
+
os.close(fd)
|
memtrails/_http.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""One HTTP client for everything a memory talks to (Qdrant, the embedding and rerank servers, the language model, the
|
|
2
|
+
downloads): the caller's, or one made for the call and closed after it when there is none. Never the environment's
|
|
3
|
+
proxies — memory's own Qdrant is on loopback, and a key meant for a server must reach that server."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import AsyncIterator
|
|
8
|
+
from contextlib import asynccontextmanager
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def new_client() -> httpx.AsyncClient:
|
|
14
|
+
"""The client a memory makes for itself when it is given none."""
|
|
15
|
+
return httpx.AsyncClient(trust_env=False, timeout=httpx.Timeout(30.0))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@asynccontextmanager
|
|
19
|
+
async def using(http: httpx.AsyncClient | None) -> AsyncIterator[httpx.AsyncClient]:
|
|
20
|
+
"""``http`` when given; else a client of its own for this call."""
|
|
21
|
+
if http is not None:
|
|
22
|
+
yield http
|
|
23
|
+
return
|
|
24
|
+
async with new_client() as own:
|
|
25
|
+
yield own
|