Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,26 @@ jobs:
env:
FMA_APIKEY: ${{ secrets.FMA_APIKEY }}
run: pytest tests/test_async_inference.py --tb=short

test_m1_clients:
needs:
- lint
runs-on: ubuntu-latest

container:
image: python:3.8

steps:
- name: Check out git repo
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Fix
run: git config --global --add safe.directory '*'

- name: Install dependencies
run: pip3 install poetry pytest-asyncio && poetry config virtualenvs.create false && poetry install

- name: Test
run: pytest tests/test_flymyai_m1_client.py --tb=short
104 changes: 89 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# FlyMy.AI

<p align="center">
<img src="https://telegra.ph/file/d76588fc58b3445be4291.png" alt="Generated with FlyMy.AI in 🚀 70ms" width="500" />
</br>Generated with FlyMy.AI <b>in 🚀 70ms </b>
</p>

Welcome to FlyMy.AI inference platform. Our goal is to provide the fastest and most affordable deployment solutions for neural networks and AI applications.


- **Fast Inference**: Experience the fastest Stable Diffusion inference globally.
- **Scalability**: Autoscaling to millions of users per second.
- **Ease of Use**: One-click deployment for any publicly available neural networks.
Expand All @@ -16,12 +16,10 @@ Welcome to FlyMy.AI inference platform. Our goal is to provide the fastest and m
For more information, visit our website: [FlyMy.AI](https://flymy.ai)
Or connect with us and other users on Discord: [Join Discord](https://discord.com/invite/t6hPBpSebw)


## Getting Started

This is a Python client for [FlyMyAI](https://flymy.ai). It allows you to easily run models and get predictions from your Python code in sync and async mode.


## Requirements

- Python 3.8+
Expand All @@ -35,12 +33,15 @@ pip install flymyai
```

## Authentication

Before using the client, you need to have your API key, username, and project name. In order to get credentials, you have to sign up on flymy.ai and get your personal data on [the profile](https://app.flymy.ai/profile).

## Basic Usage

Here's a simple example of how to use the FlyMyAI client:

#### BERT Sentiment analysis

```python
import flymyai

Expand All @@ -52,11 +53,12 @@ response = flymyai.run(
print(response.output_data["logits"][0])
```


## Sync Streams

For llms you should use stream method

#### llama 3.1 8b

```python
from flymyai import client, FlyMyAIPredictException

Expand Down Expand Up @@ -87,6 +89,7 @@ finally:
```

## Async Streams

For llms you should use stream method

#### Stable Code Instruct 3b
Expand Down Expand Up @@ -126,10 +129,10 @@ async def run_stable_code():
asyncio.run(run_stable_code())
```



## File Inputs

#### ResNet image classification

You can pass file inputs to models using file paths:

```python
Expand All @@ -145,9 +148,10 @@ response = flymyai.run(
print(response.output_data["495"])
```


## File Response Handling

Files received from the neural network are always encoded in base64 format. To process these files, you need to decode them first. Here's an example of how to handle an image file:

#### StableDiffusion Turbo image generation in ~50ms 🚀

```python
Expand All @@ -167,8 +171,8 @@ with open("generated_image.jpg", "wb") as file:
file.write(image_data)
```


## Asynchronous Requests

FlyMyAI supports asynchronous requests for improved performance. Here's how to use it:

```python
Expand Down Expand Up @@ -207,6 +211,7 @@ asyncio.run(main())
```

## Running Models in the Background

To run a model in the background, simply use the async_run() method:

```python
Expand All @@ -233,7 +238,6 @@ asyncio.run(main())
# Continue with other operations while the model runs in the background
```


## Asynchronous Prediction Tasks

For long-running operations, FlyMyAI provides asynchronous prediction tasks. This allows you to submit a task and check its status later, which is useful for handling time-consuming predictions without blocking your application.
Expand Down Expand Up @@ -280,30 +284,100 @@ from flymyai.core.exceptions import (
async def run_prediction():
# Initialize async client
fma_client = async_client(apikey="fly-secret-key")

# Submit async prediction task
prediction_task = await fma_client.predict_async_task(
model="flymyai/flux-schnell",
payload={"prompt": "Funny Cat with Stupid Dog"}
)

try:
# Await result with default timeout
result = await prediction_task.result()
print(f"Prediction completed: {result.inference_responses}")

# Check response status
all_successful = all(
resp.infer_details["status"] == 200
resp.infer_details["status"] == 200
for resp in result.inference_responses
)
print(f"All predictions successful: {all_successful}")

except RetryTimeoutExceededException:
print("Prediction is taking longer than expected")
except FlyMyAIExceptionGroup as e:
print(f"Prediction failed: {e}")

# Run async function
asyncio.run(run_prediction())
```
```

## M1 Agent Usage

### Using Synchronous Client

```python
from flymyai import m1_client

client = m1_client(apikey="fly-secret-key")
result = client.generate("An Iron Man")
print(result.data.text, result.data.file_url)
```

FlymyAI M1 client also stores request history for later generation context:

```python
from flymyai import m1_client

client = m1_client(apikey="fly-secret-key")

result = client.generate("An Iron Man")
print(result.data.text, result.data.file_url)

result = client.generate("Add him Captain America's shield")
print(result.data.text, result.data.file_url)
```

#### Passing image

```python
from pathlib import Path
from flymyai import m1_client

client = m1_client(apikey="fly-secret-key")
result = client.generate("An Iron Man", image=Path("./image.png"))
print(result.data.text, result.data.file_url)
```

### Using Asynchronous Client

```python
import asyncio
from flymyai import async_m1_client


async def main():
client = async_m1_client(apikey="fly-secret-key")
result = await client.generate("An Iron Man")
print(result.data.text, result.data.file_url)


asyncio.run(main())
```

#### Passing image

```python
import asyncio
from pathlib import Path
from flymyai import async_m1_client


async def main():
client = async_m1_client(apikey="fly-secret-key")
result = await client.generate("An Iron Man", image=Path("./image.png"))
print(result.data.text, result.data.file_url)


asyncio.run(main())
```
6 changes: 4 additions & 2 deletions flymyai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import httpx

from flymyai.core.client import FlyMyAI, AsyncFlyMyAI
from flymyai.core.client import FlyMyAI, AsyncFlyMyAI, FlyMyAIM1, AsyncFlymyAIM1
from flymyai.core.exceptions import FlyMyAIPredictException, FlyMyAIExceptionGroup


__all__ = [
"run",
"httpx",
Expand All @@ -19,3 +18,6 @@
async_client = AsyncFlyMyAI
run = client.run_predict
async_run = async_client.arun_predict

m1_client = FlyMyAIM1
async_m1_client = AsyncFlymyAIM1
46 changes: 46 additions & 0 deletions flymyai/core/_response.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json
import os
import typing
from dataclasses import dataclass

import httpx

Expand All @@ -23,3 +25,47 @@ def json(self, **kwargs) -> typing.Any:
return json.loads(self.content.removeprefix(b"event"))
else:
return super().json(**kwargs)


@dataclass
class ChatResponseData:
text: typing.Optional[str]
tool_used: typing.Optional[str]
file_url: typing.Optional[str]

@classmethod
def from_dict(
cls, data: typing.Optional[dict]
) -> typing.Optional["ChatResponseData"]:
if not data:
return None
return cls(
text=data.get("text"),
tool_used=data.get("tool_used"),
file_url=(
"".join(
[
os.getenv("FLYMYAI_M1_DSN", "https://api.chat.flymy.ai/"),
data.get("file_url"),
]
)
if data.get("file_url")
else None
),
)


@dataclass
class FlyMyAIM1Response:
success: bool
error: typing.Optional[str]
data: typing.Optional[ChatResponseData]

@classmethod
def from_httpx(cls, response):
json_data = response.json()
return cls(
success=json_data.get("success", False),
error=json_data.get("error"),
data=ChatResponseData.from_dict(json_data.get("data")),
)
8 changes: 8 additions & 0 deletions flymyai/core/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from flymyai.core.clients.AsyncClient import BaseAsyncClient
from flymyai.core.clients.SyncClient import BaseSyncClient
from flymyai.core.clients.m1Client import BaseM1SyncClient
from flymyai.core.clients.m1AsyncClient import BaseM1AsyncClient


class FlyMyAI(BaseSyncClient): ...


class AsyncFlyMyAI(BaseAsyncClient): ...


class FlyMyAIM1(BaseM1SyncClient): ...


class AsyncFlymyAIM1(BaseM1AsyncClient): ...
Loading
Loading