langfun 0.1.2.dev202510230805__py3-none-any.whl → 0.1.2.dev202511270805__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of langfun might be problematic. Click here for more details.
- langfun/core/__init__.py +2 -0
- langfun/core/agentic/__init__.py +4 -1
- langfun/core/agentic/action.py +447 -29
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +149 -21
- langfun/core/async_support.py +32 -3
- 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 +1 -0
- 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 +9 -2
- langfun/core/data/conversion/gemini_test.py +12 -9
- langfun/core/data/conversion/openai.py +145 -31
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base.py +47 -43
- 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 +1 -0
- langfun/core/eval/v2/checkpointing.py +64 -6
- langfun/core/eval/v2/checkpointing_test.py +9 -2
- langfun/core/eval/v2/eval_test_helper.py +103 -2
- langfun/core/eval/v2/evaluation.py +91 -16
- 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 +74 -8
- 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 +30 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking.py +12 -3
- langfun/core/eval/v2/progress_tracking_test.py +6 -1
- langfun/core/eval/v2/reporting.py +90 -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} +59 -142
- langfun/core/eval/v2/runners/beam.py +341 -0
- langfun/core/eval/v2/runners/beam_test.py +131 -0
- langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
- langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -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 +100 -0
- langfun/core/eval/v2/runners/parallel_test.py +95 -0
- langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun/core/eval/v2/runners/sequential_test.py +172 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +7 -5
- langfun/core/language_model.py +141 -21
- langfun/core/language_model_test.py +54 -3
- langfun/core/llms/__init__.py +9 -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 +55 -17
- langfun/core/llms/gemini_test.py +84 -0
- langfun/core/llms/google_genai.py +34 -1
- langfun/core/llms/groq.py +28 -3
- langfun/core/llms/llama_cpp.py +23 -4
- langfun/core/llms/openai.py +36 -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 +12 -1
- langfun/core/llms/vertexai.py +58 -8
- langfun/core/logging.py +1 -1
- langfun/core/mcp/client.py +77 -22
- langfun/core/mcp/client_test.py +8 -35
- langfun/core/mcp/session.py +94 -29
- langfun/core/mcp/session_test.py +54 -0
- langfun/core/mcp/tool.py +151 -22
- 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 +19 -1
- langfun/core/modalities/mime.py +64 -3
- langfun/core/modalities/mime_test.py +11 -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 +215 -142
- langfun/core/structured/querying_test.py +65 -29
- 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 +174 -49
- langfun/core/template_test.py +123 -17
- langfun/env/__init__.py +8 -2
- langfun/env/base_environment.py +320 -128
- langfun/env/base_environment_test.py +473 -0
- langfun/env/base_feature.py +92 -15
- langfun/env/base_feature_test.py +228 -0
- langfun/env/base_sandbox.py +84 -361
- langfun/env/base_sandbox_test.py +1235 -0
- langfun/env/event_handlers/__init__.py +1 -1
- langfun/env/event_handlers/chain.py +233 -0
- langfun/env/event_handlers/chain_test.py +253 -0
- langfun/env/event_handlers/event_logger.py +95 -98
- langfun/env/event_handlers/event_logger_test.py +21 -21
- langfun/env/event_handlers/metric_writer.py +225 -140
- langfun/env/event_handlers/metric_writer_test.py +23 -6
- langfun/env/interface.py +854 -40
- langfun/env/interface_test.py +112 -2
- langfun/env/load_balancers_test.py +23 -2
- langfun/env/test_utils.py +126 -84
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/METADATA +1 -1
- langfun-0.1.2.dev202511270805.dist-info/RECORD +215 -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/env/base_test.py +0 -1481
- langfun/env/event_handlers/base.py +0 -350
- langfun-0.1.2.dev202510230805.dist-info/RECORD +0 -195
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202511270805.dist-info}/top_level.txt +0 -0
|
@@ -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[
|
|
@@ -26,10 +26,10 @@ import pyglove as pg
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def unittest_gen(signature, lm, num_retries=1):
|
|
29
|
-
"""Generates unit tests for a
|
|
29
|
+
"""Generates unit tests for a Python function signature."""
|
|
30
30
|
|
|
31
31
|
class UnitTest(pg.Object):
|
|
32
|
-
"""A valid unit test for a
|
|
32
|
+
"""A valid unit test for a Python function."""
|
|
33
33
|
|
|
34
34
|
input: dict[str, Any]
|
|
35
35
|
expected_output: Any
|
|
@@ -55,7 +55,7 @@ def unittest_gen(signature, lm, num_retries=1):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def unittest_with_test_cases(f, unittests):
|
|
58
|
-
"""Applies unit tests to a
|
|
58
|
+
"""Applies unit tests to a Python function to be tested."""
|
|
59
59
|
if not unittests:
|
|
60
60
|
raise ValueError(f"No unit tests provided: {unittests}")
|
|
61
61
|
|
|
@@ -87,10 +87,10 @@ def _function_gen(
|
|
|
87
87
|
] = None,
|
|
88
88
|
unittest_num_retries: int = 1,
|
|
89
89
|
):
|
|
90
|
-
"""Generates a
|
|
90
|
+
"""Generates a Python function with LLM and verifies it with unit testing."""
|
|
91
91
|
|
|
92
92
|
class PythonFunctionPrompt(template.Template):
|
|
93
|
-
r"""A template for a
|
|
93
|
+
r"""A template for a Python function generation.
|
|
94
94
|
|
|
95
95
|
Please reply to the last PYTHON_FUNCTION_SIGNATURE with a self-sufficient,
|
|
96
96
|
error-free, and efficiently coded PYTHON_FUNCTION, crafted to the standards
|
|
@@ -195,11 +195,28 @@ def function_gen(
|
|
|
195
195
|
] = None,
|
|
196
196
|
unittest_num_retries: int = 1,
|
|
197
197
|
):
|
|
198
|
-
"""
|
|
198
|
+
r"""Decorator for generating function implementations using an LLM.
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
`lf.function_gen` is a decorator that automatically generates the
|
|
201
|
+
implementation of a Python function based on its signature and docstring,
|
|
202
|
+
using the specified language model. This is useful for quickly prototyping
|
|
203
|
+
functions or generating boilerplate code.
|
|
204
|
+
|
|
205
|
+
The decorator can also automatically generate and run unit tests to verify
|
|
206
|
+
the correctness of the generated implementation.
|
|
207
|
+
|
|
208
|
+
**Example:**
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import langfun as lf
|
|
212
|
+
|
|
213
|
+
@lf.function_gen(lm=lf.llms.Gemini25Flash())
|
|
214
|
+
def product(a: int, b: int) -> int:
|
|
215
|
+
\"\"\"Returns product of a and b.\"\"\"
|
|
216
|
+
|
|
217
|
+
print(product(2, 3))
|
|
218
|
+
# Output: 6
|
|
219
|
+
```
|
|
203
220
|
|
|
204
221
|
Args:
|
|
205
222
|
lm (lf.LanguageModel): The language model used for generating function
|
|
@@ -212,10 +229,10 @@ def function_gen(
|
|
|
212
229
|
tests. You can either provide a list of test cases as tuples of inputs
|
|
213
230
|
and outputs, or a function that throws an error if a test fails, or let
|
|
214
231
|
LLM automatically create the unit test cases. If a generated function is
|
|
215
|
-
|
|
232
|
+
returned, it should pass all the unit tests.
|
|
216
233
|
unittest_num_retries: If unittest is set to "auto", this parameter
|
|
217
|
-
specifies the number of times the LLM
|
|
218
|
-
cases.
|
|
234
|
+
specifies the number of times the LLM should attempt to generate unit
|
|
235
|
+
test cases.
|
|
219
236
|
|
|
220
237
|
Returns:
|
|
221
238
|
The implemented function object.
|
|
@@ -22,7 +22,16 @@ import pyglove as pg
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class MappingError(Exception): # pylint: disable=g-bad-exception-name
|
|
25
|
-
"""
|
|
25
|
+
"""Error raised during a structured mapping task.
|
|
26
|
+
|
|
27
|
+
`MappingError` is raised when a language model's response cannot be
|
|
28
|
+
successfully parsed or transformed into the target structure defined by
|
|
29
|
+
the schema in structured mapping operations like `lf.query` and `lf.parse`.
|
|
30
|
+
|
|
31
|
+
This error encapsulates both the original exception that occurred during
|
|
32
|
+
parsing (`cause`) and the language model response (`lm_response`) that led
|
|
33
|
+
to the failure, allowing for easier debugging of mapping issues.
|
|
34
|
+
"""
|
|
26
35
|
|
|
27
36
|
def __init__(self, lm_response: lf.Message, cause: Exception):
|
|
28
37
|
self._lm_response = lm_response
|
|
@@ -62,7 +71,53 @@ class MappingError(Exception): # pylint: disable=g-bad-exception-name
|
|
|
62
71
|
class MappingExample(lf.NaturalLanguageFormattable,
|
|
63
72
|
lf.Component,
|
|
64
73
|
pg.views.HtmlTreeView.Extension):
|
|
65
|
-
"""
|
|
74
|
+
"""Represents an example for a structured mapping task.
|
|
75
|
+
|
|
76
|
+
A `MappingExample` defines a single instance of a mapping between an input
|
|
77
|
+
value and an output value, optionally guided by a schema and/or a natural
|
|
78
|
+
language context. It is primarily used to provide few-shot examples to
|
|
79
|
+
structured mapping operations (e.g., `lf.query`, `lf.complete`,
|
|
80
|
+
and `lf.describe`), helping to guide the LLM in performing the desired mapping
|
|
81
|
+
task. If `output` is not provided, the example represents a request to perform
|
|
82
|
+
mapping on the `input`.
|
|
83
|
+
|
|
84
|
+
**Key Attributes:**
|
|
85
|
+
|
|
86
|
+
* `input`: The source value for the mapping (e.g., text, an object).
|
|
87
|
+
* `output`: The target value for the mapping (e.g., a structured object,
|
|
88
|
+
text). If not provided, this example represents a request to perform
|
|
89
|
+
the mapping.
|
|
90
|
+
* `schema`: An optional `lf.structured.Schema` that defines or constrains
|
|
91
|
+
the structure of the `output`. If provided, the LLM will be instructed
|
|
92
|
+
to produce an output conforming to this schema.
|
|
93
|
+
* `context`: Optional natural language context that provides additional
|
|
94
|
+
information relevant to the mapping task.
|
|
95
|
+
* `metadata`: Optional dictionary for additional metadata.
|
|
96
|
+
|
|
97
|
+
**Example:**
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import langfun as lf
|
|
101
|
+
import pyglove as pg
|
|
102
|
+
|
|
103
|
+
# Example for translating English to French
|
|
104
|
+
lf.MappingExample(
|
|
105
|
+
input="Hello",
|
|
106
|
+
output="Bonjour"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Example for extracting structured data
|
|
110
|
+
class Flight(pg.Object):
|
|
111
|
+
airline: str
|
|
112
|
+
flight_number: str
|
|
113
|
+
|
|
114
|
+
lf.MappingExample(
|
|
115
|
+
input="I want to book flight AA123.",
|
|
116
|
+
output=Flight(airline="AA", flight_number="123"),
|
|
117
|
+
schema=Flight
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
"""
|
|
66
121
|
|
|
67
122
|
input: pg.typing.Annotated[
|
|
68
123
|
pg.typing.Any(transform=schema_lib.mark_missing),
|
|
@@ -84,7 +139,7 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
84
139
|
# Automatic conversion from annotation to schema.
|
|
85
140
|
schema_lib.schema_spec(noneable=True),
|
|
86
141
|
(
|
|
87
|
-
'A `lf.structured.Schema` object that constrains target value '
|
|
142
|
+
'A `lf.structured.Schema` object that constrains target value. '
|
|
88
143
|
'If None, the target is expected to be a natural language-based '
|
|
89
144
|
'response returned from LMs.'
|
|
90
145
|
),
|
|
@@ -99,18 +154,16 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
99
154
|
dict[str, Any],
|
|
100
155
|
(
|
|
101
156
|
'The metadata associated with the mapping example, '
|
|
102
|
-
'which
|
|
157
|
+
'which could carry structured data, such as tool function input. '
|
|
103
158
|
'It is a `pg.Dict` object whose keys can be accessed by attributes.'
|
|
104
159
|
),
|
|
105
160
|
] = pg.Dict()
|
|
106
161
|
|
|
107
|
-
def schema_repr(
|
|
108
|
-
self, protocol: schema_lib.SchemaProtocol = 'python', **kwargs
|
|
109
|
-
) -> str:
|
|
162
|
+
def schema_repr(self, protocol: str = 'python', **kwargs) -> str:
|
|
110
163
|
"""Returns the string representation of schema based on protocol."""
|
|
111
164
|
if self.schema is None:
|
|
112
165
|
return ''
|
|
113
|
-
return self.schema
|
|
166
|
+
return schema_lib.schema_repr(self.schema, protocol=protocol, **kwargs)
|
|
114
167
|
|
|
115
168
|
@property
|
|
116
169
|
def has_output(self) -> bool:
|
|
@@ -121,12 +174,14 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
121
174
|
def value_repr(
|
|
122
175
|
cls,
|
|
123
176
|
value: Any,
|
|
124
|
-
protocol:
|
|
177
|
+
protocol: str = 'python',
|
|
125
178
|
use_modality_ref: bool = False,
|
|
126
179
|
**kwargs
|
|
127
180
|
) -> str:
|
|
128
181
|
if isinstance(value, str):
|
|
129
182
|
return value
|
|
183
|
+
if isinstance(value, lf.Message):
|
|
184
|
+
return str(value)
|
|
130
185
|
if isinstance(value, lf.Modality):
|
|
131
186
|
with lf.modality.format_modality_as_ref():
|
|
132
187
|
return str(value)
|
|
@@ -134,11 +189,11 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
134
189
|
# Placehold modalities if they are present.
|
|
135
190
|
if use_modality_ref and pg.contains(value, type=lf.Modality):
|
|
136
191
|
value = lf.ModalityRef.placehold(value)
|
|
137
|
-
return schema_lib.value_repr(
|
|
192
|
+
return schema_lib.value_repr(value, protocol=protocol, **kwargs)
|
|
138
193
|
|
|
139
194
|
def input_repr(
|
|
140
195
|
self,
|
|
141
|
-
protocol:
|
|
196
|
+
protocol: str = 'python',
|
|
142
197
|
compact: bool = False,
|
|
143
198
|
verbose: bool = True,
|
|
144
199
|
**kwargs
|
|
@@ -150,7 +205,7 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
150
205
|
|
|
151
206
|
def output_repr(
|
|
152
207
|
self,
|
|
153
|
-
protocol:
|
|
208
|
+
protocol: str = 'python',
|
|
154
209
|
compact: bool = False,
|
|
155
210
|
verbose: bool = True,
|
|
156
211
|
**kwargs
|
|
@@ -192,9 +247,7 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
192
247
|
|
|
193
248
|
def render_value(view, *, value, **kwargs):
|
|
194
249
|
if isinstance(value, lf.Template):
|
|
195
|
-
|
|
196
|
-
# the input.
|
|
197
|
-
value = value.clone().render()
|
|
250
|
+
value = value.render()
|
|
198
251
|
if value is None:
|
|
199
252
|
return None
|
|
200
253
|
return view.render(value, **kwargs)
|
|
@@ -242,7 +295,7 @@ class MappingExample(lf.NaturalLanguageFormattable,
|
|
|
242
295
|
|
|
243
296
|
|
|
244
297
|
class Mapping(lf.LangFunc):
|
|
245
|
-
"""Base class for mapping.
|
|
298
|
+
"""Base class for LLM-based mapping operations.
|
|
246
299
|
|
|
247
300
|
{{ preamble }}
|
|
248
301
|
|
|
@@ -263,19 +316,19 @@ class Mapping(lf.LangFunc):
|
|
|
263
316
|
pg.Symbolic,
|
|
264
317
|
(
|
|
265
318
|
'The mapping input. It could be `lf.Message` (a pg.Symbolic '
|
|
266
|
-
'subclass) as natural language input, or other symbolic
|
|
319
|
+
'subclass) as natural language input, or other symbolic objects '
|
|
267
320
|
'as structured input.'
|
|
268
321
|
),
|
|
269
322
|
]
|
|
270
323
|
|
|
271
324
|
context: Annotated[
|
|
272
|
-
str | None, 'The mapping context
|
|
325
|
+
str | None, 'The mapping context as a natural language string.'
|
|
273
326
|
] = None
|
|
274
327
|
|
|
275
328
|
schema: pg.typing.Annotated[
|
|
276
329
|
# Automatic conversion from annotation to schema.
|
|
277
330
|
schema_lib.schema_spec(noneable=True),
|
|
278
|
-
'A `lf.structured.Schema` object that constrains mapping output
|
|
331
|
+
'A `lf.structured.Schema` object that constrains mapping output.',
|
|
279
332
|
] = None
|
|
280
333
|
|
|
281
334
|
permission: Annotated[
|
|
@@ -286,12 +339,8 @@ class Mapping(lf.LangFunc):
|
|
|
286
339
|
@property
|
|
287
340
|
def mapping_request(self) -> MappingExample:
|
|
288
341
|
"""Returns a MappingExample as the mapping request."""
|
|
289
|
-
if isinstance(self.input, lf.Message):
|
|
290
|
-
input_value = self.input.text
|
|
291
|
-
else:
|
|
292
|
-
input_value = pg.Ref(self.input)
|
|
293
342
|
return MappingExample(
|
|
294
|
-
input=
|
|
343
|
+
input=pg.Ref(self.input),
|
|
295
344
|
schema=pg.Ref(self.schema),
|
|
296
345
|
context=self.context,
|
|
297
346
|
)
|
|
@@ -382,16 +431,16 @@ class Mapping(lf.LangFunc):
|
|
|
382
431
|
default: Annotated[
|
|
383
432
|
Any,
|
|
384
433
|
(
|
|
385
|
-
'The default value to use if
|
|
386
|
-
'
|
|
387
|
-
'
|
|
434
|
+
'The default value to use if parsing fails (after autofix). '
|
|
435
|
+
'If `lf.RAISE_IF_HAS_ERROR` is used (default), an error will be '
|
|
436
|
+
'raised instead.'
|
|
388
437
|
),
|
|
389
438
|
] = lf.RAISE_IF_HAS_ERROR
|
|
390
439
|
|
|
391
440
|
response_postprocess: Annotated[
|
|
392
441
|
Callable[[str], str] | None,
|
|
393
442
|
(
|
|
394
|
-
'A callable object that post
|
|
443
|
+
'A callable object that post-processes the raw LLM response before '
|
|
395
444
|
'parsing it into the output Python object.'
|
|
396
445
|
)
|
|
397
446
|
] = None
|
|
@@ -402,11 +451,6 @@ class Mapping(lf.LangFunc):
|
|
|
402
451
|
|
|
403
452
|
def transform_input(self, lm_input: lf.Message) -> lf.Message:
|
|
404
453
|
# Find modalities to fill the input message.
|
|
405
|
-
lm_input.metadata.update(
|
|
406
|
-
examples=pg.Ref(self.examples),
|
|
407
|
-
input=pg.Ref(self.input),
|
|
408
|
-
schema=pg.Ref(self.schema) if self.schema is not None else None,
|
|
409
|
-
)
|
|
410
454
|
if isinstance(self.input, lf.Message):
|
|
411
455
|
lm_input.source = self.input
|
|
412
456
|
return lm_input
|
|
@@ -429,7 +473,7 @@ class Mapping(lf.LangFunc):
|
|
|
429
473
|
return lm_output
|
|
430
474
|
|
|
431
475
|
def parse_result(self, lm_output: lf.Message) -> Any:
|
|
432
|
-
"""
|
|
476
|
+
"""Parses result from LLM response."""
|
|
433
477
|
schema = self.mapping_request.schema
|
|
434
478
|
if schema is None:
|
|
435
479
|
return None
|
|
@@ -443,7 +487,7 @@ class Mapping(lf.LangFunc):
|
|
|
443
487
|
response_text = '\n'.join(
|
|
444
488
|
tc.text for tc in lm_output.metadata['tool_calls']
|
|
445
489
|
)
|
|
446
|
-
return schema.
|
|
490
|
+
return schema.parse_value(
|
|
447
491
|
response_text,
|
|
448
492
|
protocol=self.protocol,
|
|
449
493
|
additional_context=self.globals(),
|
|
@@ -453,7 +497,7 @@ class Mapping(lf.LangFunc):
|
|
|
453
497
|
)
|
|
454
498
|
|
|
455
499
|
def postprocess_response(self, response: lf.Message) -> lf.Message:
|
|
456
|
-
"""Post
|
|
500
|
+
"""Post-processes LLM response."""
|
|
457
501
|
if self.response_postprocess is not None:
|
|
458
502
|
postprocessed_text = self.response_postprocess(response.text)
|
|
459
503
|
if postprocessed_text != response.text:
|
|
@@ -461,7 +505,7 @@ class Mapping(lf.LangFunc):
|
|
|
461
505
|
return response
|
|
462
506
|
|
|
463
507
|
def postprocess_result(self, result: Any) -> Any:
|
|
464
|
-
"""Post
|
|
508
|
+
"""Post-processes structured output."""
|
|
465
509
|
return result
|
|
466
510
|
|
|
467
511
|
def globals(self) -> dict[str, Any]:
|