Skip to content

Commit f3b7cba

Browse files
added TracedPipelineRunner and added distinction between recipient and requestor emails
1 parent 0a4cd3a commit f3b7cba

1 file changed

Lines changed: 55 additions & 96 deletions

File tree

app/src/pipelines/email_responses/pipeline_wrapper.py

Lines changed: 55 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
The pipeline dynamically handles all three scenarios based on which result IDs are provided.
1212
1313
API 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
@@ -21,41 +22,39 @@
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

4448
import logging
45-
from pprint import pformat
4649
from typing import Optional
4750

4851
from fastapi import HTTPException
4952
from hayhooks import BasePipelineWrapper
5053
from 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

5757
logger = logging.getLogger(__name__)
58-
tracer = phoenix_utils.tracer_provider.get_tracer(__name__)
5958

6059

6160
class 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

Comments
 (0)