feedo-sdk 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.
feedo/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .client import FeedoClient
2
+ from .router import NodeRouter
3
+
4
+ __all__ = ["FeedoClient", "NodeRouter"]
feedo/client.py ADDED
@@ -0,0 +1,13 @@
1
+ from typing import List, Optional
2
+ from .router import NodeRouter
3
+ from .modules.search import SearchModule
4
+ from .modules.consensus import ConsensusModule
5
+ from .modules.storage import StorageModule
6
+
7
+ class FeedoClient:
8
+ def __init__(self, search_seeds: Optional[List[str]] = None, consensus_seeds: Optional[List[str]] = None, storage_seeds: Optional[List[str]] = None):
9
+ self.router = NodeRouter(search_seeds, consensus_seeds, storage_seeds)
10
+
11
+ self.search = SearchModule(self.router)
12
+ self.consensus = ConsensusModule(self.router)
13
+ self.storage = StorageModule(self.router)
@@ -0,0 +1 @@
1
+ # Init modules
@@ -0,0 +1,46 @@
1
+ import httpx
2
+ from typing import Dict, Any, Optional
3
+ from ..router import NodeRouter
4
+
5
+ class ConsensusModule:
6
+ def __init__(self, router: NodeRouter):
7
+ self.router = router
8
+
9
+ async def _request(self, method: str, path: str, json: Optional[Dict] = None) -> Any:
10
+ base_url = await self.router.get_consensus_node()
11
+ url = f"{base_url}{path}"
12
+
13
+ async with httpx.AsyncClient() as client:
14
+ try:
15
+ response = await client.request(method, url, json=json)
16
+ response.raise_for_status()
17
+ return response.json()
18
+ except Exception:
19
+ print(f"Consensus request failed on {base_url}, finding new node...")
20
+ self.router.invalidate_consensus_node()
21
+ base_url = await self.router.get_consensus_node()
22
+ url = f"{base_url}{path}"
23
+ response = await client.request(method, url, json=json)
24
+ response.raise_for_status()
25
+ return response.json()
26
+
27
+ async def resolve_name(self, name: str):
28
+ return await self._request("GET", f"/resolve/{name}")
29
+
30
+ async def resolve_cid(self, cid: str):
31
+ return await self._request("GET", f"/resolve_cid/{cid}")
32
+
33
+ async def get_did_balance(self, did: str):
34
+ return await self._request("GET", f"/did/{did}/balance")
35
+
36
+ async def register_did(self, pubkey_hex: str, signature_hex: str):
37
+ return await self._request("POST", "/did/register", json={"pubkey_hex": pubkey_hex, "signature_hex": signature_hex})
38
+
39
+ async def register_name(self, name: str, did: str, cid: str, signature_hex: str):
40
+ return await self._request("POST", "/name/register", json={"name": name, "did": did, "cid": cid, "signature_hex": signature_hex})
41
+
42
+ async def update_name_cid(self, name: str, new_cid: str, signature_hex: str):
43
+ return await self._request("POST", "/name/update_cid", json={"name": name, "new_cid": new_cid, "signature_hex": signature_hex})
44
+
45
+ async def list_grants(self):
46
+ return await self._request("GET", "/grants")
@@ -0,0 +1,40 @@
1
+ import httpx
2
+ from typing import Dict, Any, Optional
3
+ from ..router import NodeRouter
4
+
5
+ class SearchModule:
6
+ def __init__(self, router: NodeRouter):
7
+ self.router = router
8
+
9
+ async def _request(self, method: str, path: str, json: Optional[Dict] = None, params: Optional[Dict] = None) -> Any:
10
+ base_url = await self.router.get_search_node()
11
+ url = f"{base_url}{path}"
12
+
13
+ async with httpx.AsyncClient() as client:
14
+ try:
15
+ response = await client.request(method, url, json=json, params=params)
16
+ response.raise_for_status()
17
+ return response.json()
18
+ except Exception as e:
19
+ print(f"Search request failed on {base_url}, finding new node...")
20
+ self.router.invalidate_search_node()
21
+ base_url = await self.router.get_search_node()
22
+ url = f"{base_url}{path}"
23
+ response = await client.request(method, url, json=json, params=params)
24
+ response.raise_for_status()
25
+ return response.json()
26
+
27
+ async def query(self, query_text: str, limit: int = 10):
28
+ return await self._request("GET", "/query", params={"q": query_text, "limit": limit})
29
+
30
+ async def index_document(self, content: str, metadata: Optional[Dict] = None):
31
+ return await self._request("POST", "/index_document", json={"content": content, "metadata": metadata or {}})
32
+
33
+ async def deploy_proxy(self, directory_path: str, domain: str):
34
+ return await self._request("POST", "/proxy/publish_feedo", json={"source_dir": directory_path, "domain": domain})
35
+
36
+ async def unpin(self, cid: str):
37
+ return await self._request("DELETE", f"/proxy/unpin_feedo/{cid}")
38
+
39
+ async def get_stats(self):
40
+ return await self._request("GET", "/explorer/stats")
@@ -0,0 +1,44 @@
1
+ import httpx
2
+ from typing import Dict, Any, Optional
3
+ from ..router import NodeRouter
4
+
5
+ class StorageModule:
6
+ def __init__(self, router: NodeRouter):
7
+ self.router = router
8
+
9
+ async def _request(self, method: str, path: str, json: Optional[Dict] = None, data: Any = None, files: Any = None) -> Any:
10
+ base_url = await self.router.get_storage_node()
11
+ url = f"{base_url}{path}"
12
+
13
+ async with httpx.AsyncClient() as client:
14
+ try:
15
+ response = await client.request(method, url, json=json, data=data, files=files)
16
+ response.raise_for_status()
17
+ # download endpoint might not return json
18
+ if response.headers.get("content-type") == "application/json":
19
+ return response.json()
20
+ return response.content
21
+ except Exception:
22
+ print(f"Storage request failed on {base_url}, finding new node...")
23
+ self.router.invalidate_storage_node()
24
+ base_url = await self.router.get_storage_node()
25
+ url = f"{base_url}{path}"
26
+ response = await client.request(method, url, json=json, data=data, files=files)
27
+ response.raise_for_status()
28
+ if response.headers.get("content-type") == "application/json":
29
+ return response.json()
30
+ return response.content
31
+
32
+ async def upload_file(self, file_path: str, filename: str = "file"):
33
+ with open(file_path, "rb") as f:
34
+ files = {"file": (filename, f)}
35
+ return await self._request("POST", "/upload", files=files)
36
+
37
+ async def download_file(self, hash_id: str) -> bytes:
38
+ return await self._request("GET", f"/download/{hash_id}")
39
+
40
+ async def ingest_json(self, payload: Dict):
41
+ return await self._request("POST", "/api/v1/ingest/post", json=payload)
42
+
43
+ async def get_recent_files(self):
44
+ return await self._request("GET", "/api/files/recent")
feedo/router.py ADDED
@@ -0,0 +1,65 @@
1
+ import httpx
2
+ import asyncio
3
+ from typing import List, Optional, Dict
4
+
5
+ DEFAULT_SEEDS = {
6
+ "search": ["http://localhost:8000"],
7
+ "consensus": ["http://localhost:8080"],
8
+ "storage": ["http://localhost:8081"]
9
+ }
10
+
11
+ class NodeRouter:
12
+ def __init__(self, search_seeds: Optional[List[str]] = None, consensus_seeds: Optional[List[str]] = None, storage_seeds: Optional[List[str]] = None):
13
+ self.search_nodes = search_seeds or DEFAULT_SEEDS["search"]
14
+ self.consensus_nodes = consensus_seeds or DEFAULT_SEEDS["consensus"]
15
+ self.storage_nodes = storage_seeds or DEFAULT_SEEDS["storage"]
16
+
17
+ self._active_search_node = None
18
+ self._active_consensus_node = None
19
+ self._active_storage_node = None
20
+
21
+ async def _find_fastest_node(self, nodes: List[str], health_endpoint: str) -> str:
22
+ async def ping(node: str) -> str:
23
+ async with httpx.AsyncClient() as client:
24
+ url = f"{node}{health_endpoint}"
25
+ response = await client.get(url, timeout=3.0)
26
+ response.raise_for_status()
27
+ return node
28
+
29
+ tasks = [asyncio.create_task(ping(node)) for node in nodes]
30
+
31
+ while tasks:
32
+ done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
33
+ for task in done:
34
+ try:
35
+ return task.result()
36
+ except Exception:
37
+ pass
38
+ tasks = list(pending)
39
+
40
+ print(f"Warning: All seed nodes failed. Falling back to {nodes[0]}")
41
+ return nodes[0]
42
+
43
+ async def get_search_node(self) -> str:
44
+ if not self._active_search_node:
45
+ self._active_search_node = await self._find_fastest_node(self.search_nodes, "/explorer/stats")
46
+ return self._active_search_node
47
+
48
+ async def get_consensus_node(self) -> str:
49
+ if not self._active_consensus_node:
50
+ self._active_consensus_node = await self._find_fastest_node(self.consensus_nodes, "/grants")
51
+ return self._active_consensus_node
52
+
53
+ async def get_storage_node(self) -> str:
54
+ if not self._active_storage_node:
55
+ self._active_storage_node = await self._find_fastest_node(self.storage_nodes, "/api/files/recent")
56
+ return self._active_storage_node
57
+
58
+ def invalidate_search_node(self):
59
+ self._active_search_node = None
60
+
61
+ def invalidate_consensus_node(self):
62
+ self._active_consensus_node = None
63
+
64
+ def invalidate_storage_node(self):
65
+ self._active_storage_node = None
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: feedo-sdk
3
+ Version: 0.1.0
4
+ Summary: The official Developer SDK for Feedo Network
5
+ Author: Feedo Network
6
+ Requires-Python: >=3.8
7
+ Requires-Dist: httpx>=0.24.0
8
+ Dynamic: author
9
+ Dynamic: requires-dist
10
+ Dynamic: requires-python
11
+ Dynamic: summary
@@ -0,0 +1,11 @@
1
+ feedo/__init__.py,sha256=5fpQuHjJKiZjZp8WoGYbTXIthth08clKPLtro-Bl840,104
2
+ feedo/client.py,sha256=y1O68cjdJE5ohaV5uieYvgIOgP_HhJHQt20-5uqhYAo,610
3
+ feedo/router.py,sha256=qSrIC24ZobpBUjJSyOgB62TxvjA3jjWFlQPxrI7UTpE,2543
4
+ feedo/modules/__init__.py,sha256=5osEvcjmclW4Wh0r8-gBp5hnY-KfklOonaDWArvLIsA,15
5
+ feedo/modules/consensus.py,sha256=xZGgGaKD5DQnHtRIc2VXBmdYYEPr5wkZfZXmBrCQEZ0,2088
6
+ feedo/modules/search.py,sha256=TpVeiAowtCgahQuul3nZgWYf5k_IJgzt26VnYojc39g,1834
7
+ feedo/modules/storage.py,sha256=lnT11HAQ-eGoYxxNjT0vxH-CUSb695Rm6zZFernbQpk,1995
8
+ feedo_sdk-0.1.0.dist-info/METADATA,sha256=WptPSj6c0gXQ04KtrZ-KxXqRpHf8v9pHy5k2XzktLDA,273
9
+ feedo_sdk-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
10
+ feedo_sdk-0.1.0.dist-info/top_level.txt,sha256=DqxeisJ-yTDMZjgoTGrPdcHSCYHMCEX-WSk4JTJXwWU,6
11
+ feedo_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ feedo