memorius 0.4.2__tar.gz → 0.4.3__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.
- {memorius-0.4.2/memorius.egg-info → memorius-0.4.3}/PKG-INFO +1 -1
- {memorius-0.4.2 → memorius-0.4.3}/memorius/__init__.py +1 -1
- {memorius-0.4.2 → memorius-0.4.3}/memorius/config.py +4 -1
- {memorius-0.4.2 → memorius-0.4.3}/memorius/web_search.py +66 -0
- {memorius-0.4.2 → memorius-0.4.3/memorius.egg-info}/PKG-INFO +1 -1
- {memorius-0.4.2 → memorius-0.4.3}/pyproject.toml +1 -1
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_web_search.py +41 -0
- {memorius-0.4.2 → memorius-0.4.3}/LICENSE +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/MANIFEST.in +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/README.md +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/examples/quickstart.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/hooks/memorius-hook.sh +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/manifest.yaml +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/cli/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/cli/main.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/cli/obsidian.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/consolidation.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/context_inject.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/LICENSE +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/examples/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/examples/quickstart.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/manifest.yaml +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/data/memorius-hook.sh +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/embeddings.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/factcheck.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/graph.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/base.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/claude_code.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/codex.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/gemini.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/generic.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/openclaude.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/openclaw.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/opencode.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/adapters/pi.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/cli.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/engine.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/hooks/models.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/llm_extract.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/mcp_server.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/meta_store.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/model_download.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/models.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/normalizers/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/normalizers/cli.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/plugin_gen/__init__.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/plugin_gen/cli.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/rest_server.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/session.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/temporal.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/utils.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/validation.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/vault.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius/vector_store.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius.egg-info/SOURCES.txt +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius.egg-info/dependency_links.txt +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius.egg-info/entry_points.txt +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius.egg-info/requires.txt +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/memorius.egg-info/top_level.txt +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/setup.cfg +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_core.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_encoding_regression.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_features.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_graph.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_integration.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_polish_regressions.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_temporal.py +0 -0
- {memorius-0.4.2 → memorius-0.4.3}/tests/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorius
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Self-contained memory vault for any AI agent — vector search, session diaries, and agent-agnostic hooks.
|
|
5
5
|
Author-email: dimonapatrick243 <dimona.patrick@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,7 +40,8 @@ hooks:
|
|
|
40
40
|
|
|
41
41
|
retrieval:
|
|
42
42
|
web_fallback: false # opt-in: augment thin local recall with web (local-first / privacy)
|
|
43
|
-
web_provider: duckduckgo # duckduckgo (keyless) | mock (tests)
|
|
43
|
+
web_provider: duckduckgo # duckduckgo (keyless) | tavily (keyed) | mock (tests)
|
|
44
|
+
tavily_api_key: null # or set TAVILY_API_KEY env var
|
|
44
45
|
web_min_results: 1 # if ZERO local hits, fall back to web
|
|
45
46
|
web_max_results: 5 # max web results to return
|
|
46
47
|
"""
|
|
@@ -107,6 +108,7 @@ def _ensure_defaults(config: dict[str, Any]):
|
|
|
107
108
|
retrieval = config.setdefault("retrieval", {})
|
|
108
109
|
retrieval.setdefault("web_fallback", False)
|
|
109
110
|
retrieval.setdefault("web_provider", "duckduckgo")
|
|
111
|
+
retrieval.setdefault("tavily_api_key", None)
|
|
110
112
|
retrieval.setdefault("web_min_results", 1)
|
|
111
113
|
retrieval.setdefault("web_max_results", 5)
|
|
112
114
|
|
|
@@ -126,6 +128,7 @@ def _apply_env_overrides(config: dict[str, Any]):
|
|
|
126
128
|
"MEMORIUS_DEFAULT_VAULT": ("vault", "default"),
|
|
127
129
|
"MEMORIUS_WEB_FALLBACK": ("retrieval", "web_fallback"),
|
|
128
130
|
"MEMORIUS_WEB_PROVIDER": ("retrieval", "web_provider"),
|
|
131
|
+
"MEMORIUS_TAVILY_API_KEY": ("retrieval", "tavily_api_key"),
|
|
129
132
|
}
|
|
130
133
|
for env_key, (section, key) in overrides.items():
|
|
131
134
|
val = os.environ.get(env_key)
|
|
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import html
|
|
18
18
|
import logging
|
|
19
|
+
import os
|
|
19
20
|
import urllib.parse
|
|
20
21
|
import urllib.request
|
|
21
22
|
from abc import ABC, abstractmethod
|
|
@@ -82,6 +83,68 @@ class DuckDuckGoProvider(WebSearchProvider):
|
|
|
82
83
|
return _parse_ddg_lite(raw, max_results)
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
class TavilyProvider(WebSearchProvider):
|
|
87
|
+
"""Tavily web search — API key required, best signal-to-noise for agents.
|
|
88
|
+
|
|
89
|
+
Key from ``api_key`` arg, config ``retrieval.tavily_api_key``, or
|
|
90
|
+
the ``TAVILY_API_KEY`` env var. Missing key -> warns + returns [].
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
name = "tavily"
|
|
94
|
+
_ENDPOINT = "https://api.tavily.com/search"
|
|
95
|
+
|
|
96
|
+
def __init__(self, api_key: str | None = None, timeout: float = 15.0):
|
|
97
|
+
self._api_key = api_key
|
|
98
|
+
self.timeout = timeout
|
|
99
|
+
|
|
100
|
+
def _resolve_key(self) -> str:
|
|
101
|
+
key = self._api_key or os.environ.get("TAVILY_API_KEY")
|
|
102
|
+
if not key:
|
|
103
|
+
raise RuntimeError(
|
|
104
|
+
"Tavily requires an API key: set TAVILY_API_KEY or "
|
|
105
|
+
"retrieval.tavily_api_key in config."
|
|
106
|
+
)
|
|
107
|
+
return key
|
|
108
|
+
|
|
109
|
+
def search(self, query: str, max_results: int = 5) -> list[WebResult]:
|
|
110
|
+
import json
|
|
111
|
+
try:
|
|
112
|
+
key = self._resolve_key()
|
|
113
|
+
except RuntimeError as e:
|
|
114
|
+
logger.warning(str(e))
|
|
115
|
+
return []
|
|
116
|
+
payload = {
|
|
117
|
+
"api_key": key,
|
|
118
|
+
"query": query,
|
|
119
|
+
"max_results": max_results,
|
|
120
|
+
"search_depth": "basic",
|
|
121
|
+
"include_answer": False,
|
|
122
|
+
"include_raw_content": False,
|
|
123
|
+
}
|
|
124
|
+
data = json.dumps(payload).encode()
|
|
125
|
+
req = urllib.request.Request(
|
|
126
|
+
self._ENDPOINT,
|
|
127
|
+
data=data,
|
|
128
|
+
headers={"Content-Type": "application/json"},
|
|
129
|
+
)
|
|
130
|
+
try:
|
|
131
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
132
|
+
body = json.loads(resp.read().decode("utf-8", "replace"))
|
|
133
|
+
except Exception as e: # network/timeout/auth — never crash the CLI
|
|
134
|
+
logger.warning("Tavily search failed: %s", e)
|
|
135
|
+
return []
|
|
136
|
+
out: list[WebResult] = []
|
|
137
|
+
for r in body.get("results", [])[:max_results]:
|
|
138
|
+
out.append(
|
|
139
|
+
WebResult(
|
|
140
|
+
title=r.get("title", ""),
|
|
141
|
+
url=r.get("url", ""),
|
|
142
|
+
snippet=r.get("content", ""),
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
return out
|
|
146
|
+
|
|
147
|
+
|
|
85
148
|
class MockProvider(WebSearchProvider):
|
|
86
149
|
"""Test double: returns canned results from a ``query -> [WebResult]`` map."""
|
|
87
150
|
|
|
@@ -164,6 +227,9 @@ def get_web_provider(
|
|
|
164
227
|
"""Build the configured web provider, or ``None`` if unavailable."""
|
|
165
228
|
retrieval = config.get("retrieval", {}) if isinstance(config, dict) else {}
|
|
166
229
|
name = (provider or retrieval.get("web_provider", "duckduckgo")).lower()
|
|
230
|
+
if name == "tavily":
|
|
231
|
+
retrieval = config.get("retrieval", {}) if isinstance(config, dict) else {}
|
|
232
|
+
return TavilyProvider(api_key=retrieval.get("tavily_api_key"))
|
|
167
233
|
if name == "mock":
|
|
168
234
|
return MockProvider()
|
|
169
235
|
if name == "duckduckgo":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memorius
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Self-contained memory vault for any AI agent — vector search, session diaries, and agent-agnostic hooks.
|
|
5
5
|
Author-email: dimonapatrick243 <dimona.patrick@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,6 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from memorius.web_search import (
|
|
10
10
|
DuckDuckGoProvider,
|
|
11
11
|
MockProvider,
|
|
12
|
+
TavilyProvider,
|
|
12
13
|
WebResult,
|
|
13
14
|
_parse_ddg_lite,
|
|
14
15
|
get_web_provider,
|
|
@@ -67,11 +68,51 @@ def test_get_web_provider_mock():
|
|
|
67
68
|
assert isinstance(p, MockProvider)
|
|
68
69
|
|
|
69
70
|
|
|
71
|
+
def test_get_web_provider_tavily():
|
|
72
|
+
p = get_web_provider(_cfg(web_provider="tavily"))
|
|
73
|
+
assert isinstance(p, TavilyProvider)
|
|
74
|
+
|
|
75
|
+
|
|
70
76
|
def test_get_web_provider_unknown_falls_back_to_duckduckgo(caplog):
|
|
71
77
|
p = get_web_provider(_cfg(web_provider="bogus"))
|
|
72
78
|
assert isinstance(p, DuckDuckGoProvider)
|
|
73
79
|
|
|
74
80
|
|
|
81
|
+
def test_tavily_provider_parses_response(monkeypatch):
|
|
82
|
+
import json
|
|
83
|
+
import urllib.request
|
|
84
|
+
|
|
85
|
+
fake = json.dumps({
|
|
86
|
+
"results": [
|
|
87
|
+
{"title": "Tavily A", "url": "https://a", "content": "snippet A"},
|
|
88
|
+
{"title": "Tavily B", "url": "https://b", "content": "snippet B"},
|
|
89
|
+
]
|
|
90
|
+
}).encode()
|
|
91
|
+
|
|
92
|
+
class _Resp:
|
|
93
|
+
def read(self):
|
|
94
|
+
return fake
|
|
95
|
+
def __enter__(self):
|
|
96
|
+
return self
|
|
97
|
+
def __exit__(self, *a):
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
monkeypatch.setattr(urllib.request, "urlopen", lambda *a, **k: _Resp())
|
|
101
|
+
p = TavilyProvider(api_key="tvly-x")
|
|
102
|
+
out = p.search("query", max_results=5)
|
|
103
|
+
assert len(out) == 2
|
|
104
|
+
assert out[0].title == "Tavily A"
|
|
105
|
+
assert out[0].url == "https://a"
|
|
106
|
+
assert out[0].snippet == "snippet A"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_tavily_provider_no_key_returns_empty(monkeypatch, capsys):
|
|
110
|
+
monkeypatch.delenv("TAVILY_API_KEY", raising=False)
|
|
111
|
+
p = TavilyProvider(api_key=None)
|
|
112
|
+
assert p.search("q") == [] # warns, never crashes
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
75
116
|
# ── Fallback decision ─────────────────────────────────────────────────────
|
|
76
117
|
def test_should_fallback_true_when_local_empty():
|
|
77
118
|
assert should_fallback(0, _cfg()) is True
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|