forked from google-agentic-commerce/AP2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathretrying_llm_agent.py
More file actions
90 lines (78 loc) · 2.84 KB
/
Copy pathretrying_llm_agent.py
File metadata and controls
90 lines (78 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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
#
# https://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.
"""An LLM agent that surfaces errors to the user and then retries.
This implementation enhances the ADK's LlmAgent by automatically retrying
requests and surfacing errors captured from the LLM.
"""
import logging
import traceback
import sys
if sys.version_info >= (3, 12):
from typing import override
else:
from typing_extensions import override
from google.adk.agents.invocation_context import InvocationContext
from google.adk.agents.llm_agent import LlmAgent
from google.adk.events.event import Event
from typing_extensions import AsyncGenerator
_logger = logging.getLogger(__name__)
class RetryingLlmAgent(LlmAgent):
"""An LLM agent that surfaces errors to the user and then retries."""
def __init__(self, *args, max_retries: int = 1, **kwargs):
super().__init__(*args, **kwargs)
self._max_retries = max_retries
async def _retry_async(
self, ctx: InvocationContext, retries_left: int = 0
) -> AsyncGenerator[Event, None]:
if retries_left <= 0:
yield Event(
author=ctx.agent.name,
invocation_id=ctx.invocation_id,
error_message=(
"Maximum retries exhausted. The remote Gemini server failed to"
" respond. Please try again later."
),
)
else:
try:
async for event in super()._run_async_impl(ctx):
yield event
except Exception as e: # pylint: disable=broad-exception-caught
_logger.error(
"%s: caught %s during LLM turn (retries_left=%s): %s\n%s",
ctx.agent.name,
type(e).__name__,
retries_left,
e,
traceback.format_exc(),
)
yield Event(
author=ctx.agent.name,
invocation_id=ctx.invocation_id,
error_message=(
f"Gemini server error ({type(e).__name__}: {e}). Retrying..."
),
custom_metadata={
"error_type": type(e).__name__,
"error": str(e),
},
)
async for event in self._retry_async(ctx, retries_left - 1):
yield event
@override
async def _run_async_impl(
self, ctx: InvocationContext
) -> AsyncGenerator[Event, None]:
async for event in self._retry_async(ctx, retries_left=self._max_retries):
yield event