1111The pipeline dynamically handles all three scenarios based on which result IDs are provided.
1212
1313API Parameters:
14- email (str): Recipient email address (required)
14+ recipient_email (str): Recipient email address (required)
15+ requestor_email (str): Email of the person requesting the send (required)
1516 resources_result_id (str, optional): ID of resources result to load and email
1617 action_plan_result_id (str, optional): ID of action plan result to load and email
1718
2122 # Email only resources
2223 POST /email_responses
2324 {
24- "email": "user@example.com",
25+ "recipient_email": "user@example.com",
26+ "requestor_email": "admin@example.com",
2527 "resources_result_id": "abc123"
2628 }
2729
2830 # Email only action plan
2931 POST /email_responses
3032 {
31- "email": "user@example.com",
33+ "recipient_email": "user@example.com",
34+ "requestor_email": "admin@example.com",
3235 "action_plan_result_id": "xyz789"
3336 }
3437
3538 # Email both resources and action plan
3639 POST /email_responses
3740 {
38- "email": "user@example.com",
41+ "recipient_email": "user@example.com",
42+ "requestor_email": "admin@example.com",
3943 "resources_result_id": "abc123",
4044 "action_plan_result_id": "xyz789"
4145 }
4246"""
4347
4448import logging
45- from pprint import pformat
4649from typing import Optional
4750
4851from fastapi import HTTPException
4952from hayhooks import BasePipelineWrapper
5053from haystack import Pipeline
51- from haystack .core .errors import PipelineRuntimeError
52- from openinference .instrumentation import _tracers , using_metadata
53- from opentelemetry .trace .status import Status , StatusCode
5454
55- from src .common import components , phoenix_utils
55+ from src .common import components , haystack_utils
5656
5757logger = logging .getLogger (__name__ )
58- tracer = phoenix_utils .tracer_provider .get_tracer (__name__ )
5958
6059
6160class PipelineWrapper (BasePipelineWrapper ):
@@ -98,18 +97,21 @@ def setup(self) -> None:
9897 pipeline .add_component ("logger" , components .ReadableLogger ())
9998
10099 self .pipeline = pipeline
100+ self .runner = haystack_utils .TracedPipelineRunner (self .name , self .pipeline )
101101
102102 def run_api (
103103 self ,
104- email : str ,
104+ recipient_email : str ,
105+ requestor_email : str ,
105106 resources_result_id : Optional [str ] = None ,
106107 action_plan_result_id : Optional [str ] = None ,
107108 ) -> dict :
108109 """
109110 Execute the email pipeline with tracing and metadata.
110111
111112 Args:
112- email: Recipient email address (required)
113+ recipient_email: Recipient email address (required)
114+ requestor_email: Email of the person requesting the send (required)
113115 resources_result_id: ID of resources result to load (optional)
114116 action_plan_result_id: ID of action plan result to load (optional)
115117
@@ -126,91 +128,48 @@ def run_api(
126128 detail = "At least one of resources_result_id or action_plan_result_id must be provided" ,
127129 )
128130
129- with using_metadata ({ "email" : email }):
130- # Must set using_metadata context before calling tracer.start_as_current_span()
131- assert isinstance ( tracer , _tracers . OITracer ), f"Got unexpected { type ( tracer ) } "
132- with tracer . start_as_current_span ( # pylint: disable=not-context-manager,unexpected-keyword-arg
133- self .name , openinference_span_kind = "chain"
134- ) as span :
135- result = self . _run ( resources_result_id , action_plan_result_id , email )
136- span . set_input (
137- {
138- "resources_result_id " : resources_result_id ,
139- "action_plan_result_id " : action_plan_result_id ,
140- "email " : email ,
141- }
142- )
143- span . set_output ( result [ "email_responses" ][ "status" ])
144- span . set_status ( Status ( StatusCode . OK ))
145- return result
146-
147- def _run (
131+ pipeline_run_args = self . create_pipeline_args (
132+ resources_result_id , action_plan_result_id , recipient_email
133+ )
134+
135+ return self .runner . return_response (
136+ pipeline_run_args ,
137+ user_id = "SYSTEM" ,
138+ metadata = { "requestor_email" : requestor_email },
139+ input_ = {
140+ "recipient_email " : recipient_email ,
141+ "requestor_email " : requestor_email ,
142+ "resources_result_id " : resources_result_id ,
143+ "action_plan_result_id" : action_plan_result_id ,
144+ },
145+ include_outputs_from = { "email_responses" },
146+ extract_output = lambda result : result [ "email_responses" ][ "status" ],
147+ )
148+
149+ def create_pipeline_args (
148150 self ,
149151 resources_result_id : Optional [str ],
150152 action_plan_result_id : Optional [str ],
151- email : str ,
153+ recipient_email : str ,
152154 ) -> dict :
153- """
154- Internal method to execute the pipeline.
155-
156- Args:
157- resources_result_id: ID of resources result to load (optional)
158- action_plan_result_id: ID of action plan result to load (optional)
159- email: Recipient email address
160-
161- Returns:
162- dict: Pipeline execution results
163-
164- Raises:
165- HTTPException: If pipeline execution fails with appropriate status code
166- """
167- try :
168- # Build run data with optional result IDs
169- # LoadResultOptional will handle None values gracefully
170- run_data = {
171- "logger" : {
172- "messages_list" : [
173- {
174- "resources_result_id" : resources_result_id or "none" ,
175- "action_plan_result_id" : action_plan_result_id or "none" ,
176- "email" : email ,
177- }
178- ],
179- },
180- "load_resources" : {
181- "result_id" : resources_result_id ,
182- },
183- "load_action_plan" : {
184- "result_id" : action_plan_result_id ,
185- },
186- "email_responses" : {
187- "email" : email ,
188- },
189- }
190-
191- response = self .pipeline .run (
192- run_data ,
193- include_outputs_from = {"email_responses" },
194- )
195- logger .debug ("Results: %s" , pformat (response , width = 160 ))
196- return response
197-
198- except PipelineRuntimeError as re :
199- # Handle pipeline errors with appropriate status codes
200- error_msg = str (re )
201-
202- # Determine if this is a user error (bad input) or server error
203- if re .component_type == components .LoadResultOptional :
204- if "Invalid JSON format in result" in error_msg :
205- status_code = 500 # Internal error - bad data in database
206- elif "No result found" in error_msg :
207- status_code = 400 # User error - invalid result_id
208- else :
209- status_code = 400 # User error - likely bad result_id
210- else :
211- status_code = 500 # Internal error - email sending or other component failure
212-
213- raise HTTPException (
214- status_code = status_code ,
215- detail = f"Error occurred: { error_msg } " ,
216- ) from re
155+ """Common args for pipeline execution"""
156+ return {
157+ "logger" : {
158+ "messages_list" : [
159+ {
160+ "resources_result_id" : resources_result_id or "none" ,
161+ "action_plan_result_id" : action_plan_result_id or "none" ,
162+ "recipient_email" : recipient_email ,
163+ }
164+ ],
165+ },
166+ "load_resources" : {
167+ "result_id" : resources_result_id ,
168+ },
169+ "load_action_plan" : {
170+ "result_id" : action_plan_result_id ,
171+ },
172+ "email_responses" : {
173+ "email" : recipient_email ,
174+ },
175+ }
0 commit comments