Skip to content

Commit c8830fc

Browse files
committed
Merge branch 'master' of https://github.com/edenai/edenai-apis into add-async-llm-chat
2 parents 5d06307 + 15f6725 commit c8830fc

File tree

12 files changed

+249
-22
lines changed

12 files changed

+249
-22
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"api_key": ""
3+
}

edenai_apis/apis/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@
7070
from .picsart import PicsartApi
7171
from .minimax import MinimaxApi
7272
from .iointelligence import IointelligenceApi
73+
from .bytedance import BytedanceApi
7374

7475
# THIS NEEDS TO BE DONE AUTOMATICALLY
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .bytedance_api import BytedanceApi
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import base64
2+
import json
3+
from io import BytesIO
4+
from typing import Dict, Literal, Optional, Any, List
5+
import mimetypes
6+
7+
import requests
8+
9+
from edenai_apis.features import ProviderInterface, ImageInterface, VideoInterface
10+
from edenai_apis.features.image.generation import (
11+
GenerationDataClass,
12+
GeneratedImageDataClass,
13+
)
14+
from edenai_apis.features.video.generation_async import GenerationAsyncDataClass
15+
from edenai_apis.loaders.data_loader import ProviderDataEnum
16+
from edenai_apis.loaders.loaders import load_provider
17+
from edenai_apis.utils.exception import ProviderException
18+
from edenai_apis.utils.types import (
19+
AsyncLaunchJobResponseType,
20+
ResponseType,
21+
AsyncPendingResponseType,
22+
AsyncResponseType,
23+
)
24+
from edenai_apis.utils.upload_s3 import USER_PROCESS, upload_file_bytes_to_s3
25+
from edenai_apis.llmengine.utils.moderation import moderate
26+
27+
28+
class BytedanceApi(ProviderInterface, ImageInterface, VideoInterface):
29+
provider_name = "bytedance"
30+
31+
def __init__(self, api_keys: Optional[Dict[str, Any]] = None):
32+
self.api_settings = load_provider(
33+
ProviderDataEnum.KEY, self.provider_name, api_keys=api_keys or {}
34+
)
35+
self.api_key = self.api_settings["api_key"]
36+
self.headers = {
37+
"Authorization": f"Bearer {self.api_key}",
38+
"Content-Type": "application/json",
39+
"Accept": "application/json",
40+
}
41+
42+
@moderate
43+
def image__generation(
44+
self,
45+
text: str,
46+
resolution: Literal["256x256", "512x512", "1024x1024"],
47+
num_images: int = 1,
48+
model: Optional[str] = None,
49+
**kwargs,
50+
) -> ResponseType[GenerationDataClass]:
51+
url = "https://ark.ap-southeast.bytepluses.com/api/v3/images/generations"
52+
payload = {
53+
"model": model,
54+
"prompt": text,
55+
"size": resolution,
56+
"response_format": "b64_json",
57+
}
58+
59+
try:
60+
response = requests.post(url, headers=self.headers, json=payload)
61+
original_response = response.json()
62+
except json.JSONDecodeError as exc:
63+
raise ProviderException("Internal Server Error", code=500) from exc
64+
65+
# Handle error
66+
if response.status_code != 200:
67+
raise ProviderException(
68+
message=original_response.get("error", {}).get("message"),
69+
code=response.status_code,
70+
)
71+
72+
generations: List[GeneratedImageDataClass] = []
73+
for generated_image in original_response.get("data"):
74+
image_b64 = generated_image.get("b64_json")
75+
76+
image_data = image_b64.encode()
77+
image_content = BytesIO(base64.b64decode(image_data))
78+
resource_url = upload_file_bytes_to_s3(image_content, ".png", USER_PROCESS)
79+
generations.append(
80+
GeneratedImageDataClass(
81+
image=image_b64, image_resource_url=resource_url
82+
)
83+
)
84+
85+
return ResponseType[GenerationDataClass](
86+
original_response=original_response,
87+
standardized_response=GenerationDataClass(items=generations),
88+
)
89+
90+
def video__generation_async__launch_job(
91+
self,
92+
text: str,
93+
duration: Optional[int] = 6,
94+
fps: Optional[int] = 24,
95+
dimension: Optional[str] = "1280x720",
96+
seed: Optional[float] = 12,
97+
file: Optional[str] = None,
98+
file_url: Optional[str] = None,
99+
model: Optional[str] = None,
100+
**kwargs,
101+
) -> AsyncLaunchJobResponseType:
102+
url = (
103+
"https://ark.ap-southeast.bytepluses.com/api/v3/contents/generations/tasks"
104+
)
105+
content = [
106+
{"type": "text", "text": text},
107+
]
108+
# if file:
109+
# with open(file, "rb") as fstream:
110+
# file_content = fstream.read()
111+
# file_b64 = base64.b64encode(file_content).decode("utf-8")
112+
# mime_type = mimetypes.guess_type(file)[0]
113+
# image_data = f"data:{mime_type};base64,{file_b64}"
114+
# content.append({"type": "image_url", "image_url": {"url": image_data}})
115+
payload = {
116+
"model": model,
117+
"content": content,
118+
}
119+
try:
120+
response = requests.post(url, headers=self.headers, json=payload)
121+
original_response = response.json()
122+
except json.JSONDecodeError as exc:
123+
raise ProviderException("Internal Server Error", code=500) from exc
124+
125+
# Handle error
126+
if response.status_code != 200:
127+
raise ProviderException(
128+
message=original_response.get("error", {}).get("message"),
129+
code=response.status_code,
130+
)
131+
provider_job_id = original_response.get("id")
132+
return AsyncLaunchJobResponseType(provider_job_id=provider_job_id)
133+
134+
def video__generation_async__get_job_result(
135+
self, provider_job_id: str
136+
) -> GenerationAsyncDataClass:
137+
url = f"https://ark.ap-southeast.volces.com/api/v3/contents/generations/tasks/{provider_job_id}"
138+
try:
139+
response = requests.get(url, headers=self.headers)
140+
original_response = response.json()
141+
except json.JSONDecodeError as exc:
142+
raise ProviderException("Internal Server Error", code=500) from exc
143+
144+
if response.status_code != 200:
145+
raise ProviderException(
146+
message=original_response["error"]["message"], code=response.status_code
147+
)
148+
if original_response["status"] == "cancelled":
149+
failure_message = original_response["error"]
150+
raise ProviderException(failure_message)
151+
if original_response["status"] != "succeeded":
152+
return AsyncPendingResponseType(provider_job_id=provider_job_id)
153+
video_uri = original_response["content"]["video_url"]
154+
return AsyncResponseType(
155+
original_response=provider_job_id,
156+
standardized_response=GenerationAsyncDataClass(
157+
video="", video_resource_url=video_uri
158+
),
159+
provider_job_id=provider_job_id,
160+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from edenai_apis.utils.exception import ProviderErrorLists
2+
3+
# NOTE: error messages should be regex patterns
4+
ERRORS: ProviderErrorLists = {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"image": {
3+
"generation": {
4+
"version": "v3"
5+
}
6+
},
7+
"video": {
8+
"generation": {
9+
"version": "v3"
10+
}
11+
}
12+
}

edenai_apis/apis/bytedance/outputs/image/generation_output.json

Lines changed: 26 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"status": "succeeded",
3+
"provider_job_id": "cgt-20250707165107-vhw95",
4+
"original_response": "cgt-20250707165107-vhw95",
5+
"standardized_response": {
6+
"video": "",
7+
"video_resource_url": "url"
8+
},
9+
"usage": null,
10+
"cost": null
11+
}

edenai_apis/features/image/generation/generation_args.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ def generation_arguments(provider_name: str) -> Dict:
1313
"replicate": "black-forest-labs/flux-pro",
1414
"leonardo": "Leonardo Phoenix",
1515
"minimax": "image-01",
16+
"bytedance": "seedream-3-0-t2i-250415",
1617
},
1718
}

edenai_apis/features/video/generation_async/generation_async_args.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,9 @@ def generation_async_arguments(provider_name: str) -> Dict:
3030
"seed": 12,
3131
"dimension": "1280x720",
3232
"file": file_wrapper,
33-
"settings": {"amazon": "amazon.nova-reel-v1:0", "minimax": "MiniMax-Hailuo-02"},
33+
"settings": {
34+
"amazon": "amazon.nova-reel-v1:0",
35+
"minimax": "MiniMax-Hailuo-02",
36+
"bytedance": "seedance-1-0-lite-t2v-250428",
37+
},
3438
}

0 commit comments

Comments
 (0)