1010from azure .functions .decorators .function_app import DecoratorApi , FunctionBuilder
1111
1212from durabletask import task
13+ from durabletask .payload import PayloadStore
1314
1415from .metadata import OrchestrationTrigger , ActivityTrigger , EntityTrigger , \
1516 DurableClient
1920 builtin_http_activity ,
2021 builtin_http_poll_orchestrator ,
2122)
22- from ..internal .compat .activity import wrap_activity
23+ from ..internal .compat .activity import wrap_activity , wrap_activity_payloads
24+ from ..internal .invocation import preserve_function_source , wrap_invocation
2325from ..worker import DurableFunctionsWorker
24- from ..orchestrator import Orchestrator
2526
2627
2728class Blueprint (TriggerApi , BindingApi ):
@@ -166,6 +167,7 @@ def configure_history_export(self, writer: Any) -> None:
166167 def _configure_orchestrator_callable (
167168 self ,
168169 wrap : Callable [[Callable [..., Any ]], FunctionBuilder ],
170+ context_name : str ,
169171 input_type : Optional [type ] = None
170172 ) -> Callable [[task .Orchestrator [Any , Any ]], FunctionBuilder ]:
171173 """Obtain decorator to construct an Orchestrator class from a user-defined Function.
@@ -193,7 +195,13 @@ def decorator(orchestrator_func: task.Orchestrator[Any, Any]) -> FunctionBuilder
193195 # feed it to a v1-style ``context.get_input()``.
194196 orchestrator_func ._df_input_type = input_type # type: ignore[attr-defined] # noqa: E501
195197
196- handle = Orchestrator .create (orchestrator_func )
198+ worker = DurableFunctionsWorker ()
199+
200+ async def handle (context : func .OrchestrationContext ) -> str :
201+ return await worker .execute_orchestration_request_async (orchestrator_func , context )
202+
203+ handle .orchestrator_function = orchestrator_func # pyright: ignore[reportFunctionMemberAccess]
204+ handle = wrap_invocation (handle , "context" , registered_trigger_name = context_name )
197205
198206 # invoke next decorator, with the Orchestrator as input
199207 handle .__name__ = orchestrator_func .__name__
@@ -204,6 +212,7 @@ def decorator(orchestrator_func: task.Orchestrator[Any, Any]) -> FunctionBuilder
204212 def _configure_entity_callable (
205213 self ,
206214 wrap : Callable [[Callable [..., Any ]], FunctionBuilder ],
215+ context_name : str ,
207216 entity_name : Optional [str ] = None
208217 ) -> Callable [[task .Entity [Any , Any ]], FunctionBuilder ]:
209218 """Obtain decorator to construct an Entity class from a user-defined Function.
@@ -235,18 +244,16 @@ def decorator(entity_func: task.Entity[Any, Any]) -> FunctionBuilder:
235244 # Construct an orchestrator based on the end-user code
236245 worker = DurableFunctionsWorker ()
237246
238- # TODO: Because this handle method is the one actually exposed to the Functions SDK decorator,
239- # the parameter name will always be "context" here, even if the user specified a different name.
240- # We need to find a way to allow custom context names (like "ctx").
241247 # The generated handle is what the Azure Functions host registers,
242248 # so its ``context`` parameter must be annotated with
243249 # ``azure.functions.EntityContext`` for the host's entityTrigger
244250 # binding converter to accept it; at runtime the host passes that
245251 # transport context (exposing ``.body``).
246- def handle (context : func .EntityContext ) -> str :
247- return worker .execute_entity_batch_request (entity_func , context )
252+ async def handle (context : func .EntityContext ) -> str :
253+ return await worker .execute_entity_batch_request_async (entity_func , context )
248254
249255 handle .entity_function = entity_func # pyright: ignore[reportFunctionMemberAccess]
256+ handle = wrap_invocation (handle , "context" , registered_trigger_name = context_name )
250257
251258 # invoke next decorator, with the Entity as input
252259 handle .__name__ = entity_func .__name__
@@ -294,14 +301,15 @@ def orchestration_trigger(self, context_name: str,
294301 def wrap (fb : FunctionBuilder ) -> FunctionBuilder :
295302
296303 def decorator () -> FunctionBuilder :
304+ registered = fb ._function ._func # pyright: ignore[reportPrivateUsage]
297305 fb .add_trigger (
298- trigger = OrchestrationTrigger (name = context_name ,
306+ trigger = OrchestrationTrigger (name = getattr ( registered , "_df_trigger_name" , context_name ) ,
299307 orchestration = orchestration ))
300308 return fb
301309
302310 return decorator ()
303311
304- return self ._configure_orchestrator_callable (wrap , input_type = input_type )
312+ return self ._configure_orchestrator_callable (wrap , context_name , input_type = input_type )
305313
306314 def activity_trigger (self , input_name : str ,
307315 activity : Optional [str ] = None
@@ -326,7 +334,13 @@ def decorator(user_fn: Callable[..., Any]) -> FunctionBuilder:
326334 # Adapt a durabletask-native two-argument activity ((ctx, input))
327335 # to the host's single-input convention; one-argument activities
328336 # pass through unchanged.
329- return wrap (wrap_activity (user_fn , input_name ))
337+ function = (user_fn ._function ._func # pyright: ignore[reportPrivateUsage]
338+ if isinstance (user_fn , FunctionBuilder ) else user_fn )
339+ registered = wrap_activity_payloads (wrap_activity (function , input_name ), input_name )
340+ if isinstance (user_fn , FunctionBuilder ):
341+ user_fn ._function ._func = registered # pyright: ignore[reportPrivateUsage]
342+ return wrap (user_fn )
343+ return wrap (registered )
330344
331345 return decorator
332346
@@ -347,14 +361,15 @@ def entity_trigger(self,
347361 @self ._build_function
348362 def wrap (fb : FunctionBuilder ) -> FunctionBuilder :
349363 def decorator () -> FunctionBuilder :
364+ registered = fb ._function ._func # pyright: ignore[reportPrivateUsage]
350365 fb .add_trigger (
351- trigger = EntityTrigger (name = context_name ,
366+ trigger = EntityTrigger (name = getattr ( registered , "_df_trigger_name" , context_name ) ,
352367 entity_name = entity_name ))
353368 return fb
354369
355370 return decorator ()
356371
357- return self ._configure_entity_callable (wrap , entity_name )
372+ return self ._configure_entity_callable (wrap , context_name , entity_name )
358373
359374 def durable_client_input (self ,
360375 client_name : str ,
@@ -439,6 +454,7 @@ def set_client_metadata(client_bound: Callable[..., Any]) -> None:
439454 annotations [client_name ] = str
440455 client_bound .__annotations__ = annotations
441456 setattr (client_bound , "client_function" , function )
457+ preserve_function_source (client_bound , function )
442458
443459 if is_async_function :
444460 @wraps (function )
@@ -477,6 +493,19 @@ class DFApp(Blueprint, FunctionRegister):
477493 Exports the decorators required to declare and index DF Function-types.
478494 """
479495
496+ def configure_large_payloads (self , * , payload_store : PayloadStore ) -> None :
497+ """Enable payload externalization for this app and its blueprints.
498+
499+ Call once at app startup in every worker process, before invocations.
500+ The store is shared by all durable clients, orchestrations, entities,
501+ and activities in the process. All scaled-out workers must have access
502+ to the same backing storage. Registering a different store in the same
503+ process raises ValueError. Re-registering the same object is allowed.
504+ """
505+ from ..internal .payloads import configure_payload_store
506+
507+ configure_payload_store (payload_store )
508+
480509 def register_functions (self , function_container : DecoratorApi ) -> None :
481510 """Register the functions of a blueprint into this app.
482511
0 commit comments