basic-memory 0.23.2.dev3__py3-none-any.whl → 0.23.3.dev2__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.
- basic_memory/__init__.py +1 -1
- basic_memory/alembic/env.py +8 -18
- basic_memory/deps/config.py +5 -1
- basic_memory/deps/read_cache.py +6 -2
- basic_memory/markdown/plugins.py +123 -22
- {basic_memory-0.23.2.dev3.dist-info → basic_memory-0.23.3.dev2.dist-info}/METADATA +1 -2
- {basic_memory-0.23.2.dev3.dist-info → basic_memory-0.23.3.dev2.dist-info}/RECORD +10 -11
- basic_memory/migration_loop.py +0 -34
- {basic_memory-0.23.2.dev3.dist-info → basic_memory-0.23.3.dev2.dist-info}/WHEEL +0 -0
- {basic_memory-0.23.2.dev3.dist-info → basic_memory-0.23.3.dev2.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.23.2.dev3.dist-info → basic_memory-0.23.3.dev2.dist-info}/licenses/LICENSE +0 -0
basic_memory/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""basic-memory - Local-first knowledge management combining Zettelkasten with knowledge graphs"""
|
|
2
2
|
|
|
3
3
|
# Package version - updated by release automation
|
|
4
|
-
__version__ = "0.23.
|
|
4
|
+
__version__ = "0.23.2"
|
|
5
5
|
|
|
6
6
|
# API version for FastAPI - independent of package version
|
|
7
7
|
__api_version__ = "v2"
|
basic_memory/alembic/env.py
CHANGED
|
@@ -2,34 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import os
|
|
5
|
-
import sys
|
|
6
5
|
from contextlib import suppress
|
|
7
6
|
from logging.config import fileConfig
|
|
8
7
|
|
|
9
|
-
from loguru import logger
|
|
10
8
|
from sqlalchemy import engine_from_config, pool
|
|
11
9
|
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
|
12
10
|
|
|
13
11
|
from alembic import context
|
|
14
12
|
|
|
15
13
|
from basic_memory.config import ConfigManager
|
|
16
|
-
from basic_memory.migration_loop import running_on_uvloop
|
|
17
14
|
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
nest_asyncio.apply()
|
|
27
|
-
except (ImportError, ValueError) as exc:
|
|
28
|
-
# Trigger: nest_asyncio is absent (ImportError) or refuses to patch the
|
|
29
|
-
# running loop (ValueError).
|
|
30
|
-
# Outcome: log at DEBUG (observable, not noisy) and fall through to the
|
|
31
|
-
# thread-based migration fallback.
|
|
32
|
-
logger.debug(f"nest_asyncio not applied ({exc!r}); using thread-based migration fallback")
|
|
15
|
+
# Constraint: never apply nest_asyncio here. It replaces asyncio.run and
|
|
16
|
+
# run_until_complete process-wide, and its run_until_complete returns the moment
|
|
17
|
+
# the root task finishes without running that task's done-callbacks. anyio stops
|
|
18
|
+
# its worker threads from exactly such a callback, so any request that touched
|
|
19
|
+
# the thread pool left a non-daemon worker parked forever and one-shot CLI
|
|
20
|
+
# commands hung at interpreter exit on Python < 3.14 (#1333, #1345). Callers
|
|
21
|
+
# already inside a running loop (pytest-asyncio, the API lifespan) take the
|
|
22
|
+
# thread fallback in run_migrations_online(), the only path 3.14 ever used.
|
|
33
23
|
|
|
34
24
|
# Trigger: only set test env when actually running under pytest
|
|
35
25
|
# Why: alembic/env.py is imported during normal operations (MCP server startup, migrations)
|
basic_memory/deps/config.py
CHANGED
|
@@ -12,12 +12,16 @@ from fastapi import Depends, Request
|
|
|
12
12
|
from basic_memory.config import BasicMemoryConfig
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def get_app_config(request: Request) -> BasicMemoryConfig:
|
|
15
|
+
async def get_app_config(request: Request) -> BasicMemoryConfig:
|
|
16
16
|
"""Resolve the application configuration from the composition root.
|
|
17
17
|
|
|
18
18
|
API requests read the container the lifespan stored on ``app.state``.
|
|
19
19
|
Requests served without a lifespan (the CLI/MCP local ASGI flow) fall back
|
|
20
20
|
to the API composition root, which creates a container on demand.
|
|
21
|
+
|
|
22
|
+
Declared ``async`` although it never awaits: FastAPI runs *sync* dependencies
|
|
23
|
+
on anyio's worker-thread pool, and an attribute read is not worth a thread
|
|
24
|
+
hop per request — nor the non-daemon worker it leaves parked (#1345).
|
|
21
25
|
"""
|
|
22
26
|
container = getattr(request.app.state, "container", None)
|
|
23
27
|
if container is not None:
|
basic_memory/deps/read_cache.py
CHANGED
|
@@ -12,8 +12,12 @@ from basic_memory.read_cache.policy import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def get_read_cache(request: Request) -> ReadCache | None:
|
|
16
|
-
"""Return the optional host-injected cache backend.
|
|
15
|
+
async def get_read_cache(request: Request) -> ReadCache | None:
|
|
16
|
+
"""Return the optional host-injected cache backend.
|
|
17
|
+
|
|
18
|
+
``async`` for the same reason as ``get_app_config``: a sync dependency would
|
|
19
|
+
hop to anyio's worker pool just to read an attribute (#1345).
|
|
20
|
+
"""
|
|
17
21
|
try:
|
|
18
22
|
container = request.app.state.container
|
|
19
23
|
except AttributeError:
|
basic_memory/markdown/plugins.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import List, Any, Dict
|
|
|
5
5
|
|
|
6
6
|
from basic_memory.utils import normalize_project_reference
|
|
7
7
|
from markdown_it import MarkdownIt
|
|
8
|
+
from markdown_it.rules_inline.backticks import backtick
|
|
8
9
|
from markdown_it.token import Token
|
|
9
10
|
|
|
10
11
|
# Transcript timecodes like [00:00:11] or [1:02:03.500] share the bracket shape of
|
|
@@ -127,6 +128,84 @@ def parse_observation(token: Token) -> Dict[str, Any]:
|
|
|
127
128
|
|
|
128
129
|
|
|
129
130
|
# Relation handling functions
|
|
131
|
+
def _relation_content(token: Token) -> str:
|
|
132
|
+
"""Return the source content of an inline token."""
|
|
133
|
+
return token.tag or token.content
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
_CODE_SPANS_KEY = "basic_memory_code_spans"
|
|
137
|
+
_CODE_SPAN_PARSER = MarkdownIt()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _record_inline_code_span(state: Any, silent: bool) -> bool:
|
|
141
|
+
"""Delegate to MarkdownIt and retain exact source spans for code tokens."""
|
|
142
|
+
start = state.pos
|
|
143
|
+
token_count = len(state.tokens)
|
|
144
|
+
matched = backtick(state, silent)
|
|
145
|
+
if matched and not silent and len(state.tokens) > token_count:
|
|
146
|
+
if state.tokens[-1].type == "code_inline":
|
|
147
|
+
state.env[_CODE_SPANS_KEY].append((start, state.pos))
|
|
148
|
+
return matched
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
_CODE_SPAN_PARSER.inline.ruler.at("backticks", _record_inline_code_span)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _inline_code_spans(content: str) -> list[tuple[int, int]]:
|
|
155
|
+
"""Return source ranges that MarkdownIt classifies as inline code.
|
|
156
|
+
|
|
157
|
+
This delegates delimiter and escape handling to the same MarkdownIt inline
|
|
158
|
+
rule used by the document parser instead of duplicating CommonMark's
|
|
159
|
+
backtick scanner. It also preserves MarkdownIt's linear-time cache for
|
|
160
|
+
unmatched delimiter runs.
|
|
161
|
+
"""
|
|
162
|
+
if "`" not in content:
|
|
163
|
+
return []
|
|
164
|
+
|
|
165
|
+
spans: list[tuple[int, int]] = []
|
|
166
|
+
_CODE_SPAN_PARSER.inline.parse(content, _CODE_SPAN_PARSER, {_CODE_SPANS_KEY: spans}, [])
|
|
167
|
+
return spans
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _mask_wikilinks_in_inline_code(content: str, code_spans: list[tuple[int, int]]) -> str:
|
|
171
|
+
"""Mask only brackets in code spans, retaining all non-link source text."""
|
|
172
|
+
if not code_spans:
|
|
173
|
+
return content
|
|
174
|
+
|
|
175
|
+
masked = list(content)
|
|
176
|
+
for start, end in code_spans:
|
|
177
|
+
for position in range(start, end):
|
|
178
|
+
if masked[position] in "[]":
|
|
179
|
+
masked[position] = " "
|
|
180
|
+
|
|
181
|
+
return "".join(masked)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _remove_links_to_directive_outside_inline_code(
|
|
185
|
+
content: str, code_spans: list[tuple[int, int]]
|
|
186
|
+
) -> tuple[str, bool]:
|
|
187
|
+
"""Remove a terminal directive only when MarkdownIt parsed it as non-code."""
|
|
188
|
+
directive_content = list(content)
|
|
189
|
+
for start, end in code_spans:
|
|
190
|
+
directive_content[start:end] = " " * (end - start)
|
|
191
|
+
|
|
192
|
+
match = _LINKS_TO_DIRECTIVE.search("".join(directive_content))
|
|
193
|
+
if not match:
|
|
194
|
+
return content, False
|
|
195
|
+
return content[: match.start()].rstrip(), True
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _relation_parsing_content(token: Token) -> tuple[str, str, list[tuple[int, int]]]:
|
|
199
|
+
"""Build source-preserving relation input from MarkdownIt's code spans."""
|
|
200
|
+
source_content = _relation_content(token)
|
|
201
|
+
code_spans = _inline_code_spans(source_content)
|
|
202
|
+
return (
|
|
203
|
+
source_content,
|
|
204
|
+
_mask_wikilinks_in_inline_code(source_content, code_spans),
|
|
205
|
+
code_spans,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
130
209
|
def parse_relation_type(content: str) -> str | None:
|
|
131
210
|
"""Return the explicit relation label before the first wikilink, if any."""
|
|
132
211
|
before_link = content.partition("[[")[0].strip()
|
|
@@ -154,15 +233,17 @@ def is_explicit_relation(token: Token) -> bool:
|
|
|
154
233
|
if token.type != "inline": # pragma: no cover
|
|
155
234
|
return False
|
|
156
235
|
|
|
157
|
-
|
|
158
|
-
content = (token.tag or token.content).strip()
|
|
236
|
+
_, content, _ = _relation_parsing_content(token)
|
|
159
237
|
if "[[" not in content or "]]" not in content:
|
|
160
238
|
return False
|
|
161
239
|
return _parse_explicit_relation(content) is not None
|
|
162
240
|
|
|
163
241
|
|
|
164
|
-
def _parse_explicit_relation(
|
|
242
|
+
def _parse_explicit_relation(
|
|
243
|
+
content: str, source_content: str | None = None
|
|
244
|
+
) -> Dict[str, Any] | None:
|
|
165
245
|
"""Parse ``type [[target]] (context)``, rejecting lines with a prose tail."""
|
|
246
|
+
source_content = source_content or content
|
|
166
247
|
rel_type = parse_relation_type(content)
|
|
167
248
|
if rel_type is None:
|
|
168
249
|
return None
|
|
@@ -172,7 +253,7 @@ def _parse_explicit_relation(content: str) -> Dict[str, Any] | None:
|
|
|
172
253
|
if start == -1 or end == -1:
|
|
173
254
|
return None
|
|
174
255
|
|
|
175
|
-
target = normalize_project_reference(
|
|
256
|
+
target = normalize_project_reference(source_content[start + 2 : end].strip())
|
|
176
257
|
if not target:
|
|
177
258
|
return None
|
|
178
259
|
|
|
@@ -189,7 +270,8 @@ def _parse_explicit_relation(content: str) -> Dict[str, Any] | None:
|
|
|
189
270
|
if after:
|
|
190
271
|
if not _is_single_parenthesized(after):
|
|
191
272
|
return None
|
|
192
|
-
|
|
273
|
+
source_after = source_content[end + 2 :].strip()
|
|
274
|
+
context = source_after[1:-1].strip() or None
|
|
193
275
|
|
|
194
276
|
return {"type": rel_type, "target": target, "context": context}
|
|
195
277
|
|
|
@@ -217,13 +299,23 @@ def _is_single_parenthesized(text: str) -> bool:
|
|
|
217
299
|
|
|
218
300
|
def parse_relation(token: Token) -> Dict[str, Any] | None:
|
|
219
301
|
"""Extract relation parts from token."""
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
302
|
+
source_content, content, _ = _relation_parsing_content(token)
|
|
303
|
+
return _parse_explicit_relation(content, source_content)
|
|
304
|
+
|
|
223
305
|
|
|
306
|
+
def _is_escaped(content: str, position: int) -> bool:
|
|
307
|
+
"""Whether the character at ``position`` is escaped by an odd slash run."""
|
|
308
|
+
slash_count = 0
|
|
309
|
+
position -= 1
|
|
310
|
+
while position >= 0 and content[position] == "\\":
|
|
311
|
+
slash_count += 1
|
|
312
|
+
position -= 1
|
|
313
|
+
return slash_count % 2 == 1
|
|
224
314
|
|
|
225
|
-
|
|
226
|
-
|
|
315
|
+
|
|
316
|
+
def parse_inline_relations(content: str, source_content: str | None = None) -> List[Dict[str, Any]]:
|
|
317
|
+
"""Find wiki-style links, extracting targets from the original source."""
|
|
318
|
+
source_content = content if source_content is None else source_content
|
|
227
319
|
relations = []
|
|
228
320
|
start = 0
|
|
229
321
|
|
|
@@ -232,6 +324,9 @@ def parse_inline_relations(content: str) -> List[Dict[str, Any]]:
|
|
|
232
324
|
start = content.find("[[", start)
|
|
233
325
|
if start == -1: # pragma: no cover
|
|
234
326
|
break
|
|
327
|
+
if _is_escaped(content, start):
|
|
328
|
+
start += 2
|
|
329
|
+
continue
|
|
235
330
|
|
|
236
331
|
# Find matching ]]
|
|
237
332
|
depth = 1
|
|
@@ -239,10 +334,10 @@ def parse_inline_relations(content: str) -> List[Dict[str, Any]]:
|
|
|
239
334
|
end = -1
|
|
240
335
|
|
|
241
336
|
while pos < len(content):
|
|
242
|
-
if content[pos : pos + 2] == "[[":
|
|
337
|
+
if content[pos : pos + 2] == "[[" and not _is_escaped(content, pos):
|
|
243
338
|
depth += 1
|
|
244
339
|
pos += 2
|
|
245
|
-
elif content[pos : pos + 2] == "]]":
|
|
340
|
+
elif content[pos : pos + 2] == "]]" and not _is_escaped(content, pos):
|
|
246
341
|
depth -= 1
|
|
247
342
|
if depth == 0:
|
|
248
343
|
end = pos
|
|
@@ -255,7 +350,7 @@ def parse_inline_relations(content: str) -> List[Dict[str, Any]]:
|
|
|
255
350
|
# No matching ]] found
|
|
256
351
|
break
|
|
257
352
|
|
|
258
|
-
target = normalize_project_reference(
|
|
353
|
+
target = normalize_project_reference(source_content[start + 2 : end].strip())
|
|
259
354
|
if target:
|
|
260
355
|
relations.append({"type": "links_to", "target": target, "context": None})
|
|
261
356
|
|
|
@@ -336,21 +431,27 @@ def relation_plugin(md: MarkdownIt) -> None:
|
|
|
336
431
|
|
|
337
432
|
# Only process inline tokens
|
|
338
433
|
if token.type == "inline":
|
|
339
|
-
|
|
340
|
-
|
|
434
|
+
source_content = _relation_content(token)
|
|
435
|
+
if "[[" not in source_content:
|
|
436
|
+
continue
|
|
437
|
+
|
|
438
|
+
code_spans = _inline_code_spans(source_content)
|
|
439
|
+
relation_content = _mask_wikilinks_in_inline_code(source_content, code_spans)
|
|
440
|
+
content_without_directive, has_directive = (
|
|
441
|
+
_remove_links_to_directive_outside_inline_code(relation_content, code_spans)
|
|
442
|
+
)
|
|
341
443
|
|
|
342
444
|
# Check for explicit relations in list items
|
|
343
|
-
if in_list_item and not has_directive
|
|
344
|
-
rel =
|
|
445
|
+
if in_list_item and not has_directive:
|
|
446
|
+
rel = _parse_explicit_relation(relation_content, source_content)
|
|
345
447
|
if rel:
|
|
346
448
|
token.meta["relations"] = [rel]
|
|
449
|
+
continue
|
|
347
450
|
|
|
348
451
|
# Always check for inline links in any text
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if rels:
|
|
353
|
-
token.meta["relations"] = token.meta.get("relations", []) + rels
|
|
452
|
+
rels = parse_inline_relations(content_without_directive, source_content)
|
|
453
|
+
if rels:
|
|
454
|
+
token.meta["relations"] = token.meta.get("relations", []) + rels
|
|
354
455
|
|
|
355
456
|
# Add the rule after inline processing
|
|
356
457
|
md.core.ruler.after("inline", "relations", relation_rule)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.3.dev2
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -29,7 +29,6 @@ Requires-Dist: mcp<3,>=2
|
|
|
29
29
|
Requires-Dist: mdformat-frontmatter>=2.0.8
|
|
30
30
|
Requires-Dist: mdformat-gfm>=0.3.7
|
|
31
31
|
Requires-Dist: mdformat>=0.7.22
|
|
32
|
-
Requires-Dist: nest-asyncio>=1.6.0
|
|
33
32
|
Requires-Dist: openai>=1.100.2
|
|
34
33
|
Requires-Dist: pillow>=11.1.0
|
|
35
34
|
Requires-Dist: psutil>=5.9.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
basic_memory/__init__.py,sha256=
|
|
1
|
+
basic_memory/__init__.py,sha256=dtmjVy3Q4_DYddSDwS9XOq3okErGMfYtVZ-_1s5p2zQ,256
|
|
2
2
|
basic_memory/config.py,sha256=gPw1piQZJANYCF-TkpEpc9-vD7-lHnSez8HQidGh5qk,12080
|
|
3
3
|
basic_memory/config_logging.py,sha256=keqWv5xPOP6fjh1LDQmKMDyduHTYAhr6Y1EKiiYVDnc,1857
|
|
4
4
|
basic_memory/config_migrations.py,sha256=OO5hwzs3x5EEix-jNMeETrJQwMx0l2iHUywXRLMjzFA,3582
|
|
@@ -6,14 +6,13 @@ basic_memory/config_models.py,sha256=TehggqA0vm9eVo9rQugAXyTmQ-R_BJVriNl8niMiVw0
|
|
|
6
6
|
basic_memory/db.py,sha256=STYA6RmCWXFaF3ZhC0_ay0Q8B2V_htrOFo6YQcW528Q,25977
|
|
7
7
|
basic_memory/file_utils.py,sha256=_cIrb3xebx2wDdoHvvIWWgA9yfePc4TDuAAnks3Dges,18540
|
|
8
8
|
basic_memory/ignore_utils.py,sha256=1OoKxiS7_ofYYHWTtH5zoUVeaMJ8-V7j0dsqxYA3dDo,8486
|
|
9
|
-
basic_memory/migration_loop.py,sha256=KGhuCLgrgwW21GAGT9cOczbtRlM4mGi7aUBjG1shQ8s,1609
|
|
10
9
|
basic_memory/project_resolver.py,sha256=vHWHw1X9_1IqtX9Dy5yNXqNRas3wcrpQJVKxJuj4qWg,3801
|
|
11
10
|
basic_memory/redaction.py,sha256=tYolNeRQdOfcELD9sGtzQbB5dS3odP9PAnzipqmMl6M,4804
|
|
12
11
|
basic_memory/telemetry.py,sha256=pAkPgbAOmivZDkCAW0S2CKSNw4AqBfMckdsbb2EipcQ,1721
|
|
13
12
|
basic_memory/utils.py,sha256=bXlhv3qHOO_1Lb_2gUspjOZ3H1-qgreWl_n5eR6_Jxg,35426
|
|
14
13
|
basic_memory/workspace_context.py,sha256=v-u4jO8VFB1WeFZ1UpzZnbf9ph71vvz1OMibpAtrtQI,3648
|
|
15
14
|
basic_memory/alembic/alembic.ini,sha256=IEZsnF8CbbZnkwBr67LzKKNobHuzTaQNUvM8Psop5xc,3733
|
|
16
|
-
basic_memory/alembic/env.py,sha256=
|
|
15
|
+
basic_memory/alembic/env.py,sha256=26K_F2KEvz3SneUQ5LuHCpvZ1FD1w9s1wP63lQFDNZI,9275
|
|
17
16
|
basic_memory/alembic/migrations.py,sha256=lriHPXDdBLSNXEW3QTpU0SJKuVd1V-8NrVkpN3qfsUQ,718
|
|
18
17
|
basic_memory/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
19
18
|
basic_memory/alembic/versions/2d26b287813b_dedupe_observations_and_purge_stale_.py,sha256=h-Q0g5g2r3GmXWK49RyxvmvHGd5idVGA4VYkK4LYTek,2139
|
|
@@ -115,11 +114,11 @@ basic_memory/cli/commands/cloud/webdav.py,sha256=5yanr7lQy6dv8RVvixI0k_3HpCOnyzV
|
|
|
115
114
|
basic_memory/cli/commands/cloud/webdav_transfer.py,sha256=ncj_eLthaN7a8-eKyieBxHoBqg3SaXVXIs6K8_UGw2Q,23340
|
|
116
115
|
basic_memory/cli/commands/cloud/workspace.py,sha256=08TdcYohmDunprmUgpGO4SvCjD7A_vynBYIkD2LNooc,3940
|
|
117
116
|
basic_memory/deps/__init__.py,sha256=thQt1C04eNMA49Y3OubiIiv0WIWie75aCd4TBTMkddE,6139
|
|
118
|
-
basic_memory/deps/config.py,sha256=
|
|
117
|
+
basic_memory/deps/config.py,sha256=zQrYU7K6XIFflvi8VYFmbEhIJ5GDQXoqHZ9SFWoLKQg,1444
|
|
119
118
|
basic_memory/deps/db.py,sha256=xpD5Mcq16f8J0lbu8PCoRcr2s4e1xKOj1nze_NW3CGg,2395
|
|
120
119
|
basic_memory/deps/importers.py,sha256=G2ulW2xdexR4indqlNYqt9kzoQCtSqnPcRgb-ae4Cuo,3786
|
|
121
120
|
basic_memory/deps/projects.py,sha256=z2Qziv58GJw-I4CofIiIXeyXBVtYHAcd-dep5Fg2slw,3352
|
|
122
|
-
basic_memory/deps/read_cache.py,sha256=
|
|
121
|
+
basic_memory/deps/read_cache.py,sha256=l6yJCUWkW2kHhK80GqzPMSJDwEe6wqWFC7gOgJg_YY8,1559
|
|
123
122
|
basic_memory/deps/repositories.py,sha256=2ZQeScK-C3VOnYs_re0FCAty5cEz3PpTPnSxtW_gFqQ,2750
|
|
124
123
|
basic_memory/deps/services.py,sha256=cjxsFhVTwqmf-nUUv3SdEKM-UXBf8OH0oQ9Dj5HO7RY,21096
|
|
125
124
|
basic_memory/hooks/__init__.py,sha256=kEvNW8tpXcnTdzwUpjoir2RZxVXRULJhB38WGwDKCHc,861
|
|
@@ -207,7 +206,7 @@ basic_memory/man/bm.1,sha256=iQhnHzJHSN3WRdMIphv22AnWTYR2SErzeKwCqQJ3LzQ,3406
|
|
|
207
206
|
basic_memory/markdown/__init__.py,sha256=DdzioCWtDnKaq05BHYLgL_78FawEHLpLXnp-kPSVfIc,501
|
|
208
207
|
basic_memory/markdown/entity_parser.py,sha256=7RUL7QXOQc00ez7OFOJB6kvsD8yY6K_yVD4VGSHOPxc,12629
|
|
209
208
|
basic_memory/markdown/markdown_processor.py,sha256=YK8KF8NY0N9nQM3I_8Pcxybf5SPOPnIL5kzmFBFBlx0,7429
|
|
210
|
-
basic_memory/markdown/plugins.py,sha256=
|
|
209
|
+
basic_memory/markdown/plugins.py,sha256=US8WhuZVfElSPGlSjP8_8C1J_dvasc6EjZJAF7-QvZ8,16464
|
|
211
210
|
basic_memory/markdown/schemas.py,sha256=IHi4sm40ew4zNjb359imwiPJXGO6cIni_1f3YNVyZPs,2798
|
|
212
211
|
basic_memory/markdown/utils.py,sha256=EP8P8uDNrhcnvpgc_iaczIQK3r9Z6k1MHMKCcZvTzm0,4122
|
|
213
212
|
basic_memory/mcp/__init__.py,sha256=dsDOhKqjYeIbCULbHIxfcItTbqudEuEg1Np86eq0GEQ,35
|
|
@@ -391,8 +390,8 @@ basic_memory/services/search_service.py,sha256=8A1vXo5IIpKXL2gxEsCEP_tcKyRcnz4R5
|
|
|
391
390
|
basic_memory/services/service.py,sha256=V-d_8gOV07zGIQDpL-Ksqs3ZN9l3qf3HZOK1f_YNTag,336
|
|
392
391
|
basic_memory/templates/prompts/continue_conversation.hbs,sha256=trrDHSXA5S0JCbInMoUJL04xvCGRB_ku1RHNQHtl6ZI,3076
|
|
393
392
|
basic_memory/templates/prompts/search.hbs,sha256=H1cCIsHKp4VC1GrH2KeUB8pGe5vXFPqb2VPotypmeCA,3098
|
|
394
|
-
basic_memory-0.23.
|
|
395
|
-
basic_memory-0.23.
|
|
396
|
-
basic_memory-0.23.
|
|
397
|
-
basic_memory-0.23.
|
|
398
|
-
basic_memory-0.23.
|
|
393
|
+
basic_memory-0.23.3.dev2.dist-info/METADATA,sha256=_c99-bcBWDk2nBo5pqbUCMbUVlYnl3TV2vtXjWFolGk,26812
|
|
394
|
+
basic_memory-0.23.3.dev2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
395
|
+
basic_memory-0.23.3.dev2.dist-info/entry_points.txt,sha256=wvE2mRF6-Pg4weIYcfQ-86NOLZD4WJg7F7TIsRVFLb8,90
|
|
396
|
+
basic_memory-0.23.3.dev2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
397
|
+
basic_memory-0.23.3.dev2.dist-info/RECORD,,
|
basic_memory/migration_loop.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"""Pure helpers for how Alembic bridges async migrations across event loops.
|
|
2
|
-
|
|
3
|
-
Extracted from ``alembic/env.py`` so these two decisions are unit-testable:
|
|
4
|
-
env.py runs migrations at import time and cannot be imported in a test, but the
|
|
5
|
-
loop-detection and error-classification logic is exactly what regresses, so it
|
|
6
|
-
lives here where it can be covered directly.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import asyncio
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def running_on_uvloop() -> bool:
|
|
15
|
-
"""Return True when the active asyncio policy is uvloop.
|
|
16
|
-
|
|
17
|
-
The Postgres backend installs the uvloop policy before the event loop starts
|
|
18
|
-
(#831/#877). nest_asyncio cannot patch a uvloop loop, but depending on the
|
|
19
|
-
nest_asyncio version ``apply()`` may NOT raise the ValueError we expect — it
|
|
20
|
-
can silently mis-patch the stdlib loop instead. The later ``asyncio.run()``
|
|
21
|
-
then fails with "this event loop is already running" rather than the standard
|
|
22
|
-
"cannot be called from a running event loop", which the thread-based fallback
|
|
23
|
-
would not recognize, crashing startup. Detecting uvloop up front lets env.py
|
|
24
|
-
skip nest_asyncio entirely and stay on the standard running-loop error path.
|
|
25
|
-
"""
|
|
26
|
-
try:
|
|
27
|
-
import uvloop
|
|
28
|
-
except ImportError:
|
|
29
|
-
return False
|
|
30
|
-
# Constraint: on supported Python 3.12-3.13 this helper runs before a loop
|
|
31
|
-
# exists, so the active policy is the only observable uvloop signal. Python
|
|
32
|
-
# 3.14+ skips this helper in alembic/env.py; remove it with the policy seam.
|
|
33
|
-
policy = asyncio.get_event_loop_policy() # ty: ignore[deprecated]
|
|
34
|
-
return isinstance(policy, uvloop.EventLoopPolicy)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|