99
1010 "pentagi/pkg/graphiti"
1111 obs "pentagi/pkg/observability"
12+ "pentagi/pkg/observability/langfuse"
1213
1314 "github.com/sirupsen/logrus"
1415)
5253 "medium" : {},
5354 "high" : {},
5455 }
56+
57+ // graphitiRetrieverTitles maps each search_type to a human-readable
58+ // langfuse observation title so the retrieval intent is clear in traces.
59+ graphitiRetrieverTitles = map [string ]string {
60+ "temporal_window" : "retrieve temporal window context from graphiti knowledge graph" ,
61+ "entity_relationships" : "retrieve entity relationships from graphiti knowledge graph" ,
62+ "diverse_results" : "retrieve diverse results from graphiti knowledge graph" ,
63+ "episode_context" : "retrieve episode context from graphiti knowledge graph" ,
64+ "successful_tools" : "retrieve successful tools from graphiti knowledge graph" ,
65+ "recent_context" : "retrieve recent context from graphiti knowledge graph" ,
66+ "entity_by_label" : "retrieve entities by label from graphiti knowledge graph" ,
67+ }
5568)
5669
5770// graphitiSearchTool provides search access to Graphiti knowledge graph
@@ -110,16 +123,36 @@ func (t *graphitiSearchTool) Handle(ctx context.Context, name string, args json.
110123 return "" , fmt .Errorf ("search_type parameter is required" )
111124 }
112125
126+ // Get group ID from flow context
127+ groupID := fmt .Sprintf ("flow-%d" , t .flowID )
128+
113129 ctx , observation := obs .Observer .NewObservation (ctx )
130+
131+ retrieverTitle , ok := graphitiRetrieverTitles [searchArgs .SearchType ]
132+ if ! ok {
133+ retrieverTitle = "retrieve context from graphiti knowledge graph"
134+ }
135+
136+ retriever := observation .Retriever (
137+ langfuse .WithRetrieverName (retrieverTitle ),
138+ langfuse .WithRetrieverInput (searchArgs .retrieverInput (groupID )),
139+ langfuse .WithRetrieverMetadata (langfuse.Metadata {
140+ "tool_name" : name ,
141+ "engine" : "graphiti" ,
142+ "search_type" : searchArgs .SearchType ,
143+ "group_id" : groupID ,
144+ "query" : searchArgs .Query ,
145+ }),
146+ )
147+ ctx , observation = retriever .Observation (ctx )
148+
149+ // Nest Graphiti server-side observations under the retriever observation
114150 observationObject := & graphiti.Observation {
115151 ID : observation .ID (),
116152 TraceID : observation .TraceID (),
117153 Time : time .Now ().UTC (),
118154 }
119155
120- // Get group ID from flow context
121- groupID := fmt .Sprintf ("flow-%d" , t .flowID )
122-
123156 // Route to appropriate search method
124157 var (
125158 err error
@@ -145,13 +178,64 @@ func (t *graphitiSearchTool) Handle(ctx context.Context, name string, args json.
145178 }
146179
147180 if err != nil {
181+ retriever .End (
182+ langfuse .WithRetrieverStatus (err .Error ()),
183+ langfuse .WithRetrieverLevel (langfuse .ObservationLevelError ),
184+ )
148185 logger .WithError (err ).Errorf ("failed to perform graphiti search '%s'" , searchArgs .SearchType )
149186 return "" , err
150187 }
151188
189+ retriever .End (
190+ langfuse .WithRetrieverStatus ("success" ),
191+ langfuse .WithRetrieverLevel (langfuse .ObservationLevelDebug ),
192+ langfuse .WithRetrieverOutput (result ),
193+ )
194+
152195 return result , nil
153196}
154197
198+ // retrieverInput builds the langfuse retriever input payload, including only
199+ // the search parameters relevant to the requested search_type.
200+ func (args GraphitiSearchAction ) retrieverInput (groupID string ) map [string ]any {
201+ input := map [string ]any {
202+ "query" : args .Query ,
203+ "search_type" : args .SearchType ,
204+ "group_id" : groupID ,
205+ }
206+ if args .MaxResults != nil {
207+ input ["max_results" ] = args .MaxResults .Int ()
208+ }
209+ if args .TimeStart != "" {
210+ input ["time_start" ] = args .TimeStart
211+ }
212+ if args .TimeEnd != "" {
213+ input ["time_end" ] = args .TimeEnd
214+ }
215+ if args .CenterNodeUUID != "" {
216+ input ["center_node_uuid" ] = args .CenterNodeUUID
217+ }
218+ if args .MaxDepth != nil {
219+ input ["max_depth" ] = args .MaxDepth .Int ()
220+ }
221+ if len (args .NodeLabels ) > 0 {
222+ input ["node_labels" ] = args .NodeLabels
223+ }
224+ if len (args .EdgeTypes ) > 0 {
225+ input ["edge_types" ] = args .EdgeTypes
226+ }
227+ if args .DiversityLevel != "" {
228+ input ["diversity_level" ] = args .DiversityLevel
229+ }
230+ if args .MinMentions != nil {
231+ input ["min_mentions" ] = args .MinMentions .Int ()
232+ }
233+ if args .RecencyWindow != "" {
234+ input ["recency_window" ] = args .RecencyWindow
235+ }
236+ return input
237+ }
238+
155239// handleTemporalWindowSearch performs time-bounded search
156240func (t * graphitiSearchTool ) handleTemporalWindowSearch (
157241 ctx context.Context ,
0 commit comments