Skip to content

Commit e5be451

Browse files
Akshat8510wuliang229
authored andcommitted
fix(web): allow session resume without new message
Merge google#4185 **Description** This PR resolves google#4100 by making the `new_message` field optional in the `RunAgentRequest` model. **The Problem:** When attempting to resume an agent session via the FastAPI web server, the request would fail with a `422 Unprocessable Entity` if `new_message` was omitted. This prevented "resume-only" workflows where a user just wants to wake up an existing session. **The Solution:** Updated `RunAgentRequest.new_message` to be `Optional[types.Content] = None`. The underlying `runner.run_async` logic already supports `None` for resuming purposes, so no further logic changes were required. **Verification:** Verified that `RunAgentRequest` now validates successfully when `new_message` is missing, defaulting the field to `None`. Co-authored-by: Liang Wu <wuliang@google.com> COPYBARA_INTEGRATE_REVIEW=google#4185 from Akshat8510:fix/fastapi-resume-4100 b6d2526 PiperOrigin-RevId: 869761179
1 parent b4c6cf9 commit e5be451

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

src/google/adk/cli/adk_web_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class RunAgentRequest(common.BaseModel):
204204
app_name: str
205205
user_id: str
206206
session_id: str
207-
new_message: types.Content
207+
new_message: Optional[types.Content] = None
208208
streaming: bool = False
209209
state_delta: Optional[dict[str, Any]] = None
210210
# for resume long-running functions

tests/unittests/cli/test_fast_api.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818
import os
1919
from pathlib import Path
2020
import signal
21-
import sys
2221
import tempfile
23-
import time
2422
from typing import Any
2523
from typing import Optional
2624
from unittest.mock import AsyncMock
@@ -38,14 +36,12 @@
3836
from google.adk.evaluation.eval_case import EvalCase
3937
from google.adk.evaluation.eval_case import Invocation
4038
from google.adk.evaluation.eval_result import EvalSetResult
41-
from google.adk.evaluation.eval_set import EvalSet
4239
from google.adk.evaluation.in_memory_eval_sets_manager import InMemoryEvalSetsManager
4340
from google.adk.events.event import Event
4441
from google.adk.events.event_actions import EventActions
4542
from google.adk.runners import Runner
4643
from google.adk.sessions.in_memory_session_service import InMemorySessionService
4744
from google.adk.sessions.session import Session
48-
from google.adk.sessions.state import State
4945
from google.genai import types
5046
from pydantic import BaseModel
5147
import pytest
@@ -1530,6 +1526,23 @@ def test_builder_save_rejects_traversal(builder_test_client, tmp_path):
15301526
assert not (tmp_path / "app" / "tmp" / "escape.yaml").exists()
15311527

15321528

1529+
def test_agent_run_resume_without_message_success(
1530+
test_app, create_test_session
1531+
):
1532+
"""Test that /run allows resuming a session with only an invocation_id, without a new message."""
1533+
info = create_test_session
1534+
url = "/run"
1535+
payload = {
1536+
"app_name": info["app_name"],
1537+
"user_id": info["user_id"],
1538+
"session_id": info["session_id"],
1539+
"invocation_id": "test_invocation_id",
1540+
"streaming": False,
1541+
}
1542+
response = test_app.post(url, json=payload)
1543+
assert response.status_code == 200
1544+
1545+
15331546
def test_health_endpoint(test_app):
15341547
"""Test the health endpoint."""
15351548
response = test_app.get("/health")

0 commit comments

Comments
 (0)