langfun 0.0.2.dev20240325__tar.gz → 0.0.2.dev20240329__tar.gz

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.
Files changed (107) hide show
  1. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/PKG-INFO +2 -2
  2. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/base.py +39 -18
  3. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/base_test.py +4 -9
  4. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/matching_test.py +2 -4
  5. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/scoring_test.py +1 -2
  6. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/schema.py +21 -20
  7. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/schema_generation.py +2 -3
  8. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/schema_test.py +38 -21
  9. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun.egg-info/PKG-INFO +2 -2
  10. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun.egg-info/requires.txt +1 -1
  11. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/LICENSE +0 -0
  12. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/README.md +0 -0
  13. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/__init__.py +0 -0
  14. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/__init__.py +0 -0
  15. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/__init__.py +0 -0
  16. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/__init__.py +0 -0
  17. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/correction.py +0 -0
  18. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/correction_test.py +0 -0
  19. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/errors.py +0 -0
  20. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/errors_test.py +0 -0
  21. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/execution.py +0 -0
  22. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/execution_test.py +0 -0
  23. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/generation.py +0 -0
  24. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/generation_test.py +0 -0
  25. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/parsing.py +0 -0
  26. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/parsing_test.py +0 -0
  27. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/permissions.py +0 -0
  28. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/coding/python/permissions_test.py +0 -0
  29. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/component.py +0 -0
  30. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/component_test.py +0 -0
  31. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/concurrent.py +0 -0
  32. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/concurrent_test.py +0 -0
  33. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/console.py +0 -0
  34. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/console_test.py +0 -0
  35. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/__init__.py +0 -0
  36. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/matching.py +0 -0
  37. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/eval/scoring.py +0 -0
  38. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/langfunc.py +0 -0
  39. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/langfunc_test.py +0 -0
  40. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/language_model.py +0 -0
  41. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/language_model_test.py +0 -0
  42. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/__init__.py +0 -0
  43. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/cache/__init__.py +0 -0
  44. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/cache/base.py +0 -0
  45. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/cache/in_memory.py +0 -0
  46. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/cache/in_memory_test.py +0 -0
  47. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/fake.py +0 -0
  48. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/fake_test.py +0 -0
  49. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/gemini.py +0 -0
  50. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/gemini_test.py +0 -0
  51. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/llama_cpp.py +0 -0
  52. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/llama_cpp_test.py +0 -0
  53. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/openai.py +0 -0
  54. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/llms/openai_test.py +0 -0
  55. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/memories/__init__.py +0 -0
  56. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/memories/conversation_history.py +0 -0
  57. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/memories/conversation_history_test.py +0 -0
  58. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/memory.py +0 -0
  59. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/message.py +0 -0
  60. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/message_test.py +0 -0
  61. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/__init__.py +0 -0
  62. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/image.py +0 -0
  63. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/image_test.py +0 -0
  64. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/mime.py +0 -0
  65. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/mime_test.py +0 -0
  66. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/video.py +0 -0
  67. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modalities/video_test.py +0 -0
  68. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modality.py +0 -0
  69. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/modality_test.py +0 -0
  70. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/natural_language.py +0 -0
  71. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/natural_language_test.py +0 -0
  72. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/sampling.py +0 -0
  73. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/sampling_test.py +0 -0
  74. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/__init__.py +0 -0
  75. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/completion.py +0 -0
  76. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/completion_test.py +0 -0
  77. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/description.py +0 -0
  78. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/description_test.py +0 -0
  79. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/mapping.py +0 -0
  80. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/mapping_test.py +0 -0
  81. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/parsing.py +0 -0
  82. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/parsing_test.py +0 -0
  83. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/prompting.py +0 -0
  84. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/prompting_test.py +0 -0
  85. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/schema_generation_test.py +0 -0
  86. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/scoring.py +0 -0
  87. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/structured/scoring_test.py +0 -0
  88. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/subscription.py +0 -0
  89. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/subscription_test.py +0 -0
  90. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/template.py +0 -0
  91. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/template_test.py +0 -0
  92. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/__init__.py +0 -0
  93. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/completion.py +0 -0
  94. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/completion_test.py +0 -0
  95. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/conversation.py +0 -0
  96. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/conversation_test.py +0 -0
  97. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/demonstration.py +0 -0
  98. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/demonstration_test.py +0 -0
  99. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/selfplay.py +0 -0
  100. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/templates/selfplay_test.py +0 -0
  101. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/text_formatting.py +0 -0
  102. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun/core/text_formatting_test.py +0 -0
  103. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun.egg-info/SOURCES.txt +0 -0
  104. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun.egg-info/dependency_links.txt +0 -0
  105. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/langfun.egg-info/top_level.txt +0 -0
  106. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/setup.cfg +0 -0
  107. {langfun-0.0.2.dev20240325 → langfun-0.0.2.dev20240329}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langfun
3
- Version: 0.0.2.dev20240325
3
+ Version: 0.0.2.dev20240329
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -24,7 +24,7 @@ License-File: LICENSE
24
24
  Requires-Dist: google-generativeai>=0.3.2
25
25
  Requires-Dist: jinja2>=3.1.2
26
26
  Requires-Dist: openai==0.27.2
27
- Requires-Dist: pyglove>=0.4.5.dev20240314
27
+ Requires-Dist: pyglove>=0.4.5.dev20240323
28
28
  Requires-Dist: python-magic>=0.4.27
29
29
  Requires-Dist: requests>=2.31.0
30
30
  Requires-Dist: termcolor==1.1.0
@@ -27,6 +27,7 @@ import time
27
27
  from typing import Annotated, Any, Callable, Iterator, Literal, Optional, Sequence, Type, Union
28
28
 
29
29
  import langfun.core as lf
30
+ import langfun.core.coding as lf_coding
30
31
  from langfun.core.llms.cache import in_memory
31
32
  import langfun.core.structured as lf_structured
32
33
  import pyglove as pg
@@ -41,14 +42,6 @@ class Evaluable(lf.Component):
41
42
  INDEX_HTML = 'index.html'
42
43
  SUMMARY_HTML = 'summary.html'
43
44
 
44
- id: Annotated[
45
- str,
46
- (
47
- 'The ID of the evaluation, which should be unique across all '
48
- 'evaluations.'
49
- ),
50
- ]
51
-
52
45
  root_dir: Annotated[
53
46
  str | None,
54
47
  (
@@ -61,6 +54,18 @@ class Evaluable(lf.Component):
61
54
  int, 'Number of decimals when reporting precision.'
62
55
  ] = lf.contextual(default=1)
63
56
 
57
+ @property
58
+ @abc.abstractmethod
59
+ def id(self) -> str:
60
+ """Returns the ID of the task.
61
+
62
+ Returns:
63
+ Evaluation task ID. Different evaluation task should have their unique
64
+ task IDs, for each task will be stored in sub-directoreis identified by
65
+ their IDs. For suites, the ID could be an empty string as they will not
66
+ produce sub-directories
67
+ """
68
+
64
69
  @property
65
70
  def dir(self) -> str | None:
66
71
  """Returns the directory for saving results and details."""
@@ -578,12 +583,15 @@ class _LeafNode:
578
583
  progress_bar: int | None = None
579
584
 
580
585
 
581
- @pg.use_init_args(['id', 'children'])
586
+ @pg.use_init_args(['children'])
582
587
  class Suite(Evaluable):
583
588
  """Evaluation suite."""
584
589
 
585
590
  children: Annotated[list[Evaluable], 'Child evaluation sets or suites.']
586
591
 
592
+ # Use empty ID as suite is just a container of child evaluations.
593
+ id: str = ''
594
+
587
595
  __kwargs__: Annotated[
588
596
  Any,
589
597
  (
@@ -841,8 +849,10 @@ class Evaluation(Evaluable):
841
849
  kwargs['evaluation'] = self
842
850
  return self.schema_fn(**kwargs)
843
851
 
844
- def _formalize_schema(self, annotation) -> lf_structured.Schema:
852
+ def _formalize_schema(self, annotation) -> lf_structured.Schema | None:
845
853
  """Formalizes schema from annotation."""
854
+ if annotation in (str, None):
855
+ return None
846
856
  if self.method == 'complete':
847
857
  if not hasattr(annotation, '__schema__'):
848
858
  raise TypeError(
@@ -883,6 +893,14 @@ class Evaluation(Evaluable):
883
893
  completion_examples.append(ex)
884
894
  return completion_examples
885
895
 
896
+ @property
897
+ def id(self) -> str:
898
+ """Returns the ID of this evaluation."""
899
+ id_prefix = self.__class__.__name__
900
+ if not self.is_deterministic:
901
+ return id_prefix
902
+ return f'{id_prefix}@{self.hash}'
903
+
886
904
  @functools.cached_property
887
905
  def children(self) -> list['Evaluation']:
888
906
  """Returns the trials as child evaluations if this evaluation is a space."""
@@ -892,7 +910,6 @@ class Evaluation(Evaluable):
892
910
  for i, child in enumerate(pg.iter(self)):
893
911
  child.sym_setparent(self)
894
912
  child.sym_setpath(self.sym_path + f'children[{i}]')
895
- child.rebind(id=f'{self.id}@{child.hash}', skip_notification=True)
896
913
  children.append(child)
897
914
  return children
898
915
 
@@ -1004,7 +1021,11 @@ class Evaluation(Evaluable):
1004
1021
  self._reset()
1005
1022
 
1006
1023
  def _process(example: Any):
1007
- return self.process(example, **(self.additional_args or {}))
1024
+ # NOTE(daiyip): set the `input` symbol of the globals to None, so LLM
1025
+ # generated code with calls to `input` will raise an error, thus not
1026
+ # blocking the evaluation.
1027
+ with lf_coding.context(input=None):
1028
+ return self.process(example, **(self.additional_args or {}))
1008
1029
 
1009
1030
  try:
1010
1031
  for example, message, error in lf.concurrent_map(
@@ -1015,10 +1036,7 @@ class Evaluation(Evaluable):
1015
1036
  status_fn=self._status,
1016
1037
  ):
1017
1038
  if error is not None:
1018
- try:
1019
- self._failures.append((example, str(error)))
1020
- except Exception as e: # pylint: disable=broad-exception-caught
1021
- self._failures.append((example, str(e)))
1039
+ self._failures.append((example, str(error)))
1022
1040
  else:
1023
1041
  output = message.text if self.schema is None else message.result
1024
1042
  self.audit(example, output, message)
@@ -1521,9 +1539,12 @@ class Summary(pg.Object):
1521
1539
  pivot_field = pivot_field or self.pivot_field
1522
1540
  s = io.StringIO()
1523
1541
  s.write('<html><body>')
1524
- for task in self.tasks():
1542
+ for task in sorted(self.tasks(), key=lambda cls: cls.__name__):
1543
+ table_id = task.__name__.lower()
1525
1544
  s.write('<div>')
1526
- s.write(f'<h2>{task.__name__}</h2>')
1545
+ s.write(f'<a id="{table_id}"')
1546
+ s.write(f'<h2><a href="#{table_id}">{task.__name__}</a></h2>')
1547
+ s.write('</a>')
1527
1548
  table = Summary.Table.from_evaluations(
1528
1549
  self.select(task=task).evaluations, pivot_field
1529
1550
  )
@@ -70,8 +70,7 @@ def eval_set(
70
70
  """Creates an evaluation object for testing."""
71
71
  tmp_dir = tempfile.gettempdir()
72
72
  return cls(
73
- id=eval_id,
74
- root_dir=tmp_dir,
73
+ root_dir=os.path.join(tmp_dir, eval_id),
75
74
  inputs=base.as_inputs([
76
75
  pg.Dict(question='Compute 1 + 1'),
77
76
  pg.Dict(question='Compute 1 + 2'),
@@ -210,7 +209,7 @@ class EvaluationTest(unittest.TestCase):
210
209
  s.result,
211
210
  dict(
212
211
  experiment_setup=dict(
213
- id='run_test',
212
+ id='Evaluation@17915dc6',
214
213
  dir=s.dir,
215
214
  model='StaticSequence',
216
215
  prompt_template='{{example.question}}',
@@ -302,7 +301,6 @@ class EvaluationTest(unittest.TestCase):
302
301
  '3',
303
302
  ])
304
303
  s = base.Evaluation(
305
- id='search_space_test',
306
304
  root_dir=tempfile.gettempdir(),
307
305
  inputs=base.as_inputs([
308
306
  pg.Dict(question='Compute 1 + 1'),
@@ -439,7 +437,6 @@ class SuiteTest(unittest.TestCase):
439
437
  '3',
440
438
  ] * 5)
441
439
  s = base.Suite(
442
- 'suite_run_test',
443
440
  [
444
441
  eval_set('run_test_1', 'query', schema_fn=answer_schema()),
445
442
  # A suite of search space. Two of the sub-experiments are identical,
@@ -548,7 +545,6 @@ class SummaryTest(unittest.TestCase):
548
545
  def _eval_set(self, root_dir):
549
546
  return base.Suite(id='select_test', children=[
550
547
  TaskA(
551
- id='task_a',
552
548
  inputs=base.as_inputs([
553
549
  pg.Dict(question='Compute 1 + 1'),
554
550
  ]),
@@ -569,7 +565,6 @@ class SummaryTest(unittest.TestCase):
569
565
  max_workers=1,
570
566
  ),
571
567
  TaskB(
572
- id='task_b',
573
568
  inputs=base.as_inputs([
574
569
  pg.Dict(question='Compute 1 + 1'),
575
570
  ]),
@@ -650,10 +645,10 @@ class SummaryTest(unittest.TestCase):
650
645
  len(base.Summary.from_dirs(root_dir)), 2 * 2 * 2 * 2 + 2 * 1 * 1 * 2
651
646
  )
652
647
  self.assertEqual(
653
- len(base.Summary.from_dirs(root_dir, 'task_b')), 2 * 1 * 1 * 2
648
+ len(base.Summary.from_dirs(root_dir, 'TaskB')), 2 * 1 * 1 * 2
654
649
  )
655
650
  self.assertEqual(
656
- len(base.Summary.from_dirs(root_dir, ('task_a'))), 2 * 2 * 2 * 2
651
+ len(base.Summary.from_dirs(root_dir, ('TaskA'))), 2 * 2 * 2 * 2
657
652
  )
658
653
 
659
654
  def test_monitor(self):
@@ -65,10 +65,8 @@ def eval_set(
65
65
  use_cache: bool = True,
66
66
  ):
67
67
  """Creates an evaluation object for testing."""
68
- tmp_dir = tempfile.gettempdir()
69
68
  return MyTask(
70
- id=eval_id,
71
- root_dir=tmp_dir,
69
+ root_dir=os.path.join(tempfile.gettempdir(), eval_id),
72
70
  inputs=base.as_inputs([
73
71
  pg.Dict(question='Compute 1 + 1', groundtruth=2),
74
72
  pg.Dict(question='Compute 1 + 2', groundtruth=3),
@@ -105,7 +103,7 @@ class MatchingTest(unittest.TestCase):
105
103
  s.result,
106
104
  dict(
107
105
  experiment_setup=dict(
108
- id='match_run_test',
106
+ id='MyTask@3d87f97f',
109
107
  dir=s.dir,
110
108
  model='StaticSequence',
111
109
  prompt_template='{{example.question}}',
@@ -43,7 +43,6 @@ def constrained_by_upperbound(upper_bound: int):
43
43
 
44
44
 
45
45
  class ConstraintFollowing(scoring.Scoring):
46
- id = 'constraint_following'
47
46
  inputs = constrained_by_upperbound(1)
48
47
  prompt = '{{example}}'
49
48
  method = 'query'
@@ -82,7 +81,7 @@ class ScoringTest(unittest.TestCase):
82
81
  s.result,
83
82
  dict(
84
83
  experiment_setup=dict(
85
- id='constraint_following',
84
+ id='ConstraintFollowing@9e51bb9e',
86
85
  dir=s.dir,
87
86
  model='StaticSequence',
88
87
  prompt_template='{{example}}',
@@ -55,10 +55,6 @@ def parse_value_spec(value) -> pg.typing.ValueSpec:
55
55
  ),
56
56
  ):
57
57
  raise ValueError(f'Unsupported schema specification: {v}')
58
- if isinstance(spec, pg.typing.Object) and not issubclass(
59
- spec.cls, pg.Symbolic
60
- ):
61
- raise ValueError(f'{v} must be a symbolic class to be parsable.')
62
58
  return spec
63
59
 
64
60
  return _parse_node(value)
@@ -208,7 +204,9 @@ def class_dependencies(
208
204
  if isinstance(value_or_spec, Schema):
209
205
  return value_or_spec.class_dependencies(include_subclasses)
210
206
 
211
- if isinstance(value_or_spec, (pg.typing.ValueSpec, pg.symbolic.ObjectMeta)):
207
+ if inspect.isclass(value_or_spec) or isinstance(
208
+ value_or_spec, pg.typing.ValueSpec
209
+ ):
212
210
  value_or_spec = (value_or_spec,)
213
211
 
214
212
  if isinstance(value_or_spec, tuple):
@@ -216,7 +214,7 @@ def class_dependencies(
216
214
  for v in value_or_spec:
217
215
  if isinstance(v, pg.typing.ValueSpec):
218
216
  value_specs.append(v)
219
- elif inspect.isclass(v) and issubclass(v, pg.Object):
217
+ elif inspect.isclass(v):
220
218
  value_specs.append(pg.typing.Object(v))
221
219
  else:
222
220
  raise TypeError(f'Unsupported spec type: {v!r}')
@@ -235,23 +233,20 @@ def class_dependencies(
235
233
 
236
234
  def _fill_dependencies(vs: pg.typing.ValueSpec, include_subclasses: bool):
237
235
  if isinstance(vs, pg.typing.Object):
238
- if issubclass(vs.cls, pg.Object) and vs.cls not in seen:
236
+ if vs.cls not in seen:
239
237
  seen.add(vs.cls)
240
238
 
241
239
  # Add base classes as dependencies.
242
240
  for base_cls in vs.cls.__bases__:
243
241
  # We only keep track of user-defined symbolic classes.
244
- if issubclass(
245
- base_cls, pg.Object
246
- ) and not base_cls.__module__.startswith('pyglove'):
242
+ if base_cls is not object and base_cls is not pg.Object:
247
243
  _fill_dependencies(
248
244
  pg.typing.Object(base_cls), include_subclasses=False
249
245
  )
250
246
 
251
247
  # Add members as dependencies.
252
- if hasattr(vs.cls, '__schema__'):
253
- for field in vs.cls.__schema__.values():
254
- _fill_dependencies(field.value, include_subclasses)
248
+ for field in _pg_schema(vs.cls).values():
249
+ _fill_dependencies(field.value, include_subclasses)
255
250
  _add_dependency(vs.cls)
256
251
 
257
252
  # Check subclasses if available.
@@ -364,17 +359,13 @@ def class_definition(
364
359
  ) -> str:
365
360
  """Returns the Python class definition."""
366
361
  out = io.StringIO()
367
- if not issubclass(cls, pg.Object):
368
- raise TypeError(
369
- 'Classes must be `pg.Object` subclasses to be used as schema. '
370
- f'Encountered: {cls}.'
371
- )
372
- schema = cls.__schema__
362
+ schema = _pg_schema(cls)
373
363
  eligible_bases = []
374
364
  for base_cls in cls.__bases__:
375
- if issubclass(base_cls, pg.Object):
365
+ if base_cls is not object:
376
366
  if include_pg_object_as_base or base_cls is not pg.Object:
377
367
  eligible_bases.append(base_cls.__name__)
368
+
378
369
  if eligible_bases:
379
370
  base_cls_str = ', '.join(eligible_bases)
380
371
  out.write(f'class {cls.__name__}({base_cls_str}):\n')
@@ -839,3 +830,13 @@ class Unknown(pg.Object, pg.typing.CustomTyping):
839
830
 
840
831
 
841
832
  UNKNOWN = Unknown()
833
+
834
+
835
+ def _pg_schema(cls: Type[Any]) -> pg.Schema:
836
+ """Returns PyGlove schema for the constructor of a class."""
837
+ schema = getattr(cls, '__schema__', None)
838
+ if schema is None:
839
+ schema = pg.symbolic.callable_schema(
840
+ cls.__init__, auto_typing=True, auto_doc=True, remove_self=True
841
+ )
842
+ return schema
@@ -143,14 +143,14 @@ def generate_class(
143
143
 
144
144
 
145
145
  def classgen_example(
146
- class_name: str, prompt: str | pg.Symbolic, cls: Type[Any]
146
+ prompt: str | pg.Symbolic, cls: Type[Any]
147
147
  ) -> mapping.MappingExample:
148
148
  """Creates a class generation example."""
149
149
  if isinstance(prompt, lf.Template):
150
150
  prompt = prompt.render()
151
151
  return mapping.MappingExample(
152
152
  input=prompt,
153
- context=class_name,
153
+ context=cls.__name__,
154
154
  output=cls,
155
155
  )
156
156
 
@@ -168,7 +168,6 @@ def default_classgen_examples() -> list[mapping.MappingExample]:
168
168
 
169
169
  return [
170
170
  classgen_example(
171
- 'Solution',
172
171
  'How to evaluate an arithmetic expression?',
173
172
  Solution,
174
173
  )
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  """Tests for structured parsing."""
15
15
 
16
+ import dataclasses
16
17
  import inspect
17
18
  import typing
18
19
  import unittest
@@ -101,12 +102,7 @@ class SchemaTest(unittest.TestCase):
101
102
 
102
103
  self.assert_unsupported_annotation(typing.Type[int])
103
104
  self.assert_unsupported_annotation(typing.Union[int, str, bool])
104
-
105
- class X:
106
- pass
107
-
108
- # X must be a symbolic type to be parsable.
109
- self.assert_unsupported_annotation(X)
105
+ self.assert_unsupported_annotation(typing.Any)
110
106
 
111
107
  def test_schema_dict(self):
112
108
  schema = schema_lib.Schema([{'x': Itinerary}])
@@ -150,6 +146,25 @@ class SchemaTest(unittest.TestCase):
150
146
  schema = schema_lib.Schema([B])
151
147
  self.assertEqual(schema.class_dependencies(), [Foo, A, Bar, X, B])
152
148
 
149
+ def test_class_dependencies_non_pyglove(self):
150
+ class Baz:
151
+ def __init__(self, x: int):
152
+ pass
153
+
154
+ @dataclasses.dataclass(frozen=True)
155
+ class AA:
156
+ foo: tuple[Baz, int]
157
+
158
+ class XX(pg.Object):
159
+ pass
160
+
161
+ @dataclasses.dataclass(frozen=True)
162
+ class BB(AA):
163
+ foo2: Baz | XX
164
+
165
+ schema = schema_lib.Schema([AA])
166
+ self.assertEqual(schema.class_dependencies(), [Baz, AA, XX, BB])
167
+
153
168
  def test_schema_repr(self):
154
169
  schema = schema_lib.Schema([{'x': Itinerary}])
155
170
  self.assertEqual(
@@ -440,13 +455,6 @@ class SchemaPythonReprTest(unittest.TestCase):
440
455
  'class A(Object):\n pass\n',
441
456
  )
442
457
 
443
- class B:
444
- pass
445
-
446
- with self.assertRaisesRegex(
447
- TypeError, 'Classes must be `pg.Object` subclasses.*'):
448
- schema_lib.class_definition(B)
449
-
450
458
  class C(pg.Object):
451
459
  x: str
452
460
  __kwargs__: typing.Any
@@ -459,9 +467,12 @@ class SchemaPythonReprTest(unittest.TestCase):
459
467
  class Foo(pg.Object):
460
468
  x: int
461
469
 
462
- class Bar(pg.Object):
470
+ @dataclasses.dataclass(frozen=True)
471
+ class Bar:
472
+ """Class Bar."""
463
473
  y: str
464
474
 
475
+ @dataclasses.dataclass(frozen=True)
465
476
  class Baz(Bar): # pylint: disable=unused-variable
466
477
  pass
467
478
 
@@ -475,7 +486,7 @@ class SchemaPythonReprTest(unittest.TestCase):
475
486
  schema = schema_lib.Schema([B])
476
487
  self.assertEqual(
477
488
  schema_lib.SchemaPythonRepr().class_definitions(schema),
478
- inspect.cleandoc("""
489
+ inspect.cleandoc('''
479
490
  class Foo:
480
491
  x: int
481
492
 
@@ -483,16 +494,18 @@ class SchemaPythonReprTest(unittest.TestCase):
483
494
  foo: Foo
484
495
 
485
496
  class Bar:
497
+ """Class Bar."""
486
498
  y: str
487
499
 
488
500
  class Baz(Bar):
501
+ """Baz(y: str)"""
489
502
  y: str
490
503
 
491
504
  class B(A):
492
505
  foo: Foo
493
506
  bar: Bar
494
507
  foo2: Foo
495
- """) + '\n',
508
+ ''') + '\n',
496
509
  )
497
510
 
498
511
  self.assertEqual(
@@ -501,7 +514,7 @@ class SchemaPythonReprTest(unittest.TestCase):
501
514
 
502
515
  self.assertEqual(
503
516
  schema_lib.SchemaPythonRepr().repr(schema),
504
- inspect.cleandoc("""
517
+ inspect.cleandoc('''
505
518
  list[B]
506
519
 
507
520
  ```python
@@ -512,9 +525,11 @@ class SchemaPythonReprTest(unittest.TestCase):
512
525
  foo: Foo
513
526
 
514
527
  class Bar:
528
+ """Class Bar."""
515
529
  y: str
516
530
 
517
531
  class Baz(Bar):
532
+ """Baz(y: str)"""
518
533
  y: str
519
534
 
520
535
  class B(A):
@@ -522,7 +537,7 @@ class SchemaPythonReprTest(unittest.TestCase):
522
537
  bar: Bar
523
538
  foo2: Foo
524
539
  ```
525
- """),
540
+ '''),
526
541
  )
527
542
  self.assertEqual(
528
543
  schema_lib.SchemaPythonRepr().repr(
@@ -531,24 +546,26 @@ class SchemaPythonReprTest(unittest.TestCase):
531
546
  include_pg_object_as_base=True,
532
547
  markdown=False,
533
548
  ),
534
- inspect.cleandoc("""
549
+ inspect.cleandoc('''
535
550
  class Foo(Object):
536
551
  x: int
537
552
 
538
553
  class A(Object):
539
554
  foo: Foo
540
555
 
541
- class Bar(Object):
556
+ class Bar:
557
+ """Class Bar."""
542
558
  y: str
543
559
 
544
560
  class Baz(Bar):
561
+ """Baz(y: str)"""
545
562
  y: str
546
563
 
547
564
  class B(A):
548
565
  foo: Foo
549
566
  bar: Bar
550
567
  foo2: Foo
551
- """),
568
+ '''),
552
569
  )
553
570
 
554
571
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langfun
3
- Version: 0.0.2.dev20240325
3
+ Version: 0.0.2.dev20240329
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -24,7 +24,7 @@ License-File: LICENSE
24
24
  Requires-Dist: google-generativeai>=0.3.2
25
25
  Requires-Dist: jinja2>=3.1.2
26
26
  Requires-Dist: openai==0.27.2
27
- Requires-Dist: pyglove>=0.4.5.dev20240314
27
+ Requires-Dist: pyglove>=0.4.5.dev20240323
28
28
  Requires-Dist: python-magic>=0.4.27
29
29
  Requires-Dist: requests>=2.31.0
30
30
  Requires-Dist: termcolor==1.1.0
@@ -1,7 +1,7 @@
1
1
  google-generativeai>=0.3.2
2
2
  jinja2>=3.1.2
3
3
  openai==0.27.2
4
- pyglove>=0.4.5.dev20240314
4
+ pyglove>=0.4.5.dev20240323
5
5
  python-magic>=0.4.27
6
6
  requests>=2.31.0
7
7
  termcolor==1.1.0