langchain-xache 0.1.0__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.
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: langchain-xache
3
+ Version: 0.1.0
4
+ Summary: LangChain integration for Xache Protocol - verifiable AI agent memory
5
+ Author-email: Xache Protocol <dev@xache.xyz>
6
+ License: MIT
7
+ Project-URL: Homepage, https://xache.xyz
8
+ Project-URL: Documentation, https://docs.xache.xyz
9
+ Project-URL: Repository, https://github.com/oliveskin/xache
10
+ Keywords: langchain,ai,agents,memory,blockchain,receipts,reputation,erc8004,x402
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: xache>=0.1.0
24
+ Requires-Dist: langchain>=0.1.0
25
+ Requires-Dist: pydantic>=2.0.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
29
+ Requires-Dist: black>=23.0.0; extra == "dev"
30
+ Requires-Dist: isort>=5.12.0; extra == "dev"
31
+
32
+ # langchain-xache
33
+
34
+ LangChain integration for [Xache Protocol](https://xache.xyz) - verifiable AI agent memory with cryptographic receipts, collective intelligence, and portable ERC-8004 reputation.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install langchain-xache
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ### One-Line Memory Replacement
45
+
46
+ ```python
47
+ # Before (standard LangChain)
48
+ from langchain.memory import ConversationBufferMemory
49
+ memory = ConversationBufferMemory()
50
+
51
+ # After (with Xache - one line change!)
52
+ from xache_langchain import XacheMemory
53
+ memory = XacheMemory(
54
+ wallet_address="0x...",
55
+ private_key="0x..."
56
+ )
57
+
58
+ # Everything else stays the same
59
+ agent = initialize_agent(tools, llm, memory=memory)
60
+ ```
61
+
62
+ ## Features
63
+
64
+ ### Memory Storage
65
+ Persistent memory that survives across sessions with cryptographic receipts:
66
+
67
+ ```python
68
+ from xache_langchain import XacheMemory
69
+
70
+ memory = XacheMemory(
71
+ wallet_address="0xYourWallet",
72
+ private_key="0xYourPrivateKey",
73
+ api_url="https://api.xache.xyz", # optional
74
+ chain="base" # or "solana"
75
+ )
76
+ ```
77
+
78
+ ### Retrieval (RAG)
79
+ Semantic search for retrieval-augmented generation:
80
+
81
+ ```python
82
+ from xache_langchain import XacheRetriever
83
+ from langchain.chains import RetrievalQA
84
+
85
+ retriever = XacheRetriever(
86
+ wallet_address="0x...",
87
+ private_key="0x...",
88
+ k=5 # number of documents
89
+ )
90
+
91
+ qa = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)
92
+ ```
93
+
94
+ ### Collective Intelligence
95
+ Query and contribute to shared knowledge:
96
+
97
+ ```python
98
+ from xache_langchain import XacheCollectiveContributeTool, XacheCollectiveQueryTool
99
+
100
+ # Add to your agent's tools
101
+ contribute = XacheCollectiveContributeTool(
102
+ wallet_address="0x...",
103
+ private_key="0x..."
104
+ )
105
+
106
+ query = XacheCollectiveQueryTool(
107
+ wallet_address="0x...",
108
+ private_key="0x..."
109
+ )
110
+
111
+ tools = [contribute, query, ...]
112
+ ```
113
+
114
+ ### Memory Extraction
115
+ Auto-extract memories from conversations:
116
+
117
+ ```python
118
+ from xache_langchain import XacheExtractor
119
+
120
+ extractor = XacheExtractor(
121
+ wallet_address="0x...",
122
+ private_key="0x...",
123
+ mode="xache-managed" # or "api-key" with your LLM key
124
+ )
125
+
126
+ result = extractor.extract(
127
+ trace="User asked about quantum computing...",
128
+ auto_store=True # automatically store extracted memories
129
+ )
130
+
131
+ print(f"Extracted {len(result.memories)} memories")
132
+ ```
133
+
134
+ ### Reputation
135
+ Check and verify agent reputation:
136
+
137
+ ```python
138
+ from xache_langchain import XacheReputationTool, XacheReputationChecker
139
+
140
+ # As a tool for your agent
141
+ rep_tool = XacheReputationTool(
142
+ wallet_address="0x...",
143
+ private_key="0x..."
144
+ )
145
+
146
+ # Or check other agents
147
+ checker = XacheReputationChecker(
148
+ wallet_address="0x...",
149
+ private_key="0x..."
150
+ )
151
+
152
+ other_rep = checker.check("did:agent:evm:0xOtherAgent...")
153
+ if other_rep.score >= 0.5:
154
+ print("Agent is trustworthy")
155
+ ```
156
+
157
+ ## Chat History
158
+
159
+ For more control over message history:
160
+
161
+ ```python
162
+ from xache_langchain import XacheChatMessageHistory
163
+ from langchain.memory import ConversationBufferMemory
164
+
165
+ history = XacheChatMessageHistory(
166
+ wallet_address="0x...",
167
+ private_key="0x...",
168
+ session_id="unique-session-id"
169
+ )
170
+
171
+ memory = ConversationBufferMemory(chat_memory=history)
172
+ ```
173
+
174
+ ## Pricing
175
+
176
+ All operations use x402 micropayments (auto-handled):
177
+
178
+ | Operation | Price |
179
+ |-----------|-------|
180
+ | Memory Store | $0.002 |
181
+ | Memory Retrieve | $0.003 |
182
+ | Collective Contribute | $0.002 |
183
+ | Collective Query | $0.011 |
184
+ | Extraction (managed) | $0.011 |
185
+
186
+ ## ERC-8004 Portable Reputation
187
+
188
+ Xache supports [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004) for portable, on-chain reputation. Enable it to make your agent's reputation verifiable across platforms.
189
+
190
+ ## Resources
191
+
192
+ - [Documentation](https://docs.xache.xyz)
193
+ - [API Reference](https://docs.xache.xyz/api)
194
+ - [GitHub](https://github.com/oliveskin/xache)
195
+ - [Discord](https://discord.gg/xache)
196
+
197
+ ## License
198
+
199
+ MIT
@@ -0,0 +1,12 @@
1
+ xache_langchain/__init__.py,sha256=OYzpiCnA75ToCTVmQcqut7kGygD9lQhjrUTiFZU5S88,1560
2
+ xache_langchain/_async_utils.py,sha256=Srhe4rJ1S4W6E0sDfgsTqMdfMFr0Je9scJ3cFNa8fWA,1699
3
+ xache_langchain/chat_history.py,sha256=MS6w8fyowWlLvvtZ0RBQWNygBytM3cnasZ6C0KwL4sE,6730
4
+ xache_langchain/collective.py,sha256=OyZKvfOx20ETK236P36fqAZ6ahLyos6FYs81IOKjti0,8092
5
+ xache_langchain/extraction.py,sha256=tFJtL3Sk1XK7UUSmlLw3eMsPA9Lh5kI-G8EhQ1Uiuf0,7407
6
+ xache_langchain/memory.py,sha256=71hxXcUtsyaAo67_xBmjel43EvhgDiCENcD6AvJxMBo,6196
7
+ xache_langchain/reputation.py,sha256=b1GVceH1QPqB7rjvTj295QllxiJ5W0rQ5rUoKJ5Np7A,7389
8
+ xache_langchain/retriever.py,sha256=qw7CSP7zR6g7mn9DUYHbId9S9K564kKXqKR1WnnHkP0,7607
9
+ langchain_xache-0.1.0.dist-info/METADATA,sha256=YXS1nXk1NsOstVUNgauJtq-Z_laum0LNEpy7_-U7Q-w,5308
10
+ langchain_xache-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
11
+ langchain_xache-0.1.0.dist-info/top_level.txt,sha256=in69PSq9agqGIAyShkm5ZVg9n0ks76QlD1tGaws9efA,16
12
+ langchain_xache-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ xache_langchain
@@ -0,0 +1,59 @@
1
+ """
2
+ LangChain integration for Xache Protocol
3
+ Drop-in memory, retrieval, and collective intelligence with verifiable receipts
4
+
5
+ Example:
6
+ ```python
7
+ # One-line memory replacement
8
+ from xache_langchain import XacheMemory
9
+
10
+ memory = XacheMemory(
11
+ wallet_address="0x...",
12
+ private_key="0x..."
13
+ )
14
+
15
+ # Everything else stays the same
16
+ agent = initialize_agent(tools, llm, memory=memory)
17
+ ```
18
+
19
+ All Xache features:
20
+ - Memory: Persistent, verifiable memory storage
21
+ - Retrieval: Semantic search for RAG pipelines
22
+ - Collective: Share and learn from agent community
23
+ - Extraction: Auto-extract memories from conversations
24
+ - Reputation: Portable ERC-8004 on-chain reputation
25
+ """
26
+
27
+ from .memory import XacheMemory, XacheConversationBufferMemory
28
+ from .chat_history import XacheChatMessageHistory
29
+ from .retriever import XacheRetriever
30
+ from .extraction import XacheExtractor, ExtractionResult
31
+ from .collective import (
32
+ XacheCollectiveContributeTool,
33
+ XacheCollectiveQueryTool,
34
+ )
35
+ from .reputation import (
36
+ XacheReputationTool,
37
+ XacheReputationChecker,
38
+ ReputationResult,
39
+ )
40
+
41
+ __version__ = "0.1.0"
42
+ __all__ = [
43
+ # Memory
44
+ "XacheMemory",
45
+ "XacheConversationBufferMemory",
46
+ "XacheChatMessageHistory",
47
+ # Retrieval
48
+ "XacheRetriever",
49
+ # Extraction
50
+ "XacheExtractor",
51
+ "ExtractionResult",
52
+ # Collective Intelligence
53
+ "XacheCollectiveContributeTool",
54
+ "XacheCollectiveQueryTool",
55
+ # Reputation
56
+ "XacheReputationTool",
57
+ "XacheReputationChecker",
58
+ "ReputationResult",
59
+ ]
@@ -0,0 +1,56 @@
1
+ """
2
+ Async utilities for running coroutines in any context.
3
+ Handles Jupyter notebooks, async frameworks, and sync contexts.
4
+ """
5
+
6
+ import asyncio
7
+ from typing import TypeVar, Coroutine, Any
8
+
9
+ T = TypeVar('T')
10
+
11
+
12
+ def run_sync(coro: Coroutine[Any, Any, T]) -> T:
13
+ """
14
+ Run an async coroutine synchronously in any context.
15
+
16
+ Works correctly in:
17
+ - Regular sync Python scripts
18
+ - Jupyter notebooks (where event loop is already running)
19
+ - Async frameworks (FastAPI, etc.)
20
+
21
+ Args:
22
+ coro: The coroutine to run
23
+
24
+ Returns:
25
+ The result of the coroutine
26
+ """
27
+ try:
28
+ # Check if there's already a running event loop
29
+ loop = asyncio.get_running_loop()
30
+ except RuntimeError:
31
+ # No running loop - we can safely use asyncio.run()
32
+ return asyncio.run(coro)
33
+
34
+ # There's a running loop - we need to handle this carefully
35
+ # This happens in Jupyter notebooks, async frameworks, etc.
36
+ try:
37
+ # Try using nest_asyncio for Jupyter compatibility
38
+ import nest_asyncio
39
+ nest_asyncio.apply()
40
+ return loop.run_until_complete(coro)
41
+ except ImportError:
42
+ # nest_asyncio not installed - use thread pool as fallback
43
+ import concurrent.futures
44
+
45
+ def _run_in_thread():
46
+ # Create a new event loop for this thread
47
+ new_loop = asyncio.new_event_loop()
48
+ asyncio.set_event_loop(new_loop)
49
+ try:
50
+ return new_loop.run_until_complete(coro)
51
+ finally:
52
+ new_loop.close()
53
+
54
+ with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
55
+ future = pool.submit(_run_in_thread)
56
+ return future.result()
@@ -0,0 +1,194 @@
1
+ """
2
+ Xache Chat Message History for LangChain
3
+ Persistent chat history with verifiable receipts
4
+ """
5
+
6
+ import os
7
+ from typing import List, Optional
8
+ from langchain.schema import BaseMessage, HumanMessage, AIMessage, SystemMessage
9
+ from langchain.memory.chat_message_histories.base import BaseChatMessageHistory
10
+
11
+ from xache import XacheClient
12
+ from ._async_utils import run_sync
13
+
14
+
15
+ class XacheChatMessageHistory(BaseChatMessageHistory):
16
+ """
17
+ Chat message history backed by Xache Protocol.
18
+
19
+ Stores chat messages with cryptographic receipts and optional
20
+ reputation tracking.
21
+
22
+ Example:
23
+ ```python
24
+ from xache_langchain import XacheChatMessageHistory
25
+ from langchain.memory import ConversationBufferMemory
26
+
27
+ history = XacheChatMessageHistory(
28
+ wallet_address="0x...",
29
+ private_key="0x...",
30
+ session_id="unique-session-id"
31
+ )
32
+
33
+ memory = ConversationBufferMemory(chat_memory=history)
34
+ ```
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ wallet_address: str,
40
+ private_key: str,
41
+ session_id: str,
42
+ api_url: Optional[str] = None,
43
+ chain: str = "base",
44
+ ):
45
+ """
46
+ Initialize Xache chat history.
47
+
48
+ Args:
49
+ wallet_address: Wallet address for authentication
50
+ private_key: Private key for signing
51
+ session_id: Unique session identifier
52
+ api_url: Xache API URL
53
+ chain: Blockchain (base, solana)
54
+ """
55
+ self.wallet_address = wallet_address
56
+ self.private_key = private_key
57
+ self.session_id = session_id
58
+ self.api_url = api_url or os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
59
+ self.chain = chain
60
+
61
+ # Build DID
62
+ chain_prefix = "sol" if chain == "solana" else "evm"
63
+ self.did = f"did:agent:{chain_prefix}:{wallet_address.lower()}"
64
+
65
+ self._client = XacheClient(
66
+ api_url=api_url,
67
+ did=self.did,
68
+ private_key=private_key,
69
+ )
70
+
71
+ @property
72
+ def messages(self) -> List[BaseMessage]:
73
+ """Retrieve all messages from Xache"""
74
+
75
+ async def _get_messages():
76
+ async with self._client as client:
77
+ # Retrieve memories for this session (filtered by context)
78
+ memories = await client.memory.list(
79
+ limit=1000,
80
+ context=f"chat:session:{self.session_id}"
81
+ )
82
+
83
+ messages = []
84
+ result_memories = memories.get("memories", []) if isinstance(memories, dict) else memories
85
+ for mem in sorted(result_memories, key=lambda x: x.get("created_at", "")):
86
+ content = mem.get("content", "")
87
+ metadata = mem.get("metadata", {})
88
+ role = metadata.get("role", "human")
89
+
90
+ if role == "human":
91
+ messages.append(HumanMessage(content=content))
92
+ elif role == "ai":
93
+ messages.append(AIMessage(content=content))
94
+ elif role == "system":
95
+ messages.append(SystemMessage(content=content))
96
+
97
+ return messages
98
+
99
+ return run_sync(_get_messages())
100
+
101
+ def add_message(self, message: BaseMessage) -> None:
102
+ """Add a message to the history"""
103
+
104
+ # Determine role from message type
105
+ if isinstance(message, HumanMessage):
106
+ role = "human"
107
+ elif isinstance(message, AIMessage):
108
+ role = "ai"
109
+ elif isinstance(message, SystemMessage):
110
+ role = "system"
111
+ else:
112
+ role = "unknown"
113
+
114
+ async def _add():
115
+ async with self._client as client:
116
+ await client.memory.store(
117
+ content=message.content,
118
+ context=f"chat:session:{self.session_id}",
119
+ metadata={
120
+ "role": role,
121
+ "session_id": self.session_id,
122
+ "source": "langchain",
123
+ "message_type": message.__class__.__name__,
124
+ }
125
+ )
126
+
127
+ run_sync(_add())
128
+
129
+ def add_user_message(self, message: str) -> None:
130
+ """Add a user message"""
131
+ self.add_message(HumanMessage(content=message))
132
+
133
+ def add_ai_message(self, message: str) -> None:
134
+ """Add an AI message"""
135
+ self.add_message(AIMessage(content=message))
136
+
137
+ def clear(self) -> None:
138
+ """Clear message history (soft delete) using bulk deletion"""
139
+ self.bulk_delete()
140
+
141
+ def bulk_delete(self, storage_keys: Optional[List[str]] = None) -> dict:
142
+ """
143
+ Delete multiple messages from history.
144
+
145
+ Args:
146
+ storage_keys: List of storage keys to delete. If None, deletes all messages
147
+ in this session.
148
+
149
+ Returns:
150
+ dict with 'deleted' count and 'errors' list
151
+ """
152
+ import asyncio
153
+
154
+ async def _bulk_delete():
155
+ async with self._client as client:
156
+ # Get storage keys if not provided
157
+ keys_to_delete = storage_keys
158
+ if keys_to_delete is None:
159
+ result = await client.memory.list(
160
+ limit=1000,
161
+ context=f"chat:session:{self.session_id}"
162
+ )
163
+ memories = result.get("memories", []) if isinstance(result, dict) else result
164
+ keys_to_delete = [
165
+ mem.get("storage_key") for mem in memories
166
+ if mem.get("storage_key")
167
+ ]
168
+
169
+ if not keys_to_delete:
170
+ return {"deleted": 0, "errors": []}
171
+
172
+ # Delete in parallel using asyncio.gather with return_exceptions
173
+ async def safe_delete(key):
174
+ try:
175
+ await client.memory.delete(key)
176
+ return {"key": key, "success": True}
177
+ except Exception as e:
178
+ return {"key": key, "success": False, "error": str(e)}
179
+
180
+ results = await asyncio.gather(
181
+ *[safe_delete(key) for key in keys_to_delete],
182
+ return_exceptions=True
183
+ )
184
+
185
+ deleted = sum(1 for r in results if isinstance(r, dict) and r.get("success"))
186
+ errors = [
187
+ r for r in results
188
+ if isinstance(r, dict) and not r.get("success")
189
+ or isinstance(r, Exception)
190
+ ]
191
+
192
+ return {"deleted": deleted, "errors": errors}
193
+
194
+ return run_sync(_bulk_delete())