langfun 0.1.2.dev202504280818__py3-none-any.whl → 0.1.2.dev202504300804__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of langfun might be problematic. Click here for more details.

langfun/__init__.py CHANGED
@@ -37,6 +37,9 @@ generate_class = structured.generate_class
37
37
 
38
38
  track_queries = structured.track_queries
39
39
 
40
+ # Context manager for setting the query protocol for the scope.
41
+ query_protocol = structured.query_protocol
42
+
40
43
  # Helper function for map-reduce style querying.
41
44
  query_and_reduce = structured.query_and_reduce
42
45
 
@@ -15,6 +15,7 @@
15
15
 
16
16
  import abc
17
17
  import contextlib
18
+ import functools
18
19
  import threading
19
20
  import time
20
21
  import typing
@@ -31,8 +32,7 @@ class Action(pg.Object):
31
32
  def _on_bound(self):
32
33
  super()._on_bound()
33
34
  self._session = None
34
- self._result = None
35
- self._metadata = {}
35
+ self._invocation: ActionInvocation | None = None
36
36
 
37
37
  @property
38
38
  def session(self) -> Optional['Session']:
@@ -42,12 +42,12 @@ class Action(pg.Object):
42
42
  @property
43
43
  def result(self) -> Any:
44
44
  """Returns the result of the action."""
45
- return self._result
45
+ return self._invocation.result if self._invocation else None
46
46
 
47
47
  @property
48
48
  def metadata(self) -> dict[str, Any] | None:
49
49
  """Returns the metadata associated with the result from previous call."""
50
- return self._metadata
50
+ return self._invocation.metadata if self._invocation else None
51
51
 
52
52
  def __call__(
53
53
  self,
@@ -58,31 +58,57 @@ class Action(pg.Object):
58
58
  **kwargs
59
59
  ) -> Any:
60
60
  """Executes the action."""
61
- new_session = session is None
62
- if new_session:
61
+ if session is None:
63
62
  session = Session()
64
63
  if show_progress:
65
64
  lf.console.display(pg.view(session, name='agent_session'))
66
65
 
67
- with session.track_action(self):
68
- if verbose:
69
- session.info(f'Executing action {self!r}...', keep=False)
70
-
71
- result = self.call(session=session, verbose=verbose, **kwargs)
66
+ # For the top-level action, we store the session in the metadata.
67
+ self._session = session
68
+ else:
69
+ self._session = None
72
70
 
71
+ with session.track_action(self) as invocation:
73
72
  if verbose:
74
- session.info(
75
- f'Action {self.__class__.__name__} completed successfully.',
76
- keep=False,
77
- result=result
78
- )
73
+ session.info('Action execution started.', keep=False, action=self)
79
74
 
80
- # For the top-level action, we store the session in the metadata.
81
- if new_session:
82
- self._session = session
83
- self._result = result
84
- self._metadata = session.current_action.metadata
85
- return self._result
75
+ try:
76
+ result = self.call(session=session, verbose=verbose, **kwargs)
77
+ self._invocation.end(result)
78
+ if verbose:
79
+ session.info(
80
+ (
81
+ f'Action execution succeeded in '
82
+ f'{self._invocation.execution.elapse:.2f} seconds.'
83
+ ),
84
+ keep=False,
85
+ result=result
86
+ )
87
+ except BaseException as e:
88
+ error = pg.utils.ErrorInfo.from_exception(e)
89
+ self._invocation.end(result=None, error=error)
90
+ if invocation.parent_action is session.root:
91
+ session.error(
92
+ (
93
+ f'Top-level action execution failed in '
94
+ f'{self._invocation.execution.elapse:.2f} seconds.'
95
+ ),
96
+ keep=True,
97
+ action=self,
98
+ error=error
99
+ )
100
+ else:
101
+ session.warning(
102
+ (
103
+ f'Action execution failed in '
104
+ f'{self._invocation.execution.elapse:.2f} seconds.'
105
+ ),
106
+ keep=False,
107
+ action=self,
108
+ error=error
109
+ )
110
+ raise
111
+ return result
86
112
 
87
113
  @abc.abstractmethod
88
114
  def call(self, session: 'Session', **kwargs) -> Any:
@@ -146,6 +172,38 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
146
172
  self._tab_control = None
147
173
  self._time_badge = None
148
174
 
175
+ def _on_parent_change(self, *args, **kwargs):
176
+ super()._on_parent_change(*args, **kwargs)
177
+ self.__dict__.pop('id', None)
178
+
179
+ def indexof(self, item: TracedItem, count_item_cls: Type[Any]) -> int:
180
+ """Returns the index of the child items of given type."""
181
+ pos = 0
182
+ for x in self._iter_children(count_item_cls):
183
+ if x is item:
184
+ return pos
185
+ pos += 1
186
+ return -1
187
+
188
+ @functools.cached_property
189
+ def id(self) -> str:
190
+ parent = self.sym_parent
191
+ if isinstance(parent, ActionInvocation):
192
+ # Current execution trace is the body of an action.
193
+ return parent.id
194
+ elif isinstance(parent, pg.List):
195
+ container = parent.sym_parent
196
+ if isinstance(container, ExecutionTrace):
197
+ # Current execution trace is a phase.
198
+ group_id = (
199
+ self.name or f'g{container.indexof(self, ExecutionTrace) + 1}'
200
+ )
201
+ return f'{container.id}/{group_id}'
202
+ elif isinstance(container, ParallelExecutions):
203
+ # Current execution trace is a parallel branch.
204
+ return f'{container.id}/b{self.sym_path.key + 1}'
205
+ return ''
206
+
149
207
  def reset(self) -> None:
150
208
  """Resets the execution trace."""
151
209
  self.rebind(items=[], skip_notification=True, raise_on_no_change=False)
@@ -194,58 +252,59 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
194
252
  @property
195
253
  def queries(self) -> list[lf_structured.QueryInvocation]:
196
254
  """Returns queries from the sequence."""
197
- return self._child_items(lf_structured.QueryInvocation)
255
+ return list(self._iter_children(lf_structured.QueryInvocation))
198
256
 
199
257
  @property
200
258
  def actions(self) -> list['ActionInvocation']:
201
259
  """Returns action invocations from the sequence."""
202
- return self._child_items(ActionInvocation)
260
+ return list(self._iter_children(ActionInvocation))
203
261
 
204
262
  @property
205
263
  def logs(self) -> list[lf.logging.LogEntry]:
206
264
  """Returns logs from the sequence."""
207
- return self._child_items(lf.logging.LogEntry)
265
+ return list(self._iter_children(lf.logging.LogEntry))
208
266
 
209
267
  @property
210
268
  def all_queries(self) -> list[lf_structured.QueryInvocation]:
211
269
  """Returns all queries from current trace and its child execution items."""
212
- return self._all_child_items(lf_structured.QueryInvocation)
270
+ return list(self._iter_subtree(lf_structured.QueryInvocation))
213
271
 
214
272
  @property
215
273
  def all_actions(self) -> list['ActionInvocation']:
216
274
  """Returns all actions from current trace and its child execution items."""
217
- return self._all_child_items(ActionInvocation)
275
+ return list(self._iter_subtree(ActionInvocation))
218
276
 
219
277
  @property
220
278
  def all_logs(self) -> list[lf.logging.LogEntry]:
221
279
  """Returns all logs from current trace and its child execution items."""
222
- return self._all_child_items(lf.logging.LogEntry)
280
+ return list(self._iter_subtree(lf.logging.LogEntry))
223
281
 
224
- def _child_items(self, item_cls: Type[Any]) -> list[Any]:
225
- child_items = []
282
+ def _iter_children(self, item_cls: Type[Any]) -> Iterator[TracedItem]:
226
283
  for item in self.items:
227
284
  if isinstance(item, item_cls):
228
- child_items.append(item)
285
+ yield item
229
286
  elif isinstance(item, ExecutionTrace):
230
- child_items.extend(item._child_items(item_cls)) # pylint: disable=protected-access
287
+ for x in item._iter_children(item_cls): # pylint: disable=protected-access
288
+ yield x
231
289
  elif isinstance(item, ParallelExecutions):
232
290
  for branch in item.branches:
233
- child_items.extend(branch._child_items(item_cls)) # pylint: disable=protected-access
234
- return child_items
291
+ for x in branch._iter_children(item_cls): # pylint: disable=protected-access
292
+ yield x
235
293
 
236
- def _all_child_items(self, item_cls: Type[Any]) -> list[Any]:
237
- child_items = []
294
+ def _iter_subtree(self, item_cls: Type[Any]) -> Iterator[TracedItem]:
238
295
  for item in self.items:
239
296
  if isinstance(item, item_cls):
240
- child_items.append(item)
297
+ yield item
241
298
  if isinstance(item, ActionInvocation):
242
- child_items.extend(item.execution._all_child_items(item_cls)) # pylint: disable=protected-access
299
+ for x in item.execution._iter_subtree(item_cls): # pylint: disable=protected-access
300
+ yield x
243
301
  elif isinstance(item, ExecutionTrace):
244
- child_items.extend(item._all_child_items(item_cls)) # pylint: disable=protected-access
302
+ for x in item._iter_subtree(item_cls): # pylint: disable=protected-access
303
+ yield x
245
304
  elif isinstance(item, ParallelExecutions):
246
305
  for branch in item.branches:
247
- child_items.extend(branch._all_child_items(item_cls)) # pylint: disable=protected-access
248
- return child_items
306
+ for x in branch._iter_subtree(item_cls): # pylint: disable=protected-access
307
+ yield x
249
308
 
250
309
  def append(self, item: TracedItem) -> None:
251
310
  """Appends an item to the sequence."""
@@ -359,7 +418,7 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
359
418
  elif isinstance(item, lf_structured.QueryInvocation):
360
419
  css_class = 'query'
361
420
  elif isinstance(item, lf.logging.LogEntry):
362
- css_class = 'log'
421
+ css_class = f'log-{item.level}'
363
422
  elif isinstance(item, ExecutionTrace):
364
423
  css_class = 'phase'
365
424
  elif isinstance(item, ParallelExecutions):
@@ -379,13 +438,7 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
379
438
  if isinstance(item, ActionInvocation):
380
439
  return pg.views.html.controls.Label(
381
440
  item.action.__class__.__name__,
382
- tooltip=pg.format(
383
- item.action,
384
- verbose=False,
385
- hide_default_values=True,
386
- max_str_len=80,
387
- max_bytes_len=20,
388
- ),
441
+ tooltip=f'[{item.id}] Action invocation',
389
442
  )
390
443
  elif isinstance(item, lf_structured.QueryInvocation):
391
444
  schema_title = 'str'
@@ -393,30 +446,22 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
393
446
  schema_title = lf_structured.annotation(item.schema.spec)
394
447
  return pg.views.html.controls.Label(
395
448
  schema_title,
396
- tooltip=(
397
- pg.format(
398
- item.input,
399
- verbose=False,
400
- hide_default_values=True,
401
- max_str_len=80,
402
- max_bytes_len=20,
403
- )
404
- ),
449
+ tooltip=f'[{item.id}] lf.Query invocation'
405
450
  )
406
451
  elif isinstance(item, lf.logging.LogEntry):
407
452
  return pg.views.html.controls.Label(
408
- 'Log',
453
+ item.level.title(),
409
454
  tooltip=item.message,
410
455
  )
411
456
  elif isinstance(item, ExecutionTrace):
412
457
  return pg.views.html.controls.Label(
413
458
  item.name or 'Phase',
414
- tooltip=f'Execution phase {item.name!r}.'
459
+ tooltip=f'[{item.id}] Execution group {item.name!r}'
415
460
  )
416
461
  elif isinstance(item, ParallelExecutions):
417
462
  return pg.views.html.controls.Label(
418
463
  item.name or 'Parallel',
419
- tooltip='Parallel executions.'
464
+ tooltip=f'[{item.id}] Parallel executions'
420
465
  )
421
466
  else:
422
467
  raise ValueError(f'Unsupported item type: {type(item)}')
@@ -431,13 +476,13 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
431
476
  padding: 10px;
432
477
  }
433
478
  .tab-button.phase > ::before {
434
- content: "P";
479
+ content: "G";
435
480
  font-weight: bold;
436
481
  color: purple;
437
482
  padding: 10px;
438
483
  }
439
484
  .tab-button.parallel > ::before {
440
- content: "||";
485
+ content: "P";
441
486
  font-weight: bold;
442
487
  color: blue;
443
488
  padding: 10px;
@@ -448,11 +493,26 @@ class ExecutionTrace(pg.Object, pg.views.html.HtmlTreeView.Extension):
448
493
  color: orange;
449
494
  padding: 10px;
450
495
  }
451
- .tab-button.log > ::before {
452
- content: "L";
453
- font-weight: bold;
454
- color: green;
455
- padding: 10px;
496
+ .tab-button.log-debug > ::before {
497
+ content: "🔍";
498
+ padding: 7px;
499
+ }
500
+ .tab-button.log-info > ::before {
501
+ content: "ⓘ";
502
+ color: blue;
503
+ padding: 7px;
504
+ }
505
+ .tab-button.log-warning > ::before {
506
+ content: "❗";
507
+ padding: 7px;
508
+ }
509
+ .tab-button.log-error > ::before {
510
+ content: "‼️";
511
+ padding: 7px;
512
+ }
513
+ .tab-button.log-fatal > ::before {
514
+ content: "❌";
515
+ padding: 7px;
456
516
  }
457
517
  .details.execution-trace, .details.action-invocation {
458
518
  border: 1px solid #eee;
@@ -504,6 +564,22 @@ class ParallelExecutions(pg.Object, pg.views.html.HtmlTreeView.Extension):
504
564
  self._tab_control = None
505
565
  self._lock = threading.Lock()
506
566
 
567
+ def _on_parent_change(self, *args, **kwargs):
568
+ super()._on_parent_change(*args, **kwargs)
569
+ self.__dict__.pop('id', None)
570
+
571
+ @functools.cached_property
572
+ def id(self) -> str:
573
+ parent = self.sym_parent
574
+ if isinstance(parent, pg.List):
575
+ container = parent.sym_parent
576
+ if isinstance(container, ExecutionTrace):
577
+ parallel_id = (
578
+ self.name or f'p{container.indexof(self, ParallelExecutions) + 1}'
579
+ )
580
+ return f'{container.id}/{parallel_id}'
581
+ return ''
582
+
507
583
  def add(self) -> ExecutionTrace:
508
584
  """Appends a branch to the parallel execution."""
509
585
  with self._lock, pg.notify_on_change(False):
@@ -547,7 +623,7 @@ class ParallelExecutions(pg.Object, pg.views.html.HtmlTreeView.Extension):
547
623
  return pg.views.html.controls.Tab(
548
624
  label=pg.views.html.controls.Label(
549
625
  branch.name,
550
- tooltip=f'Execution thread {branch.name!r}.'
626
+ tooltip=f'[{branch.id}] Branch {branch.name!r}'
551
627
  ),
552
628
  content=pg.view(branch),
553
629
  )
@@ -567,6 +643,11 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
567
643
  'The metadata returned by the action.'
568
644
  ] = {}
569
645
 
646
+ error: Annotated[
647
+ pg.utils.ErrorInfo | None,
648
+ 'Error from the action if failed.'
649
+ ] = None
650
+
570
651
  execution: Annotated[
571
652
  ExecutionTrace,
572
653
  'The execution sequence of the action.'
@@ -579,6 +660,33 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
579
660
  super()._on_bound()
580
661
  self._tab_control = None
581
662
 
663
+ def _on_parent_change(self, *args, **kwargs):
664
+ super()._on_parent_change(*args, **kwargs)
665
+ self.__dict__.pop('id', None)
666
+
667
+ @property
668
+ def parent_action(self) -> Optional['ActionInvocation']:
669
+ """Returns the parent action invocation."""
670
+ return self.sym_ancestor(lambda x: isinstance(x, ActionInvocation))
671
+
672
+ @functools.cached_property
673
+ def id(self) -> str:
674
+ """Returns the id of the action invocation."""
675
+ parent = self.sym_parent
676
+ if isinstance(parent, Session):
677
+ return f'{parent.id}:'
678
+ elif isinstance(parent, pg.List):
679
+ container = parent.sym_parent
680
+ if isinstance(container, ExecutionTrace):
681
+ action_id = f'a{container.indexof(self, ActionInvocation) + 1}'
682
+ return f'{container.id}/{action_id}'
683
+ return ''
684
+
685
+ @property
686
+ def has_error(self) -> bool:
687
+ """Returns True if the action invocation has an error."""
688
+ return self.error is not None
689
+
582
690
  @property
583
691
  def logs(self) -> list[lf.logging.LogEntry]:
584
692
  """Returns immediate child logs from execution sequence."""
@@ -618,14 +726,17 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
618
726
  """Starts the execution of the action."""
619
727
  self.execution.start()
620
728
 
621
- def end(self, result: Any, metadata: dict[str, Any]) -> None:
729
+ def end(
730
+ self,
731
+ result: Any,
732
+ error: pg.utils.ErrorInfo | None = None,
733
+ metadata: dict[str, Any] | None = None,
734
+ ) -> None:
622
735
  """Ends the execution of the action with result and metadata."""
623
- self.rebind(
624
- result=result,
625
- metadata=metadata,
626
- skip_notification=True,
627
- raise_on_no_change=False
628
- )
736
+ rebind_dict = dict(result=result, error=error)
737
+ if metadata is not None:
738
+ rebind_dict['metadata'] = metadata
739
+ self.rebind(**rebind_dict, skip_notification=True, raise_on_no_change=False)
629
740
  self.execution.stop()
630
741
  if self._tab_control is not None:
631
742
  if self.metadata:
@@ -641,19 +752,33 @@ class ActionInvocation(pg.Object, pg.views.html.HtmlTreeView.Extension):
641
752
  name='metadata',
642
753
  )
643
754
  )
644
- self._tab_control.insert(
645
- 1,
646
- pg.views.html.controls.Tab(
647
- 'result',
648
- pg.view(
649
- self.result,
650
- collapse_level=None,
651
- enable_summary_tooltip=False
652
- ),
653
- name='result',
654
- ),
655
- )
656
- self._tab_control.select(['metadata', 'result'])
755
+ if self.has_error:
756
+ self._tab_control.insert(
757
+ 1,
758
+ pg.views.html.controls.Tab(
759
+ 'error',
760
+ pg.view(
761
+ self.error,
762
+ collapse_level=None,
763
+ enable_summary_tooltip=False
764
+ ),
765
+ name='error',
766
+ )
767
+ )
768
+ else:
769
+ self._tab_control.insert(
770
+ 1,
771
+ pg.views.html.controls.Tab(
772
+ 'result',
773
+ pg.view(
774
+ self.result,
775
+ collapse_level=None,
776
+ enable_summary_tooltip=False
777
+ ),
778
+ name='result',
779
+ ),
780
+ )
781
+ self._tab_control.select(['error', 'metadata', 'result'])
657
782
 
658
783
  #
659
784
  # HTML views.
@@ -806,7 +931,7 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
806
931
  self._current_execution = self.root.execution
807
932
  if self.id is None:
808
933
  self.rebind(
809
- id=f'session@{uuid.uuid4().hex[-7:]}',
934
+ id=f'agent@{uuid.uuid4().hex[-7:]}',
810
935
  skip_notification=True
811
936
  )
812
937
 
@@ -821,6 +946,7 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
821
946
  self._current_execution.start()
822
947
 
823
948
  invocation = ActionInvocation(pg.maybe_ref(action))
949
+ action._invocation = invocation # pylint: disable=protected-access
824
950
  parent_action = self._current_action
825
951
  parent_execution = self._current_execution
826
952
  parent_execution.append(invocation)
@@ -832,16 +958,18 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
832
958
  self._current_action.start()
833
959
  yield invocation
834
960
  finally:
835
- # Stop the execution of the current action.
836
- invocation.end(action.result, action.metadata)
837
961
  self._current_execution = parent_execution
838
962
  self._current_action = parent_action
839
963
  if parent_action is self.root:
840
- parent_action.end(result=action.result, metadata=action.metadata)
964
+ parent_action.end(
965
+ result=invocation.result,
966
+ metadata=invocation.metadata,
967
+ error=invocation.error
968
+ )
841
969
 
842
970
  @contextlib.contextmanager
843
971
  def track_phase(self, name: str | None) -> Iterator[ExecutionTrace]:
844
- """Context manager for starting a new execution phase."""
972
+ """Context manager for starting a new execution phase (group)."""
845
973
  parent_execution = self._current_execution
846
974
  if name is None:
847
975
  phase = parent_execution
@@ -878,6 +1006,12 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
878
1006
  try:
879
1007
  yield queries
880
1008
  finally:
1009
+ for i, query in enumerate(queries):
1010
+ query.rebind(
1011
+ id=f'{execution.id}/q{len(execution.queries) + i + 1}',
1012
+ skip_notification=False,
1013
+ raise_on_no_change=False
1014
+ )
881
1015
  execution.extend(queries)
882
1016
 
883
1017
  #
@@ -897,7 +1031,7 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
897
1031
  ] = None,
898
1032
  default: Any = lf.RAISE_IF_HAS_ERROR,
899
1033
  *,
900
- lm: lf.LanguageModel | None = None,
1034
+ lm: lf.LanguageModel,
901
1035
  examples: list[lf_structured.MappingExample] | None = None,
902
1036
  **kwargs
903
1037
  ) -> Any:
@@ -931,14 +1065,24 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
931
1065
  The result of the query.
932
1066
  """
933
1067
  with self.track_queries():
934
- return lf_structured.query(
935
- prompt,
936
- schema=schema,
937
- default=default,
938
- lm=lm,
939
- examples=examples,
940
- **kwargs
941
- )
1068
+ start_time = time.time()
1069
+ try:
1070
+ return lf_structured.query(
1071
+ prompt,
1072
+ schema=schema,
1073
+ default=default,
1074
+ lm=lm,
1075
+ examples=examples,
1076
+ **kwargs
1077
+ )
1078
+ except BaseException as e:
1079
+ elapse = time.time() - start_time
1080
+ self.warning(
1081
+ f'Failed to query LLM ({lm.model_id}) in {elapse:.2f} seconds.',
1082
+ error=pg.utils.ErrorInfo.from_exception(e),
1083
+ keep=False,
1084
+ )
1085
+ raise
942
1086
 
943
1087
  def concurrent_map(
944
1088
  self,
@@ -1069,11 +1213,15 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1069
1213
  **kwargs
1070
1214
  ) -> None:
1071
1215
  """Logs a message to the session."""
1216
+ action_name = self._current_action.action.__class__.__name__
1217
+ execution = self._current_execution
1072
1218
  log_entry = lf.logging.log(
1073
- level, f'[{self.id}]: {message}]', **kwargs
1219
+ level,
1220
+ f'[{execution.id} ({action_name})]: {message}',
1221
+ **kwargs
1074
1222
  )
1075
1223
  if keep:
1076
- self._current_execution.append(log_entry)
1224
+ execution.append(log_entry)
1077
1225
 
1078
1226
  def debug(self, message: str, keep: bool = True, **kwargs):
1079
1227
  """Logs a debug message to the session."""
@@ -34,12 +34,19 @@ class ActionEval(lf.eval.v2.Evaluation):
34
34
  def process(self, example: lf.eval.v2.Example) -> tuple[str, dict[str, Any]]:
35
35
  example_input = example.input
36
36
  action = example_input.action
37
- session = action_lib.Session(id=str(example.id))
37
+ session = action_lib.Session(id=f'{self.id}#example-{example.id}')
38
+
39
+ # NOTE(daiyip): Setting session as metadata before action execution, so we
40
+ # could use `Evaluation.state.in_progress_examples` to access the session
41
+ # for status reporting from other threads.
42
+ example.metadata['session'] = session
43
+
38
44
  with lf.logging.use_log_level('fatal'):
39
- action(session=session, **self.action_args)
45
+ kwargs = self.action_args.copy()
46
+ kwargs.update(verbose=True)
47
+ action(session=session, **kwargs)
40
48
  return session.final_result, dict(session=session)
41
49
 
42
-
43
50
  #
44
51
  # TODO(daiyip): Remove V1 once V2 is fully launched.
45
52
  #