4141from agenta .sdk .engines .running .templates import EVALUATOR_TEMPLATES
4242from agenta .sdk .engines .running .errors import (
4343 CustomCodeServerV0Error ,
44+ CustomHookHandlerNotDefinedV0Error ,
4445 ErrorStatus ,
4546 InvalidConfigurationParametersV0Error ,
4647 InvalidConfigurationParameterV0Error ,
@@ -2226,8 +2227,15 @@ async def chat_v0(
22262227 )
22272228
22282229
2229- @instrument (ignore_inputs = ["parameters" ])
2230- async def hook_v0 (
2230+ def _extract_revision_field (value : Optional [Data ], field : str ) -> Optional [Any ]:
2231+ if isinstance (value , dict ):
2232+ data = value .get ("data" ) if "data" in value else value
2233+ if isinstance (data , dict ):
2234+ return data .get (field )
2235+ return None
2236+
2237+
2238+ async def remote_forward_v0 (
22312239 request : Optional [Data ] = None ,
22322240 revision : Optional [Data ] = None ,
22332241 #
@@ -2238,37 +2246,21 @@ async def hook_v0(
22382246 trace : Optional [Data ] = None ,
22392247 testcase : Optional [Data ] = None ,
22402248) -> Any :
2241- """
2242- Webhook-based application handler for CUSTOM app types.
2249+ """Run a workflow remotely by forwarding the request to its ``url``.
22432250
2244- Forwards the request to an external webhook URL and returns the response.
2245- The webhook URL is read from the workflow interface (``url`` field in
2246- revision data), not from ``parameters``.
2247-
2248- Args:
2249- request: Optional canonical request envelope.
2250- revision: Optional revision data containing the webhook URL.
2251- parameters: Configuration parameters forwarded to the webhook.
2252- inputs: Inputs to forward to the webhook.
2253- outputs: Optional outputs to forward to the webhook.
2254- trace: Optional trace data to forward to the webhook.
2255- testcase: Optional testcase data to forward to the webhook.
2256-
2257- Returns:
2258- The response from the webhook.
2251+ Selected when a workflow declares ``remote=True``. Reads ``url`` (and optional
2252+ ``headers``) from the revision data, POSTs to ``{url}/invoke``, and returns the
2253+ response. This is execution-location logic, not a per-URI handler.
22592254 """
22602255 from agenta .sdk .contexts .running import RunningContext
22612256
2262- def _extract_webhook_url (value : Optional [Data ]) -> Optional [str ]:
2263- if isinstance (value , dict ):
2264- data = value .get ("data" ) if "data" in value else value
2265- if isinstance (data , dict ):
2266- url = data .get ("url" )
2267- return str (url ) if url else None
2268- return None
2269-
22702257 ctx = RunningContext .get ()
2271- webhook_url = _extract_webhook_url (revision ) or _extract_webhook_url (ctx .revision )
2258+ webhook_url = _extract_revision_field (revision , "url" ) or _extract_revision_field (
2259+ ctx .revision , "url"
2260+ )
2261+ headers = _extract_revision_field (revision , "headers" ) or _extract_revision_field (
2262+ ctx .revision , "headers"
2263+ )
22722264
22732265 if not webhook_url :
22742266 raise MissingConfigurationParameterV0Error (path = "url" )
@@ -2283,6 +2275,12 @@ def _extract_webhook_url(value: Optional[Data]) -> Optional[str]:
22832275 got = webhook_url ,
22842276 ) from exc
22852277
2278+ # The stored url is the service base (pre-/invoke); the invoke surface lives
2279+ # at /invoke and is always appended.
2280+ target_url = f"{ webhook_url .rstrip ('/' )} /invoke"
2281+
2282+ log .info ("remote_forward_v0 POST" , url = target_url )
2283+
22862284 json_payload = {
22872285 "inputs" : inputs or {},
22882286 "parameters" : parameters or {},
@@ -2294,11 +2292,19 @@ def _extract_webhook_url(value: Optional[Data]) -> Optional[str]:
22942292 if testcase is not None :
22952293 json_payload ["testcase" ] = testcase
22962294
2295+ # httpx requires str->str headers; coerce values from revision data.
2296+ request_headers = (
2297+ {str (k ): str (v ) for k , v in headers .items ()}
2298+ if isinstance (headers , dict )
2299+ else None
2300+ )
2301+
22972302 async with httpx .AsyncClient () as client :
22982303 try :
22992304 response = await client .post (
2300- url = webhook_url ,
2305+ url = target_url ,
23012306 json = json_payload ,
2307+ headers = request_headers ,
23022308 timeout = httpx .Timeout (30.0 , connect = 5.0 ),
23032309 )
23042310 except Exception as e :
@@ -2330,6 +2336,26 @@ def _extract_webhook_url(value: Optional[Data]) -> Optional[str]:
23302336 return response_bytes .decode ("utf-8" )
23312337
23322338
2339+ async def hook_v0 (
2340+ request : Optional [Data ] = None ,
2341+ revision : Optional [Data ] = None ,
2342+ #
2343+ parameters : Optional [Data ] = None ,
2344+ inputs : Optional [Data ] = None ,
2345+ outputs : Optional [Union [Data , str ]] = None ,
2346+ #
2347+ trace : Optional [Data ] = None ,
2348+ testcase : Optional [Data ] = None ,
2349+ ) -> Any :
2350+ """Placeholder for the custom-hook URI. Reaching it is a misconfiguration.
2351+
2352+ A custom hook must run its own installed handler (local) or forward to its url
2353+ (``remote=True``). The URI never resolves to this function in either path, so
2354+ being here means a custom hook was invoked without a defined handler.
2355+ """
2356+ raise CustomHookHandlerNotDefinedV0Error ()
2357+
2358+
23332359def _resolve_reference_value (reference : Any , request : Dict [str , Any ]) -> Any :
23342360 """Resolve a reference that may be a JSONPath/Pointer selector or a literal value.
23352361
@@ -2839,11 +2865,28 @@ async def code_v0(
28392865 got = parameters ,
28402866 )
28412867
2842- if "code" not in parameters :
2843- raise MissingConfigurationParameterV0Error (path = "code" )
2868+ from agenta .sdk .contexts .running import RunningContext
28442869
2845- code = str (parameters ["code" ])
2846- runtime = str (parameters .get ("runtime" ) or "python" )
2870+ ctx = RunningContext .get ()
2871+
2872+ # Canonical source is the revision data; legacy `parameters["code"]` is the fallback.
2873+ code = (
2874+ _extract_revision_field (revision , "script" )
2875+ or _extract_revision_field (ctx .revision , "script" )
2876+ or parameters .get ("code" )
2877+ )
2878+ if code is None :
2879+ raise MissingConfigurationParameterV0Error (path = "script" )
2880+
2881+ runtime = (
2882+ _extract_revision_field (revision , "runtime" )
2883+ or _extract_revision_field (ctx .revision , "runtime" )
2884+ or parameters .get ("runtime" )
2885+ or "python"
2886+ )
2887+
2888+ code = str (code )
2889+ runtime = str (runtime )
28472890
28482891 if runtime not in ["python" , "javascript" , "typescript" ]:
28492892 raise InvalidConfigurationParameterV0Error (
0 commit comments