From ae413f7c7d614f6338e97fa328852a6e66b4a9de Mon Sep 17 00:00:00 2001 From: Neelr1912 Date: Sun, 7 Jun 2026 01:18:38 +0530 Subject: [PATCH 1/2] feat: add Gemini AI assistant example using uAgents --- gemini_ai_assistant/.env.example | 2 + gemini_ai_assistant/README.md | 128 +++++++++++++++++++++++++++ gemini_ai_assistant/gemini_agent.py | 102 +++++++++++++++++++++ gemini_ai_assistant/models.py | 14 +++ gemini_ai_assistant/requirements.txt | 3 + gemini_ai_assistant/user_agent.py | 53 +++++++++++ 6 files changed, 302 insertions(+) create mode 100644 gemini_ai_assistant/.env.example create mode 100644 gemini_ai_assistant/README.md create mode 100644 gemini_ai_assistant/gemini_agent.py create mode 100644 gemini_ai_assistant/models.py create mode 100644 gemini_ai_assistant/requirements.txt create mode 100644 gemini_ai_assistant/user_agent.py diff --git a/gemini_ai_assistant/.env.example b/gemini_ai_assistant/.env.example new file mode 100644 index 0000000..918a14f --- /dev/null +++ b/gemini_ai_assistant/.env.example @@ -0,0 +1,2 @@ +# Your Google Gemini API Key +GEMINI_API_KEY=your_google_gemini_api_key_here diff --git a/gemini_ai_assistant/README.md b/gemini_ai_assistant/README.md new file mode 100644 index 0000000..d55b7fa --- /dev/null +++ b/gemini_ai_assistant/README.md @@ -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/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. diff --git a/gemini_ai_assistant/gemini_agent.py b/gemini_ai_assistant/gemini_agent.py new file mode 100644 index 0000000..e1ee48e --- /dev/null +++ b/gemini_ai_assistant/gemini_agent.py @@ -0,0 +1,102 @@ +import os +from dotenv import load_dotenv +from google import genai +from google.genai import types + +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() diff --git a/gemini_ai_assistant/models.py b/gemini_ai_assistant/models.py new file mode 100644 index 0000000..7ced56e --- /dev/null +++ b/gemini_ai_assistant/models.py @@ -0,0 +1,14 @@ +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 diff --git a/gemini_ai_assistant/requirements.txt b/gemini_ai_assistant/requirements.txt new file mode 100644 index 0000000..b1d1c2f --- /dev/null +++ b/gemini_ai_assistant/requirements.txt @@ -0,0 +1,3 @@ +uagents>=0.11.0 +google-genai>=0.2.0 +python-dotenv>=1.0.0 diff --git a/gemini_ai_assistant/user_agent.py b/gemini_ai_assistant/user_agent.py new file mode 100644 index 0000000..b6e34f1 --- /dev/null +++ b/gemini_ai_assistant/user_agent.py @@ -0,0 +1,53 @@ +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() From 7f33884b79e0117417242697a6bf105fd65096ad Mon Sep 17 00:00:00 2001 From: Neelr1912 Date: Sun, 7 Jun 2026 11:15:38 +0530 Subject: [PATCH 2/2] fix: address repository review feedback --- contributors/CHANGELOG.md | 5 +++ .../gemini_ai_assistant}/.env.example | 0 .../gemini_ai_assistant}/README.md | 2 +- .../gemini_ai_assistant}/gemini_agent.py | 36 ++++++++++++------- .../gemini_ai_assistant}/models.py | 4 +++ .../gemini_ai_assistant}/requirements.txt | 0 .../gemini_ai_assistant}/user_agent.py | 18 +++++----- 7 files changed, 43 insertions(+), 22 deletions(-) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/.env.example (100%) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/README.md (98%) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/gemini_agent.py (83%) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/models.py (98%) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/requirements.txt (100%) rename {gemini_ai_assistant => contributors/gemini_ai_assistant}/user_agent.py (89%) diff --git a/contributors/CHANGELOG.md b/contributors/CHANGELOG.md index 7af5631..0f33ed3 100644 --- a/contributors/CHANGELOG.md +++ b/contributors/CHANGELOG.md @@ -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 diff --git a/gemini_ai_assistant/.env.example b/contributors/gemini_ai_assistant/.env.example similarity index 100% rename from gemini_ai_assistant/.env.example rename to contributors/gemini_ai_assistant/.env.example diff --git a/gemini_ai_assistant/README.md b/contributors/gemini_ai_assistant/README.md similarity index 98% rename from gemini_ai_assistant/README.md rename to contributors/gemini_ai_assistant/README.md index d55b7fa..9953a06 100644 --- a/gemini_ai_assistant/README.md +++ b/contributors/gemini_ai_assistant/README.md @@ -29,7 +29,7 @@ sequenceDiagram 1. Navigate to the example directory: ```bash - cd innovation-lab-examples/gemini_ai_assistant + cd innovation-lab-examples/contributors/gemini_ai_assistant ``` 2. (Optional) Create and activate a virtual environment: diff --git a/gemini_ai_assistant/gemini_agent.py b/contributors/gemini_ai_assistant/gemini_agent.py similarity index 83% rename from gemini_ai_assistant/gemini_agent.py rename to contributors/gemini_ai_assistant/gemini_agent.py index e1ee48e..ef23221 100644 --- a/gemini_ai_assistant/gemini_agent.py +++ b/contributors/gemini_ai_assistant/gemini_agent.py @@ -1,7 +1,6 @@ import os from dotenv import load_dotenv from google import genai -from google.genai import types from uagents import Agent, Context from models import PromptRequest, PromptResponse @@ -32,7 +31,8 @@ print("WARNING: GEMINI_API_KEY is not set in the environment variables.") # Model configuration -MODEL_NAME = 'gemini-2.5-flash' +MODEL_NAME = "gemini-2.5-flash" + @agent.on_event("startup") async def startup(ctx: Context): @@ -54,49 +54,59 @@ async def handle_prompt_request(ctx: Context, sender: str, msg: PromptRequest): 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.")) + 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.") + 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, + + # 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, + # 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.")) + 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)}")) + await ctx.send( + sender, PromptResponse(response="", error=f"Gemini API Error: {str(e)}") + ) + if __name__ == "__main__": agent.run() diff --git a/gemini_ai_assistant/models.py b/contributors/gemini_ai_assistant/models.py similarity index 98% rename from gemini_ai_assistant/models.py rename to contributors/gemini_ai_assistant/models.py index 7ced56e..71864e0 100644 --- a/gemini_ai_assistant/models.py +++ b/contributors/gemini_ai_assistant/models.py @@ -1,14 +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 diff --git a/gemini_ai_assistant/requirements.txt b/contributors/gemini_ai_assistant/requirements.txt similarity index 100% rename from gemini_ai_assistant/requirements.txt rename to contributors/gemini_ai_assistant/requirements.txt diff --git a/gemini_ai_assistant/user_agent.py b/contributors/gemini_ai_assistant/user_agent.py similarity index 89% rename from gemini_ai_assistant/user_agent.py rename to contributors/gemini_ai_assistant/user_agent.py index b6e34f1..2ef1378 100644 --- a/gemini_ai_assistant/user_agent.py +++ b/contributors/gemini_ai_assistant/user_agent.py @@ -6,7 +6,9 @@ # 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" +GEMINI_AGENT_ADDRESS = ( + "agent1qdzh0lq802u3a8z7w9j83vntx23x7hmsuudflh2pwy005y5a5y4yv5f3rd6" +) # Create the user agent agent = Agent( @@ -19,6 +21,7 @@ # 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): """ @@ -27,14 +30,12 @@ async def startup(ctx: Context): """ 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) - ) + 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): @@ -43,11 +44,12 @@ async def handle_prompt_response(ctx: Context, sender: str, msg: PromptResponse) 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()