langfun 0.1.2.dev202511030805__py3-none-any.whl → 0.1.2.dev202511050805__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/agentic/action.py +76 -9
- langfun/core/agentic/action_eval.py +9 -2
- 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/console.py +1 -1
- langfun/core/data/conversion/anthropic.py +10 -3
- langfun/core/data/conversion/gemini.py +9 -2
- langfun/core/data/conversion/openai.py +17 -7
- langfun/core/eval/base.py +46 -42
- 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/checkpointing.py +30 -4
- langfun/core/eval/v2/evaluation.py +59 -13
- langfun/core/eval/v2/example.py +22 -11
- langfun/core/eval/v2/experiment.py +51 -8
- langfun/core/eval/v2/metric_values.py +23 -3
- langfun/core/eval/v2/metrics.py +33 -4
- langfun/core/eval/v2/progress.py +9 -1
- langfun/core/eval/v2/reporting.py +15 -1
- langfun/core/eval/v2/runners.py +27 -7
- langfun/core/langfunc.py +45 -130
- langfun/core/language_model.py +88 -10
- langfun/core/llms/anthropic.py +27 -2
- langfun/core/llms/azure_openai.py +29 -17
- langfun/core/llms/cache/base.py +22 -2
- langfun/core/llms/cache/in_memory.py +48 -7
- langfun/core/llms/compositional.py +25 -1
- langfun/core/llms/deepseek.py +29 -1
- langfun/core/llms/fake.py +32 -1
- langfun/core/llms/gemini.py +9 -1
- langfun/core/llms/google_genai.py +29 -1
- langfun/core/llms/groq.py +27 -2
- langfun/core/llms/llama_cpp.py +22 -3
- langfun/core/llms/openai.py +29 -1
- langfun/core/llms/openai_compatible.py +18 -6
- langfun/core/llms/rest.py +12 -1
- langfun/core/llms/vertexai.py +39 -6
- langfun/core/logging.py +1 -1
- langfun/core/mcp/client.py +77 -22
- langfun/core/mcp/session.py +90 -10
- langfun/core/mcp/tool.py +83 -23
- langfun/core/memory.py +1 -0
- langfun/core/message.py +75 -11
- langfun/core/message_test.py +9 -0
- langfun/core/modalities/audio.py +21 -1
- langfun/core/modalities/image.py +19 -1
- langfun/core/modalities/mime.py +54 -4
- langfun/core/modalities/pdf.py +19 -1
- langfun/core/modalities/video.py +21 -1
- langfun/core/modality.py +66 -5
- langfun/core/natural_language.py +1 -1
- langfun/core/sampling.py +4 -4
- langfun/core/structured/completion.py +32 -37
- langfun/core/structured/description.py +54 -50
- langfun/core/structured/function_generation.py +29 -12
- langfun/core/structured/mapping.py +70 -15
- langfun/core/structured/parsing.py +90 -74
- langfun/core/structured/parsing_test.py +0 -3
- langfun/core/structured/querying.py +201 -130
- langfun/core/structured/schema.py +70 -10
- langfun/core/structured/schema_generation.py +33 -14
- langfun/core/structured/scoring.py +45 -34
- langfun/core/structured/tokenization.py +24 -9
- langfun/core/subscription.py +2 -2
- langfun/core/template.py +139 -40
- langfun/core/template_test.py +40 -0
- {langfun-0.1.2.dev202511030805.dist-info → langfun-0.1.2.dev202511050805.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202511030805.dist-info → langfun-0.1.2.dev202511050805.dist-info}/RECORD +77 -77
- {langfun-0.1.2.dev202511030805.dist-info → langfun-0.1.2.dev202511050805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202511030805.dist-info → langfun-0.1.2.dev202511050805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202511030805.dist-info → langfun-0.1.2.dev202511050805.dist-info}/top_level.txt +0 -0
|
@@ -35,38 +35,50 @@ def score(
|
|
|
35
35
|
return_scoring_results: bool = False,
|
|
36
36
|
**kwargs,
|
|
37
37
|
) -> list[float] | list[lf.LMScoringResult]:
|
|
38
|
-
"""Scores
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
38
|
+
"""Scores completions based on a prompt using a language model.
|
|
39
|
+
|
|
40
|
+
`lf.score` computes the likelihood of each completion being generated given
|
|
41
|
+
a prompt, according to the specified language model. It can score text
|
|
42
|
+
completions or structured objects. If `schema` is provided, Langfun
|
|
43
|
+
formats the prompt and completions appropriately before scoring.
|
|
44
|
+
|
|
45
|
+
**Example 1: Score text completions**
|
|
46
|
+
```python
|
|
47
|
+
import langfun as lf
|
|
48
|
+
scores = lf.score(
|
|
49
|
+
'1 + 1 =',
|
|
50
|
+
['2', '3', '4'],
|
|
51
|
+
lm=lf.llms.Gemini25Flash())
|
|
52
|
+
print([f'{s:.3f}' for s in scores])
|
|
53
|
+
# Output: ['-0.001', '-2.345', '-3.456']
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Example 2: Score structured completions**
|
|
57
|
+
```python
|
|
58
|
+
import langfun as lf
|
|
59
|
+
import pyglove as pg
|
|
60
|
+
|
|
61
|
+
class Answer(pg.Object):
|
|
62
|
+
result: int
|
|
63
|
+
|
|
64
|
+
scores = lf.score(
|
|
65
|
+
'1 + 1 =',
|
|
66
|
+
[Answer(result=2), Answer(result=3), Answer(result=4)],
|
|
67
|
+
lm=lf.llms.Gemini25Flash())
|
|
68
|
+
print([f'{s:.3f}' for s in scores])
|
|
69
|
+
# Output: ['-0.001', '-2.345', '-3.456']
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Example 3: Score multiple prompt/completion pairs**
|
|
73
|
+
```python
|
|
74
|
+
import langfun as lf
|
|
75
|
+
scores = lf.score(
|
|
76
|
+
['1 + 1 =', '2 + 2 ='],
|
|
77
|
+
['2', '4'],
|
|
78
|
+
lm=lf.llms.Gemini25Flash())
|
|
79
|
+
print([f'{s:.3f}' for s in scores])
|
|
80
|
+
# Output: ['-0.001', '-0.002']
|
|
81
|
+
```
|
|
70
82
|
|
|
71
83
|
Args:
|
|
72
84
|
prompt: The prompt(s) based on which each completion will be scored.
|
|
@@ -74,8 +86,7 @@ def score(
|
|
|
74
86
|
schema: The schema as the output type. If None, it will be inferred from
|
|
75
87
|
the completions.
|
|
76
88
|
lm: The language model used for scoring.
|
|
77
|
-
examples:
|
|
78
|
-
completions.
|
|
89
|
+
examples: Few-shot examples used to construct the prompt for scoring.
|
|
79
90
|
protocol: The protocol for formulating the prompt based on objects.
|
|
80
91
|
return_scoring_results: If True, returns a list of `lf.LMScoringResult`,
|
|
81
92
|
otherwise returns a list of floats as the scores of each completion.
|
|
@@ -23,7 +23,7 @@ import pyglove as pg
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def tokenize(
|
|
26
|
-
prompt: Union[str, pg.Symbolic
|
|
26
|
+
prompt: Union[str, pg.Symbolic, list[str | pg.Symbolic]],
|
|
27
27
|
schema: Union[
|
|
28
28
|
schema_lib.Schema, Type[Any], list[Type[Any]], dict[str, Any], None
|
|
29
29
|
] = None,
|
|
@@ -33,20 +33,35 @@ def tokenize(
|
|
|
33
33
|
protocol: schema_lib.SchemaProtocol = 'python',
|
|
34
34
|
**kwargs,
|
|
35
35
|
) -> list[tuple[str | bytes, int]]:
|
|
36
|
-
"""
|
|
36
|
+
"""Renders a prompt and tokenizes it using a language model.
|
|
37
|
+
|
|
38
|
+
`lf.tokenize` first renders a prompt based on the provided `prompt`,
|
|
39
|
+
`schema`, and `examples`, similar to `lf.query`, and then uses the
|
|
40
|
+
specified language model (`lm`) to tokenize the resulting message.
|
|
41
|
+
This is useful for understanding how a prompt is seen by the model or
|
|
42
|
+
for estimating token counts before sending requests.
|
|
43
|
+
|
|
44
|
+
**Example:**
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import langfun as lf
|
|
48
|
+
tokens = lf.tokenize('Hello world!', lm=lf.llms.Gpt4())
|
|
49
|
+
print(tokens)
|
|
50
|
+
# Output might look like: [('Hello', 15339), (' world', 1917), ('!', 0)]
|
|
51
|
+
```
|
|
37
52
|
|
|
38
53
|
Args:
|
|
39
|
-
prompt: The prompt
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
prompt: The prompt to render and tokenize. Can be a string, `pg.Symbolic`,
|
|
55
|
+
or `lf.Template`.
|
|
56
|
+
schema: The schema for formatting the prompt, if `prompt` is structured or
|
|
57
|
+
if schema-based formatting is needed.
|
|
58
|
+
lm: The language model to use for tokenization.
|
|
59
|
+
examples: Few-shot examples to include in the rendered prompt.
|
|
45
60
|
protocol: The protocol for formulating the prompt based on objects.
|
|
46
61
|
**kwargs: Keyword arguments that are referred by the prompt.
|
|
47
62
|
|
|
48
63
|
Returns:
|
|
49
|
-
A list of (
|
|
64
|
+
A list of (token_str, token_id) tuples representing the tokenized prompt.
|
|
50
65
|
"""
|
|
51
66
|
input_message = querying.query_prompt(
|
|
52
67
|
prompt,
|
langfun/core/subscription.py
CHANGED
|
@@ -35,7 +35,7 @@ EventType = TypeVar('EventType')
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class EventHandler(Generic[EventType], metaclass=abc.ABCMeta):
|
|
38
|
-
"""Interface for event
|
|
38
|
+
"""Interface for event handler."""
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
41
|
@functools.cache
|
|
@@ -51,7 +51,7 @@ class EventHandler(Generic[EventType], metaclass=abc.ABCMeta):
|
|
|
51
51
|
|
|
52
52
|
@classmethod
|
|
53
53
|
def accepts(cls, event: Event[Any]) -> bool:
|
|
54
|
-
"""Returns True if current event handler class can
|
|
54
|
+
"""Returns True if current event handler class can accept an event."""
|
|
55
55
|
return isinstance(event, cls.event_type())
|
|
56
56
|
|
|
57
57
|
@abc.abstractmethod
|
langfun/core/template.py
CHANGED
|
@@ -49,20 +49,94 @@ class Template(
|
|
|
49
49
|
pg.typing.CustomTyping,
|
|
50
50
|
pg.views.HtmlTreeView.Extension
|
|
51
51
|
):
|
|
52
|
-
"""Langfun string template.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
r"""Langfun string template.
|
|
53
|
+
|
|
54
|
+
`lf.Template` provides a flexible way to define and render prompts using
|
|
55
|
+
the Jinja2 templating engine. It supports variable substitution, composition
|
|
56
|
+
of templates, multi-modal content, and easy reuse through subclassing.
|
|
57
|
+
|
|
58
|
+
**Key Features:**
|
|
59
|
+
|
|
60
|
+
* **Jinja2 Syntax**: Leverages the full power of Jinja2 for
|
|
61
|
+
expressions, loops, and conditional logic within templates.
|
|
62
|
+
(See [Jinja2 documentation](
|
|
63
|
+
https://jinja.palletsprojects.com/en/3.1.x/templates/))
|
|
64
|
+
* **Composition**: Templates can include other `lf.Template`, `lf.Message`
|
|
65
|
+
and message-convertible objects, allowing complex prompts to be built from
|
|
66
|
+
reusable components.
|
|
67
|
+
* **Multi-Modal Support**: Seamlessly integrates multi-modal objects
|
|
68
|
+
(like `lf.Image`, `lf.Audio`) into templates.
|
|
69
|
+
* **Subclassing**: Define reusable prompt structures by subclassing
|
|
70
|
+
`lf.Template` and setting defaults.
|
|
71
|
+
* **Context Awareness**: Variables can be resolved from `render()`
|
|
72
|
+
arguments, template attributes, or the surrounding `lf.context`.
|
|
73
|
+
|
|
74
|
+
**1. Basic Usage:**
|
|
75
|
+
Variables are indicated by `{{variable_name}}`.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
t = lf.Template("Hello, {{name}}!", name="World")
|
|
79
|
+
print(t.render())
|
|
80
|
+
# Output: Hello, World!
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**2. Providing Variables at Render Time:**
|
|
84
|
+
Variables can be provided when calling `render()`:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
t = lf.Template("Hello, {{name}}!")
|
|
88
|
+
print(t.render(name="Alice"))
|
|
89
|
+
# Output: Hello, Alice!
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**3. Composition:**
|
|
93
|
+
Embed templates within other templates for modularity:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
t = lf.Template(
|
|
97
|
+
"{{greeting}}, {{question}}",
|
|
98
|
+
greeting=lf.Template("Hello {{user}}", user="Bob"),
|
|
99
|
+
question=lf.UserMessage("How are you?")
|
|
100
|
+
)
|
|
101
|
+
print(t.render())
|
|
102
|
+
# Output: Hello Bob, How are you?
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**4. Multi-Modal Content:**
|
|
106
|
+
Reference modality objects in the template string:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
image = lf.Image.from_path(...)
|
|
110
|
+
t = lf.Template("Describe this image: <<[[{{image.id}}]]>>", image=image)
|
|
111
|
+
# When rendered, 't' can be passed to a multi-modal model.
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**5. Subclassing for Reusability:**
|
|
115
|
+
Define a reusable template via subclassing. The docstring can serve as the
|
|
116
|
+
default template string if it doesn't contain "THIS IS NOT A TEMPLATE".
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
class Greeting(lf.Template):
|
|
120
|
+
'''Greeting prompt.
|
|
121
|
+
|
|
122
|
+
Hello, {{user}}!
|
|
123
|
+
'''
|
|
124
|
+
user: str = 'guest'
|
|
125
|
+
|
|
126
|
+
print(Greeting().render())
|
|
127
|
+
# Output: Hello, guest!
|
|
128
|
+
print(Greeting(user='Admin').render())
|
|
129
|
+
# Output: Hello, Admin!
|
|
130
|
+
```
|
|
57
131
|
"""
|
|
58
132
|
|
|
59
133
|
template_str: Annotated[
|
|
60
134
|
str,
|
|
61
135
|
(
|
|
62
|
-
'A template string in jinja2 syntax. During `render`,
|
|
63
|
-
'will be resolved from 1)
|
|
64
|
-
'
|
|
65
|
-
'
|
|
136
|
+
'A template string in jinja2 syntax. During `render`, variables '
|
|
137
|
+
'will be resolved from: 1) keyword arguments passed to `render`; '
|
|
138
|
+
'2) attributes of this object; and 3) attributes of its containing '
|
|
139
|
+
'objects.'
|
|
66
140
|
),
|
|
67
141
|
]
|
|
68
142
|
|
|
@@ -70,16 +144,16 @@ class Template(
|
|
|
70
144
|
bool,
|
|
71
145
|
(
|
|
72
146
|
'If True, `inspect.cleandoc` will be applied on `template_str` to '
|
|
73
|
-
'
|
|
74
|
-
'
|
|
147
|
+
'remove leading/trailing spaces and fix indentation. Otherwise, '
|
|
148
|
+
'the `template_str` will be used as is.'
|
|
75
149
|
),
|
|
76
150
|
] = True
|
|
77
151
|
|
|
78
152
|
__kwargs__: Annotated[
|
|
79
153
|
Any,
|
|
80
154
|
(
|
|
81
|
-
'Wildcard keyword arguments for `__init__` that can be referred
|
|
82
|
-
'the template string. This allows
|
|
155
|
+
'Wildcard keyword arguments for `__init__` that can be referred to '
|
|
156
|
+
'in the template string. This allows modularizing prompts with '
|
|
83
157
|
'fully or partially bound variables.'
|
|
84
158
|
),
|
|
85
159
|
]
|
|
@@ -196,12 +270,14 @@ class Template(
|
|
|
196
270
|
"""Returns referred variables.
|
|
197
271
|
|
|
198
272
|
Args:
|
|
199
|
-
specified: If True, include only variables
|
|
200
|
-
include only variables that are not specified.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
273
|
+
specified: If True, include only variables specified with a value.
|
|
274
|
+
If False, include only variables that are not specified.
|
|
275
|
+
If None, include both specified and unspecified variables.
|
|
276
|
+
closure: If True, include variables from referred `lf.Template` objects
|
|
277
|
+
recursively. Otherwise, only include variables used in this template.
|
|
278
|
+
leaf: If True, include only variables that are not `lf.Template` objects.
|
|
279
|
+
If False, include only variables that are `lf.Template` objects.
|
|
280
|
+
If None, include both.
|
|
205
281
|
|
|
206
282
|
Returns:
|
|
207
283
|
A list of variable names that match the criteria.
|
|
@@ -230,17 +306,17 @@ class Template(
|
|
|
230
306
|
|
|
231
307
|
@property
|
|
232
308
|
def missing_vars(self) -> Set[str]:
|
|
233
|
-
"""Returns
|
|
309
|
+
"""Returns missing variable names from this and referred templates."""
|
|
234
310
|
return self.vars(closure=True, specified=False)
|
|
235
311
|
|
|
236
312
|
@classmethod
|
|
237
313
|
def raw_str(cls, text: str) -> str:
|
|
238
|
-
"""Returns a template string that
|
|
314
|
+
"""Returns a template string that preserves the text as original."""
|
|
239
315
|
return '{% raw %}' + text + '{% endraw %}'
|
|
240
316
|
|
|
241
317
|
@classmethod
|
|
242
318
|
def from_raw_str(cls, text: str) -> 'Template':
|
|
243
|
-
"""Returns a template that
|
|
319
|
+
"""Returns a template that preserves the text as original."""
|
|
244
320
|
return cls(cls.raw_str(text), clean=False)
|
|
245
321
|
|
|
246
322
|
def render(
|
|
@@ -254,18 +330,20 @@ class Template(
|
|
|
254
330
|
"""Renders the template with variables from the context.
|
|
255
331
|
|
|
256
332
|
Args:
|
|
257
|
-
allow_partial:
|
|
258
|
-
variables
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
333
|
+
allow_partial: If True, allows partial rendering, which leaves unresolved
|
|
334
|
+
variables in place in the output text. Otherwise, raises error when
|
|
335
|
+
there are unresolved variables.
|
|
336
|
+
implicit: If True, reuse the rendering output if a parent `lf.Template`
|
|
337
|
+
is rendering current `lf.Template` multiple times. This is important
|
|
338
|
+
for making sure all references to the same `lf.Template` within a single
|
|
262
339
|
top-level rendering would return the same result. If False, every call
|
|
263
340
|
to `render` will trigger the actual rendering process.
|
|
264
341
|
message_cls: The message class used for creating the return value.
|
|
265
|
-
**kwargs: Values for template variables
|
|
342
|
+
**kwargs: Values for template variables, which override values from
|
|
343
|
+
member attributes or context.
|
|
266
344
|
|
|
267
345
|
Returns:
|
|
268
|
-
|
|
346
|
+
A Message object containing the rendered result.
|
|
269
347
|
"""
|
|
270
348
|
try:
|
|
271
349
|
pg.object_utils.thread_local_push(_TLS_RENDER_STACK, self)
|
|
@@ -310,6 +388,17 @@ class Template(
|
|
|
310
388
|
f'The value for template variable {var_name!r} is not '
|
|
311
389
|
f'provided. Template: {self.template_str!r}'
|
|
312
390
|
)
|
|
391
|
+
# Short-circuit common types that do not need conversions.
|
|
392
|
+
elif not isinstance(
|
|
393
|
+
var_value, (
|
|
394
|
+
bool, int, float, str, list, dict,
|
|
395
|
+
Template, message_lib.Message, modality.Modality
|
|
396
|
+
)
|
|
397
|
+
) and message_lib.Message.is_convertible(type(var_value)):
|
|
398
|
+
# Automatically convert the value to a message if it is
|
|
399
|
+
# convertible. This allows users to drop message convertible
|
|
400
|
+
# objects into template for nested rendering.
|
|
401
|
+
var_value = message_lib.Message.from_value(var_value)
|
|
313
402
|
inputs[var_name] = var_value
|
|
314
403
|
|
|
315
404
|
# Enable Python format for builtin types during template rendering,
|
|
@@ -389,7 +478,14 @@ class Template(
|
|
|
389
478
|
assert top is self, (top, self)
|
|
390
479
|
|
|
391
480
|
def additional_metadata(self) -> dict[str, Any]:
|
|
392
|
-
"""Returns additional
|
|
481
|
+
"""Returns additional metadata to be carried in the rendered message.
|
|
482
|
+
|
|
483
|
+
Subclasses can override this method to inject additional metadata based on
|
|
484
|
+
their own logic.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
A dict of metadata to be added to the output message.
|
|
488
|
+
"""
|
|
393
489
|
metadata = {}
|
|
394
490
|
# Carry metadata from `lf.context`.
|
|
395
491
|
for k, v in component.all_contextual_values().items():
|
|
@@ -414,7 +510,7 @@ class Template(
|
|
|
414
510
|
child_transform: Callable[[pg.KeyPath, pg.typing.Field, Any], Any]
|
|
415
511
|
| None = None,
|
|
416
512
|
) -> Tuple[bool, Any]:
|
|
417
|
-
"""Makes
|
|
513
|
+
"""Makes template applicable to `pg.typing.Str()`."""
|
|
418
514
|
del allow_partial
|
|
419
515
|
del child_transform
|
|
420
516
|
|
|
@@ -448,10 +544,11 @@ class Template(
|
|
|
448
544
|
|
|
449
545
|
@property
|
|
450
546
|
def DEFAULT(self) -> 'Template':
|
|
451
|
-
"""
|
|
547
|
+
"""Refers to the default value used for this template.
|
|
452
548
|
|
|
453
|
-
This
|
|
454
|
-
value of current template
|
|
549
|
+
This property is intended to be used in template string for referring to
|
|
550
|
+
the default value of current template, useful for wrapping the default
|
|
551
|
+
template. For example:
|
|
455
552
|
|
|
456
553
|
Scenario 1: Use instance-level template_str to override the class default.
|
|
457
554
|
|
|
@@ -535,22 +632,24 @@ class Template(
|
|
|
535
632
|
value: Union[str, message_lib.Message, 'Template'],
|
|
536
633
|
**kwargs
|
|
537
634
|
) -> 'Template':
|
|
538
|
-
"""
|
|
635
|
+
"""Creates a template object from a value."""
|
|
539
636
|
if isinstance(value, cls):
|
|
540
637
|
return value.clone(override=kwargs) if kwargs else value # pylint: disable=no-value-for-parameter
|
|
541
638
|
if isinstance(value, str):
|
|
542
639
|
return cls(template_str=value, **kwargs)
|
|
640
|
+
if isinstance(value, Template):
|
|
641
|
+
lfun = cls(template_str=value.template_str, **kwargs) # pylint: disable=attribute-error
|
|
642
|
+
# So lfun could acccess all attributes from value.
|
|
643
|
+
lfun.sym_setparent(value)
|
|
644
|
+
return lfun
|
|
645
|
+
if message_lib.Message.is_convertible(type(value)):
|
|
646
|
+
value = message_lib.Message.from_value(value)
|
|
543
647
|
if isinstance(value, message_lib.Message):
|
|
544
648
|
for k, v in value.metadata.sym_items(): # pylint: disable=attribute-error
|
|
545
649
|
kwargs[_ADDITIONAL_METADATA_PREFIX + k] = v
|
|
546
650
|
t = cls(template_str=value.text, **kwargs)
|
|
547
651
|
t._referred_modalities = value.referred_modalities
|
|
548
652
|
return t
|
|
549
|
-
if isinstance(value, Template):
|
|
550
|
-
lfun = cls(template_str=value.template_str, **kwargs) # pylint: disable=attribute-error
|
|
551
|
-
# So lfun could acccess all attributes from value.
|
|
552
|
-
lfun.sym_setparent(value)
|
|
553
|
-
return lfun
|
|
554
653
|
return cls(template_str='{{input}}', input=value, **kwargs)
|
|
555
654
|
|
|
556
655
|
def _html_tree_view_content(
|
langfun/core/template_test.py
CHANGED
|
@@ -198,6 +198,24 @@ class FromValueTest(unittest.TestCase):
|
|
|
198
198
|
)
|
|
199
199
|
self.assertEqual(message.metadata, m.metadata)
|
|
200
200
|
|
|
201
|
+
def test_from_message_convertible(self):
|
|
202
|
+
|
|
203
|
+
class MyFormat(pg.Object):
|
|
204
|
+
text: str
|
|
205
|
+
|
|
206
|
+
class MyConverter(message_lib.MessageConverter): # pylint: disable=unused-variable
|
|
207
|
+
FORMAT_ID = 'int'
|
|
208
|
+
OUTPUT_TYPE = MyFormat
|
|
209
|
+
|
|
210
|
+
def to_value(self, m: message_lib.Message) -> MyFormat:
|
|
211
|
+
return MyFormat(text=m.text)
|
|
212
|
+
|
|
213
|
+
def from_value(self, value: MyFormat) -> message_lib.Message:
|
|
214
|
+
return message_lib.UserMessage(value.text)
|
|
215
|
+
|
|
216
|
+
t = Template.from_value(MyFormat(text='1'))
|
|
217
|
+
self.assertEqual(t.render(), '1')
|
|
218
|
+
|
|
201
219
|
def test_from_same_template(self):
|
|
202
220
|
t = Template('Hello {{x}}', x=1)
|
|
203
221
|
t2 = Template.from_value(t)
|
|
@@ -584,6 +602,28 @@ class RenderTest(unittest.TestCase):
|
|
|
584
602
|
)
|
|
585
603
|
)
|
|
586
604
|
|
|
605
|
+
def test_render_with_message_convertible_type(self):
|
|
606
|
+
class MyFormat(pg.Object):
|
|
607
|
+
text: str
|
|
608
|
+
|
|
609
|
+
class MyConverter(message_lib.MessageConverter): # pylint: disable=unused-variable
|
|
610
|
+
FORMAT_ID = 'another-format'
|
|
611
|
+
OUTPUT_TYPE = MyFormat
|
|
612
|
+
|
|
613
|
+
def to_value(self, m: message_lib.Message) -> MyFormat:
|
|
614
|
+
return MyFormat(text=m.text)
|
|
615
|
+
|
|
616
|
+
def from_value(self, value: MyFormat) -> message_lib.Message:
|
|
617
|
+
return message_lib.UserMessage(value.text)
|
|
618
|
+
|
|
619
|
+
t = Template('Hello {{x}}', x=MyFormat(text='world'))
|
|
620
|
+
rendered_message = t.render()
|
|
621
|
+
self.assertEqual(rendered_message, 'Hello world')
|
|
622
|
+
self.assertIsInstance(
|
|
623
|
+
rendered_message.__template_input__['x'], message_lib.UserMessage
|
|
624
|
+
)
|
|
625
|
+
self.assertEqual(rendered_message.__template_input__['x'].text, 'world')
|
|
626
|
+
|
|
587
627
|
|
|
588
628
|
class TemplateRenderEventTest(unittest.TestCase):
|
|
589
629
|
|