Skip to content

Commit

Permalink
feat: Add Python implementation of Allora plugin (#217)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Jan 13, 2025
1 parent e7223f1 commit e5c84ec
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 0 deletions.
23 changes: 23 additions & 0 deletions python/src/plugins/allora/goat_plugins/allora/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from dataclasses import dataclass
from typing import Optional

from goat.classes.plugin_base import PluginBase
from .service import AlloraService


@dataclass
class AlloraPluginOptions:
api_key: Optional[str] = None
api_root: str = "https://api.upshot.xyz/v2/allora"


class AlloraPlugin(PluginBase):
def __init__(self, options: AlloraPluginOptions):
super().__init__("allora", [AlloraService(options.api_key, options.api_root)])

def supports_chain(self, chain) -> bool:
return True


def allora(options: AlloraPluginOptions) -> AlloraPlugin:
return AlloraPlugin(options)
21 changes: 21 additions & 0 deletions python/src/plugins/allora/goat_plugins/allora/parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pydantic import BaseModel, Field
from enum import Enum


class AlloraPricePredictionToken(str, Enum):
BTC = "BTC"
ETH = "ETH"


class AlloraPricePredictionTimeframe(str, Enum):
FIVE_MIN = "5m"
EIGHT_HOUR = "8h"


class GetAlloraPricePredictionParameters(BaseModel):
ticker: AlloraPricePredictionToken = Field(
description="The ticker of the currency for which to fetch a price prediction (BTC or ETH)."
)
timeframe: AlloraPricePredictionTimeframe = Field(
description='The timeframe for the prediction (currently, either "5m" or "8h").'
)
49 changes: 49 additions & 0 deletions python/src/plugins/allora/goat_plugins/allora/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import aiohttp
from typing import Optional
from goat.decorators.tool import Tool
from .parameters import GetAlloraPricePredictionParameters, AlloraPricePredictionToken, AlloraPricePredictionTimeframe


class AlloraService:
def __init__(self, api_key: Optional[str] = None, api_root: str = "https://api.upshot.xyz/v2/allora"):
self.api_key = api_key
self.api_root = api_root.rstrip('/') # Remove trailing slash if present

@Tool({
"description": "Fetch a future price prediction for BTC or ETH for a given timeframe (5m or 8h)",
"parameters_schema": GetAlloraPricePredictionParameters
})
async def get_price_prediction(self, parameters: dict):
"""Fetch a future price prediction for a crypto asset from Allora Network"""
# Default to ethereum-11155111 (Sepolia) as in TypeScript version
signature_format = "ethereum-11155111"

headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
if self.api_key:
headers["x-api-key"] = self.api_key

# Extract parameters
ticker = parameters["ticker"]
timeframe = parameters["timeframe"]

# Construct URL following TypeScript pattern
url = f"{self.api_root}/consumer/price/{signature_format}/{ticker}/{timeframe}"

async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as response:
if not response.ok:
raise Exception(
f"Allora plugin: error requesting price prediction: url={url} "
f"status={response.status} body={await response.text()}"
)

data = await response.json()

# Validate response structure
if not data.get("data", {}).get("inference_data"):
raise Exception(f"API response missing data: {data}")

return data["data"]["inference_data"]
43 changes: 43 additions & 0 deletions python/src/plugins/allora/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[tool.poetry]
name = "goat-sdk-plugin-allora"
version = "0.1.0"
description = "Goat plugin for Allora Network price predictions"
authors = ["GOAT SDK Team"]
readme = "README.md"
keywords = ["goat", "sdk", "web3", "agents", "ai", "allora", "prediction"]
homepage = "https://ohmygoat.dev/"
repository = "https://github.com/goat-sdk/goat"
packages = [
{ include = "goat_plugins/allora" },
]

[tool.poetry.dependencies]
python = "^3.10"
aiohttp = "^3.8.6"
goat-sdk = "^0.1.1"

[tool.poetry.group.test.dependencies]
pytest = "^8.3.4"
pytest-asyncio = "^0.25.0"

[tool.poetry.urls]
"Bug Tracker" = "https://github.com/goat-sdk/goat/issues"

[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
]
pythonpath = "src"
asyncio_default_fixture_loop_scope = "function"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.group.dev.dependencies]
ruff = "^0.8.6"
goat-sdk = { path = "../../goat-sdk", develop = true }

[tool.ruff]
line-length = 120
target-version = "py312"

0 comments on commit e5c84ec

Please sign in to comment.