Skip to content
Closed
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEMINI_API_KEY="your_actual_google_gemini_api_key_here"
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,58 @@ pip install a2a-sdk

You can also find more Python samples [here](https://github.com/google-a2a/a2a-samples/tree/main/samples/python) and JavaScript samples [here](https://github.com/google-a2a/a2a-samples/tree/main/samples/js).

### Blog Post Generation Agent Example

This example demonstrates an agent capable of generating blog posts using the Gemini API.

#### Prerequisites

1. **Install Dependencies**:
Ensure you have all necessary dependencies installed. If you've followed the main installation for `a2a-sdk`, you might also need `python-dotenv` and `google-generativeai`:
```bash
pip install python-dotenv google-generativeai
```
(Note: `google-generativeai` should already be installed if previous steps were followed for this agent, but `python-dotenv` is likely new for this example).

2. **Set up Gemini API Key**:
- Create a `.env` file in the root of this repository by copying the `.env.example` file:
```bash
cp .env.example .env
```
- Edit the `.env` file and replace `"your_actual_google_gemini_api_key_here"` with your actual Gemini API key.
```
GEMINI_API_KEY="your_actual_api_key_here"
```

#### Running the Example

1. Navigate to the `examples` directory (if you are not already there):
```bash
cd examples
```
2. Run the script:
```bash
python run_blog_generator.py
```
The script will:
- Generate a blog topic based on predefined keywords.
- Generate an outline for the topic.
- Write content for each section of the outline.
- Assemble the full blog post.
- Print the final blog post to the console and save it to `generated_blog_post.md` in the `examples` directory (where the script is run).

#### How it Works

The `run_blog_generator.py` script uses the `ExampleAgent` located in `src/a2a/example_agent/agent.py`. This agent has been configured with capabilities to:
- `generate_blog_topic`: Creates a topic.
- `generate_blog_outline`: Structures the blog post.
- `write_blog_section`: Writes content for each section using the Gemini API.
- `assemble_blog_post`: Compiles the sections into a final blog post.

The agent reads the `GEMINI_API_KEY` from the environment variables (loaded from the `.env` file located in the project root or the `examples/` directory).

*Note: The `ExampleAgent` currently uses placeholder classes for some core A2A SDK components (`Agent`, `AgentCapability`, etc.) as they were not found directly within the SDK during development of this example. These placeholders would ideally be replaced by actual SDK components.*

## License

This project is licensed under the terms of the [Apache 2.0 License](https://raw.githubusercontent.com/google-a2a/a2a-python/refs/heads/main/LICENSE).
Expand Down
164 changes: 164 additions & 0 deletions examples/run_blog_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import asyncio
import os
# It's good practice to handle potential ImportError for dotenv
try:
from dotenv import load_dotenv
except ImportError:
print("python-dotenv library not found. Please install it by running: pip install python-dotenv")
print("This script relies on a .env file to load your GEMINI_API_KEY.")
exit(1)


# Assuming ExampleAgent and TaskParameters are accessible via a2a.
# This might require ensuring src is in PYTHONPATH or the package is installed.

Check failure on line 13 in examples/run_blog_generator.py

View workflow job for this annotation

GitHub Actions / Check Spelling

`PYTHONPATH` is not a recognized word. (unrecognized-spelling)
# For direct script execution, you might need to adjust sys.path or set PYTHONPATH.

Check failure on line 14 in examples/run_blog_generator.py

View workflow job for this annotation

GitHub Actions / Check Spelling

`PYTHONPATH` is not a recognized word. (unrecognized-spelling)

Check warning on line 14 in examples/run_blog_generator.py

View workflow job for this annotation

GitHub Actions / Check Spelling

`PYTHONPATH` is not a recognized word. (unrecognized-spelling)
import sys
# Add src to Python path if running script directly from repo root
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))

from a2a.example_agent.agent import ExampleAgent
from a2a.example_agent.agent import TaskParameters # Using the placeholder from agent.py

# MockTaskCompleter to capture results for the script
class ScriptTaskCompleter:
def __init__(self):
self.output = None
self.error = None
self.has_failed = False

def complete_task(self, output: any):
self.output = output
self.error = None
self.has_failed = False
# print(f"Task completed successfully.") # Optional: for verbose logging

def fail_task(self, error_message: str):
self.output = None
self.error = error_message
self.has_failed = True
# print(f"Task failed: {error_message}") # Optional: for verbose logging

async def main():
# Attempt to load .env file from the project root (one level up from examples/)
dotenv_path = os.path.join(os.path.dirname(__file__), '..', '.env')
if os.path.exists(dotenv_path):
load_dotenv(dotenv_path)
print(f"Loaded .env from {dotenv_path}")
else:
# Fallback to trying to load .env from the current directory (examples/)
if load_dotenv():
print(f"Loaded .env from current directory")
else:
print("No .env file found in project root or current directory.")


api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
print("Error: GEMINI_API_KEY environment variable not set or .env file not found/loaded.")
print("Please create a .env file in the project root with your API key:")
print("Example .env content: GEMINI_API_KEY='your_actual_api_key_here'")
return

try:
agent = ExampleAgent() # Initializes with API key from env
except ValueError as e:
print(f"Error initializing agent: {e}")
return

completer = ScriptTaskCompleter()

# 1. Generate Blog Topic
print("\n--- 1. Generating Blog Topic ---")
topic_keywords = ["artificial intelligence in healthcare", "future trends", "patient outcomes"]
print(f"Keywords: {', '.join(topic_keywords)}")
topic_params = TaskParameters(parameters={
"capability_name": "generate_blog_topic",
"keywords": topic_keywords
})
await agent.execute_task(completer, topic_params)

if completer.has_failed:
print(f"Could not generate topic: {completer.error}")
return
blog_topic = completer.output
print(f"Generated Topic: {blog_topic}")

# 2. Generate Blog Outline
print("\n--- 2. Generating Blog Outline ---")
print(f"Using topic: {blog_topic}")
outline_params = TaskParameters(parameters={
"capability_name": "generate_blog_outline",
"topic": blog_topic
})
await agent.execute_task(completer, outline_params)

if completer.has_failed:
print(f"Could not generate outline: {completer.error}")
return
blog_outline = completer.output
if not blog_outline: # Check if outline is empty or None
print(f"Generated outline was empty. Stopping.")
return

print(f"Generated Outline:")
for i, section_title in enumerate(blog_outline):
print(f" {i+1}. {section_title}")

# 3. Write Blog Sections
print("\n--- 3. Writing Blog Sections ---")
written_sections = []
for i, section_title in enumerate(blog_outline):
# Check if section_title is valid
if not section_title or not isinstance(section_title, str) or not section_title.strip():
print(f" Skipping invalid section title at index {i}: '{section_title}'")
written_sections.append(f"Skipped section due to invalid title: '{section_title}'")
continue

print(f" Writing section {i+1}: '{section_title}'...")
section_params = TaskParameters(parameters={
"capability_name": "write_blog_section",
"section_prompt": section_title
# Using default model 'gemini-1.5-flash-latest'
})
await agent.execute_task(completer, section_params)

if completer.has_failed:
error_msg = completer.error if completer.error else "Unknown error"
print(f" Could not write section '{section_title}': {error_msg}")
written_sections.append(f"Content for '{section_title}' could not be generated: {error_msg}")
continue # Continue to next section for now

section_content = completer.output if completer.output else ""
written_sections.append(section_content)
print(f" Section content (first 80 chars): {section_content[:80].replace('\n', ' ')}...")


# 4. Assemble Blog Post
print("\n--- 4. Assembling Blog Post ---")
assembly_params = TaskParameters(parameters={
"capability_name": "assemble_blog_post",
"title": blog_topic, # Using the generated topic as title
"sections": written_sections
})
await agent.execute_task(completer, assembly_params)

if completer.has_failed:
print(f"Could not assemble blog post: {completer.error}")
return

final_blog_post = completer.output
print("\n--- Generated Blog Post ---")
print(final_blog_post)

# Save to file
output_filename = "generated_blog_post.md"
with open(output_filename, "w", encoding="utf-8") as f:
f.write(final_blog_post)
print(f"\nBlog post saved to: {output_filename}")

print("\n\n--- Example Script Finished ---")
print("To run this script again: python examples/run_blog_generator.py")
print("Ensure you have a .env file in the project root (../.env) or in the examples/ directory (./.env) with your GEMINI_API_KEY.")

if __name__ == "__main__":
asyncio.run(main())
4 changes: 4 additions & 0 deletions src/a2a/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
"""The A2A Python SDK."""

from .example_agent import ExampleAgent

__all__ = ["ExampleAgent"]
3 changes: 3 additions & 0 deletions src/a2a/example_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .agent import ExampleAgent

__all__ = ["ExampleAgent"]
Loading
Loading