这是indexloc提供的服务,不要输入任何密码
Skip to content
Draft
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
16 changes: 6 additions & 10 deletions .cloudbuild/terraform/build_triggers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,12 @@ locals {
value = "adk_base,cloud_run"
},
{
name = "langgraph_base_react-agent_engine"
value = "langgraph_base_react,agent_engine"
name = "langgraph_base-agent_engine"
value = "langgraph_base,agent_engine"
},
{
name = "langgraph_base_react-cloud_run"
value = "langgraph_base_react,cloud_run,-dir,tag"
},
{
name = "crewai_coding_crew-agent_engine"
value = "crewai_coding_crew,agent_engine"
name = "langgraph_base-cloud_run"
value = "langgraph_base,cloud_run,-dir,tag"
},
{
name = "crewai_coding_crew-cloud_run"
Expand Down Expand Up @@ -157,8 +153,8 @@ agent_testing_included_files = { for combo in local.agent_testing_combinations :
value = "adk_base,cloud_run,-dir,tag"
},
{
name = "langgraph_base_react-agent_engine"
value = "langgraph_base_react,agent_engine"
name = "langgraph_base-agent_engine"
value = "langgraph_base,agent_engine"
},
{
name = "agentic_rag-agent_engine-vertex_ai_search"
Expand Down
8 changes: 4 additions & 4 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ SKIP_MYPY=1 _TEST_AGENT_COMBINATION="crewai_coding_crew,cloud_run" make lint-tem

# Testing examples
_TEST_AGENT_COMBINATION="adk_base,cloud_run,--session-type,in_memory" make test-templated-agents
_TEST_AGENT_COMBINATION="langgraph_base_react,agent_engine" make test-templated-agents
_TEST_AGENT_COMBINATION="langgraph_base,agent_engine" make test-templated-agents
```

### Critical Whitespace Control Patterns
Expand Down Expand Up @@ -219,7 +219,7 @@ from opentelemetry.sdk.trace import TracerProvider, export
from vertexai import agent_engines
{% endif %}

{%- if cookiecutter.is_adk_a2a %}
{%- if cookiecutter.is_a2a %}
from {{cookiecutter.agent_directory}}.agent import app as adk_app

{% endif %}
Expand Down Expand Up @@ -337,7 +337,7 @@ find agent_starter_pack -name "fast_api_app.py" -type f
- End-of-file newline issues

2. **`fast_api_app.py`** (deployment_targets/cloud_run/)
- Conditional imports (session_type, is_adk_a2a)
- Conditional imports (session_type, is_a2a)
- Long import lines
- Complex nested conditionals

Expand Down Expand Up @@ -365,7 +365,7 @@ SKIP_MYPY=1 _TEST_AGENT_COMBINATION="adk_base,agent_engine" make lint-templated-
# For agent_engine changes:
SKIP_MYPY=1 _TEST_AGENT_COMBINATION="adk_base,agent_engine" make lint-templated-agents
SKIP_MYPY=1 _TEST_AGENT_COMBINATION="adk_live,agent_engine" make lint-templated-agents
SKIP_MYPY=1 _TEST_AGENT_COMBINATION="langgraph_base_react,agent_engine" make lint-templated-agents
SKIP_MYPY=1 _TEST_AGENT_COMBINATION="langgraph_base,agent_engine" make lint-templated-agents
```

### Quick Reference: Whitespace Control Cheat Sheet
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ See [Installation Guide](https://googlecloudplatform.github.io/agent-starter-pac
| `adk_base` | A base ReAct agent implemented using Google's [Agent Development Kit](https://github.com/google/adk-python) |
| `adk_a2a_base` | An ADK agent with [Agent2Agent (A2A) Protocol](https://a2a-protocol.org/) support for distributed agent communication and interoperability |
| `agentic_rag` | A RAG agent for document retrieval and Q&A. Supporting [Vertex AI Search](https://cloud.google.com/generative-ai-app-builder/docs/enterprise-search-introduction) and [Vector Search](https://cloud.google.com/vertex-ai/docs/vector-search/overview). |
| `langgraph_base_react` | An agent implementing a base ReAct agent using LangGraph |
| `langgraph_base` | A base ReAct agent implemented using LangChain's [LangGraph](https://github.com/langchain-ai/langgraph) |
| `crewai_coding_crew` | A multi-agent system implemented with CrewAI created to support coding activities |
| `adk_live` | A real-time multimodal RAG agent powered by Gemini, supporting audio/video/text chat |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
description: "A multi-agent system implemented with CrewAI created to support coding activities"
settings:
requires_data_ingestion: false
deployment_targets: ["agent_engine", "cloud_run"]
deployment_targets: ["cloud_run"]
extra_dependencies: [
"langchain-google-vertexai>=2.0.7",
"langchain~=0.3.14",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.

description: "An agent implementing a base ReAct agent using LangGraph"
description: "A base ReAct agent implemented using LangGraph with Agent2Agent (A2A) Protocol support"
settings:
requires_data_ingestion: false
deployment_targets: ["agent_engine", "cloud_run"]
extra_dependencies: [
"langchain-google-vertexai>=2.0.7",
"langchain~=0.3.14",
"langgraph~=0.6.2",
"langchain-community~=0.3.17",
"langchain-openai~=0.3.5",
"langchain-google-vertexai>=3.0.3",
"langchain~=1.0.5",
"langgraph~=1.0.3",
"langchain-community~=0.4.1",
"langchain-openai~=1.0.2",
"a2a-sdk[http-server]~=0.3.9",
"nest-asyncio>=1.6.0,<2.0.0",
]
frontend_type: "streamlit"
example_question: "What's the weather in San Francisco?"
tags: ["langgraph", "a2a"]
frontend_type: "inspector"
example_question: "What's the weather in San Francisco?"
25 changes: 25 additions & 0 deletions agent_starter_pack/agents/langgraph_base/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# LangGraph Base ReAct Agent with A2A Protocol

<p align="center">
<img src="https://langchain-ai.github.io/langgraph/static/wordmark_dark.svg" width="50%" alt="LangGraph Logo" style="margin-right: 40px; vertical-align: middle;">
<img src="https://github.com/a2aproject/A2A/blob/main/docs/assets/a2a-logo-white.svg?raw=true" width="40%" alt="A2A Logo" style="vertical-align: middle;">
</p>

A base ReAct agent built using **[LangGraph](https://docs.langchain.com/oss/python/langgraph/overview)** with **[Agent2Agent (A2A) Protocol](https://a2a-protocol.org/)** support. This example demonstrates how to build a LangGraph-based agent with distributed agent communication capabilities through the A2A protocol for interoperability with agents across different frameworks and languages.

## Key Features

- **Simple Architecture**: Shows the basic building blocks of a LangGraph agent
- **A2A Protocol Support**: Enables distributed agent communication and interoperability
- **Streaming Support**: Includes streaming response capability using Vertex AI
- **Sample Tool Integration**: Includes a basic search tool to demonstrate tool usage

## Validating Your A2A Implementation

When deploying to **Cloud Run**, this template includes the **[A2A Protocol Inspector](https://github.com/a2aproject/a2a-inspector)** for validating your agent's A2A implementation.

```bash
make inspector
```

For detailed setup instructions including local and remote testing workflows, refer to the `README.md` in your generated project.
17 changes: 17 additions & 0 deletions agent_starter_pack/agents/langgraph_base/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .agent import root_agent

__all__ = ["root_agent"]
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# mypy: disable-error-code="union-attr"
from langchain.agents import create_agent
from langchain_google_vertexai import ChatVertexAI
from langgraph.prebuilt import create_react_agent
from langgraph.graph.state import CompiledStateGraph

LOCATION = "global"
LLM = "gemini-2.5-flash"
Expand All @@ -29,6 +29,6 @@ def get_weather(query: str) -> str:
return "It's 90 degrees and sunny."


agent = create_react_agent(
model=llm, tools=[get_weather], prompt="You are a helpful assistant"
root_agent: CompiledStateGraph = create_agent(
model=llm, tools=[get_weather], system_prompt="You are a helpful assistant"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Converters for A2A and LangChain types."""

from .part_converter import (
convert_a2a_parts_to_langchain_content,
convert_langchain_content_to_a2a_parts,
)

__all__ = [
"convert_a2a_parts_to_langchain_content",
"convert_langchain_content_to_a2a_parts",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Converters between A2A Parts and LangChain message content."""

from __future__ import annotations

import logging
from typing import Any

from a2a.types import FilePart, FileWithBytes, FileWithUri, Part, TextPart

logger = logging.getLogger(__name__)


LangChainContent = str | list[str | dict[str, Any]]
LangChainContentDict = dict[str, Any]


def convert_a2a_part_to_langchain_content(part: Part) -> LangChainContentDict | str:
"""Convert an A2A Part to LangChain message content format."""

root = part.root

if isinstance(root, TextPart):
return {"type": "text", "text": root.text}

elif isinstance(root, FilePart):
file_data = root.file
mime_type = file_data.mime_type if hasattr(file_data, "mime_type") else None

# Determine media type from mime_type
media_type = "image" # default
if mime_type:
if mime_type.startswith("audio/"):
media_type = "audio"
elif mime_type.startswith("video/"):
media_type = "video"

if isinstance(file_data, FileWithUri):
return {"type": media_type, "url": file_data.uri}
else:
# Base64 data should already be encoded
return {
"type": media_type,
"base64": file_data.bytes,
"mime_type": mime_type or "application/octet-stream",
}

else:
import json

data_str = json.dumps(root.data, indent=2)
return {"type": "text", "text": f"[Structured Data]\n{data_str}"}


def convert_langchain_content_to_a2a_part(content: Any) -> Part:
"""Convert LangChain message content to an A2A Part."""

if isinstance(content, str):
return Part(root=TextPart(text=content))

if isinstance(content, dict):
content_type = content.get("type")

if content_type == "text":
text = content.get("text", "")
return Part(root=TextPart(text=text))

elif content_type in ("image", "audio", "video"):
# Handle URL-based media
if "url" in content:
url = content["url"]
if url.startswith("data:"):
# Parse data URI format: data:mime_type;base64,data
try:
header, base64_data = url.split(",", 1)
mime_type = header.split(";")[0].replace("data:", "")
return Part(
root=FilePart(
file=FileWithBytes(
bytes=base64_data, mime_type=mime_type or None
)
)
)
except Exception as e:
logger.warning(f"Failed to parse data URI: {e}")
return Part(root=FilePart(file=FileWithUri(uri=url)))
else:
return Part(root=FilePart(file=FileWithUri(uri=url)))

# Handle base64-encoded media
elif "base64" in content:
mime_type = content.get("mime_type")
return Part(
root=FilePart(
file=FileWithBytes(bytes=content["base64"], mime_type=mime_type)
)
)

# Handle file_id-based media
elif "file_id" in content:
return Part(
root=FilePart(file=FileWithUri(uri=f"file://{content['file_id']}"))
)

else:
import json

text = json.dumps(content)
logger.warning(f"Unknown content type '{content_type}', converting to text")
return Part(root=TextPart(text=text))

logger.warning(f"Unknown content type: {type(content)}, converting to text")
return Part(root=TextPart(text=str(content)))


def convert_a2a_parts_to_langchain_content(parts: list[Part]) -> LangChainContent:
"""Convert a list of A2A Parts to LangChain message content."""

if not parts:
return ""

converted: list[str | dict[str, Any]] = []
for part in parts:
result = convert_a2a_part_to_langchain_content(part)
converted.append(result)

if len(converted) == 1 and isinstance(converted[0], str):
return converted[0]

if (
len(converted) == 1
and isinstance(converted[0], dict)
and converted[0].get("type") == "text"
):
text_value = converted[0].get("text")
if isinstance(text_value, str):
return text_value

return converted


def convert_langchain_content_to_a2a_parts(content: LangChainContent) -> list[Part]:
"""Convert LangChain message content to a list of A2A Parts."""

if isinstance(content, str):
return [Part(root=TextPart(text=content))]

result: list[Part] = []
for item in content:
result.append(convert_langchain_content_to_a2a_part(item))
return result
13 changes: 13 additions & 0 deletions agent_starter_pack/agents/langgraph_base/app/executor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Loading