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,237 @@
1
+ """
2
+ Xache Reputation for LangChain
3
+ Portable, verifiable agent reputation with ERC-8004 support
4
+ """
5
+
6
+ from typing import Optional, Dict, Any
7
+ from langchain.tools import BaseTool
8
+ from pydantic import BaseModel, Field
9
+
10
+ from xache import XacheClient
11
+
12
+
13
+ class ReputationResult(BaseModel):
14
+ """Reputation query result"""
15
+ score: float = Field(description="Reputation score (0-1)")
16
+ level: str = Field(description="Reputation level")
17
+ total_contributions: int = Field(default=0)
18
+ total_payments: int = Field(default=0)
19
+ erc8004_enabled: bool = Field(default=False)
20
+ erc8004_agent_id: Optional[str] = Field(default=None)
21
+
22
+
23
+ class XacheReputationTool(BaseTool):
24
+ """
25
+ LangChain tool for checking Xache reputation.
26
+
27
+ Use this tool to check your agent's reputation score and status.
28
+ High reputation unlocks benefits like lower prices and higher trust.
29
+
30
+ Example:
31
+ ```python
32
+ from xache_langchain import XacheReputationTool
33
+
34
+ rep_tool = XacheReputationTool(
35
+ wallet_address="0x...",
36
+ private_key="0x..."
37
+ )
38
+
39
+ # Use in agent
40
+ tools = [rep_tool, ...]
41
+ ```
42
+ """
43
+
44
+ name: str = "xache_check_reputation"
45
+ description: str = (
46
+ "Check your current reputation score and status. "
47
+ "Returns your score (0-1), level, and ERC-8004 on-chain status. "
48
+ "Higher reputation means lower costs and more trust from other agents."
49
+ )
50
+
51
+ # Xache configuration
52
+ api_url: str = "https://api.xache.xyz"
53
+ wallet_address: str
54
+ private_key: str
55
+ chain: str = "base"
56
+
57
+ _client: Optional[XacheClient] = None
58
+
59
+ class Config:
60
+ arbitrary_types_allowed = True
61
+ underscore_attrs_are_private = True
62
+
63
+ def __init__(self, **kwargs):
64
+ super().__init__(**kwargs)
65
+ chain_prefix = "sol" if self.chain == "solana" else "evm"
66
+ did = f"did:agent:{chain_prefix}:{self.wallet_address.lower()}"
67
+ self._client = XacheClient(
68
+ api_url=self.api_url,
69
+ did=did,
70
+ private_key=self.private_key,
71
+ )
72
+
73
+ def _run(self) -> str:
74
+ """Check reputation"""
75
+ import asyncio
76
+
77
+ async def _check():
78
+ async with self._client as client:
79
+ result = await client.reputation.get_score()
80
+ return result
81
+
82
+ try:
83
+ loop = asyncio.get_event_loop()
84
+ if loop.is_running():
85
+ import concurrent.futures
86
+ with concurrent.futures.ThreadPoolExecutor() as pool:
87
+ result = pool.submit(asyncio.run, _check()).result()
88
+ else:
89
+ result = loop.run_until_complete(_check())
90
+ except RuntimeError:
91
+ result = asyncio.run(_check())
92
+
93
+ score = result.get("score", 0)
94
+ level = self._get_level(score)
95
+
96
+ output = f"Reputation Score: {score:.2f}/1.00 ({level})\n"
97
+
98
+ if result.get("erc8004AgentId"):
99
+ output += f"ERC-8004 Status: Enabled (Agent ID: {result['erc8004AgentId']})\n"
100
+ output += "Your reputation is verifiable on-chain!"
101
+ else:
102
+ output += "ERC-8004 Status: Not enabled\n"
103
+ output += "Enable ERC-8004 to make your reputation portable and verifiable."
104
+
105
+ return output
106
+
107
+ async def _arun(self) -> str:
108
+ """Async check reputation"""
109
+ async with self._client as client:
110
+ result = await client.reputation.get_score()
111
+
112
+ score = result.get("score", 0)
113
+ level = self._get_level(score)
114
+
115
+ output = f"Reputation Score: {score:.2f}/1.00 ({level})\n"
116
+
117
+ if result.get("erc8004AgentId"):
118
+ output += f"ERC-8004 Status: Enabled (Agent ID: {result['erc8004AgentId']})\n"
119
+ else:
120
+ output += "ERC-8004 Status: Not enabled\n"
121
+
122
+ return output
123
+
124
+ def _get_level(self, score: float) -> str:
125
+ """Get reputation level from score"""
126
+ if score >= 0.9:
127
+ return "Elite"
128
+ elif score >= 0.7:
129
+ return "Trusted"
130
+ elif score >= 0.5:
131
+ return "Established"
132
+ elif score >= 0.3:
133
+ return "Developing"
134
+ else:
135
+ return "New"
136
+
137
+
138
+ class XacheReputationChecker:
139
+ """
140
+ Utility class for checking reputation of any agent.
141
+
142
+ Useful for verifying other agents before interacting.
143
+
144
+ Example:
145
+ ```python
146
+ from xache_langchain import XacheReputationChecker
147
+
148
+ checker = XacheReputationChecker(
149
+ wallet_address="0x...",
150
+ private_key="0x..."
151
+ )
152
+
153
+ # Check another agent's reputation
154
+ other_rep = checker.check("did:agent:evm:0xOtherAgent...")
155
+ if other_rep.score >= 0.5:
156
+ print("Agent is trustworthy")
157
+ ```
158
+ """
159
+
160
+ def __init__(
161
+ self,
162
+ wallet_address: str,
163
+ private_key: str,
164
+ api_url: str = "https://api.xache.xyz",
165
+ chain: str = "base",
166
+ ):
167
+ self.api_url = api_url
168
+ self.chain = chain
169
+
170
+ chain_prefix = "sol" if chain == "solana" else "evm"
171
+ self.did = f"did:agent:{chain_prefix}:{wallet_address.lower()}"
172
+
173
+ self._client = XacheClient(
174
+ api_url=api_url,
175
+ did=self.did,
176
+ private_key=private_key,
177
+ )
178
+
179
+ def check(self, agent_did: str) -> ReputationResult:
180
+ """Check an agent's reputation"""
181
+ import asyncio
182
+
183
+ async def _check():
184
+ async with self._client as client:
185
+ result = await client.reputation.get_score(agent_did=agent_did)
186
+ return result
187
+
188
+ try:
189
+ loop = asyncio.get_event_loop()
190
+ if loop.is_running():
191
+ import concurrent.futures
192
+ with concurrent.futures.ThreadPoolExecutor() as pool:
193
+ result = pool.submit(asyncio.run, _check()).result()
194
+ else:
195
+ result = loop.run_until_complete(_check())
196
+ except RuntimeError:
197
+ result = asyncio.run(_check())
198
+
199
+ score = result.get("score", 0)
200
+
201
+ return ReputationResult(
202
+ score=score,
203
+ level=self._get_level(score),
204
+ total_contributions=result.get("totalContributions", 0),
205
+ total_payments=result.get("totalPayments", 0),
206
+ erc8004_enabled=bool(result.get("erc8004AgentId")),
207
+ erc8004_agent_id=result.get("erc8004AgentId"),
208
+ )
209
+
210
+ async def acheck(self, agent_did: str) -> ReputationResult:
211
+ """Async check an agent's reputation"""
212
+ async with self._client as client:
213
+ result = await client.reputation.get_score(agent_did=agent_did)
214
+
215
+ score = result.get("score", 0)
216
+
217
+ return ReputationResult(
218
+ score=score,
219
+ level=self._get_level(score),
220
+ total_contributions=result.get("totalContributions", 0),
221
+ total_payments=result.get("totalPayments", 0),
222
+ erc8004_enabled=bool(result.get("erc8004AgentId")),
223
+ erc8004_agent_id=result.get("erc8004AgentId"),
224
+ )
225
+
226
+ def _get_level(self, score: float) -> str:
227
+ """Get reputation level from score"""
228
+ if score >= 0.9:
229
+ return "Elite"
230
+ elif score >= 0.7:
231
+ return "Trusted"
232
+ elif score >= 0.5:
233
+ return "Established"
234
+ elif score >= 0.3:
235
+ return "Developing"
236
+ else:
237
+ return "New"
@@ -0,0 +1,221 @@
1
+ """
2
+ Xache Retriever for LangChain
3
+ Semantic memory retrieval for RAG pipelines
4
+ """
5
+
6
+ import os
7
+ from typing import List, Optional, Dict, Any
8
+ from langchain.schema import BaseRetriever, Document
9
+ from langchain.callbacks.manager import CallbackManagerForRetrieverRun
10
+ from pydantic import Field
11
+
12
+ from xache import XacheClient
13
+ from ._async_utils import run_sync
14
+
15
+
16
+ class XacheRetriever(BaseRetriever):
17
+ """
18
+ LangChain retriever backed by Xache Protocol.
19
+
20
+ Retrieves semantically relevant memories for RAG pipelines.
21
+ Each retrieval is paid via x402 micropayments.
22
+
23
+ Example:
24
+ ```python
25
+ from xache_langchain import XacheRetriever
26
+ from langchain.chains import RetrievalQA
27
+
28
+ retriever = XacheRetriever(
29
+ wallet_address="0x...",
30
+ private_key="0x...",
31
+ k=5
32
+ )
33
+
34
+ qa = RetrievalQA.from_chain_type(
35
+ llm=llm,
36
+ retriever=retriever
37
+ )
38
+ ```
39
+
40
+ For collective intelligence retrieval:
41
+ ```python
42
+ retriever = XacheRetriever(
43
+ wallet_address="0x...",
44
+ private_key="0x...",
45
+ collective_id="research-insights", # Query collective
46
+ k=10
47
+ )
48
+ ```
49
+ """
50
+
51
+ # Xache configuration
52
+ api_url: str = Field(
53
+ default_factory=lambda: os.environ.get("XACHE_API_URL", "https://api.xache.xyz")
54
+ )
55
+ wallet_address: str = Field(...)
56
+ private_key: str = Field(...)
57
+ chain: str = Field(default="base")
58
+
59
+ # Retrieval configuration
60
+ k: int = Field(default=5, description="Number of documents to retrieve")
61
+ collective_id: Optional[str] = Field(
62
+ default=None,
63
+ description="Collective ID for collective intelligence queries"
64
+ )
65
+ include_metadata: bool = Field(default=True)
66
+ min_relevance: float = Field(
67
+ default=0.0,
68
+ description="Minimum relevance score (0-1)"
69
+ )
70
+
71
+ # Internal
72
+ _client: Optional[XacheClient] = None
73
+
74
+ class Config:
75
+ arbitrary_types_allowed = True
76
+ underscore_attrs_are_private = True
77
+
78
+ def __init__(self, **kwargs):
79
+ super().__init__(**kwargs)
80
+ self._init_client()
81
+
82
+ def _init_client(self):
83
+ """Initialize Xache client"""
84
+ chain_prefix = "sol" if self.chain == "solana" else "evm"
85
+ did = f"did:agent:{chain_prefix}:{self.wallet_address.lower()}"
86
+
87
+ self._client = XacheClient(
88
+ api_url=self.api_url,
89
+ did=did,
90
+ private_key=self.private_key,
91
+ )
92
+
93
+ def _get_relevant_documents(
94
+ self,
95
+ query: str,
96
+ *,
97
+ run_manager: CallbackManagerForRetrieverRun,
98
+ ) -> List[Document]:
99
+ """Retrieve relevant documents from Xache"""
100
+
101
+ async def _retrieve():
102
+ async with self._client as client:
103
+ if self.collective_id:
104
+ # Query collective intelligence
105
+ result = await client.collective.query(
106
+ collective_id=self.collective_id,
107
+ query=query,
108
+ limit=self.k
109
+ )
110
+
111
+ documents = []
112
+ for item in result.get("results", []):
113
+ content = item.get("content", "")
114
+ metadata = {
115
+ "source": "xache_collective",
116
+ "collective_id": self.collective_id,
117
+ "contributor_did": item.get("contributor_did"),
118
+ "relevance": item.get("relevance", 0),
119
+ "receipt_id": item.get("receipt_id"),
120
+ }
121
+ if self.include_metadata:
122
+ metadata.update(item.get("metadata", {}))
123
+
124
+ if item.get("relevance", 1) >= self.min_relevance:
125
+ documents.append(Document(
126
+ page_content=content,
127
+ metadata=metadata
128
+ ))
129
+
130
+ return documents
131
+ else:
132
+ # Query personal memories
133
+ result = await client.memory.retrieve(
134
+ query=query,
135
+ limit=self.k
136
+ )
137
+
138
+ documents = []
139
+ # Fix: Correctly normalize result - API returns {"memories": [...]}
140
+ memories = result.get("memories", []) if isinstance(result, dict) else []
141
+
142
+ for mem in memories:
143
+ content = mem.get("content", "")
144
+ metadata = {
145
+ "source": "xache_memory",
146
+ "storage_key": mem.get("storage_key"),
147
+ "created_at": mem.get("created_at"),
148
+ "receipt_id": mem.get("receipt_id"),
149
+ }
150
+ if self.include_metadata:
151
+ metadata.update(mem.get("metadata", {}))
152
+
153
+ documents.append(Document(
154
+ page_content=content,
155
+ metadata=metadata
156
+ ))
157
+
158
+ return documents
159
+
160
+ return run_sync(_retrieve())
161
+
162
+ async def _aget_relevant_documents(
163
+ self,
164
+ query: str,
165
+ *,
166
+ run_manager: CallbackManagerForRetrieverRun,
167
+ ) -> List[Document]:
168
+ """Async retrieve relevant documents"""
169
+ async with self._client as client:
170
+ if self.collective_id:
171
+ result = await client.collective.query(
172
+ collective_id=self.collective_id,
173
+ query=query,
174
+ limit=self.k
175
+ )
176
+
177
+ documents = []
178
+ for item in result.get("results", []):
179
+ content = item.get("content", "")
180
+ metadata = {
181
+ "source": "xache_collective",
182
+ "collective_id": self.collective_id,
183
+ "contributor_did": item.get("contributor_did"),
184
+ "relevance": item.get("relevance", 0),
185
+ }
186
+ if self.include_metadata:
187
+ metadata.update(item.get("metadata", {}))
188
+
189
+ if item.get("relevance", 1) >= self.min_relevance:
190
+ documents.append(Document(
191
+ page_content=content,
192
+ metadata=metadata
193
+ ))
194
+
195
+ return documents
196
+ else:
197
+ result = await client.memory.retrieve(
198
+ query=query,
199
+ limit=self.k
200
+ )
201
+
202
+ documents = []
203
+ # Fix: Correctly normalize result - API returns {"memories": [...]}
204
+ memories = result.get("memories", []) if isinstance(result, dict) else []
205
+
206
+ for mem in memories:
207
+ content = mem.get("content", "")
208
+ metadata = {
209
+ "source": "xache_memory",
210
+ "storage_key": mem.get("storage_key"),
211
+ "created_at": mem.get("created_at"),
212
+ }
213
+ if self.include_metadata:
214
+ metadata.update(mem.get("metadata", {}))
215
+
216
+ documents.append(Document(
217
+ page_content=content,
218
+ metadata=metadata
219
+ ))
220
+
221
+ return documents