langfun 0.0.2.dev20240330__py3-none-any.whl → 0.1.2.dev202501140804__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 +22 -2
- langfun/core/__init__.py +17 -5
- langfun/core/agentic/__init__.py +30 -0
- langfun/core/agentic/action.py +854 -0
- langfun/core/agentic/action_eval.py +150 -0
- langfun/core/agentic/action_eval_test.py +109 -0
- langfun/core/agentic/action_test.py +136 -0
- langfun/core/coding/python/__init__.py +5 -11
- langfun/core/coding/python/correction.py +37 -28
- langfun/core/coding/python/correction_test.py +29 -3
- langfun/core/coding/python/execution.py +40 -216
- langfun/core/coding/python/execution_test.py +29 -89
- langfun/core/coding/python/generation.py +21 -11
- langfun/core/coding/python/generation_test.py +2 -2
- langfun/core/coding/python/parsing.py +108 -193
- langfun/core/coding/python/parsing_test.py +2 -105
- langfun/core/component.py +69 -2
- langfun/core/component_test.py +54 -0
- langfun/core/concurrent.py +414 -117
- langfun/core/concurrent_test.py +111 -24
- langfun/core/console.py +18 -5
- langfun/core/console_test.py +17 -0
- langfun/core/eval/__init__.py +17 -0
- langfun/core/eval/base.py +767 -140
- langfun/core/eval/base_test.py +238 -53
- langfun/core/eval/matching.py +80 -76
- langfun/core/eval/matching_test.py +19 -9
- langfun/core/eval/patching.py +130 -0
- langfun/core/eval/patching_test.py +170 -0
- langfun/core/eval/scoring.py +37 -28
- langfun/core/eval/scoring_test.py +21 -3
- langfun/core/eval/v2/__init__.py +42 -0
- langfun/core/eval/v2/checkpointing.py +380 -0
- langfun/core/eval/v2/checkpointing_test.py +228 -0
- langfun/core/eval/v2/eval_test_helper.py +136 -0
- langfun/core/eval/v2/evaluation.py +725 -0
- langfun/core/eval/v2/evaluation_test.py +180 -0
- langfun/core/eval/v2/example.py +305 -0
- langfun/core/eval/v2/example_test.py +128 -0
- langfun/core/eval/v2/experiment.py +1048 -0
- langfun/core/eval/v2/experiment_test.py +433 -0
- langfun/core/eval/v2/metric_values.py +156 -0
- langfun/core/eval/v2/metric_values_test.py +80 -0
- langfun/core/eval/v2/metrics.py +357 -0
- langfun/core/eval/v2/metrics_test.py +203 -0
- langfun/core/eval/v2/progress.py +348 -0
- langfun/core/eval/v2/progress_test.py +82 -0
- langfun/core/eval/v2/progress_tracking.py +210 -0
- langfun/core/eval/v2/progress_tracking_test.py +66 -0
- langfun/core/eval/v2/reporting.py +270 -0
- langfun/core/eval/v2/reporting_test.py +158 -0
- langfun/core/eval/v2/runners.py +488 -0
- langfun/core/eval/v2/runners_test.py +334 -0
- langfun/core/langfunc.py +3 -21
- langfun/core/langfunc_test.py +26 -8
- langfun/core/language_model.py +686 -48
- langfun/core/language_model_test.py +681 -44
- langfun/core/llms/__init__.py +100 -12
- langfun/core/llms/anthropic.py +488 -0
- langfun/core/llms/anthropic_test.py +235 -0
- langfun/core/llms/cache/base.py +21 -2
- langfun/core/llms/cache/in_memory.py +13 -0
- langfun/core/llms/cache/in_memory_test.py +88 -28
- langfun/core/llms/compositional.py +101 -0
- langfun/core/llms/compositional_test.py +73 -0
- langfun/core/llms/deepseek.py +117 -0
- langfun/core/llms/deepseek_test.py +61 -0
- langfun/core/llms/fake.py +39 -26
- langfun/core/llms/fake_test.py +136 -11
- langfun/core/llms/gemini.py +507 -0
- langfun/core/llms/gemini_test.py +195 -0
- langfun/core/llms/google_genai.py +62 -218
- langfun/core/llms/google_genai_test.py +9 -197
- langfun/core/llms/groq.py +276 -0
- langfun/core/llms/groq_test.py +64 -0
- langfun/core/llms/llama_cpp.py +15 -40
- langfun/core/llms/llama_cpp_test.py +4 -30
- langfun/core/llms/openai.py +436 -226
- langfun/core/llms/openai_compatible.py +179 -0
- langfun/core/llms/openai_compatible_test.py +495 -0
- langfun/core/llms/openai_test.py +35 -174
- langfun/core/llms/rest.py +113 -0
- langfun/core/llms/rest_test.py +111 -0
- langfun/core/llms/vertexai.py +192 -0
- langfun/core/llms/vertexai_test.py +52 -0
- langfun/core/logging.py +284 -0
- langfun/core/logging_test.py +125 -0
- langfun/core/message.py +319 -9
- langfun/core/message_test.py +190 -13
- langfun/core/modalities/__init__.py +6 -2
- langfun/core/modalities/audio.py +30 -0
- langfun/core/modalities/audio_test.py +63 -0
- langfun/core/modalities/image.py +39 -20
- langfun/core/modalities/image_test.py +52 -9
- langfun/core/modalities/mime.py +206 -29
- langfun/core/modalities/mime_test.py +90 -9
- langfun/core/modalities/ms_office.py +117 -0
- langfun/core/modalities/ms_office_test.py +389 -0
- langfun/core/modalities/pdf.py +22 -0
- langfun/core/modalities/pdf_test.py +57 -0
- langfun/core/modalities/video.py +9 -23
- langfun/core/modalities/video_test.py +3 -3
- langfun/core/modality.py +26 -3
- langfun/core/modality_test.py +2 -2
- langfun/core/sampling.py +11 -11
- langfun/core/structured/__init__.py +15 -16
- langfun/core/structured/completion.py +32 -5
- langfun/core/structured/completion_test.py +9 -8
- langfun/core/structured/description.py +2 -2
- langfun/core/structured/description_test.py +3 -3
- langfun/core/structured/function_generation.py +278 -0
- langfun/core/structured/function_generation_test.py +399 -0
- langfun/core/structured/mapping.py +150 -46
- langfun/core/structured/mapping_test.py +105 -0
- langfun/core/structured/parsing.py +33 -21
- langfun/core/structured/parsing_test.py +71 -22
- langfun/core/structured/querying.py +746 -0
- langfun/core/structured/{prompting_test.py → querying_test.py} +545 -60
- langfun/core/structured/schema.py +208 -99
- langfun/core/structured/schema_generation.py +1 -1
- langfun/core/structured/schema_generation_test.py +2 -2
- langfun/core/structured/schema_test.py +133 -34
- langfun/core/structured/scoring.py +125 -19
- langfun/core/structured/scoring_test.py +30 -0
- langfun/core/structured/tokenization.py +64 -0
- langfun/core/structured/tokenization_test.py +48 -0
- langfun/core/template.py +240 -11
- langfun/core/template_test.py +146 -1
- langfun/core/templates/conversation.py +9 -0
- langfun/core/templates/conversation_test.py +4 -3
- langfun/core/templates/selfplay_test.py +14 -2
- langfun-0.1.2.dev202501140804.dist-info/METADATA +225 -0
- langfun-0.1.2.dev202501140804.dist-info/RECORD +153 -0
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/WHEEL +1 -1
- langfun/core/coding/python/errors.py +0 -108
- langfun/core/coding/python/errors_test.py +0 -99
- langfun/core/coding/python/permissions.py +0 -90
- langfun/core/coding/python/permissions_test.py +0 -86
- langfun/core/structured/prompting.py +0 -217
- langfun/core/text_formatting.py +0 -162
- langfun/core/text_formatting_test.py +0 -47
- langfun-0.0.2.dev20240330.dist-info/METADATA +0 -99
- langfun-0.0.2.dev20240330.dist-info/RECORD +0 -102
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/LICENSE +0 -0
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/top_level.txt +0 -0
@@ -1,90 +0,0 @@
|
|
1
|
-
# Copyright 2023 The Langfun Authors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
"""Python code permissions."""
|
15
|
-
|
16
|
-
import contextlib
|
17
|
-
import enum
|
18
|
-
import pyglove as pg
|
19
|
-
|
20
|
-
|
21
|
-
class CodePermission(enum.Flag):
|
22
|
-
"""Permissions for code execution."""
|
23
|
-
|
24
|
-
# Allows basic Python code: Creating objects, assignment, operations.
|
25
|
-
BASIC = enum.auto()
|
26
|
-
|
27
|
-
# Allows conditions.
|
28
|
-
CONDITION = enum.auto()
|
29
|
-
|
30
|
-
# Allows loops.
|
31
|
-
LOOP = enum.auto()
|
32
|
-
|
33
|
-
# Allows exception.
|
34
|
-
EXCEPTION = enum.auto()
|
35
|
-
|
36
|
-
# Allows class definitions.
|
37
|
-
CLASS_DEFINITION = enum.auto()
|
38
|
-
|
39
|
-
# Allows function definitions.
|
40
|
-
FUNCTION_DEFINITION = enum.auto()
|
41
|
-
|
42
|
-
# Allows import.
|
43
|
-
IMPORT = enum.auto()
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
@property
|
47
|
-
def ALL(cls) -> 'CodePermission': # pylint: disable=invalid-name
|
48
|
-
"""Returns all permissions."""
|
49
|
-
return (
|
50
|
-
CodePermission.BASIC | CodePermission.CONDITION | CodePermission.LOOP |
|
51
|
-
CodePermission.EXCEPTION | CodePermission.CLASS_DEFINITION |
|
52
|
-
CodePermission.FUNCTION_DEFINITION | CodePermission.IMPORT)
|
53
|
-
|
54
|
-
|
55
|
-
_TLS_CODE_RUN_PERMISSION = '__code_run_permission__'
|
56
|
-
|
57
|
-
|
58
|
-
@contextlib.contextmanager
|
59
|
-
def permission(perm: CodePermission):
|
60
|
-
"""Context manager for controling the permission for code execution.
|
61
|
-
|
62
|
-
When the `permission` context manager is nested, the outtermost permission
|
63
|
-
will be used. This design allows users to control permission at the top level.
|
64
|
-
|
65
|
-
Args:
|
66
|
-
perm: Code execution permission.
|
67
|
-
|
68
|
-
Yields:
|
69
|
-
Actual permission applied.
|
70
|
-
"""
|
71
|
-
|
72
|
-
outter_perm = pg.object_utils.thread_local_get(_TLS_CODE_RUN_PERMISSION, None)
|
73
|
-
|
74
|
-
# Use the top-level permission as the actual permission
|
75
|
-
if outter_perm is not None:
|
76
|
-
perm = outter_perm
|
77
|
-
|
78
|
-
pg.object_utils.thread_local_set(_TLS_CODE_RUN_PERMISSION, perm)
|
79
|
-
|
80
|
-
try:
|
81
|
-
yield perm
|
82
|
-
finally:
|
83
|
-
if outter_perm is None:
|
84
|
-
pg.object_utils.thread_local_del(_TLS_CODE_RUN_PERMISSION)
|
85
|
-
|
86
|
-
|
87
|
-
def get_permission() -> CodePermission:
|
88
|
-
"""Gets the current permission for code execution."""
|
89
|
-
return pg.object_utils.thread_local_get(
|
90
|
-
_TLS_CODE_RUN_PERMISSION, CodePermission.ALL)
|
@@ -1,86 +0,0 @@
|
|
1
|
-
# Copyright 2023 The Langfun Authors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
"""Tests for Python code permissions."""
|
15
|
-
|
16
|
-
import unittest
|
17
|
-
from langfun.core.coding.python import permissions
|
18
|
-
|
19
|
-
|
20
|
-
class CodePermissionTest(unittest.TestCase):
|
21
|
-
|
22
|
-
def assert_set(
|
23
|
-
self,
|
24
|
-
permission: permissions.CodePermission,
|
25
|
-
flag: permissions.CodePermission,
|
26
|
-
):
|
27
|
-
self.assertEqual(permission & flag, flag)
|
28
|
-
|
29
|
-
def assert_not_set(
|
30
|
-
self,
|
31
|
-
permission: permissions.CodePermission,
|
32
|
-
flag: permissions.CodePermission,
|
33
|
-
):
|
34
|
-
self.assertFalse(permission & flag)
|
35
|
-
|
36
|
-
def test_all(self):
|
37
|
-
self.assert_set(
|
38
|
-
permissions.CodePermission.ALL, permissions.CodePermission.BASIC
|
39
|
-
)
|
40
|
-
self.assert_set(
|
41
|
-
permissions.CodePermission.ALL, permissions.CodePermission.CONDITION
|
42
|
-
)
|
43
|
-
self.assert_set(
|
44
|
-
permissions.CodePermission.ALL, permissions.CodePermission.LOOP
|
45
|
-
)
|
46
|
-
self.assert_set(
|
47
|
-
permissions.CodePermission.ALL, permissions.CodePermission.EXCEPTION
|
48
|
-
)
|
49
|
-
self.assert_set(
|
50
|
-
permissions.CodePermission.ALL,
|
51
|
-
permissions.CodePermission.CLASS_DEFINITION,
|
52
|
-
)
|
53
|
-
self.assert_set(
|
54
|
-
permissions.CodePermission.ALL,
|
55
|
-
permissions.CodePermission.FUNCTION_DEFINITION,
|
56
|
-
)
|
57
|
-
self.assert_set(
|
58
|
-
permissions.CodePermission.ALL, permissions.CodePermission.IMPORT
|
59
|
-
)
|
60
|
-
|
61
|
-
def test_xor(self):
|
62
|
-
self.assert_not_set(
|
63
|
-
permissions.CodePermission.ALL ^ permissions.CodePermission.BASIC,
|
64
|
-
permissions.CodePermission.BASIC,
|
65
|
-
)
|
66
|
-
self.assert_set(
|
67
|
-
permissions.CodePermission.ALL ^ permissions.CodePermission.BASIC,
|
68
|
-
permissions.CodePermission.CONDITION,
|
69
|
-
)
|
70
|
-
|
71
|
-
def test_permission_control(self):
|
72
|
-
self.assertEqual(
|
73
|
-
permissions.get_permission(), permissions.CodePermission.ALL
|
74
|
-
)
|
75
|
-
with permissions.permission(permissions.CodePermission.BASIC):
|
76
|
-
self.assertEqual(
|
77
|
-
permissions.get_permission(), permissions.CodePermission.BASIC
|
78
|
-
)
|
79
|
-
with permissions.permission(permissions.CodePermission.ALL):
|
80
|
-
self.assertEqual(
|
81
|
-
permissions.get_permission(), permissions.CodePermission.BASIC
|
82
|
-
)
|
83
|
-
|
84
|
-
|
85
|
-
if __name__ == '__main__':
|
86
|
-
unittest.main()
|
@@ -1,217 +0,0 @@
|
|
1
|
-
# Copyright 2023 The Langfun Authors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
"""Symbolic query."""
|
15
|
-
|
16
|
-
from typing import Any, Type, Union
|
17
|
-
|
18
|
-
import langfun.core as lf
|
19
|
-
from langfun.core.structured import mapping
|
20
|
-
from langfun.core.structured import schema as schema_lib
|
21
|
-
import pyglove as pg
|
22
|
-
|
23
|
-
|
24
|
-
@lf.use_init_args(['schema', 'default', 'examples'])
|
25
|
-
class QueryStructure(mapping.Mapping):
|
26
|
-
"""Query an object out from a natural language text."""
|
27
|
-
|
28
|
-
context_title = 'CONTEXT'
|
29
|
-
input_title = 'INPUT_OBJECT'
|
30
|
-
|
31
|
-
# Mark schema as required.
|
32
|
-
schema: pg.typing.Annotated[
|
33
|
-
schema_lib.schema_spec(), 'Required schema for parsing.'
|
34
|
-
]
|
35
|
-
|
36
|
-
|
37
|
-
class QueryStructureJson(QueryStructure):
|
38
|
-
"""Query a structured value using JSON as the protocol."""
|
39
|
-
|
40
|
-
preamble = """
|
41
|
-
Please respond to the last {{ input_title }} with {{ output_title}} according to {{ schema_title }}:
|
42
|
-
|
43
|
-
INSTRUCTIONS:
|
44
|
-
1. If the schema has `_type`, carry it over to the JSON output.
|
45
|
-
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
46
|
-
|
47
|
-
{{ input_title }}:
|
48
|
-
1 + 1 =
|
49
|
-
|
50
|
-
{{ schema_title }}:
|
51
|
-
{"result": {"_type": "langfun.core.structured.prompting.Answer", "final_answer": int}}
|
52
|
-
|
53
|
-
{{ output_title}}:
|
54
|
-
{"result": {"_type": "langfun.core.structured.prompting.Answer", "final_answer": 2}}
|
55
|
-
"""
|
56
|
-
|
57
|
-
protocol = 'json'
|
58
|
-
schema_title = 'SCHEMA'
|
59
|
-
output_title = 'JSON'
|
60
|
-
|
61
|
-
|
62
|
-
class QueryStructurePython(QueryStructure):
|
63
|
-
"""Query a structured value using Python as the protocol."""
|
64
|
-
|
65
|
-
preamble = """
|
66
|
-
Please respond to the last {{ input_title }} with {{ output_title }} according to {{ schema_title }}.
|
67
|
-
|
68
|
-
{{ input_title }}:
|
69
|
-
1 + 1 =
|
70
|
-
|
71
|
-
{{ schema_title }}:
|
72
|
-
Answer
|
73
|
-
|
74
|
-
```python
|
75
|
-
class Answer:
|
76
|
-
final_answer: int
|
77
|
-
```
|
78
|
-
|
79
|
-
{{ output_title }}:
|
80
|
-
```python
|
81
|
-
Answer(final_answer=2)
|
82
|
-
```
|
83
|
-
"""
|
84
|
-
protocol = 'python'
|
85
|
-
schema_title = 'OUTPUT_TYPE'
|
86
|
-
output_title = 'OUTPUT_OBJECT'
|
87
|
-
|
88
|
-
|
89
|
-
def _query_structure_cls(
|
90
|
-
protocol: schema_lib.SchemaProtocol,
|
91
|
-
) -> Type[QueryStructure]:
|
92
|
-
if protocol == 'json':
|
93
|
-
return QueryStructureJson
|
94
|
-
elif protocol == 'python':
|
95
|
-
return QueryStructurePython
|
96
|
-
else:
|
97
|
-
raise ValueError(f'Unknown protocol: {protocol!r}.')
|
98
|
-
|
99
|
-
|
100
|
-
def query(
|
101
|
-
prompt: Union[str, pg.Symbolic],
|
102
|
-
schema: Union[
|
103
|
-
schema_lib.Schema, Type[Any], list[Type[Any]], dict[str, Any], None
|
104
|
-
] = None,
|
105
|
-
default: Any = lf.RAISE_IF_HAS_ERROR,
|
106
|
-
*,
|
107
|
-
lm: lf.LanguageModel | None = None,
|
108
|
-
examples: list[mapping.MappingExample] | None = None,
|
109
|
-
cache_seed: int | None = 0,
|
110
|
-
autofix: int = 0,
|
111
|
-
autofix_lm: lf.LanguageModel | None = None,
|
112
|
-
protocol: schema_lib.SchemaProtocol = 'python',
|
113
|
-
returns_message: bool = False,
|
114
|
-
skip_lm: bool = False,
|
115
|
-
**kwargs,
|
116
|
-
) -> Any:
|
117
|
-
"""Parse a natural langugage message based on schema.
|
118
|
-
|
119
|
-
Examples:
|
120
|
-
|
121
|
-
```
|
122
|
-
class FlightDuration:
|
123
|
-
hours: int
|
124
|
-
minutes: int
|
125
|
-
|
126
|
-
class Flight(pg.Object):
|
127
|
-
airline: str
|
128
|
-
flight_number: str
|
129
|
-
departure_airport_code: str
|
130
|
-
arrival_airport_code: str
|
131
|
-
departure_time: str
|
132
|
-
arrival_time: str
|
133
|
-
duration: FlightDuration
|
134
|
-
stops: int
|
135
|
-
price: float
|
136
|
-
|
137
|
-
prompt = '''
|
138
|
-
Information about flight UA2631.
|
139
|
-
'''
|
140
|
-
|
141
|
-
r = lf.query(prompt, Flight)
|
142
|
-
assert isinstance(r, Flight)
|
143
|
-
assert r.airline == 'United Airlines'
|
144
|
-
assert r.departure_airport_code == 'SFO'
|
145
|
-
assert r.duration.hour = 7
|
146
|
-
```
|
147
|
-
|
148
|
-
Args:
|
149
|
-
prompt: A str (may contain {{}} as template) as natural language input, or a
|
150
|
-
`pg.Symbolic` object as structured input as prompt to LLM.
|
151
|
-
schema: A type annotation as the schema for output object. If str (default),
|
152
|
-
the response will be a str in natural language.
|
153
|
-
default: The default value if parsing failed. If not specified, error will
|
154
|
-
be raised.
|
155
|
-
lm: The language model to use. If not specified, the language model from
|
156
|
-
`lf.context` context manager will be used.
|
157
|
-
examples: An optional list of fewshot examples for helping parsing. If None,
|
158
|
-
the default one-shot example will be added.
|
159
|
-
cache_seed: Seed for computing cache key. The cache key is determined by a
|
160
|
-
tuple of (lm, prompt, cache seed). If None, cache will be disabled for
|
161
|
-
the query even cache is configured by the LM.
|
162
|
-
autofix: Number of attempts to auto fix the generated code. If 0, autofix is
|
163
|
-
disabled. Auto-fix is not supported for 'json' protocol.
|
164
|
-
autofix_lm: The language model to use for autofix. If not specified, the
|
165
|
-
`autofix_lm` from `lf.context` context manager will be used. Otherwise it
|
166
|
-
will use `lm`.
|
167
|
-
protocol: The protocol for schema/value representation. Applicable values
|
168
|
-
are 'json' and 'python'. By default `python` will be used.
|
169
|
-
returns_message: If True, returns `lf.Message` as the output, instead of
|
170
|
-
returning the structured `message.result`.
|
171
|
-
skip_lm: If True, returns the rendered prompt as a UserMessage object.
|
172
|
-
otherwise return the LLM response based on the rendered prompt.
|
173
|
-
**kwargs: Keyword arguments passed to the
|
174
|
-
`lf.structured.NaturalLanguageToStructureed` transform.
|
175
|
-
|
176
|
-
Returns:
|
177
|
-
The result based on the schema.
|
178
|
-
"""
|
179
|
-
# Internal usage logging.
|
180
|
-
|
181
|
-
# When `lf.query` is used for symbolic completion, schema is automatically
|
182
|
-
# inferred when it is None.
|
183
|
-
if isinstance(prompt, pg.Symbolic) and prompt.sym_partial and schema is None:
|
184
|
-
schema = prompt.__class__
|
185
|
-
|
186
|
-
if schema in (None, str):
|
187
|
-
# Query with natural language output.
|
188
|
-
output = lf.LangFunc.from_value(prompt, **kwargs)(
|
189
|
-
lm=lm, cache_seed=cache_seed, skip_lm=skip_lm
|
190
|
-
)
|
191
|
-
return output if returns_message else output.text
|
192
|
-
|
193
|
-
# Query with structured output.
|
194
|
-
if isinstance(prompt, str):
|
195
|
-
prompt = lf.Template(prompt, **kwargs)
|
196
|
-
elif isinstance(prompt, lf.Template):
|
197
|
-
prompt = prompt.rebind(**kwargs)
|
198
|
-
|
199
|
-
if isinstance(prompt, lf.Template):
|
200
|
-
prompt = prompt.render(lm=lm)
|
201
|
-
else:
|
202
|
-
prompt = schema_lib.mark_missing(prompt)
|
203
|
-
|
204
|
-
output = _query_structure_cls(protocol)(
|
205
|
-
input=prompt,
|
206
|
-
schema=schema,
|
207
|
-
default=default,
|
208
|
-
examples=examples,
|
209
|
-
autofix=autofix if protocol == 'python' else 0,
|
210
|
-
**kwargs,
|
211
|
-
)(
|
212
|
-
lm=lm,
|
213
|
-
autofix_lm=autofix_lm or lm,
|
214
|
-
cache_seed=cache_seed,
|
215
|
-
skip_lm=skip_lm,
|
216
|
-
)
|
217
|
-
return output if returns_message else output.result
|
langfun/core/text_formatting.py
DELETED
@@ -1,162 +0,0 @@
|
|
1
|
-
# Copyright 2023 The Langfun Authors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
"""Utility library for LM input/output formatting."""
|
15
|
-
|
16
|
-
import io
|
17
|
-
import re
|
18
|
-
from typing import Any
|
19
|
-
import termcolor
|
20
|
-
|
21
|
-
|
22
|
-
# Regular expression for ANSI color characters.
|
23
|
-
_ANSI_COLOR_REGEX = re.compile(r'\x1b\[[0-9;]*m')
|
24
|
-
|
25
|
-
|
26
|
-
def decolored(text: str) -> str:
|
27
|
-
"""Return the de-colored string that may contains ANSI color characters."""
|
28
|
-
return re.sub(_ANSI_COLOR_REGEX, '', text)
|
29
|
-
|
30
|
-
|
31
|
-
def colored(
|
32
|
-
text: str,
|
33
|
-
color: str | None = None,
|
34
|
-
background: str | None = None,
|
35
|
-
styles: list[str] | None = None
|
36
|
-
) -> str:
|
37
|
-
"""Returns the colored text with ANSI color characters.
|
38
|
-
|
39
|
-
Args:
|
40
|
-
text: A string that may or may not already has ANSI color characters.
|
41
|
-
color: A string for text colors. Applicable values are:
|
42
|
-
'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'.
|
43
|
-
background: A string for background colors. Applicable values are:
|
44
|
-
'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'.
|
45
|
-
styles: A list of strings for applying styles on the text.
|
46
|
-
Applicable values are:
|
47
|
-
'bold', 'dark', 'underline', 'blink', 'reverse', 'concealed'.
|
48
|
-
|
49
|
-
Returns:
|
50
|
-
A string with ANSI color characters embracing the entire text.
|
51
|
-
"""
|
52
|
-
return termcolor.colored(
|
53
|
-
text,
|
54
|
-
color=color,
|
55
|
-
on_color=('on_' + background) if background else None,
|
56
|
-
attrs=styles)
|
57
|
-
|
58
|
-
|
59
|
-
def colored_template(
|
60
|
-
text: str,
|
61
|
-
expression_color: str | None = 'white',
|
62
|
-
expression_background: str | None = 'blue',
|
63
|
-
expression_styles: list[str] | None = None,
|
64
|
-
statement_color: str | None = 'red',
|
65
|
-
statement_background: str | None = None,
|
66
|
-
statement_styles: list[str] | None = None,
|
67
|
-
comment_color: str | None = 'green',
|
68
|
-
comment_background: str | None = None,
|
69
|
-
comment_styles: list[str] | None = None,
|
70
|
-
) -> str:
|
71
|
-
"""Returns colored (maybe) Jinja2 template string."""
|
72
|
-
text = color_text_blocks(
|
73
|
-
text, '{{', '}}',
|
74
|
-
color=expression_color,
|
75
|
-
background=expression_background,
|
76
|
-
styles=expression_styles)
|
77
|
-
|
78
|
-
text = color_text_blocks(
|
79
|
-
text, '{%', '%}',
|
80
|
-
color=statement_color,
|
81
|
-
background=statement_background,
|
82
|
-
styles=statement_styles)
|
83
|
-
|
84
|
-
text = color_text_blocks(
|
85
|
-
text, '{#', '#}',
|
86
|
-
color=comment_color,
|
87
|
-
background=comment_background,
|
88
|
-
styles=comment_styles)
|
89
|
-
|
90
|
-
return text
|
91
|
-
|
92
|
-
|
93
|
-
def color_text_blocks(
|
94
|
-
text: str,
|
95
|
-
block_start: str,
|
96
|
-
block_end: str,
|
97
|
-
color: str | None = None,
|
98
|
-
background: str | None = None,
|
99
|
-
styles: list[str] | None = None
|
100
|
-
) -> str:
|
101
|
-
"""Apply colors to text blocks.
|
102
|
-
|
103
|
-
Args:
|
104
|
-
text: A string that may or may not already has ANSI color characters.
|
105
|
-
block_start: A string that signals the start of a block. E.g. '{{'
|
106
|
-
block_end: A string that signals the end of a block. E.g. '}}'.
|
107
|
-
color: A string for text colors. Applicable values are:
|
108
|
-
'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'.
|
109
|
-
background: A string for background colors. Applicable values are:
|
110
|
-
'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'.
|
111
|
-
styles: A list of strings for applying styles on the text.
|
112
|
-
Applicable values are:
|
113
|
-
'bold', 'dark', 'underline', 'blink', 'reverse', 'concealed'.
|
114
|
-
|
115
|
-
Returns:
|
116
|
-
A string with ANSI color characters embracing the matched text blocks.
|
117
|
-
"""
|
118
|
-
if not color and not background and not styles:
|
119
|
-
return text
|
120
|
-
|
121
|
-
string_buffer = io.StringIO()
|
122
|
-
start_index = 0
|
123
|
-
end_index = 0
|
124
|
-
previous_color = None
|
125
|
-
|
126
|
-
def write_nonblock_text(text: str, previous_color: str | None):
|
127
|
-
if previous_color:
|
128
|
-
string_buffer.write(previous_color)
|
129
|
-
string_buffer.write(text)
|
130
|
-
|
131
|
-
while start_index < len(text):
|
132
|
-
start_index = text.find(block_start, end_index)
|
133
|
-
if start_index == -1:
|
134
|
-
write_nonblock_text(text[end_index:], previous_color)
|
135
|
-
break
|
136
|
-
|
137
|
-
# Deal with text since last block.
|
138
|
-
since_last_block = text[end_index:start_index]
|
139
|
-
write_nonblock_text(since_last_block, previous_color)
|
140
|
-
colors = re.findall(_ANSI_COLOR_REGEX, since_last_block)
|
141
|
-
if colors:
|
142
|
-
previous_color = colors[-1]
|
143
|
-
|
144
|
-
# Match block.
|
145
|
-
end_index = text.find(block_end, start_index + len(block_start))
|
146
|
-
if end_index == -1:
|
147
|
-
write_nonblock_text(text[start_index:], previous_color)
|
148
|
-
break
|
149
|
-
end_index += len(block_end)
|
150
|
-
|
151
|
-
# Write block text.
|
152
|
-
block = text[start_index:end_index]
|
153
|
-
colored_block = colored(
|
154
|
-
block, color=color, background=background, styles=styles)
|
155
|
-
string_buffer.write(colored_block)
|
156
|
-
return string_buffer.getvalue()
|
157
|
-
|
158
|
-
|
159
|
-
def colored_print(value: Any):
|
160
|
-
"""Prints text with color."""
|
161
|
-
print(colored_template(str(value)))
|
162
|
-
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# Copyright 2023 The Langfun Authors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
"""Tests for text formatting."""
|
15
|
-
|
16
|
-
import inspect
|
17
|
-
import unittest
|
18
|
-
from langfun.core import text_formatting
|
19
|
-
|
20
|
-
|
21
|
-
class TextFormattingTest(unittest.TestCase):
|
22
|
-
|
23
|
-
def test_colored_template(self):
|
24
|
-
original_text = inspect.cleandoc("""
|
25
|
-
Hi {{ foo }}
|
26
|
-
{# print x if x is present #}
|
27
|
-
{% if x %}
|
28
|
-
{{ x }}
|
29
|
-
{% endif %}
|
30
|
-
""")
|
31
|
-
|
32
|
-
colored_text = text_formatting.colored_template(
|
33
|
-
text_formatting.colored(original_text, color='blue')
|
34
|
-
)
|
35
|
-
self.assertEqual(
|
36
|
-
colored_text,
|
37
|
-
'\x1b[34mHi \x1b[44m\x1b[37m{{ foo }}\x1b[0m\x1b[34m\n'
|
38
|
-
'\x1b[32m{# print x if x is present #}\x1b[0m\x1b[34m\n'
|
39
|
-
'\x1b[31m{% if x %}\x1b[0m\x1b[34m\n'
|
40
|
-
'\x1b[44m\x1b[37m{{ x }}\x1b[0m\x1b[34m\n'
|
41
|
-
'\x1b[31m{% endif %}\x1b[0m\x1b[34m\x1b[0m'
|
42
|
-
)
|
43
|
-
self.assertEqual(text_formatting.decolored(colored_text), original_text)
|
44
|
-
|
45
|
-
|
46
|
-
if __name__ == '__main__':
|
47
|
-
unittest.main()
|
@@ -1,99 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: langfun
|
3
|
-
Version: 0.0.2.dev20240330
|
4
|
-
Summary: Langfun: Language as Functions.
|
5
|
-
Home-page: https://github.com/google/langfun
|
6
|
-
Author: Langfun Authors
|
7
|
-
Author-email: langfun-authors@google.com
|
8
|
-
License: Apache License 2.0
|
9
|
-
Keywords: llm generative-ai machine-learning
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
11
|
-
Classifier: Intended Audience :: Developers
|
12
|
-
Classifier: Intended Audience :: Education
|
13
|
-
Classifier: Intended Audience :: Science/Research
|
14
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
17
|
-
Classifier: Programming Language :: Python :: 3.11
|
18
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
19
|
-
Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces
|
20
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
21
|
-
Classifier: Topic :: Software Development :: Libraries
|
22
|
-
Description-Content-Type: text/markdown
|
23
|
-
License-File: LICENSE
|
24
|
-
Requires-Dist: google-generativeai >=0.3.2
|
25
|
-
Requires-Dist: jinja2 >=3.1.2
|
26
|
-
Requires-Dist: openai ==0.27.2
|
27
|
-
Requires-Dist: pyglove >=0.4.5.dev20240323
|
28
|
-
Requires-Dist: python-magic >=0.4.27
|
29
|
-
Requires-Dist: requests >=2.31.0
|
30
|
-
Requires-Dist: termcolor ==1.1.0
|
31
|
-
Requires-Dist: tqdm >=4.64.1
|
32
|
-
|
33
|
-
<div align="center">
|
34
|
-
<img src="https://raw.githubusercontent.com/google/langfun/main/docs/_static/logo.svg" width="520px" alt="logo"></img>
|
35
|
-
</div>
|
36
|
-
|
37
|
-
# Langfun
|
38
|
-
|
39
|
-
[](https://badge.fury.io/py/langfun)
|
40
|
-
[](https://codecov.io/gh/google/langfun)
|
41
|
-

|
42
|
-
|
43
|
-
[**Installation**](#install) | [**Getting started**](#hello-world)
|
44
|
-
|
45
|
-
## What is Langfun
|
46
|
-
|
47
|
-
Langfun is a Python library that aims to make language models (LM) fun
|
48
|
-
to work with. Its design enables a programming model that flows naturally,
|
49
|
-
resembling the human thought process. It emphasizes the reuse and combination of
|
50
|
-
language pieces to form prompts, thereby accelerating innovation. In contrast to
|
51
|
-
other LM frameworks, which feed program-generated data into the LM, langfun
|
52
|
-
takes a distinct approach: It starts with natural language, allowing for
|
53
|
-
seamless interactions between language and program logic, and concludes with
|
54
|
-
natural language and optional structured output. Consequently, langfun can
|
55
|
-
aptly be described as Language as functions, capturing the core of its
|
56
|
-
methodology.
|
57
|
-
|
58
|
-
## Install
|
59
|
-
|
60
|
-
```
|
61
|
-
pip install langfun
|
62
|
-
```
|
63
|
-
|
64
|
-
Or install nightly build with:
|
65
|
-
|
66
|
-
```
|
67
|
-
pip install langfun --pre
|
68
|
-
```
|
69
|
-
|
70
|
-
## Hello World
|
71
|
-
|
72
|
-
```python
|
73
|
-
import langfun as lf
|
74
|
-
|
75
|
-
class NumericAnswerExtractor(lf.LangFunc):
|
76
|
-
"""Numeric answer extractor.
|
77
|
-
|
78
|
-
Here is my question:
|
79
|
-
{{question}}
|
80
|
-
|
81
|
-
Here is the response:
|
82
|
-
{{question()}}
|
83
|
-
|
84
|
-
Can you help me extract a number from the response as the answer to my
|
85
|
-
question? Your response should only contain a number in numeric form.
|
86
|
-
If the answer is not a number or you cannot extract it, respond with UNKNOWN.
|
87
|
-
"""
|
88
|
-
output_transform = lf.transforms.Match('\d+').to_int()
|
89
|
-
|
90
|
-
l = NumericAnswerExtractor()
|
91
|
-
|
92
|
-
with lf.context(lm=lf.llms.Gpt35(debug=True)):
|
93
|
-
r = l(question=lf.LangFunc('What is result of {{x}} plus {{y}}?'),
|
94
|
-
x='one',
|
95
|
-
y='two')
|
96
|
-
print('Result:', r.result)
|
97
|
-
```
|
98
|
-
|
99
|
-
*Disclaimer: this is not an officially supported Google product.*
|