amfs-http-server 0.3.43__tar.gz → 0.3.45__tar.gz
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.
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/PKG-INFO +3 -3
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/pyproject.toml +3 -3
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/server.py +47 -13
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/.gitignore +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/__init__.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/auth.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/models.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/openrouter_proxy.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/pro_provider.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/pro_proxy.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/sse.py +0 -0
- {amfs_http_server-0.3.43 → amfs_http_server-0.3.45}/src/amfs_http/tenant_middleware.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: amfs-http-server
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.45
|
|
4
4
|
Summary: AMFS HTTP/REST API server with SSE support
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
|
-
Requires-Dist: amfs-core>=0.3.
|
|
8
|
-
Requires-Dist: amfs>=0.5.
|
|
7
|
+
Requires-Dist: amfs-core>=0.3.38
|
|
8
|
+
Requires-Dist: amfs>=0.5.29
|
|
9
9
|
Requires-Dist: fastapi>=0.115
|
|
10
10
|
Requires-Dist: httpx>=0.27
|
|
11
11
|
Requires-Dist: sse-starlette>=2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "amfs-http-server"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.45"
|
|
4
4
|
description = "AMFS HTTP/REST API server with SSE support"
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
# this tree is what test_dependency_floors forbids.
|
|
14
14
|
# 0.5.18 for AgentMemory.as_agent, which the agent detail page calls to list
|
|
15
15
|
# an agent's entries as that agent rather than as the server.
|
|
16
|
-
"amfs>=0.5.
|
|
16
|
+
"amfs>=0.5.29",
|
|
17
17
|
# 0.3.23 is real in the silent way: the write path calls
|
|
18
18
|
# adapter.scope_counts(), which is a default on AdapterABC from 0.3.23. On an
|
|
19
19
|
# older core the adapters that do not override it — filesystem, S3 — have no
|
|
@@ -27,7 +27,7 @@ dependencies = [
|
|
|
27
27
|
# unexpected keyword, _scope_block swallows it, and the block disappears —
|
|
28
28
|
# while an adapter that ignored the argument instead would name another
|
|
29
29
|
# agent's private keys. Either way the floor is what makes the rule real.
|
|
30
|
-
"amfs-core>=0.3.
|
|
30
|
+
"amfs-core>=0.3.38",
|
|
31
31
|
"fastapi>=0.115",
|
|
32
32
|
# 0.37 is where uvicorn.run() grew timeout_worker_healthcheck, which
|
|
33
33
|
# main() passes unconditionally; on anything older that is a TypeError
|
|
@@ -1459,6 +1459,12 @@ async def _list_agent_entries_page(
|
|
|
1459
1459
|
)
|
|
1460
1460
|
|
|
1461
1461
|
|
|
1462
|
+
# How many distinct entity paths the memory-graph page will list to name the
|
|
1463
|
+
# authors of what an agent read. One indexed query per path; an agent that has
|
|
1464
|
+
# read across more paths than this gets the first N and ``truncated: true``.
|
|
1465
|
+
_MEMORY_GRAPH_MAX_READ_PATHS = 200
|
|
1466
|
+
|
|
1467
|
+
|
|
1462
1468
|
def _bounded_scan(items: list[Any], ceiling: int) -> tuple[list[Any], bool]:
|
|
1463
1469
|
"""Trim a scan to *ceiling* rows and say whether anything was cut.
|
|
1464
1470
|
|
|
@@ -5526,18 +5532,36 @@ def agent_memory_graph(
|
|
|
5526
5532
|
raise HTTPException(status_code=404, detail="Agent not found")
|
|
5527
5533
|
|
|
5528
5534
|
mem = _get_memory()
|
|
5529
|
-
#
|
|
5530
|
-
# it is shared or the
|
|
5531
|
-
# produced a page that could not see the agent's PRIVATE
|
|
5532
|
-
# agent whose every entry was private showed 0 memories
|
|
5533
|
-
# its card, counted by SQL with no such filter,
|
|
5534
|
-
|
|
5535
|
-
|
|
5535
|
+
# Acting AS this agent, not as the server. ``search`` and ``list`` keep an
|
|
5536
|
+
# entry only if it is shared or the acting identity's own, so asking the
|
|
5537
|
+
# server's handle produced a page that could not see the agent's PRIVATE
|
|
5538
|
+
# entries at all: an agent whose every entry was private showed 0 memories
|
|
5539
|
+
# and 0 topics while its card, counted by SQL with no such filter,
|
|
5540
|
+
# correctly said 4 and 1.
|
|
5541
|
+
handle = mem.as_agent(agent_id)
|
|
5542
|
+
ceiling = max_scan_rows()
|
|
5543
|
+
|
|
5544
|
+
def _scoped(rows: list) -> list:
|
|
5536
5545
|
# The same per-user filter every other entry-returning route applies,
|
|
5537
5546
|
# and this one did not: agent ids are matched as bare strings within an
|
|
5538
5547
|
# account, so where two people share an account and their agents share a
|
|
5539
5548
|
# default name, each was shown the other's entries.
|
|
5540
|
-
|
|
5549
|
+
if vis is not None and vis.should_filter():
|
|
5550
|
+
return vis.filter_entries(rows)
|
|
5551
|
+
return rows
|
|
5552
|
+
|
|
5553
|
+
# This agent's own entries, filtered by author in SQL. This used to be
|
|
5554
|
+
# ``handle.list()`` — every current entry in the namespace, 400k rows on a
|
|
5555
|
+
# large account, 208 s in production — filtered down to the agent's 69 in
|
|
5556
|
+
# Python afterwards. Fetched one past the ceiling so the flag is exact,
|
|
5557
|
+
# and the flag is read off the RAW rows: the per-user filter below runs
|
|
5558
|
+
# after the limit, so where two users' agents share a name, a page at the
|
|
5559
|
+
# ceiling may hold rows this caller cannot see in place of ones they can.
|
|
5560
|
+
# The response cannot recover those, but it must not call itself complete.
|
|
5561
|
+
own_raw, own_truncated = _bounded_scan(
|
|
5562
|
+
handle.search(agent_id=agent_id, limit=ceiling + 1), ceiling,
|
|
5563
|
+
)
|
|
5564
|
+
own_entries = _scoped(own_raw)
|
|
5541
5565
|
# Read counts and the trace count are aggregated by the adapter; this
|
|
5542
5566
|
# used to pull up to 10,000 full traces to tally causal_entries here.
|
|
5543
5567
|
trace_count = mem._adapter.count_traces(agent_id=agent_id)
|
|
@@ -5546,8 +5570,7 @@ def agent_memory_graph(
|
|
|
5546
5570
|
}
|
|
5547
5571
|
|
|
5548
5572
|
written_by_agent = [
|
|
5549
|
-
e for e in
|
|
5550
|
-
if e.provenance.agent_id == agent_id and not e.entity_path.startswith("_system/")
|
|
5573
|
+
e for e in own_entries if not e.entity_path.startswith("_system/")
|
|
5551
5574
|
]
|
|
5552
5575
|
entities_written: dict[str, list[dict]] = {}
|
|
5553
5576
|
for e in written_by_agent:
|
|
@@ -5576,7 +5599,6 @@ def agent_memory_graph(
|
|
|
5576
5599
|
# type, and the response says when the bound was hit.
|
|
5577
5600
|
total_read_events = 0
|
|
5578
5601
|
read_events_truncated = False
|
|
5579
|
-
ceiling = max_scan_rows()
|
|
5580
5602
|
try:
|
|
5581
5603
|
read_events, cut_a = _bounded_scan(
|
|
5582
5604
|
mem._adapter.list_events(
|
|
@@ -5602,9 +5624,21 @@ def agent_memory_graph(
|
|
|
5602
5624
|
except Exception:
|
|
5603
5625
|
pass
|
|
5604
5626
|
|
|
5627
|
+
# Who wrote what this agent read. Only the entity paths it actually read
|
|
5628
|
+
# are listed — a few dozen at most, each an indexed lookup — rather than
|
|
5629
|
+
# the whole namespace. Bounded so an agent that has read everywhere does
|
|
5630
|
+
# not turn this back into the scan it replaces; the flag says when it hit.
|
|
5605
5631
|
entry_authors: dict[str, str] = {}
|
|
5606
|
-
for e in
|
|
5632
|
+
for e in own_entries:
|
|
5607
5633
|
entry_authors[f"{e.entity_path}/{e.key}"] = e.provenance.agent_id
|
|
5634
|
+
read_paths = sorted(read_entities)
|
|
5635
|
+
authors_truncated = len(read_paths) > _MEMORY_GRAPH_MAX_READ_PATHS
|
|
5636
|
+
for ep in read_paths[:_MEMORY_GRAPH_MAX_READ_PATHS]:
|
|
5637
|
+
try:
|
|
5638
|
+
for e in _scoped(handle.list(ep)):
|
|
5639
|
+
entry_authors.setdefault(f"{ep}/{e.key}", e.provenance.agent_id)
|
|
5640
|
+
except Exception:
|
|
5641
|
+
logger.debug("memory-graph: could not list %s for authors", ep, exc_info=True)
|
|
5608
5642
|
|
|
5609
5643
|
cross_agent_reads: dict[str, list[dict]] = {}
|
|
5610
5644
|
for ep, keys in read_entities.items():
|
|
@@ -5636,7 +5670,7 @@ def agent_memory_graph(
|
|
|
5636
5670
|
"totalRecalls": total_recalls,
|
|
5637
5671
|
"recalledTokensSaved": recalled_tokens_saved,
|
|
5638
5672
|
"crossAgentReads": cross_agent_reads,
|
|
5639
|
-
"truncated": read_events_truncated,
|
|
5673
|
+
"truncated": read_events_truncated or own_truncated or authors_truncated,
|
|
5640
5674
|
}
|
|
5641
5675
|
|
|
5642
5676
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|