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
4 changes: 4 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* Add rich-click to pyproject.toml dependency '"rich-click>=1.8.8"'
* Replace all 'import click' with 'import rich_click as click' for drop in replacement

* git fetch https://github.com/google/adk-python.git main && git rebase FETCH_HEAD && git push -f origin feat/rich-click
Comment on lines +1 to +4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file appears to contain development notes and git commands. Such files are typically not committed to the source repository. Please consider removing this file from the pull request. If these notes are important for contributors, they could be moved to a more appropriate place like a wiki or a task in an issue tracker.

10 changes: 10 additions & 0 deletions contributing/samples/software_engineer_gemini/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# ruff: noqa
"""
Software Engineer Agent.

This package provides an AI-powered software engineering assistant that helps with
various software development tasks including code reviews, design patterns,
testing, debugging, documentation, and DevOps.
"""

from . import agent
125 changes: 125 additions & 0 deletions contributing/samples/software_engineer_gemini/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
Implementation of the Software Engineer Agent with knowledge and experience of sub-agents.

This is the main entry point for the software engineer agent.

It is a composite agent that uses the sub-agents to fulfill the user's request.
"""
import logging

from google.adk.agents import Agent
from google.adk.tools import load_memory

from . import prompt

# Use relative imports from the 'software_engineer' sibling directory
from .sub_agents.code_quality.agent import code_quality_agent
from .sub_agents.code_review.agent import code_review_agent
from .sub_agents.debugging.agent import debugging_agent
from .sub_agents.design_pattern.agent import design_pattern_agent
from .sub_agents.devops.agent import devops_agent
from .sub_agents.documentation.agent import documentation_agent
from .sub_agents.testing.agent import testing_agent
from .tools import (
check_command_exists_tool,
check_shell_command_safety_tool,
codebase_search_tool,
configure_shell_approval_tool,
configure_shell_whitelist_tool,
edit_file_tool,
execute_vetted_shell_command_tool,
get_os_info_tool,
google_search_grounding,
list_dir_tool,
# load_memory_from_file_tool, # Remove placeholder
read_file_tool,
)

# Import tools via the tools package __init__
from .tools import (
configure_approval_tool as configure_edit_approval_tool, # Keep alias for now
)

# save_current_session_to_file_tool, # Remove placeholder
# Import memory tools (using the wrapped variable names)
from .tools.memory_tools import add_memory_fact, search_memory_facts
from .tools.project_context import load_project_context

logger = logging.getLogger(__name__)


# --- Memory Initialization ---
def initialize_session_memory(tool_context):
"""Initializes the session memory in tool_context if it doesn't exist."""
if not hasattr(tool_context, "session_state"):
logger.warning("Tool context does not have session_state. Cannot initialize memory.")
# In a real scenario, might need to initialize session_state itself
# For now, we assume session_state exists but memory might not.
return

if "memory" not in tool_context.session_state:
logger.info("Initializing agent session memory.")
tool_context.session_state["memory"] = {
"context": {
"project_path": None, # Will be populated by load_project_context
"current_file": None,
},
"tasks": {
"active_task": None,
"completed_tasks": [],
},
"history": {
"last_read_file": None,
"last_search_query": None,
"last_error": None,
},
"user_preferences": {},
# Add other relevant fields as needed based on agent interactions
}
# else: memory already exists, do nothing


# --- Agent Definition ---

# Note: Using custom ripgrep-based codebase search in tools/code_search.py

# REF: https://ai.google.dev/gemini-api/docs/rate-limits
root_agent = Agent(
model="gemini-2.5-flash-preview-04-17",
name="root_agent",
description="An AI software engineer assistant that helps with various software development tasks",
instruction=prompt.ROOT_AGENT_INSTR,
sub_agents=[
design_pattern_agent,
documentation_agent,
code_review_agent,
code_quality_agent,
testing_agent,
debugging_agent,
devops_agent, # TODO: Move command tools to devops_agent with more guardrails
],
tools=[
read_file_tool,
list_dir_tool,
edit_file_tool,
configure_edit_approval_tool,
check_command_exists_tool,
check_shell_command_safety_tool,
configure_shell_approval_tool,
configure_shell_whitelist_tool,
execute_vetted_shell_command_tool,
google_search_grounding,
codebase_search_tool,
get_os_info_tool,
# Memory Tools:
load_memory, # Keep for transcript search
add_memory_fact, # Use wrapped tool variable name
search_memory_facts, # Use wrapped tool variable name
# Remove placeholder tools
# save_current_session_to_file_tool,
# load_memory_from_file_tool,
],
# Pass the function directly, not as a list
before_agent_callback=load_project_context,
output_key="software_engineer",
)
83 changes: 83 additions & 0 deletions contributing/samples/software_engineer_gemini/prompt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# ruff: noqa
"""Defines the prompts for the software engineer agent."""

ROOT_AGENT_INSTR = """
- You are an autonomous principal software engineer assistant
- You help and lead developers with various software development tasks including code reviews, design patterns, testing, debugging, documentation, and DevOps
- You delegate tasks to the appropriate sub-agents based on the user's request
- Format your responses back to users with markdown. Use code blocks for file contents and code snippets, and bullets for lists.
- After every tool call, summarize the result and keep your response concise
- Please use only the agents and tools to fulfill all user requests
- If you do not know the answer, please first try to use the the shell command and then the `google_search_grounding` tool to find the information.

## File System Interactions:
- To list files or directories, use the `list_directory_contents` tool. Provide the path.
- To read a file, use the `read_file_content` tool. Provide the path.
- **File Editing Approval:** By default, editing files requires user approval. You can change this setting for the current session using the `configure_edit_approval` tool. Call it with `require_approval=False` to disable approvals, or `require_approval=True` to enable them.
- **Editing/Creating Files:** To edit an existing file or create a new one, use the `edit_file_content` tool. Provide the `filepath` and the full `content`.
- If approval is required (default or enabled via `configure_edit_approval`), this tool will return a `pending_approval` status. You MUST then inform the user, show them the proposed path and content, and ask for confirmation.
- If the user approves, call `edit_file_content` again with the exact same `filepath` and `content`.
- If approval is *not* required (disabled via `configure_edit_approval`), the tool will write the file directly.

## Shell Command Execution:
- **Available Tools:**
- `configure_shell_approval`: Enables or disables the need for user approval for NON-WHITELISTED commands (Default: enabled, `require_approval=True`).
- `configure_shell_whitelist`: Manages a list of commands that ALWAYS run directly, bypassing the approval check (Actions: `add`, `remove`, `list`, `clear`). A default set of safe commands is included.
- `check_command_exists_tool`: Verifies if a command is available in the environment before attempting execution.
- `check_shell_command_safety`: Checks if a specific command can run without explicit user approval based on the whitelist and approval settings. Returns status: `whitelisted`, `approval_disabled`, or `approval_required`. **Use this BEFORE attempting execution.**
- `execute_vetted_shell_command`: Executes a vetted shell command. This is the **ONLY** way to run shell commands.

- **Workflow for Running a Command (`<command_to_run>`):**
1. **Check Existence:** Always run `check_command_exists_tool(command=<command_to_run>)` first. If it doesn't exist, inform the user and stop.
2. **Check Safety:** Then, run `check_shell_command_safety(command=<command_to_run>)`. Review the safety analysis. If significant risks are identified, **DO NOT PROCEED** unless you have explicit user confirmation or a clear, safe alternative. Explain the risks.
3. **Execute:** If checks pass, use `execute_vetted_shell_command(command=<command_to_run>, rationale=<brief_reasoning>)`. Provide a clear rationale.

## Other Tools:
- If you cannot delegate the request to a sub-agent, or if the query is about a general topic you don't know, use the `google_search_grounding` tool to find the information.

## Sub-Agent Delegation:
- First, try to delegate the request to the most relevant sub-agent based on the descriptions below.
- Inform the user that you are delegating the request to the sub-agent and the reason for the delegation.
- If the user asks for code review, transfer to the agent `code_review_agent`
- If the user asks for code quality analysis, static analysis, or quality improvements, transfer to the agent `code_quality_agent`
- If the user asks about design patterns or architecture, transfer to the agent `design_pattern_agent`
- If the user asks about testing, test generation, or test strategies, transfer to the agent `testing_agent`
- If the user asks for help with debugging or fixing errors, transfer to the agent `debugging_agent`
- If the user asks for help with documentation, transfer to the agent `documentation_agent`
- If the user asks about deployment, CI/CD, or DevOps practices, transfer to the agent `devops_agent`

## Long-Term Memory Access:
- Your conversations contain ephemeral short-term memory. Discrete facts can be stored in long-term memory using specific tools.
- **Storing Facts:** When asked to remember a specific piece of information (like a preference, goal, or detail), you MUST use the `add_memory_fact` tool. Provide a concise `entity_name` (e.g., 'favorite_food', 'project_goal_api') and the `fact_content` to store.
- **Retrieving Facts:** To recall specific facts you were previously asked to remember, you MUST use the `search_memory_facts` tool. Provide a `query` describing the fact you need.
- This searches only the facts you explicitly stored.
- **Searching History:** To search the general conversation history for context or past discussions (not specific stored facts), use the `load_memory` tool with a natural language `query`. This searches transcripts.
- **Do not guess.** If asked about something you should have remembered, use `search_memory_facts`. If asked about general past discussion, use `load_memory`.

# --- Placeholder: Manual Memory Persistence Tools (Not Implemented) ---
# - TODO: The following tools are placeholders for a potential future feature
# - TODO: allowing manual persistence if the standard MemoryService is insufficient
# - TODO: for the 'adk run' environment. DO NOT USE THEM unless explicitly told
# - TODO: that they have been fully implemented.
#
# - `save_current_session_to_file(filepath: str)`: (Placeholder) Manually saves the state
# - of the *current* session to a JSON file (default: ./.manual_agent_memory.json).
# - Useful if you need to explicitly persist the current context for later use
# - outside the standard memory service.
#
# - `load_memory_from_file(query: str, filepath: str)`: (Placeholder) Manually loads
# - sessions from a JSON file (default: ./.manual_agent_memory.json) and searches
# - them based on the query. Use this *instead* of `load_memory` if specifically
# - instructed to load from the manual file.
# --- End Placeholder ---

Current user:
<user_profile>
{user_profile}
</user_profile>

Current project:
<project_context>
{project_context}
</project_context>
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Shared libraries for the software engineer agent."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""Type definitions for the software engineer agent."""

from typing import List, Optional

from google.genai.types import GenerateContentConfig
from pydantic import BaseModel, Field

# Configure JSON response format
json_response_config = GenerateContentConfig(
temperature=0.2,
top_p=0.95,
candidate_count=1,
)


# https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key


# Define data models for agent responses
class CodeIssue(BaseModel):
"""Represents a code issue identified during code review."""

issue_type: str = Field(description="Type of issue (bug, security, performance, style)")
severity: str = Field(description="Severity of the issue (critical, high, medium, low)")
location: str = Field(description="File and line number where the issue occurs")
description: str = Field(description="Detailed description of the issue")
recommendation: str = Field(description="Suggested fix or improvement")


class CodeReviewResponse(BaseModel):
"""Response model for code review analysis."""

issues: List[CodeIssue] = Field(description="List of identified code issues")
summary: str = Field(description="Overall summary of the code review")
suggestions: List[str] = Field(description="General suggestions for improvement")


class DesignPattern(BaseModel):
"""Represents a design pattern recommendation."""

pattern_name: str = Field(description="Name of the design pattern")
category: str = Field(description="Category of the pattern (creational, structural, behavioral)")
problem_solved: str = Field(description="What problem this pattern solves")
benefits: List[str] = Field(description="Benefits of using this pattern")
tradeoffs: List[str] = Field(description="Potential drawbacks or tradeoffs")
example_code: str = Field(description="Example implementation code")


class DesignPatternResponse(BaseModel):
"""Response model for design pattern recommendations."""

recommended_patterns: List[DesignPattern] = Field(description="List of recommended design patterns")
explanation: str = Field(description="Explanation of why these patterns are recommended")


class TestCase(BaseModel):
"""Represents a test case."""

name: str = Field(description="Name of the test case")
description: str = Field(description="Description of what the test verifies")
test_type: str = Field(description="Type of test (unit, integration, system)")
prerequisites: List[str] = Field(description="Prerequisites for running the test")
test_code: str = Field(description="The test code implementation")
expected_outcome: str = Field(description="Expected outcome of the test")


class TestingResponse(BaseModel):
"""Response model for test generation."""

test_cases: List[TestCase] = Field(description="List of generated test cases")
testing_strategy: str = Field(description="Overall testing strategy")
test_coverage: Optional[str] = Field(description="Expected test coverage")


class DebuggingStep(BaseModel):
"""Represents a debugging step."""

step_number: int = Field(description="Step number in the debugging process")
description: str = Field(description="Description of the debugging step")
expected_outcome: str = Field(description="What to look for or expect from this step")
code_example: Optional[str] = Field(description="Example code for this debugging step")


class DebuggingResponse(BaseModel):
"""Response model for debugging assistance."""

problem_analysis: str = Field(description="Analysis of the problem")
root_cause: Optional[str] = Field(description="Identified root cause")
debugging_steps: List[DebuggingStep] = Field(description="Steps to debug the issue")
solution: Optional[str] = Field(description="Proposed solution")


class DocumentationItem(BaseModel):
"""Represents a documentation item."""

title: str = Field(description="Title of the documentation item")
content: str = Field(description="Content of the documentation")
doc_type: str = Field(description="Type of documentation (README, API doc, inline comment)")
format: str = Field(description="Format of the documentation (Markdown, reStructuredText, etc.)")


class DocumentationResponse(BaseModel):
"""Response model for documentation generation."""

documentation_items: List[DocumentationItem] = Field(description="List of documentation items")
suggestions: Optional[List[str]] = Field(description="Suggestions for improving documentation")


class DevOpsComponent(BaseModel):
"""Represents a DevOps component recommendation."""

component_name: str = Field(description="Name of the DevOps component")
purpose: str = Field(description="Purpose of this component")
implementation: str = Field(description="Implementation details or configuration")
alternatives: Optional[List[str]] = Field(description="Alternative options")


class DevOpsResponse(BaseModel):
"""Response model for DevOps recommendations."""

components: List[DevOpsComponent] = Field(description="List of DevOps components")
implementation_plan: str = Field(description="Overall implementation plan")
resources: Optional[List[str]] = Field(description="Helpful resources or documentation")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Sub-agents for the software engineer agent."""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Code quality sub-agent for analyzing code and suggesting improvements."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Code quality agent implementation."""

from google.adk.agents import Agent
from google.genai.types import GenerateContentConfig

from ...tools.code_analysis import (
analyze_code_tool,
get_analysis_issues_by_severity_tool,
suggest_code_fixes_tool,
)
from ...tools.filesystem import list_dir_tool, read_file_tool
from . import prompt

code_quality_agent = Agent(
model="gemini-2.5-flash-preview-04-17",
name="code_quality_agent",
description="Analyzes code for quality issues and suggests improvements",
instruction=prompt.CODE_QUALITY_AGENT_INSTR,
tools=[
analyze_code_tool,
get_analysis_issues_by_severity_tool,
suggest_code_fixes_tool,
read_file_tool,
list_dir_tool,
],
generate_content_config=GenerateContentConfig(
temperature=0.1,
top_p=0.95,
max_output_tokens=4096,
),
)
Loading
Loading