The Bedrock Client
AWS Bedrock is accessed via boto3. The image model runs in us-west-2 — the only region where Stable Image Core was available at time of writing.
import boto3, base64, json
from decouple import config
def _bedrock_client():
return boto3.client(
"bedrock-runtime",
region_name="us-west-2",
aws_access_key_id=config("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=config("AWS_SECRET_ACCESS_KEY"),
)
Invoking the Model
The model ID is stability.stable-image-core-v1:1. The payload is a JSON body with the prompt and output format; the response contains the generated image as a base64 string inside a images list.
IMAGE_MODEL = "stability.stable-image-core-v1:1"
def generate_image(prompt: str) -> bytes:
client = _bedrock_client()
response = client.invoke_model(
modelId=IMAGE_MODEL,
contentType="application/json",
accept="application/json",
body=json.dumps({
"prompt": prompt,
"aspect_ratio": "1:1",
"output_format": "png",
}),
)
body = json.loads(response["body"].read())
return base64.b64decode(body["images"][0])
Storing in S3
The image bytes are uploaded directly to S3 using put_object. The resulting URL is stored alongside the user's media record.
import uuid
def save_to_s3(image_bytes: bytes, user_id: int) -> str:
s3 = boto3.client("s3", region_name=config("AWS_S3_REGION_NAME"))
key = f"ai-generated/{user_id}/{uuid.uuid4()}.png"
bucket = config("AWS_STORAGE_BUCKET_NAME")
region = config("AWS_S3_REGION_NAME")
s3.put_object(
Bucket=bucket,
Key=key,
Body=image_bytes,
ContentType="image/png",
)
return f"https://{bucket}.s3.{region}.amazonaws.com/{key}"
Tip: For the 1:1 aspect ratio Stable Image Core charges the same as any other ratio. Use 16:9 for hero images and 1:1 for thumbnails — no extra cost, but the composition is much better for each use case.