chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:13 +08:00
commit ec2b666284
2231 changed files with 491535 additions and 0 deletions
@@ -0,0 +1,43 @@
# ADK Workflow Sample: Node Retries
## Overview
In real-world applications, interacting with external APIs, databases, or third-party services can occasionally result in transient failures (e.g., temporary network outages, rate limits, or bad gateways).
The ADK framework allows you to easily handle these scenarios by wrapping the unreliable logic in a `@node` decorator configured with `RetryConfig`. If the node raises one of the expected exceptions, the workflow engine automatically pauses, waits for a backoff delay, and reschedules the node for another attempt.
When a node raises an exception, the framework automatically emits an error event (with `error_code` and `error_message`) so the error is visible in the event stream. If the node has retry configured, it will be retried after the backoff delay.
This sample demonstrates a `get_weather` node that intentionally fails randomly (70% chance) by raising an `HTTPError` representing a 500 Internal Server error. The framework gracefully recovers and eventually succeeds, passing the result to `report_weather`.
## Graph
```mermaid
graph TD
START --> get_weather[get_weather <br/>Retries on HTTPError]
get_weather --> report_weather
```
## How To
1. **Import `RetryConfig`**: Ensure you import the configuration class to set your retry parameters.
```python
from google.adk.workflow import RetryConfig
```
1. **Configure the Decorator**: Apply the `@node` decorator to your Python function and specify the `retry_config` parameter with your desired logic (e.g., `max_attempts`, `initial_delay`).
```python
@node(retry_config=RetryConfig(max_attempts=5, initial_delay=1))
def get_weather(ctx: Context) -> str:
# ... flaky logic here ...
```
When an exception like `HTTPError` occurs, the ADK framework catches it, emits an error event, and processes the backoff delay automatically. As long as `max_attempts` hasn't been exceeded, the node executes again.
1. **Track Retries (Optional)**: If you need to know which attempt the node is currently running, you can access `ctx.attempt_count` from the `Context`.
```python
yield Event(message=f"Getting weather... attempt {ctx.attempt_count}")
```
@@ -0,0 +1,49 @@
# Copyright 2026 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.
import random
from urllib.error import HTTPError
from google.adk import Context
from google.adk import Event
from google.adk import Workflow
from google.adk.workflow import node
from google.adk.workflow import RetryConfig
@node(retry_config=RetryConfig(max_attempts=5, initial_delay=1))
def get_weather(ctx: Context) -> str:
"""A mock task that fails randomly."""
yield Event(message=f"Getting weather... attempt {ctx.attempt_count}")
if random.random() < 0.7: # 70% chance of failure
raise HTTPError(
url="http://mock-api.example.com",
code=500,
msg="Internal Server Error",
hdrs={},
fp=None,
)
yield "sunny"
def report_weather(node_input: str):
yield Event(message=f"The weather is {node_input}")
root_agent = Workflow(
name="root_agent",
edges=[("START", get_weather, report_weather)],
)
@@ -0,0 +1,131 @@
{
"appName": "retry",
"events": [
{
"author": "user",
"content": {
"parts": [
{
"text": "go"
}
],
"role": "user"
},
"id": "e-1",
"invocationId": "i-1",
"nodeInfo": {
"path": ""
}
},
{
"author": "root_agent",
"content": {
"parts": [
{
"text": "Getting weather... attempt 1"
}
],
"role": "user"
},
"id": "e-2",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/get_weather@1"
}
},
{
"author": "root_agent",
"errorCode": "HTTPError",
"errorMessage": "HTTP Error 500: Internal Server Error",
"id": "e-3",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/get_weather@1"
}
},
{
"author": "root_agent",
"content": {
"parts": [
{
"text": "Getting weather... attempt 2"
}
],
"role": "user"
},
"id": "e-4",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/get_weather@1"
}
},
{
"author": "root_agent",
"errorCode": "HTTPError",
"errorMessage": "HTTP Error 500: Internal Server Error",
"id": "e-5",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/get_weather@1"
}
},
{
"author": "root_agent",
"content": {
"parts": [
{
"text": "Getting weather... attempt 3"
}
],
"role": "user"
},
"id": "e-6",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/get_weather@1"
}
},
{
"author": "root_agent",
"id": "e-7",
"invocationId": "i-1",
"nodeInfo": {
"outputFor": [
"root_agent@1/get_weather@1"
],
"path": "root_agent@1/get_weather@1"
},
"output": "sunny"
},
{
"author": "root_agent",
"content": {
"parts": [
{
"text": "The weather is sunny"
}
],
"role": "user"
},
"id": "e-8",
"invocationId": "i-1",
"nodeInfo": {
"path": "root_agent@1/report_weather@1"
}
}
],
"id": "c0e0c167-1e63-4e18-84b6-ad64ba59376f",
"mocks": {
"random.random": [
0.5,
0.5,
0.8
]
},
"state": {
"__session_metadata__": {
"displayName": "go"
}
},
"userId": "user"
}