Skip to content

Commit c505c2f

Browse files
committed
added untested funcitonal tool for agent
1 parent c56f043 commit c505c2f

File tree

7 files changed

+174
-2
lines changed

7 files changed

+174
-2
lines changed

api/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
REPLICATE_API_TOKEN=YOUR_REPLICATE_API_TOKEN
22
GOOGLE_API_KEY=YOUR_GOOGLE_API_KEY
33
DATABASE_URL=YOUR_DATABASE_URL
4+
5+
AWS_REGION=us-east-1
6+
AWS_ACCESS_KEY_ID=your_access_key_here
7+
AWS_SECRET_ACCESS_KEY=your_secret_key_here
8+
AWS_S3_BUCKET_NAME=your-bucket-name-here

api/.pre-commit-config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ repos:
1515
rev: v1.10.0
1616
hooks:
1717
- id: mypy
18+
additional_dependencies:
19+
- types-requests
1820

1921
- repo: https://github.com/pre-commit/pre-commit-hooks
2022
rev: v4.6.0

api/llm/agent.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from langgraph.prebuilt import create_react_agent
1010

1111
from llm.prompt import system_message
12+
from llm.tools import initialize_tools
1213

1314
load_dotenv()
1415

@@ -53,10 +54,13 @@ def get_agent():
5354
# Build LLM
5455
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash")
5556

57+
# Build tools
58+
tools = initialize_tools()
59+
5660
# Create agent
5761
_agent_executor = create_react_agent(
5862
llm,
59-
tools=[],
63+
tools=tools,
6064
prompt=system_message,
6165
checkpointer=get_checkpointer(),
6266
)

api/llm/tools.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import uuid
2+
from typing import Optional
3+
4+
import replicate
5+
import requests
6+
from dotenv import load_dotenv
7+
from langchain_core.tools import tool
8+
9+
from llm.utils import upload_generated_image_to_s3
10+
11+
load_dotenv()
12+
13+
14+
def initialize_tools():
15+
"""Initialize the tools for the agent."""
16+
17+
@tool(
18+
description="Generate an image based on a prompt",
19+
)
20+
def generate_image(
21+
prompt: str,
22+
user_id: str,
23+
image_url: str,
24+
) -> str:
25+
"""
26+
Generate an image based on a prompt.
27+
"""
28+
input = {
29+
"width": 768,
30+
"height": 768,
31+
"prompt": prompt,
32+
"refine": "expert_ensemble_refiner",
33+
"apply_watermark": False,
34+
"num_inference_steps": 25,
35+
"prompt_strength": 0.5,
36+
"image": image_url,
37+
}
38+
39+
# Generate image using Replicate
40+
output = replicate.run(
41+
"stability-ai/sdxl:7762fd07cf82c948538e41f63f77d6 \
42+
85e02b063e37e496e96eefd46c929f9bdc",
43+
input=input,
44+
)
45+
46+
# Check if generation was successful
47+
if not output or len(output) == 0:
48+
return "Failed to generate image. Please try again."
49+
50+
generated_image_url = output[0] if isinstance(output, list) else output
51+
52+
# Download the generated image
53+
image_data: Optional[bytes] = None
54+
try:
55+
response = requests.get(generated_image_url)
56+
response.raise_for_status()
57+
image_data = response.content # get the actual image bytes in content into memory
58+
# Close the response to free up resources
59+
response.close()
60+
except Exception as e:
61+
return f"Failed to download generated image: {str(e)}"
62+
63+
# Generate unique ID for the image
64+
image_id = str(uuid.uuid4())
65+
66+
# Upload to S3
67+
try:
68+
s3_result = upload_generated_image_to_s3(
69+
image_data=image_data, image_id=image_id, user_id=user_id, prompt=prompt
70+
)
71+
72+
if s3_result["success"]:
73+
return f"Image generated successfully! User can find it his/her gallery. \
74+
Image ID: {image_id}"
75+
else:
76+
return (
77+
f"Image generated but failed to save: {s3_result.get('error', 'Unknown error')}"
78+
)
79+
80+
except Exception as e:
81+
# Clear image data from memory even if upload fails
82+
return f"Image generated but failed to save to storage: {str(e)}"
83+
84+
finally:
85+
if image_data:
86+
# Clear image data from memory
87+
del image_data
88+
89+
return [generate_image]

api/llm/utils.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import os
2+
from datetime import datetime
3+
from typing import Any, Dict
4+
5+
import boto3
6+
from botocore.exceptions import ClientError
7+
8+
9+
def upload_generated_image_to_s3(
10+
image_data: bytes, image_id: str, user_id: str, prompt: str
11+
) -> Dict[str, Any]:
12+
"""
13+
Upload a generated image to S3.
14+
15+
Args:
16+
image_data: The image data as bytes
17+
image_id: Unique identifier for the image
18+
user_id: User identifier
19+
prompt: The prompt used to generate the image
20+
21+
Returns:
22+
Dict with success status and URL or error message
23+
"""
24+
try:
25+
# Initialize S3 client
26+
s3_client = boto3.client(
27+
"s3",
28+
region_name=os.environ.get("AWS_REGION", "us-east-1"),
29+
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
30+
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
31+
)
32+
33+
# Generate S3 key with userId and imageId for organization
34+
key = f"users/{user_id}/images/{image_id}"
35+
bucket_name = os.environ.get("AWS_S3_BUCKET_NAME")
36+
37+
if not bucket_name:
38+
return {"success": False, "error": "AWS_S3_BUCKET_NAME environment variable is not set"}
39+
40+
# Upload to S3
41+
s3_client.put_object(
42+
Bucket=bucket_name,
43+
Key=key,
44+
Body=image_data,
45+
ContentType="image/png",
46+
Metadata={
47+
"title": "Generated Image", # TODO: add title to the image provided by agent
48+
"imageId": image_id,
49+
"userId": user_id,
50+
"uploadedAt": datetime.now().isoformat(),
51+
"type": "generated",
52+
"generationPrompt": prompt,
53+
},
54+
)
55+
56+
# Generate presigned URL for reading the uploaded file (valid for 2 hours)
57+
presigned_url = s3_client.generate_presigned_url(
58+
"get_object",
59+
Params={"Bucket": bucket_name, "Key": key},
60+
ExpiresIn=7200, # 2 hours
61+
)
62+
63+
return {"success": True, "url": presigned_url, "image_id": image_id}
64+
65+
except ClientError as e:
66+
return {"success": False, "error": str(e)}
67+
except Exception as e:
68+
return {"success": False, "error": str(e)}

api/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ dependencies = [
2121
"langchain[google-genai]",
2222
"langgraph-checkpoint-postgres>=0.2.0",
2323
"psycopg[binary]>=3.1.18",
24+
"boto3",
25+
"requests",
2426
]
2527

2628
[project.optional-dependencies]
@@ -30,6 +32,7 @@ dev = [
3032
"black>=24.4.0",
3133
"mypy>=1.10.0",
3234
"pre-commit>=3.7.0",
35+
"types-requests",
3336
]
3437

3538
[tool.setuptools.packages.find]

src/lib/actions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,11 @@ export async function uploadImageToS3(
9696
Body: buffer,
9797
ContentType: file.type,
9898
Metadata: {
99-
originalName: file.name,
99+
title: file.name,
100100
imageId: imageId,
101101
userId: userId,
102102
uploadedAt: new Date().toISOString(),
103+
type: "uploaded",
103104
},
104105
});
105106

0 commit comments

Comments
 (0)