Skip to content

Commit e5c84ec

Browse files
devin-ai-integration[bot]aigustin0xaguspunk
authored
feat: Add Python implementation of Allora plugin (#217)
* feat: Add Python implementation of Allora plugin - Create Python version of Allora plugin with same functionality as TypeScript version - Implement price prediction functionality for BTC and ETH - Support 5m and 8h timeframes - Match CoinGecko plugin structure - Add Poetry build configuration Co-Authored-By: Agus Armellini Fischer <[email protected]> * chore: add changeset for allora python plugin Co-Authored-By: Agus Armellini Fischer <[email protected]> * Delete .changeset/allora-python-plugin.md --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Agus Armellini Fischer <[email protected]> Co-authored-by: Agus <[email protected]>
1 parent e7223f1 commit e5c84ec

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from dataclasses import dataclass
2+
from typing import Optional
3+
4+
from goat.classes.plugin_base import PluginBase
5+
from .service import AlloraService
6+
7+
8+
@dataclass
9+
class AlloraPluginOptions:
10+
api_key: Optional[str] = None
11+
api_root: str = "https://api.upshot.xyz/v2/allora"
12+
13+
14+
class AlloraPlugin(PluginBase):
15+
def __init__(self, options: AlloraPluginOptions):
16+
super().__init__("allora", [AlloraService(options.api_key, options.api_root)])
17+
18+
def supports_chain(self, chain) -> bool:
19+
return True
20+
21+
22+
def allora(options: AlloraPluginOptions) -> AlloraPlugin:
23+
return AlloraPlugin(options)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pydantic import BaseModel, Field
2+
from enum import Enum
3+
4+
5+
class AlloraPricePredictionToken(str, Enum):
6+
BTC = "BTC"
7+
ETH = "ETH"
8+
9+
10+
class AlloraPricePredictionTimeframe(str, Enum):
11+
FIVE_MIN = "5m"
12+
EIGHT_HOUR = "8h"
13+
14+
15+
class GetAlloraPricePredictionParameters(BaseModel):
16+
ticker: AlloraPricePredictionToken = Field(
17+
description="The ticker of the currency for which to fetch a price prediction (BTC or ETH)."
18+
)
19+
timeframe: AlloraPricePredictionTimeframe = Field(
20+
description='The timeframe for the prediction (currently, either "5m" or "8h").'
21+
)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import aiohttp
2+
from typing import Optional
3+
from goat.decorators.tool import Tool
4+
from .parameters import GetAlloraPricePredictionParameters, AlloraPricePredictionToken, AlloraPricePredictionTimeframe
5+
6+
7+
class AlloraService:
8+
def __init__(self, api_key: Optional[str] = None, api_root: str = "https://api.upshot.xyz/v2/allora"):
9+
self.api_key = api_key
10+
self.api_root = api_root.rstrip('/') # Remove trailing slash if present
11+
12+
@Tool({
13+
"description": "Fetch a future price prediction for BTC or ETH for a given timeframe (5m or 8h)",
14+
"parameters_schema": GetAlloraPricePredictionParameters
15+
})
16+
async def get_price_prediction(self, parameters: dict):
17+
"""Fetch a future price prediction for a crypto asset from Allora Network"""
18+
# Default to ethereum-11155111 (Sepolia) as in TypeScript version
19+
signature_format = "ethereum-11155111"
20+
21+
headers = {
22+
"Content-Type": "application/json",
23+
"Accept": "application/json"
24+
}
25+
if self.api_key:
26+
headers["x-api-key"] = self.api_key
27+
28+
# Extract parameters
29+
ticker = parameters["ticker"]
30+
timeframe = parameters["timeframe"]
31+
32+
# Construct URL following TypeScript pattern
33+
url = f"{self.api_root}/consumer/price/{signature_format}/{ticker}/{timeframe}"
34+
35+
async with aiohttp.ClientSession() as session:
36+
async with session.get(url, headers=headers) as response:
37+
if not response.ok:
38+
raise Exception(
39+
f"Allora plugin: error requesting price prediction: url={url} "
40+
f"status={response.status} body={await response.text()}"
41+
)
42+
43+
data = await response.json()
44+
45+
# Validate response structure
46+
if not data.get("data", {}).get("inference_data"):
47+
raise Exception(f"API response missing data: {data}")
48+
49+
return data["data"]["inference_data"]
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[tool.poetry]
2+
name = "goat-sdk-plugin-allora"
3+
version = "0.1.0"
4+
description = "Goat plugin for Allora Network price predictions"
5+
authors = ["GOAT SDK Team"]
6+
readme = "README.md"
7+
keywords = ["goat", "sdk", "web3", "agents", "ai", "allora", "prediction"]
8+
homepage = "https://ohmygoat.dev/"
9+
repository = "https://github.com/goat-sdk/goat"
10+
packages = [
11+
{ include = "goat_plugins/allora" },
12+
]
13+
14+
[tool.poetry.dependencies]
15+
python = "^3.10"
16+
aiohttp = "^3.8.6"
17+
goat-sdk = "^0.1.1"
18+
19+
[tool.poetry.group.test.dependencies]
20+
pytest = "^8.3.4"
21+
pytest-asyncio = "^0.25.0"
22+
23+
[tool.poetry.urls]
24+
"Bug Tracker" = "https://github.com/goat-sdk/goat/issues"
25+
26+
[tool.pytest.ini_options]
27+
addopts = [
28+
"--import-mode=importlib",
29+
]
30+
pythonpath = "src"
31+
asyncio_default_fixture_loop_scope = "function"
32+
33+
[build-system]
34+
requires = ["poetry-core"]
35+
build-backend = "poetry.core.masonry.api"
36+
37+
[tool.poetry.group.dev.dependencies]
38+
ruff = "^0.8.6"
39+
goat-sdk = { path = "../../goat-sdk", develop = true }
40+
41+
[tool.ruff]
42+
line-length = 120
43+
target-version = "py312"

0 commit comments

Comments
 (0)