taguru 0.1.0__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.
@@ -0,0 +1,20 @@
1
+ /target
2
+ /data
3
+ .DS_Store
4
+
5
+ # SDK build/dev artifacts
6
+ sdk/**/node_modules/
7
+ sdk/**/dist/
8
+ sdk/**/.venv/
9
+ sdk/**/__pycache__/
10
+ sdk/**/*.egg-info/
11
+ sdk/**/.pytest_cache/
12
+ sdk/**/.mypy_cache/
13
+ sdk/**/.ruff_cache/
14
+
15
+ # SDK example artifacts (the examples workspace deliberately commits no
16
+ # lockfile: its SDK dependencies are file: paths into this repo)
17
+ examples/langchain/**/node_modules/
18
+ examples/langchain/package-lock.json
19
+ examples/langchain/.venv/
20
+ examples/langchain/**/__pycache__/
taguru-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Takashi Yamashina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
taguru-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: taguru
3
+ Version: 0.1.0
4
+ Summary: Python client SDK for the Taguru long-term semantic memory server
5
+ Project-URL: Homepage, https://github.com/t0k0sh1/taguru
6
+ Project-URL: Repository, https://github.com/t0k0sh1/taguru
7
+ Author: Takashi Yamashina
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: knowledge-graph,llm,memory,rag,taguru
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: httpx<1.0,>=0.27
21
+ Provides-Extra: dev
22
+ Requires-Dist: mypy>=1.11; extra == 'dev'
23
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: pyyaml>=6; extra == 'dev'
26
+ Requires-Dist: ruff>=0.6; extra == 'dev'
27
+ Requires-Dist: unasync>=0.6; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # taguru (Python SDK)
31
+
32
+ Official Python client SDK for the [Taguru](https://github.com/t0k0sh1/taguru)
33
+ long-term semantic memory server. The TypeScript SDK (`taguru` on npm) exposes
34
+ the identical surface — method names differ only by casing convention
35
+ (`search_passages` ↔ `searchPassages`); data fields are the wire's own
36
+ snake_case in both.
37
+
38
+ ```sh
39
+ pip install taguru
40
+ ```
41
+
42
+ ```python
43
+ from taguru import Taguru
44
+
45
+ client = Taguru() # defaults: $TAGURU_URL / $TAGURU_API_TOKEN, else http://127.0.0.1:8248
46
+ client.contexts.create("sake", description="青嶺酒造という架空の酒蔵の知識")
47
+
48
+ ctx = client.context("sake")
49
+ ctx.add_associations([
50
+ {"subject": "青嶺酒造", "label": "代表銘柄", "object": "青嶺", "weight": 1.0, "source": "docs/aomine.md"},
51
+ ])
52
+ ctx.store_passages({"docs/aomine.md": "青嶺酒造は1907年創業。代表銘柄は「青嶺」。"})
53
+
54
+ result = ctx.retrieve("青嶺酒造") # resolve → describe → activate → citations
55
+ hits = ctx.search_passages("1907年に創業した") # text lane (phrase as an answer)
56
+ ```
57
+
58
+ `AsyncTaguru` is the same surface with `async`/`await`. The behavioral
59
+ contract is the server's own protocol document — read it from the deployment
60
+ you target: `client.protocol()` (`GET /protocol`).
61
+
62
+ See the repository's `sdk/` directory for the full documentation, the
63
+ LangChain integration (`langchain-taguru`), and the cross-language surface
64
+ spec.
taguru-0.1.0/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # taguru (Python SDK)
2
+
3
+ Official Python client SDK for the [Taguru](https://github.com/t0k0sh1/taguru)
4
+ long-term semantic memory server. The TypeScript SDK (`taguru` on npm) exposes
5
+ the identical surface — method names differ only by casing convention
6
+ (`search_passages` ↔ `searchPassages`); data fields are the wire's own
7
+ snake_case in both.
8
+
9
+ ```sh
10
+ pip install taguru
11
+ ```
12
+
13
+ ```python
14
+ from taguru import Taguru
15
+
16
+ client = Taguru() # defaults: $TAGURU_URL / $TAGURU_API_TOKEN, else http://127.0.0.1:8248
17
+ client.contexts.create("sake", description="青嶺酒造という架空の酒蔵の知識")
18
+
19
+ ctx = client.context("sake")
20
+ ctx.add_associations([
21
+ {"subject": "青嶺酒造", "label": "代表銘柄", "object": "青嶺", "weight": 1.0, "source": "docs/aomine.md"},
22
+ ])
23
+ ctx.store_passages({"docs/aomine.md": "青嶺酒造は1907年創業。代表銘柄は「青嶺」。"})
24
+
25
+ result = ctx.retrieve("青嶺酒造") # resolve → describe → activate → citations
26
+ hits = ctx.search_passages("1907年に創業した") # text lane (phrase as an answer)
27
+ ```
28
+
29
+ `AsyncTaguru` is the same surface with `async`/`await`. The behavioral
30
+ contract is the server's own protocol document — read it from the deployment
31
+ you target: `client.protocol()` (`GET /protocol`).
32
+
33
+ See the repository's `sdk/` directory for the full documentation, the
34
+ LangChain integration (`langchain-taguru`), and the cross-language surface
35
+ spec.
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "taguru"
7
+ version = "0.1.0"
8
+ description = "Python client SDK for the Taguru long-term semantic memory server"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Takashi Yamashina" }]
13
+ keywords = ["taguru", "llm", "memory", "rag", "knowledge-graph"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = ["httpx>=0.27,<1.0"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/t0k0sh1/taguru"
28
+ Repository = "https://github.com/t0k0sh1/taguru"
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=8",
33
+ "pytest-asyncio>=0.24",
34
+ "ruff>=0.6",
35
+ "mypy>=1.11",
36
+ "unasync>=0.6",
37
+ "pyyaml>=6",
38
+ ]
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/taguru"]
42
+
43
+ [tool.pytest.ini_options]
44
+ asyncio_mode = "auto"
45
+ testpaths = ["tests"]
46
+
47
+ [tool.ruff]
48
+ line-length = 100
49
+ src = ["src"]
50
+
51
+ [tool.ruff.lint]
52
+ select = ["E", "F", "I", "UP", "B"]
53
+ ignore = [
54
+ # `object` is a wire-faithful parameter name on query(); shadowing the
55
+ # builtin there is a deliberate parity choice with the server vocabulary.
56
+ "A002",
57
+ ]
58
+
59
+ [tool.ruff.lint.per-file-ignores]
60
+ # Generated by scripts/generate_sync.py; import order and yield style are
61
+ # artifacts of the unasync token replacement.
62
+ "src/taguru/_sync/client.py" = ["I001", "UP028"]
63
+
64
+ [tool.mypy]
65
+ strict = true
66
+ files = ["src"]
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ """Check the Python SDK against sdk/spec/surface.yaml.
3
+
4
+ Verifies, for both the sync and Async class variants: every declared method
5
+ exists with exactly the declared positional (args) and keyword-only (options)
6
+ parameter names, and no undeclared public method exists. Run with the `taguru`
7
+ package importable (pip install -e sdk/python).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import inspect
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ import yaml
17
+
18
+ import taguru
19
+
20
+ SPEC_PATH = Path(__file__).resolve().parents[2] / "spec" / "surface.yaml"
21
+
22
+
23
+ def params_of(func: object) -> tuple[list[str], list[str]]:
24
+ signature = inspect.signature(func) # type: ignore[arg-type]
25
+ args: list[str] = []
26
+ options: list[str] = []
27
+ for name, param in signature.parameters.items():
28
+ if name == "self":
29
+ continue
30
+ if param.kind in (
31
+ inspect.Parameter.POSITIONAL_ONLY,
32
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
33
+ ):
34
+ args.append(name)
35
+ elif param.kind == inspect.Parameter.KEYWORD_ONLY:
36
+ options.append(name)
37
+ return args, options
38
+
39
+
40
+ def main() -> int:
41
+ spec = yaml.safe_load(SPEC_PATH.read_text(encoding="utf-8"))
42
+ errors: list[str] = []
43
+
44
+ for func_name, declared in spec.get("functions", {}).items():
45
+ func = getattr(taguru, func_name, None)
46
+ if func is None:
47
+ errors.append(f"missing module function: taguru.{func_name}")
48
+ continue
49
+ args, _options = params_of(func)
50
+ if args != declared.get("args", []):
51
+ errors.append(f"taguru.{func_name}: args {args} != spec {declared.get('args', [])}")
52
+
53
+ for class_name, methods in spec.get("classes", {}).items():
54
+ for prefix in ("", "Async"):
55
+ variant = prefix + class_name
56
+ cls = getattr(taguru, variant, None)
57
+ if cls is None:
58
+ errors.append(f"missing class: taguru.{variant}")
59
+ continue
60
+ declared_names = set(methods)
61
+ actual_names = {
62
+ name
63
+ for name, member in vars(cls).items()
64
+ if not name.startswith("_") and callable(member)
65
+ }
66
+ for missing in sorted(declared_names - actual_names):
67
+ errors.append(f"{variant}.{missing}: declared in spec but not implemented")
68
+ for extra in sorted(actual_names - declared_names):
69
+ errors.append(f"{variant}.{extra}: public method not declared in spec")
70
+ for method_name, entry in methods.items():
71
+ if method_name not in actual_names:
72
+ continue
73
+ entry = entry or {}
74
+ args, options = params_of(getattr(cls, method_name))
75
+ want_args = entry.get("args", [])
76
+ want_options = entry.get("options", [])
77
+ if args != want_args:
78
+ errors.append(f"{variant}.{method_name}: args {args} != spec {want_args}")
79
+ if options != want_options:
80
+ errors.append(
81
+ f"{variant}.{method_name}: options {options} != spec {want_options}"
82
+ )
83
+
84
+ if errors:
85
+ print(f"surface parity check FAILED ({len(errors)} problems):", file=sys.stderr)
86
+ for error in errors:
87
+ print(f" - {error}", file=sys.stderr)
88
+ return 1
89
+ print("python surface matches sdk/spec/surface.yaml")
90
+ return 0
91
+
92
+
93
+ if __name__ == "__main__":
94
+ sys.exit(main())
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ """Generate taguru._sync.client from taguru._async.client via unasync.
3
+
4
+ Run from anywhere; paths are resolved relative to this script. CI re-runs
5
+ this and fails if the committed sync client is stale.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ import unasync
16
+
17
+ ROOT = Path(__file__).resolve().parent.parent
18
+ ASYNC_DIR = ROOT / "src" / "taguru" / "_async"
19
+ SYNC_DIR = ROOT / "src" / "taguru" / "_sync"
20
+
21
+ REPLACEMENTS = {
22
+ "AsyncTaguru": "Taguru",
23
+ "AsyncContexts": "Contexts",
24
+ "AsyncContext": "Context",
25
+ "AsyncClient": "Client",
26
+ "AsyncIterator": "Iterator",
27
+ "aiter_bytes": "iter_bytes",
28
+ "aread": "read",
29
+ "aclose": "close",
30
+ "asyncio": "time",
31
+ "__aenter__": "__enter__",
32
+ "__aexit__": "__exit__",
33
+ }
34
+
35
+ SYNC_DOCSTRING = '''"""Sync client — GENERATED, do not edit.
36
+
37
+ This file is produced from ``taguru._async.client`` by
38
+ ``scripts/generate_sync.py`` (unasync). Edit the async source and regenerate.
39
+ """'''
40
+
41
+
42
+ def main() -> int:
43
+ rule = unasync.Rule(
44
+ fromdir=str(ASYNC_DIR) + "/",
45
+ todir=str(SYNC_DIR) + "/",
46
+ additional_replacements=REPLACEMENTS,
47
+ )
48
+ unasync.unasync_files([str(ASYNC_DIR / "client.py")], [rule])
49
+
50
+ target = SYNC_DIR / "client.py"
51
+ text = target.read_text(encoding="utf-8")
52
+
53
+ # Replace the module docstring (unasync only rewrites NAME tokens).
54
+ text = re.sub(r'\A""".*?"""', SYNC_DOCSTRING, text, count=1, flags=re.DOTALL)
55
+
56
+ # The asyncio -> time replacement leaves `import time` twice.
57
+ lines = text.splitlines(keepends=True)
58
+ seen_import_time = False
59
+ kept: list[str] = []
60
+ for line in lines:
61
+ if line.strip() == "import time":
62
+ if seen_import_time:
63
+ continue
64
+ seen_import_time = True
65
+ kept.append(line)
66
+ target.write_text("".join(kept), encoding="utf-8")
67
+
68
+ # unasync keeps the async source's line wrapping, but dropping `async `
69
+ # lets some signatures fit on one line — and CI holds the committed file
70
+ # to `ruff format --check`. Format here so both checks agree.
71
+ subprocess.run([sys.executable, "-m", "ruff", "format", "--quiet", str(target)], check=True)
72
+
73
+ init = SYNC_DIR / "__init__.py"
74
+ if not init.exists():
75
+ init.write_text("", encoding="utf-8")
76
+ print(f"generated {target.relative_to(ROOT)}")
77
+ return 0
78
+
79
+
80
+ if __name__ == "__main__":
81
+ sys.exit(main())
@@ -0,0 +1,142 @@
1
+ """Python client SDK for the Taguru long-term semantic memory server.
2
+
3
+ Quick start::
4
+
5
+ from taguru import Taguru
6
+
7
+ client = Taguru() # TAGURU_URL / TAGURU_API_TOKEN, else localhost:8248
8
+ ctx = client.context("sake")
9
+ hits = ctx.search_passages("酒蔵の創業年", limit=5)
10
+
11
+ ``AsyncTaguru`` is the identical async surface. The behavioral contract is
12
+ the server's own protocol document: ``client.protocol()`` (GET /protocol).
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from ._async.client import AsyncContext, AsyncContexts, AsyncTaguru
18
+ from ._errors import (
19
+ AuthenticationError,
20
+ ConflictError,
21
+ EmbeddingUnavailableError,
22
+ NotFoundError,
23
+ PayloadTooLargeError,
24
+ PermissionDeniedError,
25
+ RateLimitError,
26
+ RequestTimeoutError,
27
+ ServerError,
28
+ ServiceUnavailableError,
29
+ StorageFullError,
30
+ TaguruError,
31
+ TransportError,
32
+ UnexpectedStatusError,
33
+ ValidationError,
34
+ )
35
+ from ._models import (
36
+ Activation,
37
+ ActivationPage,
38
+ AliasEntry,
39
+ AliasPage,
40
+ Association,
41
+ Attribution,
42
+ BatchApplyResult,
43
+ Citation,
44
+ CompactOutcome,
45
+ ConceptDescription,
46
+ ContextMeta,
47
+ ContextPage,
48
+ ContextStats,
49
+ ContextUsage,
50
+ DirectoryEntry,
51
+ ExplorePage,
52
+ ImportOutcome,
53
+ LabelPage,
54
+ LabelUsage,
55
+ LaneEvidence,
56
+ MatchPage,
57
+ PassageHit,
58
+ PassageLanes,
59
+ PassageLookup,
60
+ Recollection,
61
+ RefreshBreakdown,
62
+ RefreshOutcome,
63
+ RetractOutcome,
64
+ RetrievalResult,
65
+ SourcePage,
66
+ StoredPassages,
67
+ TieredResolution,
68
+ TwinPair,
69
+ VocabularyAudit,
70
+ )
71
+ from ._shared import citation_key
72
+ from ._sync.client import Context, Contexts, Taguru
73
+ from ._types import AssocOp, QuestionSpec, SectionSpec
74
+
75
+ __version__ = "0.1.0"
76
+
77
+ __all__ = [
78
+ "__version__",
79
+ # clients
80
+ "Taguru",
81
+ "AsyncTaguru",
82
+ "Context",
83
+ "AsyncContext",
84
+ "Contexts",
85
+ "AsyncContexts",
86
+ "citation_key",
87
+ # request types
88
+ "AssocOp",
89
+ "QuestionSpec",
90
+ "SectionSpec",
91
+ # errors
92
+ "TaguruError",
93
+ "AuthenticationError",
94
+ "PermissionDeniedError",
95
+ "NotFoundError",
96
+ "ConflictError",
97
+ "ValidationError",
98
+ "PayloadTooLargeError",
99
+ "RequestTimeoutError",
100
+ "RateLimitError",
101
+ "ServerError",
102
+ "ServiceUnavailableError",
103
+ "StorageFullError",
104
+ "EmbeddingUnavailableError",
105
+ "TransportError",
106
+ "UnexpectedStatusError",
107
+ # models
108
+ "Activation",
109
+ "ActivationPage",
110
+ "AliasEntry",
111
+ "AliasPage",
112
+ "Association",
113
+ "Attribution",
114
+ "BatchApplyResult",
115
+ "Citation",
116
+ "CompactOutcome",
117
+ "ConceptDescription",
118
+ "ContextMeta",
119
+ "ContextPage",
120
+ "ContextStats",
121
+ "ContextUsage",
122
+ "DirectoryEntry",
123
+ "ExplorePage",
124
+ "ImportOutcome",
125
+ "LabelPage",
126
+ "LabelUsage",
127
+ "LaneEvidence",
128
+ "MatchPage",
129
+ "PassageHit",
130
+ "PassageLanes",
131
+ "PassageLookup",
132
+ "Recollection",
133
+ "RefreshBreakdown",
134
+ "RefreshOutcome",
135
+ "RetractOutcome",
136
+ "RetrievalResult",
137
+ "SourcePage",
138
+ "StoredPassages",
139
+ "TieredResolution",
140
+ "TwinPair",
141
+ "VocabularyAudit",
142
+ ]
File without changes