langfun 0.0.2.dev20240104__tar.gz → 0.0.2.dev20240106__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 (99) hide show
  1. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/PKG-INFO +2 -2
  2. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/correction.py +1 -1
  3. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/generation.py +14 -11
  4. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/base.py +12 -14
  5. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/base_test.py +13 -13
  6. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/natural_language.py +7 -10
  7. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/natural_language_test.py +3 -0
  8. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/__init__.py +0 -7
  9. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/completion.py +75 -50
  10. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/completion_test.py +29 -36
  11. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/description.py +17 -14
  12. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/description_test.py +19 -9
  13. langfun-0.0.2.dev20240106/langfun/core/structured/mapping.py +335 -0
  14. langfun-0.0.2.dev20240106/langfun/core/structured/mapping_test.py +125 -0
  15. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/parsing.py +33 -33
  16. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/parsing_test.py +30 -26
  17. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/prompting.py +34 -41
  18. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/prompting_test.py +16 -16
  19. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/schema.py +18 -3
  20. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/structured/schema_test.py +27 -1
  21. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/template.py +13 -2
  22. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/template_test.py +2 -2
  23. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun.egg-info/PKG-INFO +2 -2
  24. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun.egg-info/requires.txt +1 -1
  25. langfun-0.0.2.dev20240104/langfun/core/structured/mapping.py +0 -445
  26. langfun-0.0.2.dev20240104/langfun/core/structured/mapping_test.py +0 -166
  27. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/LICENSE +0 -0
  28. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/README.md +0 -0
  29. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/__init__.py +0 -0
  30. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/__init__.py +0 -0
  31. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/__init__.py +0 -0
  32. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/__init__.py +0 -0
  33. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/correction_test.py +0 -0
  34. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/errors.py +0 -0
  35. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/errors_test.py +0 -0
  36. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/execution.py +0 -0
  37. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/execution_test.py +0 -0
  38. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/generation_test.py +0 -0
  39. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/parsing.py +0 -0
  40. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/parsing_test.py +0 -0
  41. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/permissions.py +0 -0
  42. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/coding/python/permissions_test.py +0 -0
  43. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/component.py +0 -0
  44. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/component_test.py +0 -0
  45. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/concurrent.py +0 -0
  46. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/concurrent_test.py +0 -0
  47. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/console.py +0 -0
  48. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/console_test.py +0 -0
  49. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/__init__.py +0 -0
  50. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/matching.py +0 -0
  51. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/matching_test.py +0 -0
  52. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/scoring.py +0 -0
  53. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/eval/scoring_test.py +0 -0
  54. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/langfunc.py +0 -0
  55. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/langfunc_test.py +0 -0
  56. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/language_model.py +0 -0
  57. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/language_model_test.py +0 -0
  58. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/__init__.py +0 -0
  59. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/cache/__init__.py +0 -0
  60. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/cache/base.py +0 -0
  61. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/cache/in_memory.py +0 -0
  62. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/cache/in_memory_test.py +0 -0
  63. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/fake.py +0 -0
  64. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/fake_test.py +0 -0
  65. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/llama_cpp.py +0 -0
  66. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/llama_cpp_test.py +0 -0
  67. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/openai.py +0 -0
  68. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/llms/openai_test.py +0 -0
  69. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/memories/__init__.py +0 -0
  70. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/memories/conversation_history.py +0 -0
  71. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/memories/conversation_history_test.py +0 -0
  72. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/memory.py +0 -0
  73. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/message.py +0 -0
  74. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/message_test.py +0 -0
  75. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/modalities/__init__.py +0 -0
  76. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/modalities/image.py +0 -0
  77. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/modalities/image_test.py +0 -0
  78. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/modality.py +0 -0
  79. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/modality_test.py +0 -0
  80. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/sampling.py +0 -0
  81. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/sampling_test.py +0 -0
  82. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/subscription.py +0 -0
  83. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/subscription_test.py +0 -0
  84. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/__init__.py +0 -0
  85. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/completion.py +0 -0
  86. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/completion_test.py +0 -0
  87. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/conversation.py +0 -0
  88. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/conversation_test.py +0 -0
  89. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/demonstration.py +0 -0
  90. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/demonstration_test.py +0 -0
  91. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/selfplay.py +0 -0
  92. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/templates/selfplay_test.py +0 -0
  93. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/text_formatting.py +0 -0
  94. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun/core/text_formatting_test.py +0 -0
  95. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun.egg-info/SOURCES.txt +0 -0
  96. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun.egg-info/dependency_links.txt +0 -0
  97. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/langfun.egg-info/top_level.txt +0 -0
  98. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/setup.cfg +0 -0
  99. {langfun-0.0.2.dev20240104 → langfun-0.0.2.dev20240106}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langfun
3
- Version: 0.0.2.dev20240104
3
+ Version: 0.0.2.dev20240106
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: jinja2>=3.1.2
25
25
  Requires-Dist: openai==0.27.2
26
- Requires-Dist: pyglove>=0.4.4.dev20231213
26
+ Requires-Dist: pyglove>=0.4.5.dev20240105
27
27
  Requires-Dist: requests>=2.31.0
28
28
  Requires-Dist: termcolor==1.1.0
29
29
  Requires-Dist: tqdm>=4.64.1
@@ -107,7 +107,7 @@ def run_with_correction(
107
107
 
108
108
  examples = examples or DEFAULT_CODE_CORRECTION_EXAMPLES
109
109
  examples = [ # pylint: disable=g-complex-comprehension
110
- completion.completion_example(
110
+ completion.mapping.MappingExample(
111
111
  CodeCorrection.partial(ex.latest_code, ex.correction_history),
112
112
  ex,
113
113
  )
@@ -21,12 +21,12 @@ import pyglove as pg
21
21
 
22
22
 
23
23
  class PythonCode(pg.Object):
24
- """Symbolic class for Python code."""
24
+ """Symbolic class for Python code.
25
25
 
26
- source: Annotated[
27
- str,
28
- 'Source code.'
29
- ]
26
+ The source code will be directly passed into eval() for execution. The value
27
+ of the last expression of the source will be returned.
28
+ """
29
+ source: Annotated[str, 'Source code.']
30
30
 
31
31
  _TLS_AUTO_RUN = '__auto_run__'
32
32
 
@@ -135,18 +135,21 @@ class PythonCode(pg.Object):
135
135
  global_vars=global_vars,
136
136
  sandbox=sandbox,
137
137
  timeout=timeout,
138
- outputs_intermediate=True)
138
+ outputs_intermediate=True,
139
+ )
139
140
 
140
141
 
141
142
  class PythonFunction(pg.Object):
142
- """Generated Python function via source code."""
143
+ """Generated Python function via source code.
144
+
145
+ The source code will be directly passed into eval() for execution and the
146
+ output of the function will be returned.
147
+ """
148
+
143
149
  name: str
144
150
  args: dict[str, str]
145
151
  returns: str
146
- source: Annotated[
147
- str,
148
- 'Source code for the Python function. '
149
- ]
152
+ source: Annotated[str, 'Source code for the Python function. ']
150
153
 
151
154
  def _on_bound(self):
152
155
  super()._on_bound()
@@ -634,7 +634,8 @@ class Evaluation(Evaluable):
634
634
  'examples will be used for parsing. For "query" and "complete", it '
635
635
  'must be provided, and the fewshot examples will be used directly '
636
636
  'for prompting. Here are the example code on how the '
637
- 'functors should be defined:' + inspect.cleandoc("""
637
+ 'functors should be defined:'
638
+ + inspect.cleandoc("""
638
639
  ```
639
640
  @pg.functor()
640
641
  def solution():
@@ -648,9 +649,9 @@ class Evaluation(Evaluable):
648
649
  final_answer: int
649
650
  return Solution, [
650
651
  lf.structured.MappingExample(
651
- nl_text='Compute 1 + 2',
652
- schema=Solution,
653
- value=Solution(3))
652
+ input='Compute 1 + 2',
653
+ output=Solution(3),
654
+ schema=Solution)
654
655
  ]
655
656
  ```
656
657
  """)
@@ -832,16 +833,13 @@ class Evaluation(Evaluable):
832
833
 
833
834
  completion_examples = []
834
835
  for ex in fewshot_examples:
835
- if ex.nl_context is not None:
836
- init_args = dict(ex.value.sym_init_args)
837
- example_cls = ex.value.__class__
838
- self._maybe_adjust_schema_for_completion(example_cls)
839
- ex = lf.structured.MappingExample(
840
- value=lf.structured.mapping.Pair(
841
- left=example_cls.partial(ex.nl_context),
842
- right=example_cls(ex.nl_context, **init_args),
843
- )
844
- )
836
+ example_cls = ex.output.__class__
837
+ self._maybe_adjust_schema_for_completion(example_cls)
838
+ ex = lf.structured.MappingExample(
839
+ context=ex.context,
840
+ input=example_cls.partial(ex.input),
841
+ output=example_cls(ex.input, **ex.output.sym_init_args),
842
+ )
845
843
  completion_examples.append(ex)
846
844
  return completion_examples
847
845
 
@@ -45,9 +45,9 @@ def answer_schema():
45
45
  def answer_schema_with_fewshot_examples():
46
46
  return Solution, [
47
47
  lf_structured.MappingExample(
48
- nl_text='The result of one plus two',
48
+ input='The result of one plus two',
49
+ output=Solution(3),
49
50
  schema=Solution,
50
- value=Solution(3)
51
51
  )
52
52
  ]
53
53
 
@@ -120,10 +120,11 @@ class EvaluationTest(unittest.TestCase):
120
120
  s.fewshot_examples,
121
121
  [
122
122
  lf_structured.MappingExample(
123
- nl_text='The result of one plus two',
123
+ input='The result of one plus two',
124
+ output=Solution(3),
124
125
  schema=Solution,
125
- value=Solution(3))
126
- ]
126
+ )
127
+ ],
127
128
  )
128
129
  )
129
130
 
@@ -137,9 +138,9 @@ class EvaluationTest(unittest.TestCase):
137
138
 
138
139
  return Solution1, [
139
140
  lf.structured.MappingExample(
140
- nl_context='The result of one plus two',
141
+ input='The result of one plus two',
142
+ output=Solution1(3),
141
143
  schema=Solution1,
142
- value=Solution1(3)
143
144
  )
144
145
  ]
145
146
 
@@ -159,13 +160,12 @@ class EvaluationTest(unittest.TestCase):
159
160
  fewshot_examples,
160
161
  [
161
162
  lf.structured.MappingExample(
162
- value=lf.structured.mapping.Pair(
163
- left=solution_cls.partial(
164
- question='The result of one plus two'),
165
- right=solution_cls('The result of one plus two', 3),
166
- )
163
+ input=solution_cls.partial(
164
+ question='The result of one plus two'
165
+ ),
166
+ output=solution_cls('The result of one plus two', 3),
167
167
  )
168
- ]
168
+ ],
169
169
  )
170
170
  )
171
171
 
@@ -26,16 +26,13 @@ class NaturalLanguageFormattable(pg.Formattable):
26
26
 
27
27
  def format(
28
28
  self,
29
- compact: bool = False,
30
- verbose: bool = True,
31
- root_indent: int = 0,
29
+ *args,
30
+ natural_language: bool = False,
32
31
  **kwargs
33
32
  ) -> str:
34
- if compact:
35
- # For `repr(x)`, which returns the symbolic representation of
36
- # current object.
37
- return super().format(compact, verbose, root_indent, **kwargs)
38
- else:
39
- # For `str(x)`, which returns the natural language representation of
40
- # current object.
33
+ if natural_language:
41
34
  return self.natural_language_format()
35
+ return super().format(*args, **kwargs)
36
+
37
+ def __str__(self):
38
+ return self.natural_language_format()
@@ -16,6 +16,7 @@ import unittest
16
16
 
17
17
  from langfun.core import component
18
18
  from langfun.core import natural_language as nl
19
+ import pyglove as pg
19
20
 
20
21
 
21
22
  class NaturalLanguageFormattableTest(unittest.TestCase):
@@ -31,6 +32,8 @@ class NaturalLanguageFormattableTest(unittest.TestCase):
31
32
 
32
33
  a = A(1, 'abc')
33
34
  self.assertEqual(repr(a), 'A(x=1, y=\'abc\')')
35
+ with pg.object_utils.repr_format(natural_language=True):
36
+ self.assertEqual(repr(a), "A simple object with 1 and 'abc'.")
34
37
  self.assertEqual(str(a), "A simple object with 1 and 'abc'.")
35
38
 
36
39
 
@@ -47,11 +47,6 @@ from langfun.core.structured.schema import value_repr
47
47
  from langfun.core.structured.mapping import Mapping
48
48
  from langfun.core.structured.mapping import MappingExample
49
49
 
50
- # Mappings of between different forms of content.
51
- from langfun.core.structured.mapping import NaturalLanguageToStructure
52
- from langfun.core.structured.mapping import StructureToNaturalLanguage
53
- from langfun.core.structured.mapping import StructureToStructure
54
-
55
50
  from langfun.core.structured.parsing import ParseStructure
56
51
  from langfun.core.structured.parsing import ParseStructureJson
57
52
  from langfun.core.structured.parsing import ParseStructurePython
@@ -74,8 +69,6 @@ from langfun.core.structured.completion import complete
74
69
  from langfun.core.structured.parsing import DEFAULT_PARSE_EXAMPLES
75
70
  from langfun.core.structured.description import DEFAULT_DESCRIBE_EXAMPLES
76
71
 
77
- from langfun.core.structured.completion import completion_example
78
-
79
72
  # Default examples.
80
73
 
81
74
 
@@ -11,9 +11,9 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Structure-to-structure mappings."""
14
+ """Symbolic completion."""
15
15
 
16
- from typing import Any
16
+ from typing import Annotated, Any, Type
17
17
 
18
18
  import langfun.core as lf
19
19
  from langfun.core.structured import mapping
@@ -21,53 +21,37 @@ from langfun.core.structured import schema as schema_lib
21
21
  import pyglove as pg
22
22
 
23
23
 
24
- class CompleteStructure(mapping.StructureToStructure):
24
+ class CompleteStructure(mapping.Mapping):
25
25
  """Complete structure by filling the missing fields."""
26
26
 
27
+ input: Annotated[
28
+ pg.Symbolic, 'A symbolic object with `lf.MISSING` values to complete.'
29
+ ] = lf.contextual()
30
+
27
31
  mapping_template = lf.Template("""
28
- {{ input_value_title }}:
29
- {{ value_str(example.value.left) | indent(2, True) }}
32
+ {{ input_title }}:
33
+ {{ example.input_repr() | indent(2, True) }}
30
34
 
31
- {%- if missing_type_dependencies(example.value.left) %}
35
+ {%- if missing_type_dependencies(example.input) %}
32
36
 
33
- {{ type_definitions_title }}:
34
- {{ type_definitions_str(example.value.left) | indent(2, True) }}
37
+ {{ schema_title }}:
38
+ {{ class_defs_repr(example.input) | indent(2, True) }}
35
39
  {%- endif %}
36
- {%- if has_modalities(example.value.left) %}
40
+ {%- if has_modalities(example.input) %}
37
41
 
38
42
  {{ modality_refs_title }}:
39
- {{ modality_refs_str(example.value.left) | indent(2, True) }}
43
+ {{ modality_refs_repr(example.input) | indent(2, True) }}
40
44
  {%- endif %}
41
45
 
42
- {{ output_value_title }}:
43
- {%- if example.value.has_right %}
44
- {{ value_str(example.value.right) | indent(2, True) }}
46
+ {{ output_title }}:
47
+ {%- if example.has_output %}
48
+ {{ example.output_repr() | indent(2, True) }}
45
49
  {% endif -%}
46
50
  """)
47
51
 
48
- @property
49
- def mapping_request(self) -> mapping.MappingExample:
50
- return mapping.MappingExample(
51
- nl_context=None,
52
- nl_text=None,
53
- schema=self.input_value.__class__,
54
- value=mapping.Pair(
55
- left=pg.Ref(self.input_value), right=schema_lib.MISSING
56
- ),
57
- )
58
-
59
- def _on_bound(self):
60
- super()._on_bound()
61
- if self.examples:
62
- for example in self.examples:
63
- if not isinstance(example.value, mapping.Pair):
64
- raise ValueError(
65
- 'The value of example must be a `lf.structured.Pair` object. '
66
- f'Encountered: { example.value }.'
67
- )
68
-
69
- input_value_title = 'INPUT_OBJECT'
70
- output_value_title = 'OUTPUT_OBJECT'
52
+ input_title = 'INPUT_OBJECT'
53
+ output_title = 'OUTPUT_OBJECT'
54
+ schema_title = 'CLASS_DEFINITIONS'
71
55
 
72
56
  preamble = lf.LangFunc(
73
57
  """
@@ -77,7 +61,7 @@ class CompleteStructure(mapping.StructureToStructure):
77
61
  1. Each MISSING field contains a Python annotation, please fill the value based on the annotation.
78
62
  2. Classes for the MISSING fields are defined under CLASS_DEFINITIONS.
79
63
 
80
- {{input_value_title}}:
64
+ {{input_title}}:
81
65
  ```python
82
66
  Answer(
83
67
  question='1 + 1 =',
@@ -85,7 +69,7 @@ class CompleteStructure(mapping.StructureToStructure):
85
69
  )
86
70
  ```
87
71
 
88
- {{output_value_title}}:
72
+ {{output_title}}:
89
73
  ```python
90
74
  Answer(
91
75
  question='1 + 1 =',
@@ -93,16 +77,62 @@ class CompleteStructure(mapping.StructureToStructure):
93
77
  )
94
78
  ```
95
79
  """,
96
- input_value_title=input_value_title,
97
- output_value_title=output_value_title,
80
+ input_title=input_title,
81
+ output_title=output_title,
98
82
  )
99
83
 
100
- # NOTE(daiyip): Set the input path of the transform to root, so this transform
101
- # could access the input via the `message.result` field.
102
- input_path = ''
84
+ @property
85
+ def mapping_request(self) -> mapping.MappingExample:
86
+ """Returns a MappingExample as the mapping request."""
87
+ return mapping.MappingExample(
88
+ input=pg.Ref(self.input),
89
+ # Use the schema of input object.
90
+ schema=pg.Ref(schema_lib.Schema.from_value(self.input.__class__)),
91
+ context=self.context,
92
+ )
93
+
94
+ def transform_input(self, lm_input: lf.Message) -> lf.Message:
95
+ if not pg.contains(self.input, type=schema_lib.Missing):
96
+ raise ValueError(
97
+ 'The input of `lf.complete` must contain a least one '
98
+ f'missing value. Encountered: {self.input}.'
99
+ )
100
+
101
+ # Find modalities to fill the input message.
102
+ modalities = self.modalities(self.input)
103
+ modalities.update(
104
+ self.modalities(self.examples, root_path=pg.KeyPath('examples'))
105
+ )
106
+ if modalities:
107
+ lm_input.metadata.update(pg.object_utils.canonicalize(modalities))
108
+ return lm_input
109
+
110
+ def missing_type_dependencies(self, value: Any) -> list[Type[Any]]:
111
+ value_specs = tuple(
112
+ [v.value_spec for v in schema_lib.Missing.find_missing(value).values()]
113
+ )
114
+ return schema_lib.class_dependencies(value_specs, include_subclasses=True)
115
+
116
+ def class_defs_repr(self, value: Any) -> str | None:
117
+ return schema_lib.class_definitions(
118
+ self.missing_type_dependencies(value), markdown=True
119
+ )
120
+
121
+ def postprocess_result(self, result: Any) -> Any:
122
+ """Postprocess result."""
123
+ # Try restore modality objects from the input value to output value.
124
+ modalities = self.modalities(self.input)
125
+ if modalities:
126
+ result.rebind(modalities)
127
+ return result
103
128
 
104
129
  def globals(self):
105
130
  context = super().globals()
131
+
132
+ # Add class dependencies from the input value to the globals.
133
+ classes = schema_lib.class_dependencies(self.input)
134
+ context.update({cls.__name__: cls for cls in classes})
135
+
106
136
  # NOTE(daiyip): since `lf.complete` could have fields of Any type, which
107
137
  # could be user provided objects. For LLMs to restores these objects, we
108
138
  # need to expose their types to the code evaluation context.
@@ -117,15 +147,10 @@ class CompleteStructure(mapping.StructureToStructure):
117
147
  if isinstance(v, pg.Object):
118
148
  cls = v.__class__
119
149
  context[cls.__name__] = cls
120
- pg.traverse(self.input_value, _visit)
150
+ pg.traverse(self.input, _visit)
121
151
  return context
122
152
 
123
153
 
124
- def completion_example(left: Any, right: Any) -> mapping.MappingExample:
125
- """Makes a mapping example for completion."""
126
- return mapping.MappingExample(value=mapping.Pair(left=left, right=right))
127
-
128
-
129
154
  def complete(
130
155
  input_value: pg.Symbolic,
131
156
  default: Any = lf.RAISE_IF_HAS_ERROR,
@@ -200,5 +225,5 @@ def complete(
200
225
  context.update(kwargs)
201
226
 
202
227
  with t.override(**context):
203
- output = t(input_value=schema_lib.mark_missing(input_value))
228
+ output = t(input=schema_lib.mark_missing(input_value))
204
229
  return output if returns_message else output.result
@@ -59,7 +59,7 @@ class CompleteStructureTest(unittest.TestCase):
59
59
  )
60
60
  )
61
61
  self.assertEqual(
62
- l.render(input_value=input_value).text,
62
+ l.render(input=input_value).text,
63
63
  inspect.cleandoc("""
64
64
  Please generate the OUTPUT_OBJECT by completing the MISSING fields from the last INPUT_OBJECT.
65
65
 
@@ -133,7 +133,7 @@ class CompleteStructureTest(unittest.TestCase):
133
133
  )
134
134
  )
135
135
  self.assertEqual(
136
- l.render(input_value=input_value).text,
136
+ l.render(input=input_value).text,
137
137
  inspect.cleandoc("""
138
138
  Please generate the OUTPUT_OBJECT by completing the MISSING fields from the last INPUT_OBJECT.
139
139
 
@@ -213,7 +213,7 @@ class CompleteStructureTest(unittest.TestCase):
213
213
  )
214
214
  )
215
215
  self.assertEqual(
216
- l.render(input_value=input_value).text,
216
+ l.render(input=input_value).text,
217
217
  inspect.cleandoc("""
218
218
  Please generate the OUTPUT_OBJECT by completing the MISSING fields from the last INPUT_OBJECT.
219
219
 
@@ -410,15 +410,13 @@ class CompleteStructureTest(unittest.TestCase):
410
410
  l = completion.CompleteStructure(
411
411
  examples=[
412
412
  mapping.MappingExample(
413
- value=mapping.Pair(
414
- left=Animal.partial(
415
- modalities.Image.from_bytes(b'image_of_rabbit')
416
- ),
417
- right=Animal(
418
- modalities.Image.from_bytes(b'image_of_rabbit'),
419
- 'rabbit',
420
- ),
421
- )
413
+ input=Animal.partial(
414
+ modalities.Image.from_bytes(b'image_of_rabbit')
415
+ ),
416
+ output=Animal(
417
+ modalities.Image.from_bytes(b'image_of_rabbit'),
418
+ 'rabbit',
419
+ ),
422
420
  )
423
421
  ]
424
422
  )
@@ -427,7 +425,8 @@ class CompleteStructureTest(unittest.TestCase):
427
425
  modalities.Image.from_bytes(b'image_of_elephant'),
428
426
  )
429
427
  )
430
- lm_input = l.render(input_value=input_value)
428
+ lm_input = l.render(input=input_value)
429
+ self.maxDiff = None
431
430
  self.assertEqual(
432
431
  lm_input.text,
433
432
  inspect.cleandoc("""
@@ -463,9 +462,9 @@ class CompleteStructureTest(unittest.TestCase):
463
462
  )
464
463
  ```
465
464
 
466
- MODALITY_REFS:
465
+ MODALITY_REFERENCES:
467
466
  {
468
- 'image': {{examples[0].value.left.image}}
467
+ 'image': {{examples[0].input.image}}
469
468
  }
470
469
 
471
470
  OUTPUT_OBJECT:
@@ -489,7 +488,7 @@ class CompleteStructureTest(unittest.TestCase):
489
488
  )
490
489
  ```
491
490
 
492
- MODALITY_REFS:
491
+ MODALITY_REFERENCES:
493
492
  {
494
493
  'image': {{image}}
495
494
  }
@@ -504,28 +503,20 @@ class CompleteStructureTest(unittest.TestCase):
504
503
  'image': lm_input.get('image'),
505
504
  },
506
505
  {
507
- 'examples': [
508
- {
509
- 'value': {
510
- 'left': {
511
- 'image': modalities.Image.from_bytes(
512
- b'image_of_rabbit'
513
- )
514
- },
515
- 'right': {
516
- 'image': modalities.Image.from_bytes(
517
- b'image_of_rabbit'
518
- )
519
- },
520
- }
521
- }
522
- ],
506
+ 'examples': [{
507
+ 'input': {
508
+ 'image': modalities.Image.from_bytes(b'image_of_rabbit')
509
+ },
510
+ 'output': {
511
+ 'image': modalities.Image.from_bytes(b'image_of_rabbit')
512
+ },
513
+ }],
523
514
  'image': modalities.Image.from_bytes(b'image_of_elephant'),
524
515
  },
525
516
  )
526
517
  )
527
518
  lm_output = l(
528
- input_value=input_value,
519
+ input=input_value,
529
520
  lm=fake.StaticResponse(inspect.cleandoc("""
530
521
  ```python
531
522
  Animal(
@@ -595,9 +586,11 @@ class CompleteStructureTest(unittest.TestCase):
595
586
  with self.assertRaises(IndexError):
596
587
  completion.complete(Activity.partial(), lm=lm)
597
588
 
598
- def test_bad_init(self):
599
- with self.assertRaisesRegex(ValueError, '.*must be.*Pair'):
600
- completion.CompleteStructure(examples=[mapping.MappingExample(value=1)])
589
+ def test_bad_call(self):
590
+ with self.assertRaisesRegex(
591
+ ValueError, '.*must contain a least .* missing'
592
+ ):
593
+ completion.complete(Activity('foo'), lm=fake.StaticResponse(''))
601
594
 
602
595
  def test_bad_transform(self):
603
596
  with lf.context(
@@ -11,7 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Structured value to natural language."""
14
+ """Symbolic description."""
15
15
 
16
16
  import inspect
17
17
  from typing import Any, Literal
@@ -22,11 +22,15 @@ import pyglove as pg
22
22
 
23
23
 
24
24
  @pg.use_init_args(['examples'])
25
- class DescribeStructure(mapping.StructureToNaturalLanguage):
25
+ class DescribeStructure(mapping.Mapping):
26
26
  """Describe a structured value in natural language."""
27
27
 
28
+ input_title = 'PYTHON_OBJECT'
29
+ context_title = 'CONTEXT_FOR_DESCRIPTION'
30
+ output_title = 'NATURAL_LANGUAGE_TEXT'
31
+
28
32
  preamble = """
29
- Please help describe {{ value_title }} in natural language.
33
+ Please help describe {{ input_title }} in natural language.
30
34
 
31
35
  INSTRUCTIONS:
32
36
  1. Do not add details which are not present in the object.
@@ -103,7 +107,7 @@ def describe(
103
107
  if lm is not None:
104
108
  kwargs['lm'] = lm
105
109
  return DescribeStructure(examples)(
106
- input_value=value, nl_context=context, **kwargs
110
+ input=value, context=context, **kwargs
107
111
  ).text
108
112
 
109
113
 
@@ -130,16 +134,8 @@ class _Country(pg.Object):
130
134
 
131
135
  DEFAULT_DESCRIBE_EXAMPLES: list[mapping.MappingExample] = [
132
136
  mapping.MappingExample(
133
- nl_context='Brief intro to United States',
134
- nl_text=inspect.cleandoc("""
135
- The United States of America is a country primarily located in North America
136
- consisting of fifty states. It shares land borders with Canada to its north
137
- and with Mexico to its south and has maritime borders with the Bahamas, Cuba,
138
- Russia, and other nations. With a population of over 333 million. The national
139
- capital of the United States is Washington, D.C.
140
- """),
141
- schema=None,
142
- value=_Country(
137
+ context='Brief intro to United States',
138
+ input=_Country(
143
139
  name='The United States of America',
144
140
  continents=['North America'],
145
141
  num_states=50,
@@ -154,5 +150,12 @@ DEFAULT_DESCRIBE_EXAMPLES: list[mapping.MappingExample] = [
154
150
  capital='Washington, D.C',
155
151
  president=None,
156
152
  ),
153
+ output=inspect.cleandoc("""
154
+ The United States of America is a country primarily located in North America
155
+ consisting of fifty states. It shares land borders with Canada to its north
156
+ and with Mexico to its south and has maritime borders with the Bahamas, Cuba,
157
+ Russia, and other nations. With a population of over 333 million. The national
158
+ capital of the United States is Washington, D.C.
159
+ """),
157
160
  ),
158
161
  ]