langfun 0.1.2.dev202504250804__py3-none-any.whl → 0.1.2.dev202504260803__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.

@@ -15,7 +15,6 @@
15
15
 
16
16
  import abc
17
17
  import contextlib
18
- import datetime
19
18
  import threading
20
19
  import time
21
20
  import typing
@@ -55,7 +54,9 @@ class Action(pg.Object):
55
54
  session: Optional['Session'] = None,
56
55
  *,
57
56
  show_progress: bool = True,
58
- **kwargs) -> Any:
57
+ verbose: bool = False,
58
+ **kwargs
59
+ ) -> Any:
59
60
  """Executes the action."""
60
61
  new_session = session is None
61
62
  if new_session:
@@ -64,11 +65,17 @@ class Action(pg.Object):
64
65
  lf.console.display(pg.view(session, name='agent_session'))
65
66
 
66
67
  with session.track_action(self):
67
- pg.logging.info('Example %s starts action %s', session.id, repr(self))
68
- result = self.call(session=session, **kwargs)
69
- pg.logging.info(
70
- 'Example %s ends action %s', session.id, self.__class__.__name__
71
- )
68
+ if verbose:
69
+ session.info(f'Executing action {self!r}...', keep=False)
70
+
71
+ result = self.call(session=session, verbose=verbose, **kwargs)
72
+
73
+ if verbose:
74
+ session.info(
75
+ f'Action {self.__class__.__name__} completed successfully.',
76
+ keep=False,
77
+ result=result
78
+ )
72
79
 
73
80
  # For the top-level action, we store the session in the metadata.
74
81
  if new_session:
@@ -798,7 +805,10 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
798
805
  self._current_action = self.root
799
806
  self._current_execution = self.root.execution
800
807
  if self.id is None:
801
- self.rebind(id=uuid.uuid4().hex[-7:], skip_notification=True)
808
+ self.rebind(
809
+ id=f'session@{uuid.uuid4().hex[-7:]}',
810
+ skip_notification=True
811
+ )
802
812
 
803
813
  #
804
814
  # Context-manager for information tracking.
@@ -1051,35 +1061,39 @@ class Session(pg.Object, pg.views.html.HtmlTreeView.Extension):
1051
1061
  with self.track_queries():
1052
1062
  return lf_structured.query_output(response, schema=schema, **kwargs)
1053
1063
 
1054
- def _log(self, level: lf.logging.LogLevel, message: str, **kwargs):
1055
- self._current_execution.append(
1056
- lf.logging.LogEntry(
1057
- level=level,
1058
- time=datetime.datetime.now(),
1059
- message=message,
1060
- metadata=kwargs,
1061
- )
1064
+ def _log(
1065
+ self,
1066
+ level: lf.logging.LogLevel,
1067
+ message: str,
1068
+ keep: bool,
1069
+ **kwargs
1070
+ ) -> None:
1071
+ """Logs a message to the session."""
1072
+ log_entry = lf.logging.log(
1073
+ level, f'[{self.id}]: {message}]', **kwargs
1062
1074
  )
1075
+ if keep:
1076
+ self._current_execution.append(log_entry)
1063
1077
 
1064
- def debug(self, message: str, **kwargs):
1078
+ def debug(self, message: str, keep: bool = True, **kwargs):
1065
1079
  """Logs a debug message to the session."""
1066
- self._log('debug', message, **kwargs)
1080
+ self._log('debug', message, keep=keep, **kwargs)
1067
1081
 
1068
- def info(self, message: str, **kwargs):
1082
+ def info(self, message: str, keep: bool = True, **kwargs):
1069
1083
  """Logs an info message to the session."""
1070
- self._log('info', message, **kwargs)
1084
+ self._log('info', message, keep=keep, **kwargs)
1071
1085
 
1072
- def warning(self, message: str, **kwargs):
1086
+ def warning(self, message: str, keep: bool = True, **kwargs):
1073
1087
  """Logs a warning message to the session."""
1074
- self._log('warning', message, **kwargs)
1088
+ self._log('warning', message, keep=keep, **kwargs)
1075
1089
 
1076
- def error(self, message: str, **kwargs):
1090
+ def error(self, message: str, keep: bool = True, **kwargs):
1077
1091
  """Logs an error message to the session."""
1078
- self._log('error', message, **kwargs)
1092
+ self._log('error', message, keep=keep, **kwargs)
1079
1093
 
1080
1094
  def fatal(self, message: str, **kwargs):
1081
1095
  """Logs a fatal message to the session."""
1082
- self._log('fatal', message, **kwargs)
1096
+ self._log('fatal', message, keep=True, **kwargs)
1083
1097
 
1084
1098
  def as_message(self) -> lf.AIMessage:
1085
1099
  """Returns the session as a message."""
@@ -65,10 +65,10 @@ class SessionTest(unittest.TestCase):
65
65
 
66
66
  lm = fake.StaticResponse('lm response')
67
67
  foo = Foo(1)
68
- self.assertEqual(foo(lm=lm), 3)
68
+ self.assertEqual(foo(lm=lm, verbose=True), 3)
69
69
 
70
70
  session = foo.session
71
- self.assertEqual(len(session.id), 7)
71
+ self.assertIn('session@', session.id)
72
72
  self.assertIsNotNone(session)
73
73
  self.assertIsInstance(session.root.action, action_lib.RootAction)
74
74
  self.assertIs(session.current_action, session.root)
langfun/core/logging.py CHANGED
@@ -17,10 +17,10 @@ import contextlib
17
17
  import datetime
18
18
  import functools
19
19
  import typing
20
- from typing import Any, Iterator, Literal
20
+ from typing import Any, Callable, Iterator, Literal
21
21
 
22
22
  from langfun.core import component
23
- from langfun.core import console
23
+ from langfun.core import console as console_lib
24
24
  import pyglove as pg
25
25
 
26
26
 
@@ -43,6 +43,22 @@ def get_log_level() -> LogLevel:
43
43
  return component.context_value('__event_log_level__', 'info')
44
44
 
45
45
 
46
+ def system_log_func(level: LogLevel) -> Callable[..., None]:
47
+ match level:
48
+ case 'debug':
49
+ return pg.logging.debug
50
+ case 'info':
51
+ return pg.logging.info
52
+ case 'warning':
53
+ return pg.logging.warning
54
+ case 'error':
55
+ return pg.logging.error
56
+ case 'fatal':
57
+ return pg.logging.error
58
+ case _:
59
+ raise ValueError(f'Unsupported log level: {level}')
60
+
61
+
46
62
  class LogEntry(pg.Object, pg.views.HtmlTreeView.Extension):
47
63
  """Event log entry."""
48
64
  time: datetime.datetime
@@ -241,6 +257,7 @@ def log(level: LogLevel,
241
257
  message: str,
242
258
  *,
243
259
  indent: int = 0,
260
+ console: bool = False,
244
261
  **kwargs) -> LogEntry:
245
262
  """Logs a message."""
246
263
  entry = LogEntry(
@@ -250,35 +267,71 @@ def log(level: LogLevel,
250
267
  message=message,
251
268
  metadata=kwargs,
252
269
  )
270
+
253
271
  if entry.should_output(get_log_level()):
254
- if console.under_notebook():
255
- console.display(entry)
256
- else:
272
+ if console_lib.under_notebook():
273
+ console_lib.display(entry)
274
+ elif console:
257
275
  # TODO(daiyip): Improve the console output formatting.
258
- console.write(entry)
276
+ console_lib.write(entry)
277
+
278
+ if not console:
279
+ if kwargs:
280
+ message = f'{message} (metadata: {pg.format(kwargs)})'
281
+ system_log_func(level)(message)
259
282
  return entry
260
283
 
261
284
 
262
- def debug(message: str, *, indent: int = 0, **kwargs) -> LogEntry:
285
+ def debug(
286
+ message: str,
287
+ *,
288
+ indent: int = 0,
289
+ console: bool = False,
290
+ **kwargs
291
+ ) -> LogEntry:
263
292
  """Logs a debug message to the session."""
264
- return log('debug', message, indent=indent, **kwargs)
293
+ return log('debug', message, indent=indent, console=console, **kwargs)
265
294
 
266
295
 
267
- def info(message: str, *, indent: int = 0, **kwargs) -> LogEntry:
296
+ def info(
297
+ message: str,
298
+ *,
299
+ indent: int = 0,
300
+ console: bool = False,
301
+ **kwargs
302
+ ) -> LogEntry:
268
303
  """Logs an info message to the session."""
269
- return log('info', message, indent=indent, **kwargs)
304
+ return log('info', message, indent=indent, console=console, **kwargs)
270
305
 
271
306
 
272
- def warning(message: str, *, indent: int = 0, **kwargs) -> LogEntry:
307
+ def warning(
308
+ message: str,
309
+ *,
310
+ indent: int = 0,
311
+ console: bool = False,
312
+ **kwargs
313
+ ) -> LogEntry:
273
314
  """Logs an info message to the session."""
274
- return log('warning', message, indent=indent, **kwargs)
315
+ return log('warning', message, indent=indent, console=console, **kwargs)
275
316
 
276
317
 
277
- def error(message: str, *, indent: int = 0, **kwargs) -> LogEntry:
318
+ def error(
319
+ message: str,
320
+ *,
321
+ indent: int = 0,
322
+ console: bool = False,
323
+ **kwargs
324
+ ) -> LogEntry:
278
325
  """Logs an error message to the session."""
279
- return log('error', message, indent=indent, **kwargs)
326
+ return log('error', message, indent=indent, console=console, **kwargs)
280
327
 
281
328
 
282
- def fatal(message: str, *, indent: int = 0, **kwargs) -> LogEntry:
329
+ def fatal(
330
+ message: str,
331
+ *,
332
+ indent: int = 0,
333
+ console: bool = False,
334
+ **kwargs
335
+ ) -> LogEntry:
283
336
  """Logs a fatal message to the session."""
284
- return log('fatal', message, indent=indent, **kwargs)
337
+ return log('fatal', message, indent=indent, console=console, **kwargs)
@@ -13,8 +13,10 @@
13
13
  # limitations under the License.
14
14
  """Tests for langfun.core.logging."""
15
15
 
16
+ import contextlib
16
17
  import datetime
17
18
  import inspect
19
+ import io
18
20
  import unittest
19
21
 
20
22
  from langfun.core import logging
@@ -44,6 +46,13 @@ class LoggingTest(unittest.TestCase):
44
46
  self.assertEqual(logging.error('hi').level, 'error')
45
47
  self.assertEqual(logging.fatal('hi').level, 'fatal')
46
48
 
49
+ def test_log_without_system_logging(self):
50
+ with contextlib.redirect_stdout(io.StringIO()) as stdout:
51
+ logging.log('info', 'hi', indent=1, x=1, y=2, console=True)
52
+ self.assertIn(
53
+ 'hi (metadata: {x=1, y=2})', stdout.getvalue()
54
+ )
55
+
47
56
  def test_repr_html(self):
48
57
  def assert_color(entry, color):
49
58
  self.assertIn(f'background-color: {color}', entry._repr_html_())
@@ -17,6 +17,7 @@ import contextlib
17
17
  import functools
18
18
  import time
19
19
  from typing import Annotated, Any, Callable, Iterator, Type, Union
20
+ import uuid
20
21
 
21
22
  import langfun.core as lf
22
23
  from langfun.core.structured import mapping
@@ -118,6 +119,7 @@ def query(
118
119
  protocol: schema_lib.SchemaProtocol = 'python',
119
120
  returns_message: bool = False,
120
121
  skip_lm: bool = False,
122
+ invocation_id: str | None = None,
121
123
  **kwargs,
122
124
  ) -> Any:
123
125
  """Query one or more language models for structured or unstructured outputs.
@@ -263,6 +265,9 @@ def query(
263
265
  the final parsed result.
264
266
  skip_lm: If `True`, skips the LLM call and returns the rendered
265
267
  prompt as a `UserMessage` object.
268
+ invocation_id: The ID of the query invocation, which will be passed to
269
+ `lf.QueryInvocation` when `lf.trackIf `None`, a unique ID will
270
+ be generated.
266
271
  **kwargs: Additional keyword arguments for:
267
272
  - Rendering templates (e.g., `template_str`, `preamble`),
268
273
  - Configuring `lf.structured.Mapping`.
@@ -275,11 +280,12 @@ def query(
275
280
  """
276
281
  # Internal usage logging.
277
282
 
283
+ invocation_id = invocation_id or f'query@{uuid.uuid4().hex[-7:]}'
278
284
  # Multiple quries will be issued when `lm` is a list or `num_samples` is
279
285
  # greater than 1.
280
286
  if isinstance(lm, list) or num_samples != 1:
281
287
  def _single_query(inputs):
282
- lm, example_i = inputs
288
+ i, (lm, example_i) = inputs
283
289
  return query(
284
290
  prompt,
285
291
  schema,
@@ -298,6 +304,7 @@ def query(
298
304
  protocol=protocol,
299
305
  returns_message=returns_message,
300
306
  skip_lm=skip_lm,
307
+ invocation_id=f'{invocation_id}:{i}',
301
308
  **kwargs,
302
309
  )
303
310
  lm_list = lm if isinstance(lm, list) else [lm]
@@ -317,7 +324,8 @@ def query(
317
324
 
318
325
  samples = []
319
326
  for _, output, error in lf.concurrent_map(
320
- _single_query, query_inputs, max_workers=max(64, total_queries),
327
+ _single_query, enumerate(query_inputs),
328
+ max_workers=max(64, total_queries),
321
329
  ordered=True,
322
330
  ):
323
331
  if error is None:
@@ -409,6 +417,7 @@ def query(
409
417
  metadata.pop('usage', None)
410
418
 
411
419
  invocation = QueryInvocation(
420
+ id=invocation_id,
412
421
  input=pg.Ref(query_input),
413
422
  schema=(
414
423
  schema_lib.Schema.from_value(schema)
@@ -565,6 +574,11 @@ def _reward_fn(cls) -> Callable[
565
574
  class QueryInvocation(pg.Object, pg.views.HtmlTreeView.Extension):
566
575
  """A class to represent the invocation of `lf.query`."""
567
576
 
577
+ id: Annotated[
578
+ str,
579
+ 'The ID of the query invocation.'
580
+ ]
581
+
568
582
  input: Annotated[
569
583
  Union[lf.Template, pg.Symbolic],
570
584
  'Mapping input of `lf.query`.'
@@ -1076,8 +1076,9 @@ class QueryInvocationTest(unittest.TestCase):
1076
1076
  'Activity(description="hi"',
1077
1077
  ])
1078
1078
  with querying.track_queries() as queries:
1079
- querying.query('foo', Activity, default=None, lm=lm)
1079
+ querying.query('foo', Activity, default=None, lm=lm, invocation_id='123')
1080
1080
 
1081
+ self.assertEqual(queries[0].id, '123')
1081
1082
  self.assertTrue(queries[0].has_error)
1082
1083
  self.assertIsInstance(queries[0].output, mapping.MappingError)
1083
1084
 
@@ -1088,6 +1089,7 @@ class QueryInvocationTest(unittest.TestCase):
1088
1089
  with querying.track_queries() as queries:
1089
1090
  querying.query('foo', Activity, lm=lm)
1090
1091
 
1092
+ self.assertIn('query@', queries[0].id)
1091
1093
  self.assertIn('schema', queries[0].to_html_str())
1092
1094
  self.assertEqual(queries[0].lm_response.score, 1.0)
1093
1095
  self.assertFalse(queries[0].lm_response.is_cached)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202504250804
3
+ Version: 0.1.2.dev202504260803
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -10,8 +10,8 @@ langfun/core/langfunc.py,sha256=G50YgoVZ0y1GFw2ev41MlOqr6qa8YakbvNC0h_E0PiA,1114
10
10
  langfun/core/langfunc_test.py,sha256=CDn-gJCa5EnjN7cotAVCfSCbuzddq2o-HzEt7kV8HbY,8882
11
11
  langfun/core/language_model.py,sha256=_hT8dGHw7CWQgMTQ-Ons5VTOwpF_j8k_gn2P0hoG-lE,46307
12
12
  langfun/core/language_model_test.py,sha256=iA5uo7rIj2jAtCYzMzhyNg1fWqE2Onn60bOO58q72C0,36454
13
- langfun/core/logging.py,sha256=W3mLEMXdo210Q5OX3a1ZTc4nU-xMy73-IfNKnsA-RFo,8051
14
- langfun/core/logging_test.py,sha256=N7-YvSXC8zvnr2SNwWHOykn1CFmqvIuTLDgn41Ku9JU,6642
13
+ langfun/core/logging.py,sha256=ERTIE08dQ36K0qQ-6Vu-5uaEZIFWdHj9rVCXpEtc_7U,8959
14
+ langfun/core/logging_test.py,sha256=vbVGOQxwMmVSiFfbt2897gUt-8nqDpV64jCAeUG_q5U,6924
15
15
  langfun/core/memory.py,sha256=f-asN1F7Vehgdn_fK84v73GrEUOxRtaW934keutTKjk,2416
16
16
  langfun/core/message.py,sha256=qUJZ9NfrlYG3aU_K2ud236gdTnvZ7Qw2T4pv4hI9ivg,32817
17
17
  langfun/core/message_test.py,sha256=XE7SaYJUK8TCJBdih4XtnpnB6NhcU-vLxJoaP67WbSU,40793
@@ -26,10 +26,10 @@ langfun/core/subscription_test.py,sha256=Y4ZdbZEwm83YNZBxHff0QR4QUa4rdaNXA3_jfIc
26
26
  langfun/core/template.py,sha256=jNhYSrbLIn9kZOa03w5QZbyjgfnzJzE_ZrrMvvWY4t4,24929
27
27
  langfun/core/template_test.py,sha256=AQv_m9qE93WxhEhSlm1xaBgB4hu0UVtA53dljngkUW0,17090
28
28
  langfun/core/agentic/__init__.py,sha256=qR3jlfUO4rhIoYdRDLz-d22YZf3FvU4FW88vsjiGDQQ,1224
29
- langfun/core/agentic/action.py,sha256=zm4R1D7NFi2YCMpUpc4gIR5_3Q1UgVfgYSGxhfxlvu0,33410
29
+ langfun/core/agentic/action.py,sha256=zMFCyfG52pP_nDTZzJKw5BjMvREz4ER_wHLbhDzEA_g,33731
30
30
  langfun/core/agentic/action_eval.py,sha256=-ZcWt_eYqnTyOe9HFGyrLJkQRd4iG3hfN4IZ-NYAWwA,4534
31
31
  langfun/core/agentic/action_eval_test.py,sha256=tRUkWmOE9p0rpNOq19xAY2oDEnYsEEykjg6sUpAwJk0,2832
32
- langfun/core/agentic/action_test.py,sha256=HvNE175bjd5sH-Oz2rRenW9-7aw4DFTHPBn9zT6z4PA,5693
32
+ langfun/core/agentic/action_test.py,sha256=ilirTICURoAR6LYjlg3oC3P1KSbLzJQDK4WaZONed2Q,5708
33
33
  langfun/core/coding/__init__.py,sha256=5utju_fwEsImaiftx4oXKl9FAM8p281k8-Esdh_-m1w,835
34
34
  langfun/core/coding/python/__init__.py,sha256=4ByknuoNU-mOIHwHKnTtmo6oD64oMFtlqPlYWmA5Wic,1736
35
35
  langfun/core/coding/python/correction.py,sha256=7zBedlhQKMPA4cfchUMxAOFl6Zl5RqCyllRHGWys40s,7092
@@ -137,8 +137,8 @@ langfun/core/structured/mapping.py,sha256=of-EeBq0RgmkiUaSk2rVEDVCzgn_wXU8tRke7N
137
137
  langfun/core/structured/mapping_test.py,sha256=OntYvfDitAf0tAnzQty3YS90vyEn6FY1Mi93r_ViEk8,9594
138
138
  langfun/core/structured/parsing.py,sha256=MGvI7ypXlwfzr5XB8_TFU9Ei0_5reYqkWkv64eAy0EA,12015
139
139
  langfun/core/structured/parsing_test.py,sha256=kNPrhpdPY3iWhUld0TFYU-Zgn44wC0d6YuQ9XdVbQ8o,22346
140
- langfun/core/structured/querying.py,sha256=DI6GbwgFMqHNtE9__jTtJJGJH6-ZNIWEgkoWgmYsWCk,24430
141
- langfun/core/structured/querying_test.py,sha256=5gNYus8Vf6lxMM7rxQGIUR_lzQw4MY7eikQgA5NNA4U,34156
140
+ langfun/core/structured/querying.py,sha256=GGNtHtJcKh8rzLBNx_Df1ATvsPZzyfZuGkzSQVabdpo,24885
141
+ langfun/core/structured/querying_test.py,sha256=vUuVUClYBFGEaO9KuD60huPE1dmP6RCRLeRnBv67NmQ,34263
142
142
  langfun/core/structured/schema.py,sha256=_iqhHEGDQsHk0AsybWnK44sOspTWkKJjci781PWD7x0,27988
143
143
  langfun/core/structured/schema_generation.py,sha256=3AcuKvv3VOtKY5zMVqODrxfOuDxzoZtGeBxHlOWDOWw,5308
144
144
  langfun/core/structured/schema_generation_test.py,sha256=RM9s71kMNg2jTePwInkiW9fK1ACN37eyPeF8OII-0zw,2950
@@ -156,8 +156,8 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
156
156
  langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
157
157
  langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
158
158
  langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
159
- langfun-0.1.2.dev202504250804.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
160
- langfun-0.1.2.dev202504250804.dist-info/METADATA,sha256=pb3hyM9a6nqaEvlFDUKBWKkZUWJue66LME5Zz26qVs0,7692
161
- langfun-0.1.2.dev202504250804.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
162
- langfun-0.1.2.dev202504250804.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
163
- langfun-0.1.2.dev202504250804.dist-info/RECORD,,
159
+ langfun-0.1.2.dev202504260803.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
160
+ langfun-0.1.2.dev202504260803.dist-info/METADATA,sha256=QUbCmM8v2vdPPyI7qvlJ47cvgUyQ5Z-gyRRbjLjJi8w,7692
161
+ langfun-0.1.2.dev202504260803.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
162
+ langfun-0.1.2.dev202504260803.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
163
+ langfun-0.1.2.dev202504260803.dist-info/RECORD,,