langfun 0.1.2.dev202509120804__py3-none-any.whl → 0.1.2.dev202512150805__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.
- langfun/__init__.py +1 -1
- langfun/core/__init__.py +7 -1
- langfun/core/agentic/__init__.py +8 -1
- langfun/core/agentic/action.py +740 -112
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +189 -24
- langfun/core/async_support.py +104 -5
- langfun/core/async_support_test.py +23 -0
- langfun/core/coding/python/correction.py +19 -9
- langfun/core/coding/python/execution.py +14 -12
- langfun/core/coding/python/generation.py +21 -16
- langfun/core/coding/python/sandboxing.py +23 -3
- langfun/core/component.py +42 -3
- langfun/core/concurrent.py +70 -6
- langfun/core/concurrent_test.py +9 -2
- langfun/core/console.py +1 -1
- langfun/core/data/conversion/anthropic.py +12 -3
- langfun/core/data/conversion/anthropic_test.py +8 -6
- langfun/core/data/conversion/gemini.py +11 -2
- langfun/core/data/conversion/gemini_test.py +48 -9
- langfun/core/data/conversion/openai.py +145 -31
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base.py +48 -44
- langfun/core/eval/base_test.py +5 -5
- langfun/core/eval/matching.py +5 -2
- langfun/core/eval/patching.py +3 -3
- langfun/core/eval/scoring.py +4 -3
- langfun/core/eval/v2/__init__.py +3 -0
- langfun/core/eval/v2/checkpointing.py +148 -46
- langfun/core/eval/v2/checkpointing_test.py +9 -2
- langfun/core/eval/v2/config_saver.py +37 -0
- langfun/core/eval/v2/config_saver_test.py +36 -0
- langfun/core/eval/v2/eval_test_helper.py +104 -3
- langfun/core/eval/v2/evaluation.py +102 -19
- langfun/core/eval/v2/evaluation_test.py +9 -3
- langfun/core/eval/v2/example.py +50 -40
- langfun/core/eval/v2/example_test.py +16 -8
- langfun/core/eval/v2/experiment.py +95 -20
- langfun/core/eval/v2/experiment_test.py +19 -0
- langfun/core/eval/v2/metric_values.py +31 -3
- langfun/core/eval/v2/metric_values_test.py +32 -0
- langfun/core/eval/v2/metrics.py +157 -44
- langfun/core/eval/v2/metrics_test.py +39 -18
- langfun/core/eval/v2/progress.py +31 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking.py +13 -5
- langfun/core/eval/v2/progress_tracking_test.py +9 -1
- langfun/core/eval/v2/reporting.py +88 -71
- langfun/core/eval/v2/reporting_test.py +24 -6
- langfun/core/eval/v2/runners/__init__.py +30 -0
- langfun/core/eval/v2/{runners.py → runners/base.py} +73 -180
- langfun/core/eval/v2/runners/beam.py +354 -0
- langfun/core/eval/v2/runners/beam_test.py +153 -0
- langfun/core/eval/v2/runners/ckpt_monitor.py +350 -0
- langfun/core/eval/v2/runners/ckpt_monitor_test.py +213 -0
- langfun/core/eval/v2/runners/debug.py +40 -0
- langfun/core/eval/v2/runners/debug_test.py +76 -0
- langfun/core/eval/v2/runners/parallel.py +243 -0
- langfun/core/eval/v2/runners/parallel_test.py +182 -0
- langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun/core/eval/v2/runners/sequential_test.py +169 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +7 -5
- langfun/core/language_model.py +189 -36
- langfun/core/language_model_test.py +54 -3
- langfun/core/llms/__init__.py +14 -1
- langfun/core/llms/anthropic.py +157 -2
- langfun/core/llms/azure_openai.py +29 -17
- langfun/core/llms/cache/base.py +25 -3
- langfun/core/llms/cache/in_memory.py +48 -7
- langfun/core/llms/cache/in_memory_test.py +14 -4
- langfun/core/llms/compositional.py +25 -1
- langfun/core/llms/deepseek.py +30 -2
- langfun/core/llms/fake.py +32 -1
- langfun/core/llms/gemini.py +90 -12
- langfun/core/llms/gemini_test.py +110 -0
- langfun/core/llms/google_genai.py +52 -1
- langfun/core/llms/groq.py +28 -3
- langfun/core/llms/llama_cpp.py +23 -4
- langfun/core/llms/openai.py +120 -3
- langfun/core/llms/openai_compatible.py +148 -27
- langfun/core/llms/openai_compatible_test.py +207 -20
- langfun/core/llms/openai_test.py +0 -2
- langfun/core/llms/rest.py +16 -1
- langfun/core/llms/vertexai.py +78 -8
- langfun/core/logging.py +1 -1
- langfun/core/mcp/__init__.py +10 -0
- langfun/core/mcp/client.py +177 -0
- langfun/core/mcp/client_test.py +71 -0
- langfun/core/mcp/session.py +241 -0
- langfun/core/mcp/session_test.py +54 -0
- langfun/core/mcp/testing/simple_mcp_client.py +33 -0
- langfun/core/mcp/testing/simple_mcp_server.py +33 -0
- langfun/core/mcp/tool.py +254 -0
- langfun/core/mcp/tool_test.py +197 -0
- langfun/core/memory.py +1 -0
- langfun/core/message.py +160 -55
- langfun/core/message_test.py +65 -81
- langfun/core/modalities/__init__.py +8 -0
- langfun/core/modalities/audio.py +21 -1
- langfun/core/modalities/image.py +73 -3
- langfun/core/modalities/image_test.py +116 -0
- langfun/core/modalities/mime.py +78 -4
- langfun/core/modalities/mime_test.py +59 -0
- langfun/core/modalities/pdf.py +19 -1
- langfun/core/modalities/video.py +21 -1
- langfun/core/modality.py +167 -29
- langfun/core/modality_test.py +42 -12
- langfun/core/natural_language.py +1 -1
- langfun/core/sampling.py +4 -4
- langfun/core/sampling_test.py +20 -4
- langfun/core/structured/__init__.py +2 -24
- langfun/core/structured/completion.py +34 -44
- langfun/core/structured/completion_test.py +23 -43
- langfun/core/structured/description.py +54 -50
- langfun/core/structured/function_generation.py +29 -12
- langfun/core/structured/mapping.py +81 -37
- langfun/core/structured/parsing.py +95 -79
- langfun/core/structured/parsing_test.py +0 -3
- langfun/core/structured/querying.py +230 -154
- langfun/core/structured/querying_test.py +69 -33
- langfun/core/structured/schema/__init__.py +49 -0
- langfun/core/structured/schema/base.py +664 -0
- langfun/core/structured/schema/base_test.py +531 -0
- langfun/core/structured/schema/json.py +174 -0
- langfun/core/structured/schema/json_test.py +121 -0
- langfun/core/structured/schema/python.py +316 -0
- langfun/core/structured/schema/python_test.py +410 -0
- langfun/core/structured/schema_generation.py +33 -14
- langfun/core/structured/scoring.py +47 -36
- langfun/core/structured/tokenization.py +26 -11
- langfun/core/subscription.py +2 -2
- langfun/core/template.py +175 -50
- langfun/core/template_test.py +123 -17
- langfun/env/__init__.py +43 -0
- langfun/env/base_environment.py +827 -0
- langfun/env/base_environment_test.py +473 -0
- langfun/env/base_feature.py +304 -0
- langfun/env/base_feature_test.py +228 -0
- langfun/env/base_sandbox.py +842 -0
- langfun/env/base_sandbox_test.py +1235 -0
- langfun/env/event_handlers/__init__.py +14 -0
- langfun/env/event_handlers/chain.py +233 -0
- langfun/env/event_handlers/chain_test.py +253 -0
- langfun/env/event_handlers/event_logger.py +472 -0
- langfun/env/event_handlers/event_logger_test.py +304 -0
- langfun/env/event_handlers/metric_writer.py +726 -0
- langfun/env/event_handlers/metric_writer_test.py +214 -0
- langfun/env/interface.py +1640 -0
- langfun/env/interface_test.py +153 -0
- langfun/env/load_balancers.py +59 -0
- langfun/env/load_balancers_test.py +141 -0
- langfun/env/test_utils.py +507 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/METADATA +7 -3
- langfun-0.1.2.dev202512150805.dist-info/RECORD +217 -0
- langfun/core/eval/v2/runners_test.py +0 -343
- langfun/core/structured/schema.py +0 -987
- langfun/core/structured/schema_test.py +0 -982
- langfun-0.1.2.dev202509120804.dist-info/RECORD +0 -172
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/top_level.txt +0 -0
langfun/core/modality_test.py
CHANGED
|
@@ -29,34 +29,64 @@ class ModalityTest(unittest.TestCase):
|
|
|
29
29
|
|
|
30
30
|
def test_basic(self):
|
|
31
31
|
v = CustomModality('a')
|
|
32
|
-
self.
|
|
32
|
+
self.assertEqual(v.id, 'custom_modality:0cc175b9')
|
|
33
33
|
self.assertEqual(str(v), "CustomModality(\n content = 'a'\n)")
|
|
34
34
|
self.assertEqual(v.hash, '0cc175b9')
|
|
35
35
|
|
|
36
36
|
_ = pg.Dict(metadata=pg.Dict(x=pg.Dict(metadata=pg.Dict(y=v))))
|
|
37
|
-
self.assertEqual(v.
|
|
37
|
+
self.assertEqual(v.id, 'custom_modality:0cc175b9')
|
|
38
38
|
self.assertEqual(str(v), "CustomModality(\n content = 'a'\n)")
|
|
39
39
|
with modality.format_modality_as_ref():
|
|
40
|
-
self.assertEqual(str(v), '<<[[
|
|
40
|
+
self.assertEqual(str(v), '<<[[custom_modality:0cc175b9]]>>')
|
|
41
|
+
|
|
42
|
+
def test_capture_rendered_modalities(self):
|
|
43
|
+
x = CustomModality('a')
|
|
44
|
+
y = CustomModality('b')
|
|
45
|
+
z = CustomModality('b')
|
|
46
|
+
|
|
47
|
+
with modality.capture_rendered_modalities() as rendered_modalities:
|
|
48
|
+
with modality.format_modality_as_ref():
|
|
49
|
+
self.assertEqual(
|
|
50
|
+
f'Hello {x} {y} {z}',
|
|
51
|
+
(
|
|
52
|
+
'Hello <<[[custom_modality:0cc175b9]]>> '
|
|
53
|
+
'<<[[custom_modality:92eb5ffe]]>> '
|
|
54
|
+
'<<[[custom_modality:92eb5ffe]]>>'
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
self.assertEqual(len(rendered_modalities), 2)
|
|
58
|
+
self.assertIs(rendered_modalities['custom_modality:0cc175b9'].value, x)
|
|
59
|
+
# y and z share the same content will be treated as the same object.
|
|
60
|
+
self.assertIs(rendered_modalities['custom_modality:92eb5ffe'].value, z)
|
|
41
61
|
|
|
42
62
|
|
|
43
63
|
class ModalityRefTest(unittest.TestCase):
|
|
44
64
|
|
|
45
|
-
def
|
|
65
|
+
def test_placehold_and_restore(self):
|
|
46
66
|
class A(pg.Object):
|
|
47
67
|
x: Any
|
|
48
68
|
y: Any
|
|
49
69
|
|
|
50
|
-
|
|
70
|
+
image_a = CustomModality('a')
|
|
71
|
+
image_b = CustomModality('b')
|
|
72
|
+
a = A(x=dict(z=image_a), y=image_b)
|
|
73
|
+
a_placehold = modality.ModalityRef.placehold(a)
|
|
51
74
|
self.assertEqual(
|
|
52
|
-
|
|
53
|
-
A(x=dict(z=modality.ModalityRef(
|
|
75
|
+
a_placehold,
|
|
76
|
+
A(x=dict(z=modality.ModalityRef(image_a.id)),
|
|
77
|
+
y=modality.ModalityRef(image_b.id)),
|
|
78
|
+
)
|
|
79
|
+
a_restore = modality.ModalityRef.restore(
|
|
80
|
+
a_placehold.clone(),
|
|
81
|
+
{image_a.id: image_a, image_b.id: image_b},
|
|
54
82
|
)
|
|
83
|
+
self.assertTrue(pg.eq(a_restore, a))
|
|
55
84
|
self.assertEqual(
|
|
56
85
|
modality.ModalityRef.placehold(a.x),
|
|
57
|
-
|
|
58
|
-
dict(z=modality.ModalityRef('x.z')),
|
|
86
|
+
dict(z=modality.ModalityRef(image_a.id)),
|
|
59
87
|
)
|
|
88
|
+
with self.assertRaisesRegex(ValueError, 'Modality .* not found'):
|
|
89
|
+
modality.ModalityRef.restore(a_placehold, {image_a.id: image_a})
|
|
60
90
|
|
|
61
91
|
def test_from_value(self):
|
|
62
92
|
class A(pg.Object):
|
|
@@ -68,8 +98,8 @@ class ModalityRefTest(unittest.TestCase):
|
|
|
68
98
|
pg.eq(
|
|
69
99
|
modality.Modality.from_value(a),
|
|
70
100
|
{
|
|
71
|
-
'
|
|
72
|
-
'
|
|
101
|
+
'custom_modality:0cc175b9': CustomModality('a'),
|
|
102
|
+
'custom_modality:92eb5ffe': CustomModality('b'),
|
|
73
103
|
},
|
|
74
104
|
)
|
|
75
105
|
)
|
|
@@ -77,7 +107,7 @@ class ModalityRefTest(unittest.TestCase):
|
|
|
77
107
|
pg.eq(
|
|
78
108
|
modality.Modality.from_value(a.x.z),
|
|
79
109
|
{
|
|
80
|
-
'
|
|
110
|
+
'custom_modality:0cc175b9': CustomModality('a'),
|
|
81
111
|
},
|
|
82
112
|
)
|
|
83
113
|
)
|
langfun/core/natural_language.py
CHANGED
|
@@ -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
|
-
"""Natural language
|
|
14
|
+
"""Natural language formatting."""
|
|
15
15
|
|
|
16
16
|
import abc
|
|
17
17
|
import pyglove as pg
|
langfun/core/sampling.py
CHANGED
|
@@ -38,10 +38,10 @@ def sweep(
|
|
|
38
38
|
Union[message_lib.Message, BaseException, None], # LM output.
|
|
39
39
|
],
|
|
40
40
|
]:
|
|
41
|
-
"""Sweeps the input/output of
|
|
41
|
+
"""Sweeps the input/output of a LangFunc search space concurrently.
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
|
-
lfun: An LangFunc object that contains `pg.oneof` as the search space
|
|
44
|
+
lfun: An LangFunc object that contains `pg.oneof` as the search space
|
|
45
45
|
for sampling.
|
|
46
46
|
num_examples: Number of examples to sample.
|
|
47
47
|
max_workers: Max number of concurrent workers to do sampling.
|
|
@@ -84,10 +84,10 @@ def random_sample(
|
|
|
84
84
|
Union[message_lib.Message, BaseException, None], # LM output.
|
|
85
85
|
],
|
|
86
86
|
]:
|
|
87
|
-
"""Random samples the input/output of
|
|
87
|
+
"""Random samples the input/output of a LangFunc search space concurrently.
|
|
88
88
|
|
|
89
89
|
Args:
|
|
90
|
-
lfun: An LangFunc object that contains `pg.oneof` as the search space
|
|
90
|
+
lfun: An LangFunc object that contains `pg.oneof` as the search space
|
|
91
91
|
for sampling.
|
|
92
92
|
num_examples: Number of examples to sample.
|
|
93
93
|
max_workers: Max number of concurrent workers to do sampling.
|
langfun/core/sampling_test.py
CHANGED
|
@@ -39,8 +39,13 @@ class SamplingTest(unittest.TestCase):
|
|
|
39
39
|
l = LangFunc('Compute {{x}} and {{y}}', x=pg.oneof([1, 2]))
|
|
40
40
|
with component.context(lm=ExcitedEchoer()):
|
|
41
41
|
samples = list(sampling.sweep(l, y=pg.oneof([3, 4])))
|
|
42
|
-
samples = sorted(
|
|
43
|
-
|
|
42
|
+
samples = sorted(
|
|
43
|
+
samples,
|
|
44
|
+
key=lambda x: (
|
|
45
|
+
x[0].__template_input__.x,
|
|
46
|
+
x[0].__template_input__.y
|
|
47
|
+
)
|
|
48
|
+
)
|
|
44
49
|
self.assertEqual(
|
|
45
50
|
samples,
|
|
46
51
|
[
|
|
@@ -57,7 +62,12 @@ class SamplingTest(unittest.TestCase):
|
|
|
57
62
|
samples = list(
|
|
58
63
|
sampling.random_sample(l, y=pg.oneof([2, 4]), num_examples=3, seed=1)
|
|
59
64
|
)
|
|
60
|
-
samples = sorted(
|
|
65
|
+
samples = sorted(
|
|
66
|
+
samples, key=lambda x: (
|
|
67
|
+
x[0].__template_input__.x,
|
|
68
|
+
x[0].__template_input__.y
|
|
69
|
+
)
|
|
70
|
+
)
|
|
61
71
|
|
|
62
72
|
self.assertEqual(
|
|
63
73
|
samples,
|
|
@@ -97,7 +107,13 @@ class SamplingTest(unittest.TestCase):
|
|
|
97
107
|
silence_on_errors=(AttributeError,),
|
|
98
108
|
ignore_examples_with_errors=False))
|
|
99
109
|
|
|
100
|
-
samples = sorted(
|
|
110
|
+
samples = sorted(
|
|
111
|
+
samples,
|
|
112
|
+
key=lambda x: (
|
|
113
|
+
x[0].__template_input__.x,
|
|
114
|
+
x[0].__template_input__.y
|
|
115
|
+
)
|
|
116
|
+
)
|
|
101
117
|
self.assertEqual(
|
|
102
118
|
[x[0] for x in samples],
|
|
103
119
|
[
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 The Langfun Authors
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -16,29 +16,7 @@
|
|
|
16
16
|
# pylint: disable=g-bad-import-order
|
|
17
17
|
# pylint: disable=g-importing-member
|
|
18
18
|
|
|
19
|
-
from langfun.core.structured.schema import
|
|
20
|
-
|
|
21
|
-
from langfun.core.structured.schema import Missing
|
|
22
|
-
from langfun.core.structured.schema import MISSING
|
|
23
|
-
from langfun.core.structured.schema import Unknown
|
|
24
|
-
from langfun.core.structured.schema import UNKNOWN
|
|
25
|
-
|
|
26
|
-
from langfun.core.structured.schema import Schema
|
|
27
|
-
from langfun.core.structured.schema import SchemaProtocol
|
|
28
|
-
from langfun.core.structured.schema import schema_spec
|
|
29
|
-
|
|
30
|
-
from langfun.core.structured.schema import SchemaError
|
|
31
|
-
from langfun.core.structured.schema import JsonError
|
|
32
|
-
|
|
33
|
-
from langfun.core.structured.schema import class_dependencies
|
|
34
|
-
from langfun.core.structured.schema import class_definition
|
|
35
|
-
from langfun.core.structured.schema import class_definitions
|
|
36
|
-
from langfun.core.structured.schema import annotation
|
|
37
|
-
from langfun.core.structured.schema import structure_from_python
|
|
38
|
-
|
|
39
|
-
from langfun.core.structured.schema import schema_repr
|
|
40
|
-
from langfun.core.structured.schema import source_form
|
|
41
|
-
from langfun.core.structured.schema import value_repr
|
|
19
|
+
from langfun.core.structured.schema import *
|
|
42
20
|
|
|
43
21
|
from langfun.core.structured.schema_generation import generate_class
|
|
44
22
|
from langfun.core.structured.schema_generation import classgen_example
|
|
@@ -116,15 +116,10 @@ class _CompleteStructure(mapping.Mapping):
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
def postprocess_result(self, result: Any) -> Any:
|
|
119
|
-
"""
|
|
119
|
+
"""Postprocesses result."""
|
|
120
120
|
# Try restore modality objects from the input value to output value.
|
|
121
|
-
modalities
|
|
122
|
-
|
|
123
|
-
# Remove the `input` prefix for all entries.
|
|
124
|
-
modalities = pg.object_utils.flatten(
|
|
125
|
-
pg.object_utils.canonicalize(modalities)['input']
|
|
126
|
-
)
|
|
127
|
-
result.rebind(modalities)
|
|
121
|
+
if modalities := self.modalities(self.input):
|
|
122
|
+
result = lf.ModalityRef.restore(result, modalities)
|
|
128
123
|
return result
|
|
129
124
|
|
|
130
125
|
def globals(self):
|
|
@@ -156,7 +151,7 @@ class _CompleteStructure(mapping.Mapping):
|
|
|
156
151
|
#
|
|
157
152
|
|
|
158
153
|
def has_modality_refs(self, value: Any) -> bool:
|
|
159
|
-
"""Returns
|
|
154
|
+
"""Returns True if the value has modalities."""
|
|
160
155
|
return not isinstance(value, lf.Modality) and pg.contains(
|
|
161
156
|
value, type=lf.Modality
|
|
162
157
|
)
|
|
@@ -186,41 +181,36 @@ def complete(
|
|
|
186
181
|
returns_message: bool = False,
|
|
187
182
|
**kwargs,
|
|
188
183
|
) -> Any:
|
|
189
|
-
"""
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
r = lf.query(prompt, Flight)
|
|
214
|
-
assert isinstance(r, Flight)
|
|
215
|
-
assert r.airline == 'United Airlines'
|
|
216
|
-
assert r.departure_airport_code == 'SFO'
|
|
217
|
-
assert r.duration.hour = 7
|
|
218
|
-
```
|
|
184
|
+
"""Completes a symbolic value by filling its missing fields using an LLM.
|
|
185
|
+
|
|
186
|
+
`lf.complete` is used to fill in missing information in structured
|
|
187
|
+
data. It takes a partially defined `pg.Object` instance where some fields
|
|
188
|
+
are marked as `lf.MISSING`, and uses a language model to infer and
|
|
189
|
+
populate those fields based on the provided values.
|
|
190
|
+
|
|
191
|
+
**Example:**
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import langfun as lf
|
|
195
|
+
import pyglove as pg
|
|
196
|
+
|
|
197
|
+
class Country(pg.Object):
|
|
198
|
+
name: str
|
|
199
|
+
capital: str = lf.MISSING
|
|
200
|
+
population: int = lf.MISSING
|
|
201
|
+
|
|
202
|
+
# Filling missing fields of Country(name='France')
|
|
203
|
+
country = lf.complete(Country(name='France'), lm=lf.llms.Gemini25Flash())
|
|
204
|
+
print(country)
|
|
205
|
+
# Output: Country(name='France', capital='Paris', population=67000000)
|
|
206
|
+
```
|
|
219
207
|
|
|
220
208
|
Args:
|
|
221
|
-
input_value: A symbolic value that may contain missing values
|
|
222
|
-
|
|
223
|
-
|
|
209
|
+
input_value: A symbolic value that may contain missing values marked
|
|
210
|
+
by `lf.MISSING`.
|
|
211
|
+
default: The default value to return if parsing fails. If
|
|
212
|
+
`lf.RAISE_IF_HAS_ERROR` is used (default), an error will be raised
|
|
213
|
+
instead.
|
|
224
214
|
lm: The language model to use. If not specified, the language model from
|
|
225
215
|
`lf.context` context manager will be used.
|
|
226
216
|
examples: An optional list of fewshot examples for helping parsing. If None,
|
|
@@ -236,10 +226,10 @@ def complete(
|
|
|
236
226
|
returns_message: If True, returns `lf.Message` as the output, instead of
|
|
237
227
|
returning the structured `message.result`.
|
|
238
228
|
**kwargs: Keyword arguments passed to the
|
|
239
|
-
`lf.structured.
|
|
229
|
+
`lf.structured.Mapping` transform.
|
|
240
230
|
|
|
241
231
|
Returns:
|
|
242
|
-
The
|
|
232
|
+
The input object with missing fields completed by LLM.
|
|
243
233
|
"""
|
|
244
234
|
t = _CompleteStructure(
|
|
245
235
|
input=schema_lib.mark_missing(input_value),
|
|
@@ -407,22 +407,17 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
407
407
|
image: modalities.Image
|
|
408
408
|
name: str
|
|
409
409
|
|
|
410
|
+
image_elephant = modalities.Image.from_bytes(b'image_of_elephant')
|
|
411
|
+
image_rabbit = modalities.Image.from_bytes(b'image_of_rabbit')
|
|
410
412
|
input_value = schema_lib.mark_missing(
|
|
411
|
-
Animal.partial(
|
|
412
|
-
modalities.Image.from_bytes(b'image_of_elephant'),
|
|
413
|
-
)
|
|
413
|
+
Animal.partial(image_elephant)
|
|
414
414
|
)
|
|
415
415
|
l = completion._CompleteStructure(
|
|
416
416
|
input=input_value,
|
|
417
417
|
examples=[
|
|
418
418
|
mapping.MappingExample(
|
|
419
|
-
input=Animal.partial(
|
|
420
|
-
|
|
421
|
-
),
|
|
422
|
-
output=Animal(
|
|
423
|
-
modalities.Image.from_bytes(b'image_of_rabbit'),
|
|
424
|
-
'rabbit',
|
|
425
|
-
),
|
|
419
|
+
input=Animal.partial(image_rabbit),
|
|
420
|
+
output=Animal(image_rabbit, 'rabbit'),
|
|
426
421
|
)
|
|
427
422
|
],
|
|
428
423
|
)
|
|
@@ -430,7 +425,7 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
430
425
|
self.maxDiff = None
|
|
431
426
|
self.assertEqual(
|
|
432
427
|
lm_input.text,
|
|
433
|
-
inspect.cleandoc("""
|
|
428
|
+
inspect.cleandoc(f"""
|
|
434
429
|
Please generate the OUTPUT_OBJECT by completing the MISSING fields from the last INPUT_OBJECT.
|
|
435
430
|
|
|
436
431
|
INSTRUCTIONS:
|
|
@@ -457,22 +452,22 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
457
452
|
```python
|
|
458
453
|
Animal(
|
|
459
454
|
image=ModalityRef(
|
|
460
|
-
|
|
455
|
+
id='{image_rabbit.id}'
|
|
461
456
|
),
|
|
462
457
|
name=MISSING(str)
|
|
463
458
|
)
|
|
464
459
|
```
|
|
465
460
|
|
|
466
461
|
MODALITY_REFERENCES:
|
|
467
|
-
{
|
|
468
|
-
'
|
|
469
|
-
}
|
|
462
|
+
{{
|
|
463
|
+
'{image_rabbit.id}': <<[[{image_rabbit.id}]]>>
|
|
464
|
+
}}
|
|
470
465
|
|
|
471
466
|
OUTPUT_OBJECT:
|
|
472
467
|
```python
|
|
473
468
|
Animal(
|
|
474
469
|
image=ModalityRef(
|
|
475
|
-
|
|
470
|
+
id='{image_rabbit.id}'
|
|
476
471
|
),
|
|
477
472
|
name='rabbit'
|
|
478
473
|
)
|
|
@@ -483,16 +478,16 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
483
478
|
```python
|
|
484
479
|
Animal(
|
|
485
480
|
image=ModalityRef(
|
|
486
|
-
|
|
481
|
+
id='{image_elephant.id}'
|
|
487
482
|
),
|
|
488
483
|
name=MISSING(str)
|
|
489
484
|
)
|
|
490
485
|
```
|
|
491
486
|
|
|
492
487
|
MODALITY_REFERENCES:
|
|
493
|
-
{
|
|
494
|
-
'
|
|
495
|
-
}
|
|
488
|
+
{{
|
|
489
|
+
'{image_elephant.id}': <<[[{image_elephant.id}]]>>
|
|
490
|
+
}}
|
|
496
491
|
|
|
497
492
|
OUTPUT_OBJECT:
|
|
498
493
|
"""),
|
|
@@ -500,39 +495,27 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
500
495
|
self.assertTrue(
|
|
501
496
|
pg.eq(
|
|
502
497
|
{
|
|
503
|
-
'examples': lm_input.
|
|
504
|
-
'input': lm_input.
|
|
498
|
+
'examples': lm_input.__template_input__.examples,
|
|
499
|
+
'input': lm_input.__template_input__.mapping_request.input,
|
|
505
500
|
},
|
|
506
501
|
{
|
|
507
502
|
'examples': [
|
|
508
503
|
mapping.MappingExample(
|
|
509
|
-
input=Animal.partial(
|
|
510
|
-
|
|
511
|
-
b'image_of_rabbit'
|
|
512
|
-
)
|
|
513
|
-
),
|
|
514
|
-
output=Animal.partial(
|
|
515
|
-
image=modalities.Image.from_bytes(
|
|
516
|
-
b'image_of_rabbit'
|
|
517
|
-
),
|
|
518
|
-
name='rabbit',
|
|
519
|
-
),
|
|
504
|
+
input=Animal.partial(image_rabbit),
|
|
505
|
+
output=Animal.partial(image_rabbit, 'rabbit'),
|
|
520
506
|
)
|
|
521
507
|
],
|
|
522
|
-
'input': Animal(
|
|
523
|
-
image=modalities.Image.from_bytes(b'image_of_elephant'),
|
|
524
|
-
name=schema_lib.MISSING,
|
|
525
|
-
),
|
|
508
|
+
'input': Animal(image_elephant, name=schema_lib.MISSING),
|
|
526
509
|
},
|
|
527
510
|
)
|
|
528
511
|
)
|
|
529
512
|
lm_output = l(
|
|
530
513
|
input=input_value,
|
|
531
|
-
lm=fake.StaticResponse(inspect.cleandoc("""
|
|
514
|
+
lm=fake.StaticResponse(inspect.cleandoc(f"""
|
|
532
515
|
```python
|
|
533
516
|
Animal(
|
|
534
517
|
image=ModalityRef(
|
|
535
|
-
|
|
518
|
+
id='{image_elephant.id}'
|
|
536
519
|
),
|
|
537
520
|
name='elephant'
|
|
538
521
|
)
|
|
@@ -542,10 +525,7 @@ class CompleteStructureTest(unittest.TestCase):
|
|
|
542
525
|
self.assertTrue(
|
|
543
526
|
pg.eq(
|
|
544
527
|
lm_output.result,
|
|
545
|
-
Animal(
|
|
546
|
-
image=modalities.Image.from_bytes(b'image_of_elephant'),
|
|
547
|
-
name='elephant',
|
|
548
|
-
),
|
|
528
|
+
Animal(image=image_elephant, name='elephant'),
|
|
549
529
|
)
|
|
550
530
|
)
|
|
551
531
|
|
|
@@ -23,7 +23,7 @@ import pyglove as pg
|
|
|
23
23
|
|
|
24
24
|
@pg.use_init_args(['examples'])
|
|
25
25
|
class _DescribeStructure(mapping.Mapping):
|
|
26
|
-
"""
|
|
26
|
+
"""Describes a structured value in natural language."""
|
|
27
27
|
|
|
28
28
|
input_title = 'PYTHON_OBJECT'
|
|
29
29
|
context_title = 'CONTEXT_FOR_DESCRIPTION'
|
|
@@ -47,64 +47,68 @@ def describe(
|
|
|
47
47
|
cache_seed: int | None = 0,
|
|
48
48
|
**kwargs,
|
|
49
49
|
) -> str:
|
|
50
|
-
"""Describes a structured value
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
50
|
+
"""Describes a structured value in natural language using an LLM.
|
|
51
|
+
|
|
52
|
+
`lf.describe` takes a Python object, often a `pg.Object` instance,
|
|
53
|
+
and uses a language model to generate a human-readable, natural language
|
|
54
|
+
description of its content. It is the inverse of `lf.parse`.
|
|
55
|
+
|
|
56
|
+
**Example:**
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import langfun as lf
|
|
60
|
+
import pyglove as pg
|
|
61
|
+
|
|
62
|
+
class FlightDuration(pg.Object):
|
|
63
|
+
hours: int
|
|
64
|
+
minutes: int
|
|
65
|
+
|
|
66
|
+
class Flight(pg.Object):
|
|
67
|
+
airline: str
|
|
68
|
+
flight_number: str
|
|
69
|
+
departure_airport: str
|
|
70
|
+
arrival_airport: str
|
|
71
|
+
departure_time: str
|
|
72
|
+
arrival_time: str
|
|
73
|
+
duration: FlightDuration
|
|
74
|
+
stops: int
|
|
75
|
+
price: float
|
|
76
|
+
|
|
77
|
+
flight_info = Flight(
|
|
78
|
+
airline='United Airlines',
|
|
79
|
+
flight_number='UA2631',
|
|
80
|
+
departure_airport='SFO',
|
|
81
|
+
arrival_airport='JFK',
|
|
82
|
+
departure_time='2023-09-07T05:15:00',
|
|
83
|
+
arrival_time='2023-09-07T12:12:00',
|
|
84
|
+
duration=FlightDuration(hours=7, minutes=57),
|
|
85
|
+
stops=1,
|
|
86
|
+
price=227,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
description = lf.describe(flight_info, lm=lf.llms.Gemini25Flash())
|
|
90
|
+
print(description)
|
|
91
|
+
# Possible output:
|
|
92
|
+
# The flight is operated by United Airlines, with the flight number UA2631,
|
|
93
|
+
# departing from SFO at 2023-09-07T05:15:00 and arriving at JFK at
|
|
94
|
+
# 2023-09-07T12:12:00. The flight duration is 7 hours and 57 minutes,
|
|
95
|
+
# with 1 stop, and costs $227.
|
|
96
|
+
```
|
|
93
97
|
|
|
94
98
|
Args:
|
|
95
99
|
value: A structured value to be mapped.
|
|
96
100
|
context: The context information for describing the structured value.
|
|
97
101
|
lm: The language model to use. If not specified, the language model from
|
|
98
102
|
`lf.context` context manager will be used.
|
|
99
|
-
examples: An optional list of fewshot examples for
|
|
100
|
-
|
|
103
|
+
examples: An optional list of fewshot examples for guiding description.
|
|
104
|
+
If None, default examples will be used.
|
|
101
105
|
cache_seed: Seed for computing cache key. The cache key is determined by a
|
|
102
106
|
tuple of (lm, prompt, cache seed). If None, cache will be disabled for
|
|
103
107
|
the query even cache is configured by the LM.
|
|
104
|
-
**kwargs: Keyword arguments passed to the `
|
|
108
|
+
**kwargs: Keyword arguments passed to the `_DescribeStructure`.
|
|
105
109
|
|
|
106
110
|
Returns:
|
|
107
|
-
|
|
111
|
+
A natural language description of the input value.
|
|
108
112
|
"""
|
|
109
113
|
return _DescribeStructure(
|
|
110
114
|
input=value,
|
|
@@ -115,10 +119,10 @@ def describe(
|
|
|
115
119
|
|
|
116
120
|
|
|
117
121
|
def default_describe_examples() -> list[mapping.MappingExample]:
|
|
118
|
-
"""
|
|
122
|
+
"""Returns default examples for `lf.describe`."""
|
|
119
123
|
|
|
120
124
|
class Country(pg.Object):
|
|
121
|
-
"""
|
|
125
|
+
"""An example dataclass for structured mapping."""
|
|
122
126
|
|
|
123
127
|
name: str
|
|
124
128
|
continents: list[
|