Skip to content

Commit abea6e6

Browse files
committed
move activity registration
1 parent b90ee97 commit abea6e6

3 files changed

Lines changed: 50 additions & 52 deletions

File tree

src/google/adk/integrations/temporal/__init__.py

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,51 @@ async def wrapper(*args, **kw):
139139

140140
return wrapper
141141

142+
async def before_model_callback(
143+
self, *, callback_context: CallbackContext, llm_request: LlmRequest
144+
) -> LlmResponse | None:
145+
# Construct dynamic activity name for visibility
146+
agent_name = callback_context.agent_name
147+
activity_name = f"{agent_name}.generate_content"
148+
149+
# Execute with dynamic name
150+
response_dicts = await workflow.execute_activity(
151+
activity_name,
152+
args=[llm_request],
153+
**self.activity_options
154+
)
155+
156+
# Rehydrate LlmResponse objects safely
157+
responses = []
158+
for d in response_dicts:
159+
try:
160+
responses.append(LlmResponse.model_validate(d))
161+
except Exception as e:
162+
raise RuntimeError(f"Failed to deserialized LlmResponse from activity result: {e}") from e
163+
164+
# Simple consolidation: return the last complete response
165+
return responses[-1] if responses else None
166+
167+
168+
169+
170+
class AdkWorkerPlugin(SimplePlugin):
171+
"""A Temporal Worker Plugin configured for ADK.
172+
173+
This plugin configures:
174+
1. Pydantic Payload Converter (required for ADK objects).
175+
2. Sandbox Passthrough for `google.adk` and `google.genai`.
176+
"""
177+
178+
def __init__(self):
179+
super().__init__(
180+
name="adk_worker_plugin",
181+
data_converter=self._configure_data_converter,
182+
workflow_runner=self._configure_workflow_runner,
183+
activities=[self.dynamic_activity],
184+
worker_interceptors=[AdkInterceptor()]
185+
)
186+
142187
@staticmethod
143188
@activity.defn(dynamic=True)
144189
async def dynamic_activity(args: Sequence[RawValue]) -> Any:
@@ -147,8 +192,8 @@ async def dynamic_activity(args: Sequence[RawValue]) -> Any:
147192

148193
# Check if this is a generate_content call
149194
if activity_type.endswith(".generate_content") or activity_type == "google.adk.generate_content":
150-
return await TemporalPlugin._handle_generate_content(args)
151-
195+
return await AdkWorkerPlugin._handle_generate_content(args)
196+
152197
raise ValueError(f"Unknown dynamic activity: {activity_type}")
153198

154199
@staticmethod
@@ -191,50 +236,6 @@ async def _handle_generate_content(args: List[Any]) -> list[dict[str, Any]]:
191236
for r in responses
192237
]
193238

194-
195-
async def before_model_callback(
196-
self, *, callback_context: CallbackContext, llm_request: LlmRequest
197-
) -> LlmResponse | None:
198-
# Construct dynamic activity name for visibility
199-
agent_name = callback_context.agent_name
200-
activity_name = f"{agent_name}.generate_content"
201-
202-
# Execute with dynamic name
203-
response_dicts = await workflow.execute_activity(
204-
activity_name,
205-
args=[llm_request],
206-
**self.activity_options
207-
)
208-
209-
# Rehydrate LlmResponse objects safely
210-
responses = []
211-
for d in response_dicts:
212-
try:
213-
responses.append(LlmResponse.model_validate(d))
214-
except Exception as e:
215-
raise RuntimeError(f"Failed to deserialized LlmResponse from activity result: {e}") from e
216-
217-
# Simple consolidation: return the last complete response
218-
return responses[-1] if responses else None
219-
220-
221-
222-
223-
class AdkWorkerPlugin(SimplePlugin):
224-
"""A Temporal Worker Plugin configured for ADK.
225-
226-
This plugin configures:
227-
1. Pydantic Payload Converter (required for ADK objects).
228-
2. Sandbox Passthrough for `google.adk` and `google.genai`.
229-
"""
230-
def __init__(self):
231-
super().__init__(
232-
name="adk_worker_plugin",
233-
data_converter=self._configure_data_converter,
234-
workflow_runner=self._configure_workflow_runner,
235-
worker_interceptors=[AdkInterceptor()]
236-
)
237-
238239
def _configure_data_converter(self, converter: DataConverter | None) -> DataConverter:
239240
if converter is None:
240241
return DataConverter(

tests/integration/manual_test_temporal_integration.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ async def run(self, topic: str) -> str:
154154
agent=coordinator,
155155
app_name="multi_agent_app",
156156
session_service=session_service,
157-
plugins=[TemporalPlugin()]
157+
plugins=[TemporalPlugin(activity_options={'start_to_close_timeout': timedelta(minutes=2)})]
158158
)
159159

160160
# 4. Run
@@ -196,12 +196,9 @@ async def test_temporal_integration():
196196
task_queue="adk-task-queue",
197197
activities=[
198198
get_weather,
199-
TemporalPlugin.dynamic_activity,
200-
# Note: We can add specific wrapper activities if we want distinct names in UI,
201-
# but generate_content is the generic one used by TemporalPlugin.
202199
],
203200
workflows=[WeatherAgent, MultiAgentWorkflow],
204-
plugins=[AdkWorkerPlugin()] # <--- Use the class based plugin
201+
plugins=[AdkWorkerPlugin()]
205202
):
206203
print("Worker started.")
207204
# Test Weather Agent

tests/unittests/integrations/test_temporal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def test_temporal_plugin_before_model(self):
143143
callback_context.agent_name = "test-agent"
144144
callback_context.invocation_context.agent.model = "test-agent-model"
145145

146-
llm_request = LlmRequest(model=None, prompt="hi")
146+
llm_request = LlmRequest(model="test-agent-model", prompt="hi")
147147

148148
# Run callback
149149
loop = asyncio.new_event_loop()

0 commit comments

Comments
 (0)