@@ -179,142 +179,147 @@ def get_context(
179179 include_parts: Whether to include parts manifest
180180 include_versions: Whether to include version navigation
181181 """
182- from .perf_stats import perf
183- import time
184- _ctx_t0 = time .monotonic ()
182+ with get_tracer ("keeper" ).start_as_current_span (
183+ "get_context" ,
184+ attributes = {"item_id" : id },
185+ ) as span :
186+ # Ensure state docs are in the store for the read flow.
187+ self ._ensure_sysdocs ()
188+
189+ # Parse @V{N} version ref from ID (explicit version= takes precedence)
190+ base_id , id_version = parse_version_ref (id )
191+ if id_version is not None and version is None :
192+ version = id_version
193+ id = base_id
194+ id = normalize_id (id )
195+ resolved = self .resolve_version_offset (id , version )
196+ if resolved is None :
197+ span .set_attribute ("found" , False )
198+ return None
199+ offset = resolved
200+ span .set_attribute ("viewing_offset" , offset )
201+ if offset > 0 :
202+ item = self .get_version (id , offset )
203+ else :
204+ item = self .get (id )
205+ if item is None :
206+ span .set_attribute ("found" , False )
207+ return None
208+
209+ # System docs are authored reference/configuration content.
210+ # Rendering them should show the document itself only; surrounding
211+ # context assembly (similar/meta/parts/edges/version nav) adds noise
212+ # and unnecessary work.
213+ if is_system_id (item .id ):
214+ span .set_attribute ("system_id" , True )
215+ return ItemContext (
216+ item = item ,
217+ viewing_offset = offset ,
218+ similar = [],
219+ meta = {},
220+ edges = {},
221+ parts = [],
222+ prev = [],
223+ next = [],
224+ )
185225
186- # Ensure state docs are in the store for the read flow.
187- self ._ensure_sysdocs ()
226+ # Version navigation
227+ prev_refs : list [VersionRef ] = []
228+ next_refs : list [VersionRef ] = []
229+ if include_versions :
230+ if offset == 0 :
231+ nav = self .get_version_nav (id , None , limit = versions_limit )
232+ for i , v in enumerate (nav .get ("prev" , [])[:versions_limit ]):
233+ prev_refs .append (VersionRef (
234+ offset = i + 1 ,
235+ date = local_date (v .tags .get ("_created" ) or v .created_at or "" ),
236+ summary = v .summary ,
237+ ))
238+ else :
239+ # Keep navigation in user-visible offset space.
240+ # This avoids mixing offset (V{N}) with internal version numbers.
241+ older = self .get_version (id , offset + 1 )
242+ if older is not None :
243+ prev_refs .append (VersionRef (
244+ offset = offset + 1 ,
245+ date = local_date (older .tags .get ("_created" , "" )),
246+ summary = older .summary ,
247+ ))
248+ if offset > 1 :
249+ newer = self .get_version (id , offset - 1 )
250+ if newer is not None :
251+ next_refs .append (VersionRef (
252+ offset = offset - 1 ,
253+ date = local_date (newer .tags .get ("_created" , "" )),
254+ summary = newer .summary ,
255+ ))
256+
257+ # Data gathering via state-doc flow (similar, parts, meta).
258+ # Edge resolution stays inline — it requires direct database
259+ # queries (inverse edges, explicit edge-tag lookup) that the
260+ # generic traverse action doesn't support.
261+ similar_refs : list [SimilarRef ] = []
262+ meta_refs : dict [str , list [MetaRef ]] = {}
263+ part_refs : list [PartRef ] = []
264+ edge_refs : dict [str , list [EdgeRef ]] = {}
188265
189- # Parse @V{N} version ref from ID (explicit version= takes precedence)
190- base_id , id_version = parse_version_ref (id )
191- if id_version is not None and version is None :
192- version = id_version
193- id = base_id
194- id = normalize_id (id )
195- resolved = self .resolve_version_offset (id , version )
196- if resolved is None :
197- return None
198- offset = resolved
199- if offset > 0 :
200- item = self .get_version (id , offset )
201- else :
202- item = self .get (id )
203- if item is None :
204- return None
266+ if offset == 0 :
267+ if include_similar or include_meta or include_parts :
268+ with get_tracer ("keeper" ).start_as_current_span (
269+ "get_context.read_flow" ,
270+ attributes = {"item_id" : id },
271+ ):
272+ flow_result = self ._run_read_flow (
273+ "get" ,
274+ {
275+ "item_id" : id ,
276+ "similar_limit" : similar_limit if include_similar else 0 ,
277+ "meta_limit" : meta_limit if include_meta else 0 ,
278+ "parts_limit" : parts_limit if include_parts else 0 ,
279+ "edges_limit" : edges_limit ,
280+ "versions_limit" : versions_limit if include_versions else 0 ,
281+ },
282+ )
283+ if flow_result .status == "done" :
284+ bindings = flow_result .bindings
285+ if include_similar :
286+ similar_refs = self ._map_flow_similar (bindings .get ("similar" , {}))
287+ similar_refs = similar_refs [:similar_limit ]
288+ if include_meta :
289+ meta_refs = self ._map_flow_meta (bindings .get ("meta" , {}))
290+ # Cap each meta section to meta_limit
291+ for key in list (meta_refs .keys ()):
292+ meta_refs [key ] = meta_refs [key ][:meta_limit ]
293+ if include_parts :
294+ part_refs = self ._map_flow_parts (bindings .get ("parts" , {}))
295+ part_refs = part_refs [:parts_limit ]
296+ edge_refs = self ._map_flow_edges (bindings .get ("edges" , {}))
297+ # Cap each edge predicate to edges_limit
298+ for key in list (edge_refs .keys ()):
299+ edge_refs [key ] = edge_refs [key ][:edges_limit ]
300+ else :
301+ logger .warning (
302+ "get-context flow returned %s for %r: %s" ,
303+ flow_result .status , id , flow_result .data ,
304+ )
305+ # Fallback: inline edge resolution when flow fails
306+ edge_refs = self ._resolve_edge_refs (item , id )
205307
206- # System docs are authored reference/configuration content.
207- # Rendering them should show the document itself only; surrounding
208- # context assembly (similar/meta/parts/edges/version nav) adds noise
209- # and unnecessary work.
210- if is_system_id (item .id ):
211- perf .record ("get_context" , "total" , time .monotonic () - _ctx_t0 ,
212- context_id = id )
308+ span .set_attribute ("similar_count" , len (similar_refs ))
309+ span .set_attribute ("meta_count" , sum (len (v ) for v in meta_refs .values ()))
310+ span .set_attribute ("edge_count" , sum (len (v ) for v in edge_refs .values ()))
311+ span .set_attribute ("part_count" , len (part_refs ))
213312 return ItemContext (
214313 item = item ,
215314 viewing_offset = offset ,
216- similar = [] ,
217- meta = {} ,
218- edges = {} ,
219- parts = [] ,
220- prev = [] ,
221- next = [] ,
315+ similar = similar_refs ,
316+ meta = meta_refs ,
317+ edges = edge_refs ,
318+ parts = part_refs ,
319+ prev = prev_refs ,
320+ next = next_refs ,
222321 )
223322
224- # Version navigation
225- prev_refs : list [VersionRef ] = []
226- next_refs : list [VersionRef ] = []
227- if include_versions :
228- if offset == 0 :
229- nav = self .get_version_nav (id , None , limit = versions_limit )
230- for i , v in enumerate (nav .get ("prev" , [])[:versions_limit ]):
231- prev_refs .append (VersionRef (
232- offset = i + 1 ,
233- date = local_date (v .tags .get ("_created" ) or v .created_at or "" ),
234- summary = v .summary ,
235- ))
236- else :
237- # Keep navigation in user-visible offset space.
238- # This avoids mixing offset (V{N}) with internal version numbers.
239- older = self .get_version (id , offset + 1 )
240- if older is not None :
241- prev_refs .append (VersionRef (
242- offset = offset + 1 ,
243- date = local_date (older .tags .get ("_created" , "" )),
244- summary = older .summary ,
245- ))
246- if offset > 1 :
247- newer = self .get_version (id , offset - 1 )
248- if newer is not None :
249- next_refs .append (VersionRef (
250- offset = offset - 1 ,
251- date = local_date (newer .tags .get ("_created" , "" )),
252- summary = newer .summary ,
253- ))
254-
255- # Data gathering via state-doc flow (similar, parts, meta).
256- # Edge resolution stays inline — it requires direct database
257- # queries (inverse edges, explicit edge-tag lookup) that the
258- # generic traverse action doesn't support.
259- similar_refs : list [SimilarRef ] = []
260- meta_refs : dict [str , list [MetaRef ]] = {}
261- part_refs : list [PartRef ] = []
262- edge_refs : dict [str , list [EdgeRef ]] = {}
263-
264- if offset == 0 :
265- if include_similar or include_meta or include_parts :
266- from .tracing import get_tracer as _get_tracer
267- with perf .timer ("get_context" , "read_flow" , context_id = id ), \
268- _get_tracer ("keeper" ).start_as_current_span ("get_context" , attributes = {"item_id" : id }):
269- flow_result = self ._run_read_flow (
270- "get" ,
271- {
272- "item_id" : id ,
273- "similar_limit" : similar_limit if include_similar else 0 ,
274- "meta_limit" : meta_limit if include_meta else 0 ,
275- "parts_limit" : parts_limit if include_parts else 0 ,
276- "edges_limit" : edges_limit ,
277- "versions_limit" : versions_limit if include_versions else 0 ,
278- },
279- )
280- if flow_result .status == "done" :
281- bindings = flow_result .bindings
282- if include_similar :
283- similar_refs = self ._map_flow_similar (bindings .get ("similar" , {}))
284- similar_refs = similar_refs [:similar_limit ]
285- if include_meta :
286- meta_refs = self ._map_flow_meta (bindings .get ("meta" , {}))
287- # Cap each meta section to meta_limit
288- for key in list (meta_refs .keys ()):
289- meta_refs [key ] = meta_refs [key ][:meta_limit ]
290- if include_parts :
291- part_refs = self ._map_flow_parts (bindings .get ("parts" , {}))
292- part_refs = part_refs [:parts_limit ]
293- edge_refs = self ._map_flow_edges (bindings .get ("edges" , {}))
294- # Cap each edge predicate to edges_limit
295- for key in list (edge_refs .keys ()):
296- edge_refs [key ] = edge_refs [key ][:edges_limit ]
297- else :
298- logger .warning (
299- "get-context flow returned %s for %r: %s" ,
300- flow_result .status , id , flow_result .data ,
301- )
302- # Fallback: inline edge resolution when flow fails
303- edge_refs = self ._resolve_edge_refs (item , id )
304-
305- perf .record ("get_context" , "total" , time .monotonic () - _ctx_t0 ,
306- context_id = id )
307- return ItemContext (
308- item = item ,
309- viewing_offset = offset ,
310- similar = similar_refs ,
311- meta = meta_refs ,
312- edges = edge_refs ,
313- parts = part_refs ,
314- prev = prev_refs ,
315- next = next_refs ,
316- )
317-
318323 def resolve_version_offset (self , id : str , selector : int | None ) -> Optional [int ]:
319324 """Resolve a public version selector to a concrete offset.
320325
0 commit comments