Skip to content

Commit a828460

Browse files
authored
Merge branch 'main' into fix/fastapi-resume-4100
2 parents 74f2a14 + 585ebfd commit a828460

File tree

36 files changed

+3426
-2455
lines changed

36 files changed

+3426
-2455
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Mypy New Error Check
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
10+
jobs:
11+
mypy-diff:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
python-version: ['3.10', '3.11', '3.12', '3.13',]
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
27+
- name: Install uv
28+
uses: astral-sh/setup-uv@v5
29+
30+
- name: Generate Baseline (Main)
31+
run: |
32+
# Switch to main branch to generate baseline
33+
git checkout origin/main
34+
35+
git checkout ${{ github.sha }} -- pyproject.toml
36+
37+
# Install dependencies for main
38+
uv venv .venv
39+
source .venv/bin/activate
40+
uv sync --all-extras
41+
42+
# Run mypy, filter for errors only, remove line numbers (file:123: -> file::), and sort
43+
# We ignore exit code (|| true) because we expect errors on main
44+
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > main_errors.txt || true
45+
46+
echo "Found $(wc -l < main_errors.txt) errors on main."
47+
48+
- name: Check PR Branch
49+
run: |
50+
# Switch back to the PR commit
51+
git checkout ${{ github.sha }}
52+
53+
# Re-sync dependencies in case the PR changed them
54+
source .venv/bin/activate
55+
uv sync --all-extras
56+
57+
# Run mypy on PR code, apply same processing
58+
uv run mypy . | grep "error:" | sed 's/:\([0-9]\+\):/::/g' | sort > pr_errors.txt || true
59+
60+
echo "Found $(wc -l < pr_errors.txt) errors on PR branch."
61+
62+
- name: Compare and Fail on New Errors
63+
run: |
64+
# 'comm -13' suppresses unique lines in file1 (main) and common lines,
65+
# leaving only lines unique to file2 (PR) -> The new errors.
66+
comm -13 main_errors.txt pr_errors.txt > new_errors.txt
67+
68+
if [ -s new_errors.txt ]; then
69+
echo "::error::The following NEW mypy errors were introduced:"
70+
cat new_errors.txt
71+
exit 1
72+
else
73+
echo "Great job! No new mypy errors introduced."
74+
fi

.github/workflows/mypy.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Mypy Type Check
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
mypy:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ['3.10', '3.11', '3.12', '3.13',]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v1
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
27+
- name: Install dependencies
28+
run: uv sync --all-extras
29+
30+
- name: Run mypy
31+
32+
run: uv run mypy . --strict

contributing/samples/adk_documentation/adk_release_analyzer/agent.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,25 @@
5757
from google.adk.agents.loop_agent import LoopAgent
5858
from google.adk.agents.readonly_context import ReadonlyContext
5959
from google.adk.agents.sequential_agent import SequentialAgent
60+
from google.adk.models import Gemini
6061
from google.adk.tools.exit_loop_tool import exit_loop
6162
from google.adk.tools.tool_context import ToolContext
63+
from google.genai import types
64+
65+
# Retry configuration for handling API rate limits and overload
66+
_RETRY_OPTIONS = types.HttpRetryOptions(
67+
initial_delay=10,
68+
attempts=8,
69+
exp_base=2,
70+
max_delay=300,
71+
http_status_codes=[429, 503],
72+
)
73+
74+
# Use gemini-3-pro-preview for planning and summary (better quality)
75+
GEMINI_PRO_WITH_RETRY = Gemini(
76+
model="gemini-3-pro-preview",
77+
retry_options=_RETRY_OPTIONS,
78+
)
6279

6380
# Maximum number of files per analysis group to avoid context overflow
6481
MAX_FILES_PER_GROUP = 5
@@ -249,7 +266,7 @@ def get_release_context(tool_context: ToolContext) -> dict[str, Any]:
249266
# =============================================================================
250267

251268
planner_agent = Agent(
252-
model="gemini-2.5-pro",
269+
model=GEMINI_PRO_WITH_RETRY,
253270
name="release_planner",
254271
description=(
255272
"Plans the analysis by fetching release info and organizing files into"
@@ -272,12 +289,12 @@ def get_release_context(tool_context: ToolContext) -> dict[str, Any]:
272289
273290
3. Call `get_changed_files_summary` to get the list of changed files WITHOUT
274291
the full patches (to save context space).
275-
- **IMPORTANT**: Pass `local_repo_path="{LOCAL_REPOS_DIR_PATH}/{CODE_REPO}"`
276-
to use local git and avoid GitHub API's 300-file limit.
292+
- **IMPORTANT**: Pass these parameters:
293+
- `local_repo_path="{LOCAL_REPOS_DIR_PATH}/{CODE_REPO}"` to avoid 300-file limit
294+
- `path_filter="src/google/adk/"` to only get ADK source files (reduces token usage)
277295
278-
4. Filter and organize the files:
279-
- **INCLUDE** only files in `src/google/adk/` directory
280-
- **EXCLUDE** test files, `__init__.py`, and files outside src/
296+
4. Further filter the returned files:
297+
- **EXCLUDE** test files and `__init__.py` files
281298
- **IMPORTANT**: Do NOT exclude any file just because it has few changes.
282299
Even single-line changes to public APIs need documentation updates.
283300
- **PRIORITIZE** by importance:
@@ -423,7 +440,7 @@ def file_analyzer_instruction(readonly_context: ReadonlyContext) -> str:
423440

424441

425442
file_group_analyzer = Agent(
426-
model="gemini-2.5-pro",
443+
model=GEMINI_PRO_WITH_RETRY,
427444
name="file_group_analyzer",
428445
description=(
429446
"Analyzes a group of changed files and generates recommendations."
@@ -507,7 +524,7 @@ def summary_instruction(readonly_context: ReadonlyContext) -> str:
507524

508525

509526
summary_agent = Agent(
510-
model="gemini-2.5-pro",
527+
model=GEMINI_PRO_WITH_RETRY,
511528
name="summary_agent",
512529
description="Compiles recommendations and creates the GitHub issue.",
513530
instruction=summary_instruction,
@@ -542,7 +559,7 @@ def summary_instruction(readonly_context: ReadonlyContext) -> str:
542559
# =============================================================================
543560

544561
root_agent = Agent(
545-
model="gemini-2.5-pro",
562+
model=GEMINI_PRO_WITH_RETRY,
546563
name="adk_release_analyzer",
547564
description=(
548565
"Analyzes ADK Python releases and generates documentation update"

contributing/samples/adk_documentation/tools.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ def get_changed_files_summary(
605605
start_tag: str,
606606
end_tag: str,
607607
local_repo_path: Optional[str] = None,
608+
path_filter: Optional[str] = None,
608609
) -> Dict[str, Any]:
609610
"""Gets a summary of changed files between two releases without patches.
610611
@@ -620,14 +621,17 @@ def get_changed_files_summary(
620621
local_repo_path: Optional absolute path to local git repo. If provided
621622
and valid, uses git diff instead of GitHub API to get complete
622623
file list (avoids 300-file limit).
624+
path_filter: Optional path prefix to filter files. Only files whose
625+
path starts with this prefix will be included. Example:
626+
"src/google/adk/" to only include ADK source files.
623627
624628
Returns:
625629
A dictionary containing the status and a summary of changed files.
626630
"""
627631
# Use local git if valid path is provided (avoids GitHub API 300-file limit)
628632
if local_repo_path and os.path.isdir(os.path.join(local_repo_path, ".git")):
629633
return _get_changed_files_from_local_git(
630-
local_repo_path, start_tag, end_tag, repo_owner, repo_name
634+
local_repo_path, start_tag, end_tag, repo_owner, repo_name, path_filter
631635
)
632636

633637
# Fall back to GitHub API (limited to 300 files)
@@ -689,6 +693,7 @@ def _get_changed_files_from_local_git(
689693
end_tag: str,
690694
repo_owner: str,
691695
repo_name: str,
696+
path_filter: Optional[str] = None,
692697
) -> Dict[str, Any]:
693698
"""Gets changed files using local git commands (no file limit).
694699
@@ -698,6 +703,7 @@ def _get_changed_files_from_local_git(
698703
end_tag: The newer tag (head) for the comparison.
699704
repo_owner: Repository owner for compare URL.
700705
repo_name: Repository name for compare URL.
706+
path_filter: Optional path prefix to filter files.
701707
702708
Returns:
703709
A dictionary containing the status and a summary of changed files.
@@ -766,6 +772,10 @@ def _get_changed_files_from_local_git(
766772
status_code = parts[0][0] # First char is the status
767773
filename = parts[-1] # Last part is filename (handles renames)
768774

775+
# Apply path filter if specified
776+
if path_filter and not filename.startswith(path_filter):
777+
continue
778+
769779
stats = file_stats.get(
770780
filename,
771781
{

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies = [
3030
"anyio>=4.9.0, <5.0.0", # For MCP Session Manager
3131
"authlib>=1.6.6, <2.0.0", # For RestAPI Tool
3232
"click>=8.1.8, <9.0.0", # For CLI tools
33-
"fastapi>=0.115.0, <0.124.0", # FastAPI framework
33+
"fastapi>=0.124.1, <1.0.0", # FastAPI framework
3434
"google-api-python-client>=2.157.0, <3.0.0", # Google API client discovery
3535
"google-auth>=2.47.0", # Google Auth library
3636
"google-cloud-aiplatform[agent_engines]>=1.132.0, <2.0.0", # For VertexAI integrations, e.g. example store.
@@ -216,9 +216,9 @@ asyncio_mode = "auto"
216216

217217
[tool.mypy]
218218
python_version = "3.10"
219-
exclude = "tests/"
219+
exclude = ["tests/", "contributing/samples/"]
220220
plugins = ["pydantic.mypy"]
221221
# Start with non-strict mode, and swtich to strict mode later.
222-
# strict = true
222+
strict = true
223223
disable_error_code = ["import-not-found", "import-untyped", "unused-ignore"]
224224
follow_imports = "skip"

src/google/adk/a2a/converters/event_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def _get_context_metadata(
131131
_get_adk_metadata_key("session_id"): invocation_context.session.id,
132132
_get_adk_metadata_key("invocation_id"): event.invocation_id,
133133
_get_adk_metadata_key("author"): event.author,
134+
_get_adk_metadata_key("event_id"): event.id,
134135
}
135136

136137
# Add optional metadata fields if present
@@ -479,7 +480,6 @@ def _create_status_update_event(
479480
task_id: Optional task ID to use for generated events.
480481
context_id: Optional Context ID to use for generated events.
481482
482-
483483
Returns:
484484
A TaskStatusUpdateEvent with RUNNING state.
485485
"""

src/google/adk/cli/adk_web_server.py

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -946,27 +946,6 @@ async def create_eval_set(
946946
detail=str(ve),
947947
) from ve
948948

949-
@deprecated(
950-
"Please use create_eval_set instead. This will be removed in future"
951-
" releases."
952-
)
953-
@app.post(
954-
"/apps/{app_name}/eval_sets/{eval_set_id}",
955-
response_model_exclude_none=True,
956-
tags=[TAG_EVALUATION],
957-
)
958-
async def create_eval_set_legacy(
959-
app_name: str,
960-
eval_set_id: str,
961-
):
962-
"""Creates an eval set, given the id."""
963-
await create_eval_set(
964-
app_name=app_name,
965-
create_eval_set_request=CreateEvalSetRequest(
966-
eval_set=EvalSet(eval_set_id=eval_set_id, eval_cases=[])
967-
),
968-
)
969-
970949
@app.get(
971950
"/apps/{app_name}/eval-sets",
972951
response_model_exclude_none=True,
@@ -982,19 +961,6 @@ async def list_eval_sets(app_name: str) -> ListEvalSetsResponse:
982961

983962
return ListEvalSetsResponse(eval_set_ids=eval_sets)
984963

985-
@deprecated(
986-
"Please use list_eval_sets instead. This will be removed in future"
987-
" releases."
988-
)
989-
@app.get(
990-
"/apps/{app_name}/eval_sets",
991-
response_model_exclude_none=True,
992-
tags=[TAG_EVALUATION],
993-
)
994-
async def list_eval_sets_legacy(app_name: str) -> list[str]:
995-
list_eval_sets_response = await list_eval_sets(app_name)
996-
return list_eval_sets_response.eval_set_ids
997-
998964
@app.post(
999965
"/apps/{app_name}/eval-sets/{eval_set_id}/add-session",
1000966
response_model_exclude_none=True,
@@ -1142,22 +1108,6 @@ async def delete_eval(
11421108
except NotFoundError as nfe:
11431109
raise HTTPException(status_code=404, detail=str(nfe)) from nfe
11441110

1145-
@deprecated(
1146-
"Please use run_eval instead. This will be removed in future releases."
1147-
)
1148-
@app.post(
1149-
"/apps/{app_name}/eval_sets/{eval_set_id}/run_eval",
1150-
response_model_exclude_none=True,
1151-
tags=[TAG_EVALUATION],
1152-
)
1153-
async def run_eval_legacy(
1154-
app_name: str, eval_set_id: str, req: RunEvalRequest
1155-
) -> list[RunEvalResult]:
1156-
run_eval_response = await run_eval(
1157-
app_name=app_name, eval_set_id=eval_set_id, req=req
1158-
)
1159-
return run_eval_response.run_eval_results
1160-
11611111
@app.post(
11621112
"/apps/{app_name}/eval-sets/{eval_set_id}/run",
11631113
response_model_exclude_none=True,
@@ -1251,28 +1201,6 @@ async def get_eval_result(
12511201
except ValidationError as ve:
12521202
raise HTTPException(status_code=500, detail=str(ve)) from ve
12531203

1254-
@deprecated(
1255-
"Please use get_eval_result instead. This will be removed in future"
1256-
" releases."
1257-
)
1258-
@app.get(
1259-
"/apps/{app_name}/eval_results/{eval_result_id}",
1260-
response_model_exclude_none=True,
1261-
tags=[TAG_EVALUATION],
1262-
)
1263-
async def get_eval_result_legacy(
1264-
app_name: str,
1265-
eval_result_id: str,
1266-
) -> EvalSetResult:
1267-
try:
1268-
return self.eval_set_results_manager.get_eval_set_result(
1269-
app_name, eval_result_id
1270-
)
1271-
except ValueError as ve:
1272-
raise HTTPException(status_code=404, detail=str(ve)) from ve
1273-
except ValidationError as ve:
1274-
raise HTTPException(status_code=500, detail=str(ve)) from ve
1275-
12761204
@app.get(
12771205
"/apps/{app_name}/eval-results",
12781206
response_model_exclude_none=True,
@@ -1285,19 +1213,6 @@ async def list_eval_results(app_name: str) -> ListEvalResultsResponse:
12851213
)
12861214
return ListEvalResultsResponse(eval_result_ids=eval_result_ids)
12871215

1288-
@deprecated(
1289-
"Please use list_eval_results instead. This will be removed in future"
1290-
" releases."
1291-
)
1292-
@app.get(
1293-
"/apps/{app_name}/eval_results",
1294-
response_model_exclude_none=True,
1295-
tags=[TAG_EVALUATION],
1296-
)
1297-
async def list_eval_results_legacy(app_name: str) -> list[str]:
1298-
list_eval_results_response = await list_eval_results(app_name)
1299-
return list_eval_results_response.eval_result_ids
1300-
13011216
@app.get(
13021217
"/apps/{app_name}/metrics-info",
13031218
response_model_exclude_none=True,

0 commit comments

Comments
 (0)