langchain-pythia 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.
- langchain_pythia-0.1.0/.gitignore +7 -0
- langchain_pythia-0.1.0/LICENSE +21 -0
- langchain_pythia-0.1.0/PKG-INFO +120 -0
- langchain_pythia-0.1.0/README.md +95 -0
- langchain_pythia-0.1.0/langchain_pythia/__init__.py +17 -0
- langchain_pythia-0.1.0/langchain_pythia/_client.py +42 -0
- langchain_pythia-0.1.0/langchain_pythia/py.typed +0 -0
- langchain_pythia-0.1.0/langchain_pythia/tools.py +317 -0
- langchain_pythia-0.1.0/pyproject.toml +42 -0
- langchain_pythia-0.1.0/tests/__init__.py +0 -0
- langchain_pythia-0.1.0/tests/integration_tests/__init__.py +0 -0
- langchain_pythia-0.1.0/tests/unit_tests/__init__.py +0 -0
- langchain_pythia-0.1.0/tests/unit_tests/test_tools.py +68 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pythia Oracle
|
|
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.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: langchain-pythia
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangChain integration for Pythia Oracle — on-chain calculated crypto indicators (EMA, RSI, Bollinger, Volatility) via Chainlink
|
|
5
|
+
Project-URL: Homepage, https://pythia.c3x-solutions.com
|
|
6
|
+
Project-URL: Repository, https://github.com/pythia-the-oracle/langchain-pythia
|
|
7
|
+
Project-URL: Documentation, https://github.com/pythia-the-oracle/langchain-pythia#readme
|
|
8
|
+
Author-email: Pythia Oracle <ivan.jeremic@c3x-solutions.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agent,bollinger,chainlink,crypto,defi,ema,langchain,on-chain,oracle,rsi,smart-contracts,technical-analysis,volatility
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: httpx<1.0.0,>=0.25.0
|
|
20
|
+
Requires-Dist: langchain-core<2.0.0,>=1.2.21
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.20; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# langchain-pythia
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/langchain-pythia/)
|
|
29
|
+
[](https://opensource.org/licenses/MIT)
|
|
30
|
+
|
|
31
|
+
**LangChain integration for Pythia Oracle — on-chain calculated crypto indicators via Chainlink.**
|
|
32
|
+
|
|
33
|
+
Access EMA, RSI, Bollinger Bands, Volatility, and more for 22 crypto tokens directly from your LangChain agents. Pythia is the first oracle delivering calculated technical indicators on-chain, not just prices.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install langchain-pythia
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from langchain_pythia import PythiaListTokensTool, PythiaTokenFeedsTool
|
|
45
|
+
|
|
46
|
+
# Use with any LangChain agent
|
|
47
|
+
tools = [PythiaListTokensTool(), PythiaTokenFeedsTool()]
|
|
48
|
+
|
|
49
|
+
# Or call directly
|
|
50
|
+
list_tool = PythiaListTokensTool()
|
|
51
|
+
print(list_tool.invoke(""))
|
|
52
|
+
|
|
53
|
+
feeds_tool = PythiaTokenFeedsTool()
|
|
54
|
+
print(feeds_tool.invoke({"engine_id": "bitcoin"}))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## With a LangChain Agent
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from langchain_openai import ChatOpenAI
|
|
61
|
+
from langgraph.prebuilt import create_react_agent
|
|
62
|
+
from langchain_pythia import (
|
|
63
|
+
PythiaListTokensTool,
|
|
64
|
+
PythiaTokenFeedsTool,
|
|
65
|
+
PythiaHealthCheckTool,
|
|
66
|
+
PythiaContractsTool,
|
|
67
|
+
PythiaPricingTool,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
tools = [
|
|
71
|
+
PythiaListTokensTool(),
|
|
72
|
+
PythiaTokenFeedsTool(),
|
|
73
|
+
PythiaHealthCheckTool(),
|
|
74
|
+
PythiaContractsTool(),
|
|
75
|
+
PythiaPricingTool(),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
79
|
+
agent = create_react_agent(llm, tools)
|
|
80
|
+
|
|
81
|
+
# Ask about on-chain indicators
|
|
82
|
+
response = agent.invoke(
|
|
83
|
+
{"messages": [{"role": "user", "content": "What RSI feeds does Pythia have for Bitcoin?"}]}
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Available Tools
|
|
88
|
+
|
|
89
|
+
| Tool | Description |
|
|
90
|
+
|------|-------------|
|
|
91
|
+
| `PythiaListTokensTool` | List all 22 tracked tokens with status, uptime, and data sources |
|
|
92
|
+
| `PythiaTokenFeedsTool` | Get all indicator feed names (EMA, RSI, Bollinger, Volatility) for a token |
|
|
93
|
+
| `PythiaHealthCheckTool` | Per-token 30-day uptime, data source health, incident report |
|
|
94
|
+
| `PythiaContractsTool` | Contract addresses (operator, consumers, faucet, LINK) for on-chain integration |
|
|
95
|
+
| `PythiaPricingTool` | Pricing tiers and free trial faucet info |
|
|
96
|
+
|
|
97
|
+
## What Pythia Provides
|
|
98
|
+
|
|
99
|
+
- **484 indicator feeds** across 22 tokens (BTC, SOL, TAO, RENDER, ONDO, AAVE, UNI, and more)
|
|
100
|
+
- **5 indicator types:** EMA, RSI, Bollinger (upper/lower), Volatility, USD Price
|
|
101
|
+
- **4 timeframes:** 5-minute, 1-hour, 1-day, 1-week
|
|
102
|
+
- **On-chain delivery** via Chainlink on Polygon
|
|
103
|
+
- **Free trial** via PythiaFaucet — no LINK needed, 5 requests/day
|
|
104
|
+
|
|
105
|
+
## Use Cases
|
|
106
|
+
|
|
107
|
+
- **AI trading agents** that need on-chain technical signals
|
|
108
|
+
- **DeFi vault rebalancing** based on RSI or volatility thresholds
|
|
109
|
+
- **Risk management** using Bollinger Band width
|
|
110
|
+
- **Portfolio analysis** with real-time calculated metrics
|
|
111
|
+
|
|
112
|
+
## Related
|
|
113
|
+
|
|
114
|
+
- [Pythia MCP Server](https://pypi.org/project/pythia-oracle-mcp/) — MCP integration for Claude, Cursor, VS Code
|
|
115
|
+
- [Integration Examples](https://github.com/pythia-the-oracle/pythia-oracle-examples) — Solidity contracts with Hardhat
|
|
116
|
+
- [Website & Feed Explorer](https://pythia.c3x-solutions.com)
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# langchain-pythia
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/langchain-pythia/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**LangChain integration for Pythia Oracle — on-chain calculated crypto indicators via Chainlink.**
|
|
7
|
+
|
|
8
|
+
Access EMA, RSI, Bollinger Bands, Volatility, and more for 22 crypto tokens directly from your LangChain agents. Pythia is the first oracle delivering calculated technical indicators on-chain, not just prices.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install langchain-pythia
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from langchain_pythia import PythiaListTokensTool, PythiaTokenFeedsTool
|
|
20
|
+
|
|
21
|
+
# Use with any LangChain agent
|
|
22
|
+
tools = [PythiaListTokensTool(), PythiaTokenFeedsTool()]
|
|
23
|
+
|
|
24
|
+
# Or call directly
|
|
25
|
+
list_tool = PythiaListTokensTool()
|
|
26
|
+
print(list_tool.invoke(""))
|
|
27
|
+
|
|
28
|
+
feeds_tool = PythiaTokenFeedsTool()
|
|
29
|
+
print(feeds_tool.invoke({"engine_id": "bitcoin"}))
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## With a LangChain Agent
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from langchain_openai import ChatOpenAI
|
|
36
|
+
from langgraph.prebuilt import create_react_agent
|
|
37
|
+
from langchain_pythia import (
|
|
38
|
+
PythiaListTokensTool,
|
|
39
|
+
PythiaTokenFeedsTool,
|
|
40
|
+
PythiaHealthCheckTool,
|
|
41
|
+
PythiaContractsTool,
|
|
42
|
+
PythiaPricingTool,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
tools = [
|
|
46
|
+
PythiaListTokensTool(),
|
|
47
|
+
PythiaTokenFeedsTool(),
|
|
48
|
+
PythiaHealthCheckTool(),
|
|
49
|
+
PythiaContractsTool(),
|
|
50
|
+
PythiaPricingTool(),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
llm = ChatOpenAI(model="gpt-4o")
|
|
54
|
+
agent = create_react_agent(llm, tools)
|
|
55
|
+
|
|
56
|
+
# Ask about on-chain indicators
|
|
57
|
+
response = agent.invoke(
|
|
58
|
+
{"messages": [{"role": "user", "content": "What RSI feeds does Pythia have for Bitcoin?"}]}
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Available Tools
|
|
63
|
+
|
|
64
|
+
| Tool | Description |
|
|
65
|
+
|------|-------------|
|
|
66
|
+
| `PythiaListTokensTool` | List all 22 tracked tokens with status, uptime, and data sources |
|
|
67
|
+
| `PythiaTokenFeedsTool` | Get all indicator feed names (EMA, RSI, Bollinger, Volatility) for a token |
|
|
68
|
+
| `PythiaHealthCheckTool` | Per-token 30-day uptime, data source health, incident report |
|
|
69
|
+
| `PythiaContractsTool` | Contract addresses (operator, consumers, faucet, LINK) for on-chain integration |
|
|
70
|
+
| `PythiaPricingTool` | Pricing tiers and free trial faucet info |
|
|
71
|
+
|
|
72
|
+
## What Pythia Provides
|
|
73
|
+
|
|
74
|
+
- **484 indicator feeds** across 22 tokens (BTC, SOL, TAO, RENDER, ONDO, AAVE, UNI, and more)
|
|
75
|
+
- **5 indicator types:** EMA, RSI, Bollinger (upper/lower), Volatility, USD Price
|
|
76
|
+
- **4 timeframes:** 5-minute, 1-hour, 1-day, 1-week
|
|
77
|
+
- **On-chain delivery** via Chainlink on Polygon
|
|
78
|
+
- **Free trial** via PythiaFaucet — no LINK needed, 5 requests/day
|
|
79
|
+
|
|
80
|
+
## Use Cases
|
|
81
|
+
|
|
82
|
+
- **AI trading agents** that need on-chain technical signals
|
|
83
|
+
- **DeFi vault rebalancing** based on RSI or volatility thresholds
|
|
84
|
+
- **Risk management** using Bollinger Band width
|
|
85
|
+
- **Portfolio analysis** with real-time calculated metrics
|
|
86
|
+
|
|
87
|
+
## Related
|
|
88
|
+
|
|
89
|
+
- [Pythia MCP Server](https://pypi.org/project/pythia-oracle-mcp/) — MCP integration for Claude, Cursor, VS Code
|
|
90
|
+
- [Integration Examples](https://github.com/pythia-the-oracle/pythia-oracle-examples) — Solidity contracts with Hardhat
|
|
91
|
+
- [Website & Feed Explorer](https://pythia.c3x-solutions.com)
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""LangChain integration for Pythia Oracle — on-chain calculated crypto indicators."""
|
|
2
|
+
|
|
3
|
+
from langchain_pythia.tools import (
|
|
4
|
+
PythiaListTokensTool,
|
|
5
|
+
PythiaTokenFeedsTool,
|
|
6
|
+
PythiaHealthCheckTool,
|
|
7
|
+
PythiaContractsTool,
|
|
8
|
+
PythiaPricingTool,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"PythiaListTokensTool",
|
|
13
|
+
"PythiaTokenFeedsTool",
|
|
14
|
+
"PythiaHealthCheckTool",
|
|
15
|
+
"PythiaContractsTool",
|
|
16
|
+
"PythiaPricingTool",
|
|
17
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Internal HTTP client for fetching Pythia Oracle data."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
DATA_URL = "https://pythia.c3x-solutions.com/feed-status.json"
|
|
8
|
+
CACHE_TTL_SECONDS = 60
|
|
9
|
+
|
|
10
|
+
_cache: dict = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def fetch_data() -> dict:
|
|
14
|
+
"""Fetch feed-status.json with 60s cache."""
|
|
15
|
+
now = datetime.now(timezone.utc)
|
|
16
|
+
cached = _cache.get("data")
|
|
17
|
+
if cached and (now - cached["at"]).total_seconds() < CACHE_TTL_SECONDS:
|
|
18
|
+
return cached["data"]
|
|
19
|
+
|
|
20
|
+
async with httpx.AsyncClient(timeout=15) as client:
|
|
21
|
+
resp = await client.get(DATA_URL)
|
|
22
|
+
resp.raise_for_status()
|
|
23
|
+
data = resp.json()
|
|
24
|
+
|
|
25
|
+
_cache["data"] = {"data": data, "at": now}
|
|
26
|
+
return data
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def fetch_data_sync() -> dict:
|
|
30
|
+
"""Synchronous version of fetch_data."""
|
|
31
|
+
now = datetime.now(timezone.utc)
|
|
32
|
+
cached = _cache.get("data")
|
|
33
|
+
if cached and (now - cached["at"]).total_seconds() < CACHE_TTL_SECONDS:
|
|
34
|
+
return cached["data"]
|
|
35
|
+
|
|
36
|
+
with httpx.Client(timeout=15) as client:
|
|
37
|
+
resp = client.get(DATA_URL)
|
|
38
|
+
resp.raise_for_status()
|
|
39
|
+
data = resp.json()
|
|
40
|
+
|
|
41
|
+
_cache["data"] = {"data": data, "at": now}
|
|
42
|
+
return data
|
|
File without changes
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Pythia Oracle tools for LangChain.
|
|
2
|
+
|
|
3
|
+
Provides access to on-chain calculated crypto indicators (EMA, RSI,
|
|
4
|
+
Bollinger Bands, Volatility) for 22 tokens via Chainlink on Polygon.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Optional, Type
|
|
9
|
+
|
|
10
|
+
from langchain_core.callbacks import (
|
|
11
|
+
AsyncCallbackManagerForToolRun,
|
|
12
|
+
CallbackManagerForToolRun,
|
|
13
|
+
)
|
|
14
|
+
from langchain_core.tools import BaseTool
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from langchain_pythia._client import fetch_data, fetch_data_sync
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Contracts (static, rarely changes)
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
CONTRACTS = {
|
|
24
|
+
"chain": "Polygon",
|
|
25
|
+
"chain_id": 137,
|
|
26
|
+
"link_token_erc677": "0xb0897686c545045aFc77CF20eC7A532E3120E0F1",
|
|
27
|
+
"operator": "0xAA37710aF244514691629Aa15f4A5c271EaE6891",
|
|
28
|
+
"faucet": "0x640fC3B9B607E324D7A3d89Fcb62C77Cc0Bd420A",
|
|
29
|
+
"consumers": {
|
|
30
|
+
"discovery": {
|
|
31
|
+
"address": "0xeC2865d66ae6Af47926B02edd942A756b394F820",
|
|
32
|
+
"fee": "0.01 LINK",
|
|
33
|
+
"returns": "uint256 (single indicator)",
|
|
34
|
+
},
|
|
35
|
+
"analysis": {
|
|
36
|
+
"address": "0x3b3aC62d73E537E3EF84D97aB5B84B51aF8dB316",
|
|
37
|
+
"fee": "0.03 LINK",
|
|
38
|
+
"returns": "uint256[] (1H/1D/1W bundle)",
|
|
39
|
+
},
|
|
40
|
+
"speed": {
|
|
41
|
+
"address": "0xC406e7d9AC385e7AB43cBD56C74ad487f085d47B",
|
|
42
|
+
"fee": "0.05 LINK",
|
|
43
|
+
"returns": "uint256[] (5M bundle)",
|
|
44
|
+
},
|
|
45
|
+
"complete": {
|
|
46
|
+
"address": "0x2dEC98fd7173802b351d1E28d0Cd5DdD20C24252",
|
|
47
|
+
"fee": "0.10 LINK",
|
|
48
|
+
"returns": "uint256[] (all indicators)",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Tool: List Tokens
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PythiaListTokensTool(BaseTool):
|
|
60
|
+
"""List all tokens tracked by Pythia Oracle with status and reliability.
|
|
61
|
+
|
|
62
|
+
Returns token symbols, categories, data source count, 30-day uptime,
|
|
63
|
+
and operational status. Covers cross-chain tokens (BTC, SOL, TAO,
|
|
64
|
+
RENDER, ONDO, etc.) and Polygon DeFi tokens.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
name: str = "pythia_list_tokens"
|
|
68
|
+
description: str = (
|
|
69
|
+
"List all crypto tokens tracked by Pythia Oracle with their status, "
|
|
70
|
+
"30-day uptime, and data source count. Covers 22 tokens including "
|
|
71
|
+
"BTC, SOL, TAO, RENDER, ONDO, AAVE, UNI, and more."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def _run(
|
|
75
|
+
self, run_manager: Optional[CallbackManagerForToolRun] = None
|
|
76
|
+
) -> str:
|
|
77
|
+
data = fetch_data_sync()
|
|
78
|
+
return _format_token_list(data)
|
|
79
|
+
|
|
80
|
+
async def _arun(
|
|
81
|
+
self, run_manager: Optional[AsyncCallbackManagerForToolRun] = None
|
|
82
|
+
) -> str:
|
|
83
|
+
data = await fetch_data()
|
|
84
|
+
return _format_token_list(data)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _format_token_list(data: dict) -> str:
|
|
88
|
+
tokens = data.get("tokens", [])
|
|
89
|
+
stats = data.get("stats", {})
|
|
90
|
+
lines = [
|
|
91
|
+
f"Pythia Oracle — {stats.get('tokens', len(tokens))} tokens, "
|
|
92
|
+
f"{stats.get('total_indicators', '?')} indicator feeds\n"
|
|
93
|
+
]
|
|
94
|
+
lines.append(
|
|
95
|
+
f"{'Symbol':<8} {'Engine ID':<28} {'Category':<16} "
|
|
96
|
+
f"{'Status':<6} {'Uptime':>7} {'Src':>3}"
|
|
97
|
+
)
|
|
98
|
+
lines.append("-" * 78)
|
|
99
|
+
for t in sorted(tokens, key=lambda x: x.get("category", "")):
|
|
100
|
+
uptime = (
|
|
101
|
+
f"{t['uptime_30d']:.1f}%"
|
|
102
|
+
if t.get("uptime_30d") is not None
|
|
103
|
+
else "?"
|
|
104
|
+
)
|
|
105
|
+
lines.append(
|
|
106
|
+
f"{t['symbol']:<8} {t['engine_id']:<28} "
|
|
107
|
+
f"{t.get('category', '?'):<16} {t.get('status', '?'):<6} "
|
|
108
|
+
f"{uptime:>7} {t.get('sources', '?'):>3}"
|
|
109
|
+
)
|
|
110
|
+
lines.append(f"\nWebsite: https://pythia.c3x-solutions.com")
|
|
111
|
+
return "\n".join(lines)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# Tool: Token Feeds
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class _TokenFeedsInput(BaseModel):
|
|
120
|
+
engine_id: str = Field(
|
|
121
|
+
description=(
|
|
122
|
+
"Token engine ID, e.g. 'bitcoin', 'solana', 'bittensor', "
|
|
123
|
+
"'aave', 'render-token'. Use pythia_list_tokens to see all IDs."
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class PythiaTokenFeedsTool(BaseTool):
|
|
129
|
+
"""Get all indicator feed names for a specific token.
|
|
130
|
+
|
|
131
|
+
Shows every available feed (EMA, RSI, Bollinger, Volatility across
|
|
132
|
+
all timeframes) plus the token's reliability stats.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
name: str = "pythia_token_feeds"
|
|
136
|
+
description: str = (
|
|
137
|
+
"Get all available on-chain indicator feeds (EMA, RSI, Bollinger, "
|
|
138
|
+
"Volatility) for a specific crypto token from Pythia Oracle. "
|
|
139
|
+
"Returns feed names that can be used in smart contract calls."
|
|
140
|
+
)
|
|
141
|
+
args_schema: Type[BaseModel] = _TokenFeedsInput
|
|
142
|
+
|
|
143
|
+
def _run(
|
|
144
|
+
self,
|
|
145
|
+
engine_id: str,
|
|
146
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
147
|
+
) -> str:
|
|
148
|
+
data = fetch_data_sync()
|
|
149
|
+
return _format_token_feeds(data, engine_id)
|
|
150
|
+
|
|
151
|
+
async def _arun(
|
|
152
|
+
self,
|
|
153
|
+
engine_id: str,
|
|
154
|
+
run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
|
155
|
+
) -> str:
|
|
156
|
+
data = await fetch_data()
|
|
157
|
+
return _format_token_feeds(data, engine_id)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _format_token_feeds(data: dict, engine_id: str) -> str:
|
|
161
|
+
tokens = data.get("tokens", [])
|
|
162
|
+
token = next((t for t in tokens if t["engine_id"] == engine_id), None)
|
|
163
|
+
if not token:
|
|
164
|
+
available = sorted(t["engine_id"] for t in tokens)
|
|
165
|
+
return f"No token found for '{engine_id}'.\nAvailable: {', '.join(available)}"
|
|
166
|
+
|
|
167
|
+
feed_names = token.get("feed_names", [])
|
|
168
|
+
lines = [
|
|
169
|
+
f"{token['symbol']} ({token['name']}) — {token.get('pair', '?')}",
|
|
170
|
+
f"Status: {token.get('status', '?')} | "
|
|
171
|
+
f"30d uptime: {token.get('uptime_30d', '?')}% | "
|
|
172
|
+
f"Data sources: {token.get('sources', '?')}",
|
|
173
|
+
f"\n{len(feed_names)} indicator feeds:\n",
|
|
174
|
+
]
|
|
175
|
+
groups: dict[str, list[str]] = {}
|
|
176
|
+
for name in sorted(feed_names):
|
|
177
|
+
suffix = name[len(engine_id) + 1 :]
|
|
178
|
+
cat = suffix.split("_")[0]
|
|
179
|
+
groups.setdefault(cat, []).append(name)
|
|
180
|
+
for cat, feeds in sorted(groups.items()):
|
|
181
|
+
lines.append(f" {cat}:")
|
|
182
|
+
for feed in feeds:
|
|
183
|
+
lines.append(f" {feed}")
|
|
184
|
+
lines.append("")
|
|
185
|
+
return "\n".join(lines)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# Tool: Health Check
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class PythiaHealthCheckTool(BaseTool):
|
|
194
|
+
"""Check Pythia Oracle reliability and uptime.
|
|
195
|
+
|
|
196
|
+
Returns per-token 30-day uptime (worst-first), data source health,
|
|
197
|
+
infrastructure status, and active incidents.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
name: str = "pythia_health_check"
|
|
201
|
+
description: str = (
|
|
202
|
+
"Check the reliability and uptime of Pythia Oracle. Returns "
|
|
203
|
+
"per-token 30-day uptime, data source health, infrastructure "
|
|
204
|
+
"status, and active incidents. Use before integrating."
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def _run(
|
|
208
|
+
self, run_manager: Optional[CallbackManagerForToolRun] = None
|
|
209
|
+
) -> str:
|
|
210
|
+
data = fetch_data_sync()
|
|
211
|
+
return _format_health(data)
|
|
212
|
+
|
|
213
|
+
async def _arun(
|
|
214
|
+
self, run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
|
215
|
+
) -> str:
|
|
216
|
+
data = await fetch_data()
|
|
217
|
+
return _format_health(data)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _format_health(data: dict) -> str:
|
|
221
|
+
tokens = data.get("tokens", [])
|
|
222
|
+
system = data.get("system", {})
|
|
223
|
+
stats = data.get("stats", {})
|
|
224
|
+
generated = data.get("generated_at", "unknown")
|
|
225
|
+
|
|
226
|
+
lines = [f"Pythia Oracle — Health Report ({generated})\n"]
|
|
227
|
+
|
|
228
|
+
incidents = stats.get("active_incidents", 0)
|
|
229
|
+
lines.append(
|
|
230
|
+
f" *** {incidents} ACTIVE INCIDENT(S) ***\n"
|
|
231
|
+
if incidents > 0
|
|
232
|
+
else " No active incidents.\n"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
sources = system.get("sources", [])
|
|
236
|
+
for s in sources:
|
|
237
|
+
marker = " " if s["status"] == "ok" else "!"
|
|
238
|
+
lines.append(f" {marker} {s['name']:<15} {s['status']}")
|
|
239
|
+
lines.append("")
|
|
240
|
+
|
|
241
|
+
lines.append(f"{'Token':<8} {'Uptime 30d':>10} {'Status':<6} {'Src':>3}")
|
|
242
|
+
lines.append("-" * 40)
|
|
243
|
+
for t in sorted(tokens, key=lambda x: x.get("uptime_30d", 0)):
|
|
244
|
+
uptime = (
|
|
245
|
+
f"{t['uptime_30d']:.1f}%"
|
|
246
|
+
if t.get("uptime_30d") is not None
|
|
247
|
+
else "?"
|
|
248
|
+
)
|
|
249
|
+
lines.append(
|
|
250
|
+
f"{t['symbol']:<8} {uptime:>10} "
|
|
251
|
+
f"{t.get('status', '?'):<6} {t.get('sources', '?'):>3}"
|
|
252
|
+
)
|
|
253
|
+
return "\n".join(lines)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# ---------------------------------------------------------------------------
|
|
257
|
+
# Tool: Contracts
|
|
258
|
+
# ---------------------------------------------------------------------------
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class PythiaContractsTool(BaseTool):
|
|
262
|
+
"""Get Pythia contract addresses for on-chain integration."""
|
|
263
|
+
|
|
264
|
+
name: str = "pythia_contracts"
|
|
265
|
+
description: str = (
|
|
266
|
+
"Get Pythia Oracle contract addresses on Polygon for smart contract "
|
|
267
|
+
"integration. Returns operator, LINK token, faucet, and consumer "
|
|
268
|
+
"contract addresses for all pricing tiers."
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def _run(
|
|
272
|
+
self, run_manager: Optional[CallbackManagerForToolRun] = None
|
|
273
|
+
) -> str:
|
|
274
|
+
return json.dumps(CONTRACTS, indent=2)
|
|
275
|
+
|
|
276
|
+
async def _arun(
|
|
277
|
+
self, run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
|
278
|
+
) -> str:
|
|
279
|
+
return json.dumps(CONTRACTS, indent=2)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
# Tool: Pricing
|
|
284
|
+
# ---------------------------------------------------------------------------
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class PythiaPricingTool(BaseTool):
|
|
288
|
+
"""Get Pythia Oracle pricing tiers and free trial info."""
|
|
289
|
+
|
|
290
|
+
name: str = "pythia_pricing"
|
|
291
|
+
description: str = (
|
|
292
|
+
"Get Pythia Oracle pricing tiers (Discovery 0.01 LINK, Analysis "
|
|
293
|
+
"0.03, Speed 0.05, Complete 0.10) and free trial faucet info."
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def _run(
|
|
297
|
+
self, run_manager: Optional[CallbackManagerForToolRun] = None
|
|
298
|
+
) -> str:
|
|
299
|
+
return _PRICING_TEXT
|
|
300
|
+
|
|
301
|
+
async def _arun(
|
|
302
|
+
self, run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
|
303
|
+
) -> str:
|
|
304
|
+
return _PRICING_TEXT
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
_PRICING_TEXT = """Pythia Oracle Pricing
|
|
308
|
+
|
|
309
|
+
DISCOVERY — 0.01 LINK: Any single indicator. Returns uint256.
|
|
310
|
+
ANALYSIS — 0.03 LINK: All 1H/1D/1W indicators bundled. Returns uint256[].
|
|
311
|
+
SPEED — 0.05 LINK: All 5-minute indicators bundled. Returns uint256[].
|
|
312
|
+
COMPLETE — 0.10 LINK: Every indicator for a token. Returns uint256[].
|
|
313
|
+
|
|
314
|
+
FREE TRIAL — PythiaFaucet (0x640fC3B9B607E324D7A3d89Fcb62C77Cc0Bd420A)
|
|
315
|
+
No LINK needed. 5 requests/day/address. Real data.
|
|
316
|
+
|
|
317
|
+
Website: https://pythia.c3x-solutions.com"""
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "langchain-pythia"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "LangChain integration for Pythia Oracle — on-chain calculated crypto indicators (EMA, RSI, Bollinger, Volatility) via Chainlink"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Pythia Oracle", email = "ivan.jeremic@c3x-solutions.com" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"langchain", "oracle", "chainlink", "defi", "crypto",
|
|
17
|
+
"ema", "rsi", "bollinger", "volatility", "on-chain",
|
|
18
|
+
"technical-analysis", "ai-agent", "smart-contracts",
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
"Topic :: Office/Business :: Financial",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"langchain-core>=1.2.21,<2.0.0",
|
|
30
|
+
"httpx>=0.25.0,<1.0.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=7.0",
|
|
36
|
+
"pytest-asyncio>=0.20",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://pythia.c3x-solutions.com"
|
|
41
|
+
Repository = "https://github.com/pythia-the-oracle/langchain-pythia"
|
|
42
|
+
Documentation = "https://github.com/pythia-the-oracle/langchain-pythia#readme"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Unit tests for Pythia Oracle LangChain tools."""
|
|
2
|
+
|
|
3
|
+
from langchain_pythia import (
|
|
4
|
+
PythiaListTokensTool,
|
|
5
|
+
PythiaTokenFeedsTool,
|
|
6
|
+
PythiaHealthCheckTool,
|
|
7
|
+
PythiaContractsTool,
|
|
8
|
+
PythiaPricingTool,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_tool_names():
|
|
13
|
+
"""Each tool has a unique, descriptive name."""
|
|
14
|
+
tools = [
|
|
15
|
+
PythiaListTokensTool(),
|
|
16
|
+
PythiaTokenFeedsTool(),
|
|
17
|
+
PythiaHealthCheckTool(),
|
|
18
|
+
PythiaContractsTool(),
|
|
19
|
+
PythiaPricingTool(),
|
|
20
|
+
]
|
|
21
|
+
names = [t.name for t in tools]
|
|
22
|
+
assert len(names) == len(set(names)), "Tool names must be unique"
|
|
23
|
+
for name in names:
|
|
24
|
+
assert name.startswith("pythia_"), f"Tool name should start with pythia_: {name}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_tool_descriptions():
|
|
28
|
+
"""Each tool has a non-empty description."""
|
|
29
|
+
tools = [
|
|
30
|
+
PythiaListTokensTool(),
|
|
31
|
+
PythiaTokenFeedsTool(),
|
|
32
|
+
PythiaHealthCheckTool(),
|
|
33
|
+
PythiaContractsTool(),
|
|
34
|
+
PythiaPricingTool(),
|
|
35
|
+
]
|
|
36
|
+
for tool in tools:
|
|
37
|
+
assert len(tool.description) > 20, f"Description too short for {tool.name}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_contracts_tool_returns_json():
|
|
41
|
+
"""Contracts tool returns valid JSON with expected keys."""
|
|
42
|
+
import json
|
|
43
|
+
|
|
44
|
+
tool = PythiaContractsTool()
|
|
45
|
+
result = tool._run()
|
|
46
|
+
data = json.loads(result)
|
|
47
|
+
assert "operator" in data
|
|
48
|
+
assert "faucet" in data
|
|
49
|
+
assert "consumers" in data
|
|
50
|
+
assert "discovery" in data["consumers"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_pricing_tool_returns_tiers():
|
|
54
|
+
"""Pricing tool mentions all 4 tiers."""
|
|
55
|
+
tool = PythiaPricingTool()
|
|
56
|
+
result = tool._run()
|
|
57
|
+
assert "DISCOVERY" in result
|
|
58
|
+
assert "ANALYSIS" in result
|
|
59
|
+
assert "SPEED" in result
|
|
60
|
+
assert "COMPLETE" in result
|
|
61
|
+
assert "FREE TRIAL" in result
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_token_feeds_input_schema():
|
|
65
|
+
"""Token feeds tool has proper input schema."""
|
|
66
|
+
tool = PythiaTokenFeedsTool()
|
|
67
|
+
schema = tool.args_schema.model_json_schema()
|
|
68
|
+
assert "engine_id" in schema["properties"]
|