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 +3 -0
- langfun/core/agentic/action.py +253 -105
- langfun/core/agentic/action_eval.py +10 -3
- langfun/core/agentic/action_test.py +173 -47
- langfun/core/eval/base_test.py +4 -4
- langfun/core/eval/v2/evaluation.py +78 -12
- langfun/core/eval/v2/evaluation_test.py +2 -0
- langfun/core/structured/__init__.py +3 -0
- langfun/core/structured/mapping.py +5 -2
- langfun/core/structured/parsing_test.py +1 -1
- langfun/core/structured/querying.py +205 -18
- langfun/core/structured/querying_test.py +286 -47
- {langfun-0.1.2.dev202504280818.dist-info → langfun-0.1.2.dev202504300804.dist-info}/METADATA +29 -6
- {langfun-0.1.2.dev202504280818.dist-info → langfun-0.1.2.dev202504300804.dist-info}/RECORD +17 -17
- {langfun-0.1.2.dev202504280818.dist-info → langfun-0.1.2.dev202504300804.dist-info}/WHEEL +1 -1
- {langfun-0.1.2.dev202504280818.dist-info → langfun-0.1.2.dev202504300804.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202504280818.dist-info → langfun-0.1.2.dev202504300804.dist-info}/top_level.txt +0 -0
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
|
|
langfun/core/agentic/action.py
CHANGED
@@ -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.
|
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.
|
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.
|
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
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
81
|
-
|
82
|
-
self.
|
83
|
-
|
84
|
-
|
85
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
280
|
+
return list(self._iter_subtree(lf.logging.LogEntry))
|
223
281
|
|
224
|
-
def
|
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
|
-
|
285
|
+
yield item
|
229
286
|
elif isinstance(item, ExecutionTrace):
|
230
|
-
|
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
|
-
|
234
|
-
|
291
|
+
for x in branch._iter_children(item_cls): # pylint: disable=protected-access
|
292
|
+
yield x
|
235
293
|
|
236
|
-
def
|
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
|
-
|
297
|
+
yield item
|
241
298
|
if isinstance(item, ActionInvocation):
|
242
|
-
|
299
|
+
for x in item.execution._iter_subtree(item_cls): # pylint: disable=protected-access
|
300
|
+
yield x
|
243
301
|
elif isinstance(item, ExecutionTrace):
|
244
|
-
|
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
|
-
|
248
|
-
|
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=
|
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
|
-
|
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
|
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: "
|
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: "
|
453
|
-
|
454
|
-
|
455
|
-
|
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'
|
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(
|
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
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
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.
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
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'
|
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(
|
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
|
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
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
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,
|
1219
|
+
level,
|
1220
|
+
f'[{execution.id} ({action_name})]: {message}',
|
1221
|
+
**kwargs
|
1074
1222
|
)
|
1075
1223
|
if keep:
|
1076
|
-
|
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=
|
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
|
-
|
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
|
#
|