-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from ainft-team/release/0.1.0
Release/0.1.0
- Loading branch information
Showing
20 changed files
with
2,057 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.11.8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,29 @@ | ||
# ainft-py | ||
# ainft-py | ||
|
||
[![PyPi Version](https://img.shields.io/pypi/v/ainft-py.svg)](https://pypi.python.org/pypi/ainft-py/) | ||
|
||
A python version of [ainft-js](https://github.com/ainft-team/ainft-js). | ||
|
||
## Installation | ||
|
||
```sh | ||
pip install ainft-py | ||
``` | ||
|
||
## Usage | ||
|
||
```python | ||
import os | ||
from ainft import Ainft | ||
|
||
ainft = Ainft( | ||
private_key=os.environ.get("AIN_PRIVATE_KEY"), | ||
api_url="https://ainft-api-dev.ainetwork.ai", | ||
blockchain_url="https://testnet-api.ainetwork.ai", | ||
chain_id=0, | ||
) | ||
``` | ||
|
||
## Requirements | ||
|
||
Python version should be at least 3.8 but less than 3.12. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from __future__ import annotations | ||
|
||
from .client import Ainft | ||
from .types import ( | ||
Thread, | ||
Message, | ||
TransactionResult, | ||
ThreadTransactionResult, | ||
MessageTransactionResult, | ||
) | ||
from .utils import truncate_text | ||
|
||
__all__ = [ | ||
"Ainft", | ||
"Thread", | ||
"Message", | ||
"TransactionResult", | ||
"ThreadTransactionResult", | ||
"MessageTransactionResult", | ||
"truncate_text", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from .chat import Chat | ||
from .threads import Threads | ||
|
||
__all__ = [ | ||
"Chat", | ||
"Threads", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from __future__ import annotations | ||
|
||
from functools import cached_property | ||
|
||
from ain.ain import Ain | ||
|
||
from .threads import Threads | ||
from .messages import Messages | ||
|
||
|
||
class Chat: | ||
def __init__(self, ain: Ain) -> None: | ||
self._ain = ain | ||
|
||
@cached_property | ||
def threads(self) -> Threads: | ||
return Threads(self._ain) | ||
|
||
@cached_property | ||
def messages(self) -> Messages: | ||
return Messages(self._ain) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
from typing import List | ||
|
||
from ain.ain import Ain | ||
|
||
from ..types import ( | ||
SetOperation, | ||
SetMultiOperation, | ||
TransactionInput, | ||
Message, | ||
MessageTransactionResult, | ||
) | ||
from ..utils import * | ||
|
||
|
||
class Messages: | ||
def __init__(self, ain: Ain): | ||
self._ain = ain | ||
|
||
async def store( | ||
self, | ||
*, | ||
messages: List[Message], | ||
object_id: str, | ||
token_id: str, | ||
) -> MessageTransactionResult: | ||
""" | ||
Store a list of messages. | ||
Args: | ||
messages: The list of messages. | ||
object_id: The ID of the AINFT object. | ||
token_id: The ID of the AINFT token. | ||
""" | ||
app_id = get_app_id(object_id) | ||
user_addr = validate_user_address(self._ain.wallet) | ||
|
||
await self._validate(app_id, token_id, user_addr, messages) | ||
|
||
return await self._send_tx_for_store_message(**dict( | ||
messages=messages, | ||
app_id=app_id, | ||
token_id=token_id, | ||
address=user_addr, | ||
)) | ||
|
||
async def _validate( | ||
self, app_id: str, token_id: str, address: str, messages: List[Message] | ||
): | ||
await validate_app(app_id, self._ain.db) | ||
await validate_token(app_id, token_id, self._ain.db) | ||
for message in messages: | ||
await validate_thread( | ||
app_id, token_id, address, message.thread_id, self._ain.db | ||
) | ||
validate_message_id(message.id) | ||
|
||
async def _send_tx_for_store_message(self, **kwargs) -> MessageTransactionResult: | ||
timestamp = int(now()) | ||
tx_body = self._build_tx_body_for_store_message(timestamp=timestamp, **kwargs) | ||
tx_result = await self._ain.sendTransaction(tx_body) | ||
|
||
if not is_tx_success(tx_result): | ||
raise RuntimeError(f"Failed to send transaction: {json.dumps(tx_result)}") | ||
|
||
return self._format_tx_result(tx_result=tx_result, **kwargs) | ||
|
||
def _build_tx_body_for_store_message( | ||
self, | ||
app_id: str, | ||
token_id: str, | ||
messages: List[Message], | ||
address: str, | ||
timestamp: int, | ||
) -> TransactionInput: | ||
ops = [] | ||
for msg in messages: | ||
path = join_paths( | ||
[ | ||
"apps", | ||
app_id, | ||
"tokens", | ||
token_id, | ||
"ai", | ||
"ainize_openai", | ||
"history", | ||
address, | ||
"threads", | ||
msg.thread_id, | ||
"messages", | ||
str(msg.created_at), | ||
] | ||
) | ||
content = truncate_text(msg.content, 200) | ||
content = content.encode("unicode-escape").decode("ASCII") | ||
message = { | ||
"id": msg.id, | ||
"role": msg.role, | ||
"content": content, | ||
**({"metadata": msg.metadata} if msg.metadata else {}), | ||
} | ||
op = SetOperation(type="SET_VALUE", ref=path, value=message) | ||
ops.append(op) | ||
|
||
multi_op = SetMultiOperation(type="SET", op_list=ops) | ||
return TransactionInput( | ||
operation=multi_op, | ||
timestamp=timestamp, | ||
nonce=-1, | ||
address=address, | ||
gas_price=500, | ||
) | ||
|
||
def _format_tx_result( | ||
self, tx_result: dict, messages: List[Message], **kwargs | ||
) -> MessageTransactionResult: | ||
return MessageTransactionResult(messages=messages, **tx_result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
from typing import Optional | ||
|
||
from ain.ain import Ain | ||
|
||
from ..types import SetOperation, TransactionInput, Thread, ThreadTransactionResult | ||
from ..utils import * | ||
|
||
|
||
class Threads: | ||
def __init__(self, ain: Ain): | ||
self._ain = ain | ||
|
||
async def store( | ||
self, | ||
*, | ||
thread_id: str, | ||
object_id: str, | ||
token_id: str, | ||
metadata: Optional[dict] = None, | ||
) -> ThreadTransactionResult: | ||
""" | ||
Store a thread. | ||
Args: | ||
thread_id: The ID of the thread, | ||
must be a 20-character alphanumeric starting with 'thread_' or a uuid4. | ||
object_id: The ID of the AINFT object. | ||
token_id: The ID of the AINFT token. | ||
metadata: The metadata can contain up to 16 key-value pairs, | ||
with the keys limited to 64 characters and the values to 512 characters. | ||
""" | ||
app_id = get_app_id(object_id) | ||
user_addr = validate_user_address(self._ain.wallet) | ||
|
||
await self._validate(app_id, token_id, thread_id) | ||
|
||
return await self._send_tx_for_store_thread(**dict( | ||
thread_id=thread_id, | ||
app_id=app_id, | ||
token_id=token_id, | ||
address=user_addr, | ||
metadata=metadata, | ||
)) | ||
|
||
async def _validate(self, app_id: str, token_id: str, thread_id: str): | ||
await validate_app(app_id, self._ain.db) | ||
await validate_token(app_id, token_id, self._ain.db) | ||
validate_thread_id(thread_id) | ||
|
||
async def _send_tx_for_store_thread(self, **kwargs) -> ThreadTransactionResult: | ||
timestamp = int(now()) | ||
tx_body = self._build_tx_body_for_store_thread(timestamp=timestamp, **kwargs) | ||
tx_result = await self._ain.sendTransaction(tx_body) | ||
|
||
if not is_tx_success(tx_result): | ||
raise RuntimeError(f"Failed to send transaction: {json.dumps(tx_result)}") | ||
|
||
return self._format_tx_result( | ||
tx_result=tx_result, timestamp=timestamp, **kwargs | ||
) | ||
|
||
def _build_tx_body_for_store_thread( | ||
self, | ||
app_id: str, | ||
token_id: str, | ||
thread_id: str, | ||
address: str, | ||
timestamp: int, | ||
metadata: Optional[dict], | ||
) -> TransactionInput: | ||
thread_path = join_paths( | ||
[ | ||
"apps", | ||
app_id, | ||
"tokens", | ||
token_id, | ||
"ai", | ||
"ainize_openai", | ||
"history", | ||
address, | ||
"threads", | ||
thread_id, | ||
] | ||
) | ||
thread = { | ||
"messages": True, | ||
**({"metadata": metadata} if metadata else {}), | ||
} | ||
op = SetOperation(type="SET_VALUE", ref=thread_path, value=thread) | ||
return TransactionInput( | ||
operation=op, | ||
timestamp=timestamp, | ||
nonce=-1, | ||
address=address, | ||
gas_price=500, | ||
) | ||
|
||
def _format_tx_result( | ||
self, | ||
tx_result: dict, | ||
timestamp: int, | ||
thread_id: str, | ||
**kwargs, | ||
) -> ThreadTransactionResult: | ||
metadata = kwargs.get("metadata", {}) | ||
thread = Thread(id=thread_id, metadata=metadata, created_at=timestamp) | ||
return ThreadTransactionResult(thread=thread, **tx_result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Literal | ||
|
||
from ain.ain import Ain | ||
|
||
from .chat import Chat | ||
|
||
|
||
class Ainft: | ||
def __init__( | ||
self, | ||
*, | ||
private_key: str, | ||
api_url: str, | ||
blockchain_url: str, | ||
chain_id: Literal[0, 1], | ||
): | ||
self._base_url = api_url | ||
self._blockchain_url = blockchain_url | ||
self._chain_id = chain_id | ||
|
||
self._ain = Ain(self._blockchain_url, self._chain_id) | ||
self._set_default_account(private_key) | ||
|
||
self.chat = Chat(self._ain) | ||
|
||
def _set_default_account(self, private_key: str): | ||
self._ain.wallet.clear() | ||
self._ain.wallet.addAndSetDefaultAccount(private_key) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from __future__ import annotations | ||
|
||
class AinftError(Exception): | ||
pass |
Oops, something went wrong.