11from dataclasses import dataclass
22import logging
33from collections import defaultdict
4+ from typing import Optional
45
56from opentelemetry .sdk .trace import ReadableSpan
67from opentelemetry .sdk .trace .export import SimpleSpanProcessor , SpanExporter
@@ -81,7 +82,10 @@ def on_end(self, span: ReadableSpan) -> None:
8182 span_id = span .context .span_id
8283 if is_humanloop_span (span = span ):
8384 if not self ._must_wait (span ):
84- self ._send_to_exporter (span , self ._dependencies [span .context .span_id ])
85+ self ._send_to_exporter (
86+ span = span ,
87+ dependencies = [dependency .span for dependency in self ._dependencies [span .context .span_id ]],
88+ )
8589 else :
8690 # Must wait for dependencies
8791 self ._waiting [span_id ] = span
@@ -93,7 +97,10 @@ def on_end(self, span: ReadableSpan) -> None:
9397
9498 waiting_span = self ._get_waiting_parent (span )
9599 if waiting_span is not None :
96- self ._send_to_exporter (span , self ._dependencies [span ])
100+ self ._send_to_exporter (
101+ span = span ,
102+ dependencies = [dependency .span for dependency in self ._dependencies [span .context .span_id ]],
103+ )
97104 return
98105
99106 # Be unopinionated and pass all other spans to Exporter
@@ -106,18 +113,20 @@ def _must_wait(self, span: ReadableSpan) -> bool:
106113 return False
107114 return True
108115
109- def _get_waiting_parent (self , span : ReadableSpan ) -> ReadableSpan | None :
110- parent_span_id = span .parent .span_id
116+ def _get_waiting_parent (self , span : ReadableSpan ) -> Optional [ReadableSpan ]:
117+ # We know this span has a parent, need to satisfy the type checker
118+ parent_span_id = span .parent .span_id # type: ignore
111119 if parent_span_id in self ._waiting :
112120 if all ([dependency .finished for dependency in self ._dependencies [parent_span_id ]]):
113121 waiting_span = self ._waiting [parent_span_id ]
114122 del self ._dependencies [parent_span_id ]
115- del waiting_span [parent_span_id ]
123+ del self . _waiting [parent_span_id ]
116124 return waiting_span
117125 return None
118126
119127 def _add_dependency_to_await (self , span : ReadableSpan ):
120- parent_span_id = span .parent .span_id if span .parent else None
128+ # We know this span has a parent, need to satisfy the type checker
129+ parent_span_id = span .parent .span_id # type: ignore
121130 if self ._is_dependency (span ):
122131 self ._dependencies [parent_span_id ].append (DependantSpan (span = span , finished = False ))
123132
@@ -138,7 +147,8 @@ def _track_flow_traces(self, span: ReadableSpan):
138147
139148 def _mark_dependency_arrival (self , span : ReadableSpan ):
140149 span_id = span .context .span_id
141- parent_span_id = span .parent .span_id
150+ # We know this span has a parent, need to satisfy type checker
151+ parent_span_id = span .parent .span_id # type: ignore
142152 self ._dependencies [parent_span_id ] = [
143153 dependency if dependency .span .context .span_id != span_id else DependantSpan (span = span , finished = True )
144154 for dependency in self ._dependencies [parent_span_id ]
@@ -160,28 +170,27 @@ def _send_to_exporter(
160170 # Processing specific to each Humanloop File type
161171 file_type = span .attributes [HUMANLOOP_FILE_TYPE_KEY ] # type: ignore
162172 span_id = span .context .span_id
163- match file_type :
164- case "prompt" :
165- enhance_prompt_span (
166- prompt_span = span ,
167- dependencies = [dependency .span for dependency in dependencies ],
168- )
169- case "tool" :
170- # No extra processing needed
171- pass
172- case "flow" :
173- trace = self ._spans_to_complete_flow_trace .get (span_id , [])
174- write_to_opentelemetry_span (
175- span = span ,
176- key = HUMANLOOP_FLOW_PREREQUISITES_KEY ,
177- value = trace ,
178- )
179- case _:
180- logger .error (
181- "[HumanloopSpanProcessor] Unknown Humanloop File span %s %s" ,
182- span_id ,
183- span .name ,
184- )
173+ if file_type == "prompt" :
174+ enhance_prompt_span (
175+ prompt_span = span ,
176+ dependencies = dependencies ,
177+ )
178+ elif file_type == "tool" :
179+ # No extra processing needed
180+ pass
181+ elif file_type == "flow" :
182+ trace = self ._spans_to_complete_flow_trace .get (span_id , [])
183+ write_to_opentelemetry_span (
184+ span = span ,
185+ key = HUMANLOOP_FLOW_PREREQUISITES_KEY ,
186+ value = trace ,
187+ )
188+ else :
189+ logger .error (
190+ "[HumanloopSpanProcessor] Unknown Humanloop File span %s %s" ,
191+ span_id ,
192+ span .name ,
193+ )
185194
186195 self .span_exporter .export ([span ])
187196
0 commit comments