google-adk 0.4.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/adk/agents/active_streaming_tool.py +1 -0
- google/adk/agents/base_agent.py +91 -47
- google/adk/agents/base_agent.py.orig +330 -0
- google/adk/agents/callback_context.py +4 -9
- google/adk/agents/invocation_context.py +1 -0
- google/adk/agents/langgraph_agent.py +1 -0
- google/adk/agents/live_request_queue.py +1 -0
- google/adk/agents/llm_agent.py +172 -35
- google/adk/agents/loop_agent.py +1 -1
- google/adk/agents/parallel_agent.py +7 -0
- google/adk/agents/readonly_context.py +7 -1
- google/adk/agents/run_config.py +5 -1
- google/adk/agents/sequential_agent.py +31 -0
- google/adk/agents/transcription_entry.py +5 -2
- google/adk/artifacts/base_artifact_service.py +5 -10
- google/adk/artifacts/gcs_artifact_service.py +9 -9
- google/adk/artifacts/in_memory_artifact_service.py +6 -6
- google/adk/auth/auth_credential.py +9 -5
- google/adk/auth/auth_preprocessor.py +7 -1
- google/adk/auth/auth_tool.py +3 -4
- google/adk/cli/agent_graph.py +5 -5
- google/adk/cli/browser/index.html +2 -2
- google/adk/cli/browser/{main-HWIBUY2R.js → main-QOEMUXM4.js} +58 -58
- google/adk/cli/cli.py +7 -7
- google/adk/cli/cli_deploy.py +7 -2
- google/adk/cli/cli_eval.py +181 -106
- google/adk/cli/cli_tools_click.py +147 -62
- google/adk/cli/fast_api.py +340 -158
- google/adk/cli/fast_api.py.orig +822 -0
- google/adk/cli/utils/common.py +23 -0
- google/adk/cli/utils/evals.py +83 -1
- google/adk/cli/utils/logs.py +13 -5
- google/adk/code_executors/__init__.py +3 -1
- google/adk/code_executors/built_in_code_executor.py +52 -0
- google/adk/evaluation/__init__.py +1 -1
- google/adk/evaluation/agent_evaluator.py +168 -128
- google/adk/evaluation/eval_case.py +102 -0
- google/adk/evaluation/eval_set.py +37 -0
- google/adk/evaluation/eval_sets_manager.py +42 -0
- google/adk/evaluation/evaluation_constants.py +1 -0
- google/adk/evaluation/evaluation_generator.py +89 -114
- google/adk/evaluation/evaluator.py +56 -0
- google/adk/evaluation/local_eval_sets_manager.py +264 -0
- google/adk/evaluation/response_evaluator.py +107 -3
- google/adk/evaluation/trajectory_evaluator.py +83 -2
- google/adk/events/event.py +7 -1
- google/adk/events/event_actions.py +7 -1
- google/adk/examples/example.py +1 -0
- google/adk/examples/example_util.py +3 -2
- google/adk/flows/__init__.py +0 -1
- google/adk/flows/llm_flows/_code_execution.py +19 -11
- google/adk/flows/llm_flows/audio_transcriber.py +4 -3
- google/adk/flows/llm_flows/base_llm_flow.py +86 -22
- google/adk/flows/llm_flows/basic.py +3 -0
- google/adk/flows/llm_flows/functions.py +10 -9
- google/adk/flows/llm_flows/instructions.py +28 -9
- google/adk/flows/llm_flows/single_flow.py +1 -1
- google/adk/memory/__init__.py +1 -1
- google/adk/memory/_utils.py +23 -0
- google/adk/memory/base_memory_service.py +25 -21
- google/adk/memory/base_memory_service.py.orig +76 -0
- google/adk/memory/in_memory_memory_service.py +59 -27
- google/adk/memory/memory_entry.py +37 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +40 -17
- google/adk/models/anthropic_llm.py +36 -11
- google/adk/models/base_llm.py +45 -4
- google/adk/models/gemini_llm_connection.py +15 -2
- google/adk/models/google_llm.py +9 -44
- google/adk/models/google_llm.py.orig +305 -0
- google/adk/models/lite_llm.py +94 -38
- google/adk/models/llm_request.py +1 -1
- google/adk/models/llm_response.py +15 -3
- google/adk/models/registry.py +1 -1
- google/adk/runners.py +68 -44
- google/adk/sessions/__init__.py +1 -1
- google/adk/sessions/_session_util.py +14 -0
- google/adk/sessions/base_session_service.py +8 -32
- google/adk/sessions/database_session_service.py +58 -61
- google/adk/sessions/in_memory_session_service.py +108 -26
- google/adk/sessions/session.py +4 -0
- google/adk/sessions/vertex_ai_session_service.py +23 -45
- google/adk/telemetry.py +3 -0
- google/adk/tools/__init__.py +4 -7
- google/adk/tools/{built_in_code_execution_tool.py → _built_in_code_execution_tool.py} +11 -0
- google/adk/tools/_memory_entry_utils.py +30 -0
- google/adk/tools/agent_tool.py +16 -13
- google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
- google/adk/tools/application_integration_tool/application_integration_toolset.py +107 -85
- google/adk/tools/application_integration_tool/clients/connections_client.py +29 -25
- google/adk/tools/application_integration_tool/clients/integration_client.py +6 -6
- google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
- google/adk/tools/base_toolset.py +58 -0
- google/adk/tools/enterprise_search_tool.py +65 -0
- google/adk/tools/function_parameter_parse_util.py +2 -2
- google/adk/tools/google_api_tool/__init__.py +18 -70
- google/adk/tools/google_api_tool/google_api_tool.py +11 -5
- google/adk/tools/google_api_tool/google_api_toolset.py +126 -0
- google/adk/tools/google_api_tool/google_api_toolsets.py +102 -0
- google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
- google/adk/tools/langchain_tool.py +96 -49
- google/adk/tools/load_artifacts_tool.py +4 -4
- google/adk/tools/load_memory_tool.py +16 -5
- google/adk/tools/mcp_tool/__init__.py +3 -2
- google/adk/tools/mcp_tool/conversion_utils.py +1 -1
- google/adk/tools/mcp_tool/mcp_session_manager.py +167 -16
- google/adk/tools/mcp_tool/mcp_session_manager.py.orig +322 -0
- google/adk/tools/mcp_tool/mcp_tool.py +12 -12
- google/adk/tools/mcp_tool/mcp_toolset.py +155 -195
- google/adk/tools/openapi_tool/common/common.py +2 -5
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +32 -7
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +43 -33
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
- google/adk/tools/preload_memory_tool.py +27 -18
- google/adk/tools/retrieval/__init__.py +1 -1
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
- google/adk/tools/tool_context.py +4 -4
- google/adk/tools/toolbox_toolset.py +79 -0
- google/adk/tools/transfer_to_agent_tool.py +0 -1
- google/adk/version.py +1 -1
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/METADATA +7 -5
- google_adk-1.0.0.dist-info/RECORD +195 -0
- google/adk/agents/remote_agent.py +0 -50
- google/adk/tools/google_api_tool/google_api_tool_set.py +0 -110
- google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
- google/adk/tools/toolbox_tool.py +0 -46
- google_adk-0.4.0.dist-info/RECORD +0 -179
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
- {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
google/adk/models/lite_llm.py
CHANGED
@@ -51,7 +51,7 @@ from .base_llm import BaseLlm
|
|
51
51
|
from .llm_request import LlmRequest
|
52
52
|
from .llm_response import LlmResponse
|
53
53
|
|
54
|
-
logger = logging.getLogger(__name__)
|
54
|
+
logger = logging.getLogger("google_adk." + __name__)
|
55
55
|
|
56
56
|
_NEW_LINE = "\n"
|
57
57
|
_EXCLUDED_PART_FIELD = {"inline_data": {"data"}}
|
@@ -67,6 +67,12 @@ class TextChunk(BaseModel):
|
|
67
67
|
text: str
|
68
68
|
|
69
69
|
|
70
|
+
class UsageMetadataChunk(BaseModel):
|
71
|
+
prompt_tokens: int
|
72
|
+
completion_tokens: int
|
73
|
+
total_tokens: int
|
74
|
+
|
75
|
+
|
70
76
|
class LiteLLMClient:
|
71
77
|
"""Provides acompletion method (for better testability)."""
|
72
78
|
|
@@ -172,19 +178,19 @@ def _content_to_message_param(
|
|
172
178
|
tool_calls = []
|
173
179
|
content_present = False
|
174
180
|
for part in content.parts:
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
)
|
181
|
+
if part.function_call:
|
182
|
+
tool_calls.append(
|
183
|
+
ChatCompletionMessageToolCall(
|
184
|
+
type="function",
|
185
|
+
id=part.function_call.id,
|
186
|
+
function=Function(
|
187
|
+
name=part.function_call.name,
|
188
|
+
arguments=part.function_call.args,
|
189
|
+
),
|
185
190
|
)
|
186
|
-
|
187
|
-
|
191
|
+
)
|
192
|
+
elif part.text or part.inline_data:
|
193
|
+
content_present = True
|
188
194
|
|
189
195
|
final_content = message_content if content_present else None
|
190
196
|
|
@@ -344,15 +350,20 @@ def _function_declaration_to_tool_param(
|
|
344
350
|
def _model_response_to_chunk(
|
345
351
|
response: ModelResponse,
|
346
352
|
) -> Generator[
|
347
|
-
Tuple[
|
353
|
+
Tuple[
|
354
|
+
Optional[Union[TextChunk, FunctionChunk, UsageMetadataChunk]],
|
355
|
+
Optional[str],
|
356
|
+
],
|
357
|
+
None,
|
358
|
+
None,
|
348
359
|
]:
|
349
|
-
"""Converts a litellm message to text or
|
360
|
+
"""Converts a litellm message to text, function or usage metadata chunk.
|
350
361
|
|
351
362
|
Args:
|
352
363
|
response: The response from the model.
|
353
364
|
|
354
365
|
Yields:
|
355
|
-
A tuple of text or function chunk and finish reason.
|
366
|
+
A tuple of text or function or usage metadata chunk and finish reason.
|
356
367
|
"""
|
357
368
|
|
358
369
|
message = None
|
@@ -384,11 +395,21 @@ def _model_response_to_chunk(
|
|
384
395
|
if not message:
|
385
396
|
yield None, None
|
386
397
|
|
398
|
+
# Ideally usage would be expected with the last ModelResponseStream with a
|
399
|
+
# finish_reason set. But this is not the case we are observing from litellm.
|
400
|
+
# So we are sending it as a separate chunk to be set on the llm_response.
|
401
|
+
if response.get("usage", None):
|
402
|
+
yield UsageMetadataChunk(
|
403
|
+
prompt_tokens=response["usage"].get("prompt_tokens", 0),
|
404
|
+
completion_tokens=response["usage"].get("completion_tokens", 0),
|
405
|
+
total_tokens=response["usage"].get("total_tokens", 0),
|
406
|
+
), None
|
407
|
+
|
387
408
|
|
388
409
|
def _model_response_to_generate_content_response(
|
389
410
|
response: ModelResponse,
|
390
411
|
) -> LlmResponse:
|
391
|
-
"""Converts a litellm response to LlmResponse.
|
412
|
+
"""Converts a litellm response to LlmResponse. Also adds usage metadata.
|
392
413
|
|
393
414
|
Args:
|
394
415
|
response: The model response.
|
@@ -403,7 +424,15 @@ def _model_response_to_generate_content_response(
|
|
403
424
|
|
404
425
|
if not message:
|
405
426
|
raise ValueError("No message in response")
|
406
|
-
|
427
|
+
|
428
|
+
llm_response = _message_to_generate_content_response(message)
|
429
|
+
if response.get("usage", None):
|
430
|
+
llm_response.usage_metadata = types.GenerateContentResponseUsageMetadata(
|
431
|
+
prompt_token_count=response["usage"].get("prompt_tokens", 0),
|
432
|
+
candidates_token_count=response["usage"].get("completion_tokens", 0),
|
433
|
+
total_token_count=response["usage"].get("total_tokens", 0),
|
434
|
+
)
|
435
|
+
return llm_response
|
407
436
|
|
408
437
|
|
409
438
|
def _message_to_generate_content_response(
|
@@ -453,9 +482,9 @@ def _get_completion_inputs(
|
|
453
482
|
for content in llm_request.contents or []:
|
454
483
|
message_param_or_list = _content_to_message_param(content)
|
455
484
|
if isinstance(message_param_or_list, list):
|
456
|
-
|
457
|
-
elif message_param_or_list:
|
458
|
-
|
485
|
+
messages.extend(message_param_or_list)
|
486
|
+
elif message_param_or_list: # Ensure it's not None before appending
|
487
|
+
messages.append(message_param_or_list)
|
459
488
|
|
460
489
|
if llm_request.config.system_instruction:
|
461
490
|
messages.insert(
|
@@ -573,7 +602,6 @@ class LiteLlm(BaseLlm):
|
|
573
602
|
Attributes:
|
574
603
|
model: The name of the LiteLlm model.
|
575
604
|
llm_client: The LLM client to use for the model.
|
576
|
-
model_config: The model config.
|
577
605
|
"""
|
578
606
|
|
579
607
|
llm_client: LiteLLMClient = Field(default_factory=LiteLLMClient)
|
@@ -611,7 +639,8 @@ class LiteLlm(BaseLlm):
|
|
611
639
|
LlmResponse: The model response.
|
612
640
|
"""
|
613
641
|
|
614
|
-
|
642
|
+
self._maybe_append_user_content(llm_request)
|
643
|
+
logger.debug(_build_request_log(llm_request))
|
615
644
|
|
616
645
|
messages, tools = _get_completion_inputs(llm_request)
|
617
646
|
|
@@ -628,6 +657,10 @@ class LiteLlm(BaseLlm):
|
|
628
657
|
function_args = ""
|
629
658
|
function_id = None
|
630
659
|
completion_args["stream"] = True
|
660
|
+
aggregated_llm_response = None
|
661
|
+
aggregated_llm_response_with_tool_call = None
|
662
|
+
usage_metadata = None
|
663
|
+
|
631
664
|
for part in self.llm_client.completion(**completion_args):
|
632
665
|
for chunk, finish_reason in _model_response_to_chunk(part):
|
633
666
|
if isinstance(chunk, FunctionChunk):
|
@@ -645,32 +678,55 @@ class LiteLlm(BaseLlm):
|
|
645
678
|
),
|
646
679
|
is_partial=True,
|
647
680
|
)
|
681
|
+
elif isinstance(chunk, UsageMetadataChunk):
|
682
|
+
usage_metadata = types.GenerateContentResponseUsageMetadata(
|
683
|
+
prompt_token_count=chunk.prompt_tokens,
|
684
|
+
candidates_token_count=chunk.completion_tokens,
|
685
|
+
total_token_count=chunk.total_tokens,
|
686
|
+
)
|
687
|
+
|
648
688
|
if finish_reason == "tool_calls" and function_id:
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
689
|
+
aggregated_llm_response_with_tool_call = (
|
690
|
+
_message_to_generate_content_response(
|
691
|
+
ChatCompletionAssistantMessage(
|
692
|
+
role="assistant",
|
693
|
+
content="",
|
694
|
+
tool_calls=[
|
695
|
+
ChatCompletionMessageToolCall(
|
696
|
+
type="function",
|
697
|
+
id=function_id,
|
698
|
+
function=Function(
|
699
|
+
name=function_name,
|
700
|
+
arguments=function_args,
|
701
|
+
),
|
702
|
+
)
|
703
|
+
],
|
704
|
+
)
|
663
705
|
)
|
664
706
|
)
|
665
707
|
function_name = ""
|
666
708
|
function_args = ""
|
667
709
|
function_id = None
|
668
710
|
elif finish_reason == "stop" and text:
|
669
|
-
|
711
|
+
aggregated_llm_response = _message_to_generate_content_response(
|
670
712
|
ChatCompletionAssistantMessage(role="assistant", content=text)
|
671
713
|
)
|
672
714
|
text = ""
|
673
715
|
|
716
|
+
# waiting until streaming ends to yield the llm_response as litellm tends
|
717
|
+
# to send chunk that contains usage_metadata after the chunk with
|
718
|
+
# finish_reason set to tool_calls or stop.
|
719
|
+
if aggregated_llm_response:
|
720
|
+
if usage_metadata:
|
721
|
+
aggregated_llm_response.usage_metadata = usage_metadata
|
722
|
+
usage_metadata = None
|
723
|
+
yield aggregated_llm_response
|
724
|
+
|
725
|
+
if aggregated_llm_response_with_tool_call:
|
726
|
+
if usage_metadata:
|
727
|
+
aggregated_llm_response_with_tool_call.usage_metadata = usage_metadata
|
728
|
+
yield aggregated_llm_response_with_tool_call
|
729
|
+
|
674
730
|
else:
|
675
731
|
response = await self.llm_client.acompletion(**completion_args)
|
676
732
|
yield _model_response_to_generate_content_response(response)
|
google/adk/models/llm_request.py
CHANGED
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
17
17
|
from typing import Any, Optional
|
18
18
|
|
19
19
|
from google.genai import types
|
20
|
+
from pydantic import alias_generators
|
20
21
|
from pydantic import BaseModel
|
21
22
|
from pydantic import ConfigDict
|
22
23
|
|
@@ -40,8 +41,12 @@ class LlmResponse(BaseModel):
|
|
40
41
|
custom_metadata: The custom metadata of the LlmResponse.
|
41
42
|
"""
|
42
43
|
|
43
|
-
model_config = ConfigDict(
|
44
|
-
|
44
|
+
model_config = ConfigDict(
|
45
|
+
extra='forbid',
|
46
|
+
alias_generator=alias_generators.to_camel,
|
47
|
+
populate_by_name=True,
|
48
|
+
)
|
49
|
+
"""The pydantic model config."""
|
45
50
|
|
46
51
|
content: Optional[types.Content] = None
|
47
52
|
"""The content of the response."""
|
@@ -80,6 +85,9 @@ class LlmResponse(BaseModel):
|
|
80
85
|
NOTE: the entire dict must be JSON serializable.
|
81
86
|
"""
|
82
87
|
|
88
|
+
usage_metadata: Optional[types.GenerateContentResponseUsageMetadata] = None
|
89
|
+
"""The usage metadata of the LlmResponse"""
|
90
|
+
|
83
91
|
@staticmethod
|
84
92
|
def create(
|
85
93
|
generate_content_response: types.GenerateContentResponse,
|
@@ -93,18 +101,20 @@ class LlmResponse(BaseModel):
|
|
93
101
|
Returns:
|
94
102
|
The LlmResponse.
|
95
103
|
"""
|
96
|
-
|
104
|
+
usage_metadata = generate_content_response.usage_metadata
|
97
105
|
if generate_content_response.candidates:
|
98
106
|
candidate = generate_content_response.candidates[0]
|
99
107
|
if candidate.content and candidate.content.parts:
|
100
108
|
return LlmResponse(
|
101
109
|
content=candidate.content,
|
102
110
|
grounding_metadata=candidate.grounding_metadata,
|
111
|
+
usage_metadata=usage_metadata,
|
103
112
|
)
|
104
113
|
else:
|
105
114
|
return LlmResponse(
|
106
115
|
error_code=candidate.finish_reason,
|
107
116
|
error_message=candidate.finish_message,
|
117
|
+
usage_metadata=usage_metadata,
|
108
118
|
)
|
109
119
|
else:
|
110
120
|
if generate_content_response.prompt_feedback:
|
@@ -112,9 +122,11 @@ class LlmResponse(BaseModel):
|
|
112
122
|
return LlmResponse(
|
113
123
|
error_code=prompt_feedback.block_reason,
|
114
124
|
error_message=prompt_feedback.block_reason_message,
|
125
|
+
usage_metadata=usage_metadata,
|
115
126
|
)
|
116
127
|
else:
|
117
128
|
return LlmResponse(
|
118
129
|
error_code='UNKNOWN_ERROR',
|
119
130
|
error_message='Unknown error.',
|
131
|
+
usage_metadata=usage_metadata,
|
120
132
|
)
|
google/adk/models/registry.py
CHANGED
google/adk/runners.py
CHANGED
@@ -21,8 +21,8 @@ import threading
|
|
21
21
|
from typing import AsyncGenerator
|
22
22
|
from typing import Generator
|
23
23
|
from typing import Optional
|
24
|
+
import warnings
|
24
25
|
|
25
|
-
from deprecated import deprecated
|
26
26
|
from google.genai import types
|
27
27
|
|
28
28
|
from .agents.active_streaming_tool import ActiveStreamingTool
|
@@ -32,7 +32,6 @@ from .agents.invocation_context import new_invocation_context_id
|
|
32
32
|
from .agents.live_request_queue import LiveRequestQueue
|
33
33
|
from .agents.llm_agent import LlmAgent
|
34
34
|
from .agents.run_config import RunConfig
|
35
|
-
from .agents.run_config import StreamingMode
|
36
35
|
from .artifacts.base_artifact_service import BaseArtifactService
|
37
36
|
from .artifacts.in_memory_artifact_service import InMemoryArtifactService
|
38
37
|
from .events.event import Event
|
@@ -42,9 +41,9 @@ from .sessions.base_session_service import BaseSessionService
|
|
42
41
|
from .sessions.in_memory_session_service import InMemorySessionService
|
43
42
|
from .sessions.session import Session
|
44
43
|
from .telemetry import tracer
|
45
|
-
from .tools.
|
44
|
+
from .tools._built_in_code_execution_tool import built_in_code_execution
|
46
45
|
|
47
|
-
logger = logging.getLogger(__name__)
|
46
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
48
47
|
|
49
48
|
|
50
49
|
class Runner:
|
@@ -172,7 +171,7 @@ class Runner:
|
|
172
171
|
The events generated by the agent.
|
173
172
|
"""
|
174
173
|
with tracer.start_as_current_span('invocation'):
|
175
|
-
session = self.session_service.get_session(
|
174
|
+
session = await self.session_service.get_session(
|
176
175
|
app_name=self.app_name, user_id=user_id, session_id=session_id
|
177
176
|
)
|
178
177
|
if not session:
|
@@ -186,7 +185,7 @@ class Runner:
|
|
186
185
|
root_agent = self.agent
|
187
186
|
|
188
187
|
if new_message:
|
189
|
-
self._append_new_message_to_session(
|
188
|
+
await self._append_new_message_to_session(
|
190
189
|
session,
|
191
190
|
new_message,
|
192
191
|
invocation_context,
|
@@ -196,10 +195,10 @@ class Runner:
|
|
196
195
|
invocation_context.agent = self._find_agent_to_run(session, root_agent)
|
197
196
|
async for event in invocation_context.agent.run_async(invocation_context):
|
198
197
|
if not event.partial:
|
199
|
-
self.session_service.append_event(session=session, event=event)
|
198
|
+
await self.session_service.append_event(session=session, event=event)
|
200
199
|
yield event
|
201
200
|
|
202
|
-
def _append_new_message_to_session(
|
201
|
+
async def _append_new_message_to_session(
|
203
202
|
self,
|
204
203
|
session: Session,
|
205
204
|
new_message: types.Content,
|
@@ -225,7 +224,7 @@ class Runner:
|
|
225
224
|
if part.inline_data is None:
|
226
225
|
continue
|
227
226
|
file_name = f'artifact_{invocation_context.invocation_id}_{i}'
|
228
|
-
self.artifact_service.save_artifact(
|
227
|
+
await self.artifact_service.save_artifact(
|
229
228
|
app_name=self.app_name,
|
230
229
|
user_id=session.user_id,
|
231
230
|
session_id=session.id,
|
@@ -241,30 +240,57 @@ class Runner:
|
|
241
240
|
author='user',
|
242
241
|
content=new_message,
|
243
242
|
)
|
244
|
-
self.session_service.append_event(session=session, event=event)
|
243
|
+
await self.session_service.append_event(session=session, event=event)
|
245
244
|
|
246
245
|
async def run_live(
|
247
246
|
self,
|
248
247
|
*,
|
249
|
-
|
248
|
+
user_id: Optional[str] = None,
|
249
|
+
session_id: Optional[str] = None,
|
250
250
|
live_request_queue: LiveRequestQueue,
|
251
251
|
run_config: RunConfig = RunConfig(),
|
252
|
+
session: Optional[Session] = None,
|
252
253
|
) -> AsyncGenerator[Event, None]:
|
253
254
|
"""Runs the agent in live mode (experimental feature).
|
254
255
|
|
255
256
|
Args:
|
256
|
-
|
257
|
+
user_id: The user ID for the session. Required if `session` is None.
|
258
|
+
session_id: The session ID for the session. Required if `session` is
|
259
|
+
None.
|
257
260
|
live_request_queue: The queue for live requests.
|
258
261
|
run_config: The run config for the agent.
|
262
|
+
session: The session to use. This parameter is deprecated, please use
|
263
|
+
`user_id` and `session_id` instead.
|
259
264
|
|
260
265
|
Yields:
|
261
|
-
|
266
|
+
AsyncGenerator[Event, None]: An asynchronous generator that yields
|
267
|
+
`Event`
|
268
|
+
objects as they are produced by the agent during its live execution.
|
262
269
|
|
263
270
|
.. warning::
|
264
271
|
This feature is **experimental** and its API or behavior may change
|
265
272
|
in future releases.
|
273
|
+
|
274
|
+
.. note::
|
275
|
+
Either `session` or both `user_id` and `session_id` must be provided.
|
266
276
|
"""
|
267
|
-
|
277
|
+
if session is None and (user_id is None or session_id is None):
|
278
|
+
raise ValueError(
|
279
|
+
'Either session or user_id and session_id must be provided.'
|
280
|
+
)
|
281
|
+
if session is not None:
|
282
|
+
warnings.warn(
|
283
|
+
'The `session` parameter is deprecated. Please use `user_id` and'
|
284
|
+
' `session_id` instead.',
|
285
|
+
DeprecationWarning,
|
286
|
+
stacklevel=2,
|
287
|
+
)
|
288
|
+
if not session:
|
289
|
+
session = self.session_service.get_session(
|
290
|
+
app_name=self.app_name, user_id=user_id, session_id=session_id
|
291
|
+
)
|
292
|
+
if not session:
|
293
|
+
raise ValueError(f'Session not found: {session_id}')
|
268
294
|
invocation_context = self._new_invocation_context_for_live(
|
269
295
|
session,
|
270
296
|
live_request_queue=live_request_queue,
|
@@ -276,37 +302,29 @@ class Runner:
|
|
276
302
|
|
277
303
|
invocation_context.active_streaming_tools = {}
|
278
304
|
# TODO(hangfei): switch to use canonical_tools.
|
279
|
-
for
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
305
|
+
# for shell agents, there is no tools associated with it so we should skip.
|
306
|
+
if hasattr(invocation_context.agent, 'tools'):
|
307
|
+
for tool in invocation_context.agent.tools:
|
308
|
+
# replicate a LiveRequestQueue for streaming tools that relis on
|
309
|
+
# LiveRequestQueue
|
310
|
+
from typing import get_type_hints
|
311
|
+
|
312
|
+
type_hints = get_type_hints(tool)
|
313
|
+
for arg_type in type_hints.values():
|
314
|
+
if arg_type is LiveRequestQueue:
|
315
|
+
if not invocation_context.active_streaming_tools:
|
316
|
+
invocation_context.active_streaming_tools = {}
|
317
|
+
active_streaming_tools = ActiveStreamingTool(
|
318
|
+
stream=LiveRequestQueue()
|
319
|
+
)
|
320
|
+
invocation_context.active_streaming_tools[tool.__name__] = (
|
321
|
+
active_streaming_tools
|
322
|
+
)
|
295
323
|
|
296
324
|
async for event in invocation_context.agent.run_live(invocation_context):
|
297
|
-
self.session_service.append_event(session=session, event=event)
|
325
|
+
await self.session_service.append_event(session=session, event=event)
|
298
326
|
yield event
|
299
327
|
|
300
|
-
def close_session(self, session: Session):
|
301
|
-
"""Closes a session and adds it to the memory service (experimental feature).
|
302
|
-
|
303
|
-
Args:
|
304
|
-
session: The session to close.
|
305
|
-
"""
|
306
|
-
if self.memory_service:
|
307
|
-
self.memory_service.add_session_to_memory(session)
|
308
|
-
self.session_service.close_session(session=session)
|
309
|
-
|
310
328
|
def _find_agent_to_run(
|
311
329
|
self, session: Session, root_agent: BaseAgent
|
312
330
|
) -> BaseAgent:
|
@@ -391,7 +409,7 @@ class Runner:
|
|
391
409
|
f'CFC is not supported for model: {model_name} in agent:'
|
392
410
|
f' {self.agent.name}'
|
393
411
|
)
|
394
|
-
if built_in_code_execution not in self.agent.canonical_tools:
|
412
|
+
if built_in_code_execution not in self.agent.canonical_tools():
|
395
413
|
self.agent.tools.append(built_in_code_execution)
|
396
414
|
|
397
415
|
return InvocationContext(
|
@@ -430,6 +448,9 @@ class Runner:
|
|
430
448
|
run_config.output_audio_transcription = (
|
431
449
|
types.AudioTranscriptionConfig()
|
432
450
|
)
|
451
|
+
if not run_config.input_audio_transcription:
|
452
|
+
# need this input transcription for agent transferring in live mode.
|
453
|
+
run_config.input_audio_transcription = types.AudioTranscriptionConfig()
|
433
454
|
return self._new_invocation_context(
|
434
455
|
session,
|
435
456
|
live_request_queue=live_request_queue,
|
@@ -448,9 +469,11 @@ class InMemoryRunner(Runner):
|
|
448
469
|
agent: The root agent to run.
|
449
470
|
app_name: The application name of the runner. Defaults to
|
450
471
|
'InMemoryRunner'.
|
472
|
+
_in_memory_session_service: Deprecated. Please don't use. The in-memory
|
473
|
+
session service for the runner.
|
451
474
|
"""
|
452
475
|
|
453
|
-
def __init__(self, agent:
|
476
|
+
def __init__(self, agent: BaseAgent, *, app_name: str = 'InMemoryRunner'):
|
454
477
|
"""Initializes the InMemoryRunner.
|
455
478
|
|
456
479
|
Args:
|
@@ -458,10 +481,11 @@ class InMemoryRunner(Runner):
|
|
458
481
|
app_name: The application name of the runner. Defaults to
|
459
482
|
'InMemoryRunner'.
|
460
483
|
"""
|
484
|
+
self._in_memory_session_service = InMemorySessionService()
|
461
485
|
super().__init__(
|
462
486
|
app_name=app_name,
|
463
487
|
agent=agent,
|
464
488
|
artifact_service=InMemoryArtifactService(),
|
465
|
-
session_service=
|
489
|
+
session_service=self._in_memory_session_service,
|
466
490
|
memory_service=InMemoryMemoryService(),
|
467
491
|
)
|
google/adk/sessions/__init__.py
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# Copyright 2025 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
1
15
|
"""Utility functions for session service."""
|
2
16
|
|
3
17
|
import base64
|
@@ -26,6 +26,7 @@ from .state import State
|
|
26
26
|
|
27
27
|
class GetSessionConfig(BaseModel):
|
28
28
|
"""The configuration of getting a session."""
|
29
|
+
|
29
30
|
num_recent_events: Optional[int] = None
|
30
31
|
after_timestamp: Optional[float] = None
|
31
32
|
|
@@ -35,13 +36,8 @@ class ListSessionsResponse(BaseModel):
|
|
35
36
|
|
36
37
|
The events and states are not set within each Session object.
|
37
38
|
"""
|
38
|
-
sessions: list[Session] = Field(default_factory=list)
|
39
|
-
|
40
39
|
|
41
|
-
|
42
|
-
"""The response of listing events in a session."""
|
43
|
-
events: list[Event] = Field(default_factory=list)
|
44
|
-
next_page_token: Optional[str] = None
|
40
|
+
sessions: list[Session] = Field(default_factory=list)
|
45
41
|
|
46
42
|
|
47
43
|
class BaseSessionService(abc.ABC):
|
@@ -51,7 +47,7 @@ class BaseSessionService(abc.ABC):
|
|
51
47
|
"""
|
52
48
|
|
53
49
|
@abc.abstractmethod
|
54
|
-
def create_session(
|
50
|
+
async def create_session(
|
55
51
|
self,
|
56
52
|
*,
|
57
53
|
app_name: str,
|
@@ -71,10 +67,9 @@ class BaseSessionService(abc.ABC):
|
|
71
67
|
Returns:
|
72
68
|
session: The newly created session instance.
|
73
69
|
"""
|
74
|
-
pass
|
75
70
|
|
76
71
|
@abc.abstractmethod
|
77
|
-
def get_session(
|
72
|
+
async def get_session(
|
78
73
|
self,
|
79
74
|
*,
|
80
75
|
app_name: str,
|
@@ -83,39 +78,20 @@ class BaseSessionService(abc.ABC):
|
|
83
78
|
config: Optional[GetSessionConfig] = None,
|
84
79
|
) -> Optional[Session]:
|
85
80
|
"""Gets a session."""
|
86
|
-
pass
|
87
81
|
|
88
82
|
@abc.abstractmethod
|
89
|
-
def list_sessions(
|
83
|
+
async def list_sessions(
|
90
84
|
self, *, app_name: str, user_id: str
|
91
85
|
) -> ListSessionsResponse:
|
92
86
|
"""Lists all the sessions."""
|
93
|
-
pass
|
94
87
|
|
95
88
|
@abc.abstractmethod
|
96
|
-
def delete_session(
|
89
|
+
async def delete_session(
|
97
90
|
self, *, app_name: str, user_id: str, session_id: str
|
98
91
|
) -> None:
|
99
92
|
"""Deletes a session."""
|
100
|
-
pass
|
101
|
-
|
102
|
-
@abc.abstractmethod
|
103
|
-
def list_events(
|
104
|
-
self,
|
105
|
-
*,
|
106
|
-
app_name: str,
|
107
|
-
user_id: str,
|
108
|
-
session_id: str,
|
109
|
-
) -> ListEventsResponse:
|
110
|
-
"""Lists events in a session."""
|
111
|
-
pass
|
112
|
-
|
113
|
-
def close_session(self, *, session: Session):
|
114
|
-
"""Closes a session."""
|
115
|
-
# TODO: determine whether we want to finalize the session here.
|
116
|
-
pass
|
117
93
|
|
118
|
-
def append_event(self, session: Session, event: Event) -> Event:
|
94
|
+
async def append_event(self, session: Session, event: Event) -> Event:
|
119
95
|
"""Appends an event to a session object."""
|
120
96
|
if event.partial:
|
121
97
|
return event
|
@@ -123,7 +99,7 @@ class BaseSessionService(abc.ABC):
|
|
123
99
|
session.events.append(event)
|
124
100
|
return event
|
125
101
|
|
126
|
-
def __update_session_state(self, session: Session, event: Event):
|
102
|
+
def __update_session_state(self, session: Session, event: Event) -> None:
|
127
103
|
"""Updates the session state based on the event."""
|
128
104
|
if not event.actions or not event.actions.state_delta:
|
129
105
|
return
|