Skip to content
Open
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
5 changes: 5 additions & 0 deletions contributors/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Added

- `contributors/gemini_ai_assistant/` — moved from repository root; Gemini AI Assistant example
- Google Gemini integration
- uAgents messaging example
- Environment-based configuration
- Error handling
- `contributors/` folder and contribution guide for community agent examples
- `contributors/community_agent/` — moved from repository root; AI community growth agent for events and hackathons
2 changes: 2 additions & 0 deletions contributors/gemini_ai_assistant/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Your Google Gemini API Key
GEMINI_API_KEY=your_google_gemini_api_key_here
128 changes: 128 additions & 0 deletions contributors/gemini_ai_assistant/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Gemini AI Assistant Example

This example demonstrates how to integrate Google's Generative AI (Gemini) with the Fetch.ai `uagents` framework. It consists of a multi-agent system where a **User Agent** interacts with a **Gemini Agent** to receive AI-generated responses based on given prompts.

## Architecture & Message Flow

The system uses asynchronous messaging via the `uagents` protocol:

```mermaid
sequenceDiagram
participant UserAgent as User Agent
participant GeminiAgent as Gemini Agent
participant GeminiAPI as Google Gemini API

UserAgent->>GeminiAgent: Sends PromptRequest (message)
Note over GeminiAgent: Validates prompt & checks API key
GeminiAgent->>GeminiAPI: client.models.generate_content()
GeminiAPI-->>GeminiAgent: Returns Generated Text
GeminiAgent-->>UserAgent: Sends PromptResponse (message)
Note over UserAgent: Logs AI Response
```

## Prerequisites

- Python 3.10 or higher
- A Google Gemini API Key. You can get one from [Google AI Studio](https://aistudio.google.com/).

## Installation

1. Navigate to the example directory:
```bash
cd innovation-lab-examples/contributors/gemini_ai_assistant
```

2. (Optional) Create and activate a virtual environment:
```bash
python -m venv venv
# On Windows: venv\Scripts\activate
# On Linux/macOS: source venv/bin/activate
```

3. Install the required dependencies:
```bash
pip install -r requirements.txt
```

## Configuration

1. Rename the `.env.example` file to `.env`:
```bash
# On Windows
ren .env.example .env
# On Linux/macOS
mv .env.example .env
```

2. Open the `.env` file and add your Google Gemini API key:
```ini
GEMINI_API_KEY=your_actual_api_key_here
```

## Running the Agents

To see the interaction, you will need to run the two agents in separate terminal windows.

### Terminal 1: Start the Gemini Agent

This agent will start up, load the API key, and listen for incoming prompts.

```bash
python gemini_agent.py
```

*Expected Output:*
```text
INFO: [gemini_agent]: Starting Gemini Agent: gemini_agent
INFO: [gemini_agent]: Agent address: agent1qdzh0lq802u3a8z7w9j83vntx23x7hmsuudflh2pwy005y5a5y4yv5f3rd6
INFO: [gemini_agent]: Gemini client initialized successfully.
...
```

### Terminal 2: Start the User Agent

Once the Gemini Agent is running, start the User Agent. It will immediately send a `PromptRequest` to the Gemini Agent upon startup.

```bash
python user_agent.py
```

*Expected Output in Terminal 2:*
```text
INFO: [user_agent]: Starting User Agent: user_agent
INFO: [user_agent]: Sending prompt to Gemini Agent at agent1qdzh0lq802u3a8z7w9j83vntx23x7hmsuudflh2pwy005y5a5y4yv5f3rd6...
...
INFO: [user_agent]: Received response from agent1qdzh0lq802u3a8z7w9j83vntx23x7hmsuudflh2pwy005y5a5y4yv5f3rd6:
INFO: [user_agent]: AI Response:
Decentralized AI agents are autonomous software programs that operate on distributed networks like blockchain, making decisions without a central authority. They interact with each other and their environment to solve complex problems or provide services in a secure and transparent manner.
```

*Expected Output in Terminal 1 (Gemini Agent):*
```text
INFO: [gemini_agent]: Received prompt from agent1qv9cx...: 'Explain the concept of decentralized AI agents in two short sentences.'
INFO: [gemini_agent]: Querying Gemini API...
INFO: [gemini_agent]: Successfully generated response from Gemini.
```

## Error Handling

The example includes robust error handling for various failure states:
- **Missing API Key:** If the `GEMINI_API_KEY` is not set, the Gemini Agent will log a warning on startup and return an error response for any prompt it receives.
- **Empty Prompts:** Prompts that are empty or contain only whitespace will be rejected with a specific error message.
- **API/Network Failures:** If the call to Google's servers fails (e.g., due to network issues or an invalid key), the error is caught and safely communicated back to the User Agent inside the `PromptResponse`.

## Troubleshooting

### Common Errors and Fixes

- **Error:** `ModuleNotFoundError: No module named 'google.genai'`
- **Fix:** Make sure you installed the dependencies with `pip install -r requirements.txt`.

- **Error:** `WARNING: GEMINI_API_KEY is not set in the environment variables.`
- **Fix:** Ensure you have created the `.env` file and added your API key.

- **Error:** `Gemini client not initialized. Check API key.`
- **Fix:** Provide a valid API key in the `.env` file and restart the `gemini_agent.py`.

- **Error from Gemini Agent:** `Gemini API Error: 400 API key not valid.`
- **Fix:** Your API key is incorrect. Verify it from Google AI Studio.
112 changes: 112 additions & 0 deletions contributors/gemini_ai_assistant/gemini_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import os
from dotenv import load_dotenv
from google import genai

from uagents import Agent, Context
from models import PromptRequest, PromptResponse

# Load environment variables
load_dotenv()

# Get the API key from environment variables
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")

# Create the Gemini agent
agent = Agent(
name="gemini_agent",
seed="gemini_agent_recovery_seed",
port=8001,
endpoint=["http://127.0.0.1:8001/submit"],
)

# Initialize the Gemini client
# Robust error handling for missing API key
client = None
if GEMINI_API_KEY:
try:
client = genai.Client(api_key=GEMINI_API_KEY)
except Exception as e:
print(f"Failed to initialize Gemini client: {e}")
else:
print("WARNING: GEMINI_API_KEY is not set in the environment variables.")

# Model configuration
MODEL_NAME = "gemini-2.5-flash"


@agent.on_event("startup")
async def startup(ctx: Context):
"""
Startup event handler to log the agent's details.
"""
ctx.logger.info(f"Starting Gemini Agent: {agent.name}")
ctx.logger.info(f"Agent address: {agent.address}")
if client is None:
ctx.logger.error("Gemini client is not initialized. Please check your API key.")
else:
ctx.logger.info("Gemini client initialized successfully.")


@agent.on_message(model=PromptRequest, replies=PromptResponse)
async def handle_prompt_request(ctx: Context, sender: str, msg: PromptRequest):
"""
Message handler for receiving PromptRequest messages.
Queries the Gemini API and responds with the generated text.
"""
ctx.logger.info(f"Received prompt from {sender}: '{msg.prompt}'")

# 1. Error handling: empty prompt
if not msg.prompt or not msg.prompt.strip():
ctx.logger.warning("Received empty prompt.")
await ctx.send(
sender, PromptResponse(response="", error="Prompt cannot be empty.")
)
return

# 2. Error handling: missing API key / client uninitialized
if client is None:
ctx.logger.error("Cannot process prompt: Gemini client is not initialized.")
await ctx.send(
sender,
PromptResponse(
response="", error="Gemini client not initialized. Check API key."
),
)
return

try:
ctx.logger.info("Querying Gemini API...")

# Asynchronous flow: we use run_in_executor if the SDK is synchronous,
# but the google-genai SDK can be called directly. Since it might block,
# in a fully async environment, it's best practice to run it safely,
# but for simplicity in this example we will just call it synchronously
# or use the async capabilities if available (genai.Client is generally sync,
# though async client can be used, we'll use the default one and assume it handles well
# or it is fast enough for the example).

response = client.models.generate_content(
model=MODEL_NAME,
contents=msg.prompt,
)

if response.text:
ctx.logger.info("Successfully generated response from Gemini.")
await ctx.send(sender, PromptResponse(response=response.text))
else:
ctx.logger.warning("Gemini returned an empty response.")
await ctx.send(
sender,
PromptResponse(response="", error="Gemini returned an empty response."),
)

# 3. Error handling: API failures or network failures
except Exception as e:
ctx.logger.error(f"Error querying Gemini API: {str(e)}")
await ctx.send(
sender, PromptResponse(response="", error=f"Gemini API Error: {str(e)}")
)


if __name__ == "__main__":
agent.run()
18 changes: 18 additions & 0 deletions contributors/gemini_ai_assistant/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from uagents import Model


class PromptRequest(Model):
"""
Message model for sending a prompt to the Gemini agent.
"""

prompt: str


class PromptResponse(Model):
"""
Message model for receiving a response from the Gemini agent.
"""

response: str
error: str | None = None
3 changes: 3 additions & 0 deletions contributors/gemini_ai_assistant/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
uagents>=0.11.0
google-genai>=0.2.0
python-dotenv>=1.0.0
55 changes: 55 additions & 0 deletions contributors/gemini_ai_assistant/user_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from uagents import Agent, Context
from models import PromptRequest, PromptResponse

# Address of the Gemini agent we want to communicate with.
# In a real scenario, this would be the actual address printed by the gemini_agent on startup.
# Since we are running locally with a known seed, we can pre-calculate it or configure it.
# For this example, we'll assume the user agent knows the gemini agent's address.
# The address below is derived from the seed "gemini_agent_recovery_seed".
GEMINI_AGENT_ADDRESS = (
"agent1qdzh0lq802u3a8z7w9j83vntx23x7hmsuudflh2pwy005y5a5y4yv5f3rd6"
)

# Create the user agent
agent = Agent(
name="user_agent",
seed="user_agent_recovery_seed",
port=8002,
endpoint=["http://127.0.0.1:8002/submit"],
)

# Example prompt to send
PROMPT_TEXT = "Explain the concept of decentralized AI agents in two short sentences."


@agent.on_event("startup")
async def startup(ctx: Context):
"""
Startup event handler.
The agent will send a PromptRequest to the Gemini agent when it starts.
"""
ctx.logger.info(f"Starting User Agent: {agent.name}")
ctx.logger.info(f"Agent address: {agent.address}")

ctx.logger.info(f"Sending prompt to Gemini Agent at {GEMINI_AGENT_ADDRESS}...")

# Send the PromptRequest message
await ctx.send(GEMINI_AGENT_ADDRESS, PromptRequest(prompt=PROMPT_TEXT))


@agent.on_message(model=PromptResponse)
async def handle_prompt_response(ctx: Context, sender: str, msg: PromptResponse):
"""
Message handler for receiving PromptResponse messages.
Logs the response generated by the AI or any errors.
"""
ctx.logger.info(f"Received response from {sender}:")

if msg.error:
ctx.logger.error(f"Error from Gemini Agent: {msg.error}")
else:
ctx.logger.info(f"AI Response: \n{msg.response}")


if __name__ == "__main__":
agent.run()
Loading