langfun 0.0.2.dev20240429__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.
Files changed (144) hide show
  1. langfun/__init__.py +20 -2
  2. langfun/core/__init__.py +16 -5
  3. langfun/core/agentic/__init__.py +30 -0
  4. langfun/core/agentic/action.py +854 -0
  5. langfun/core/agentic/action_eval.py +150 -0
  6. langfun/core/agentic/action_eval_test.py +109 -0
  7. langfun/core/agentic/action_test.py +136 -0
  8. langfun/core/coding/python/__init__.py +5 -11
  9. langfun/core/coding/python/correction.py +37 -21
  10. langfun/core/coding/python/correction_test.py +29 -3
  11. langfun/core/coding/python/execution.py +40 -216
  12. langfun/core/coding/python/execution_test.py +29 -89
  13. langfun/core/coding/python/generation.py +21 -11
  14. langfun/core/coding/python/generation_test.py +2 -2
  15. langfun/core/coding/python/parsing.py +108 -193
  16. langfun/core/coding/python/parsing_test.py +2 -105
  17. langfun/core/component.py +63 -2
  18. langfun/core/component_test.py +53 -0
  19. langfun/core/concurrent.py +414 -117
  20. langfun/core/concurrent_test.py +111 -24
  21. langfun/core/console.py +18 -5
  22. langfun/core/console_test.py +17 -0
  23. langfun/core/eval/__init__.py +16 -1
  24. langfun/core/eval/base.py +622 -174
  25. langfun/core/eval/base_test.py +200 -54
  26. langfun/core/eval/matching.py +63 -76
  27. langfun/core/eval/matching_test.py +17 -8
  28. langfun/core/eval/patching.py +130 -0
  29. langfun/core/eval/patching_test.py +170 -0
  30. langfun/core/eval/scoring.py +26 -26
  31. langfun/core/eval/scoring_test.py +19 -2
  32. langfun/core/eval/v2/__init__.py +42 -0
  33. langfun/core/eval/v2/checkpointing.py +380 -0
  34. langfun/core/eval/v2/checkpointing_test.py +228 -0
  35. langfun/core/eval/v2/eval_test_helper.py +136 -0
  36. langfun/core/eval/v2/evaluation.py +725 -0
  37. langfun/core/eval/v2/evaluation_test.py +180 -0
  38. langfun/core/eval/v2/example.py +305 -0
  39. langfun/core/eval/v2/example_test.py +128 -0
  40. langfun/core/eval/v2/experiment.py +1048 -0
  41. langfun/core/eval/v2/experiment_test.py +433 -0
  42. langfun/core/eval/v2/metric_values.py +156 -0
  43. langfun/core/eval/v2/metric_values_test.py +80 -0
  44. langfun/core/eval/v2/metrics.py +357 -0
  45. langfun/core/eval/v2/metrics_test.py +203 -0
  46. langfun/core/eval/v2/progress.py +348 -0
  47. langfun/core/eval/v2/progress_test.py +82 -0
  48. langfun/core/eval/v2/progress_tracking.py +210 -0
  49. langfun/core/eval/v2/progress_tracking_test.py +66 -0
  50. langfun/core/eval/v2/reporting.py +270 -0
  51. langfun/core/eval/v2/reporting_test.py +158 -0
  52. langfun/core/eval/v2/runners.py +488 -0
  53. langfun/core/eval/v2/runners_test.py +334 -0
  54. langfun/core/langfunc.py +4 -17
  55. langfun/core/langfunc_test.py +22 -6
  56. langfun/core/language_model.py +577 -39
  57. langfun/core/language_model_test.py +470 -56
  58. langfun/core/llms/__init__.py +87 -16
  59. langfun/core/llms/anthropic.py +312 -87
  60. langfun/core/llms/anthropic_test.py +71 -3
  61. langfun/core/llms/cache/base.py +21 -2
  62. langfun/core/llms/cache/in_memory.py +13 -0
  63. langfun/core/llms/cache/in_memory_test.py +53 -2
  64. langfun/core/llms/compositional.py +101 -0
  65. langfun/core/llms/compositional_test.py +73 -0
  66. langfun/core/llms/deepseek.py +117 -0
  67. langfun/core/llms/deepseek_test.py +61 -0
  68. langfun/core/llms/fake.py +11 -7
  69. langfun/core/llms/fake_test.py +14 -0
  70. langfun/core/llms/gemini.py +507 -0
  71. langfun/core/llms/gemini_test.py +195 -0
  72. langfun/core/llms/google_genai.py +62 -218
  73. langfun/core/llms/google_genai_test.py +9 -202
  74. langfun/core/llms/groq.py +160 -144
  75. langfun/core/llms/groq_test.py +31 -137
  76. langfun/core/llms/llama_cpp.py +15 -42
  77. langfun/core/llms/llama_cpp_test.py +4 -30
  78. langfun/core/llms/openai.py +395 -203
  79. langfun/core/llms/openai_compatible.py +179 -0
  80. langfun/core/llms/openai_compatible_test.py +495 -0
  81. langfun/core/llms/openai_test.py +30 -395
  82. langfun/core/llms/rest.py +113 -0
  83. langfun/core/llms/rest_test.py +111 -0
  84. langfun/core/llms/vertexai.py +192 -0
  85. langfun/core/llms/vertexai_test.py +52 -0
  86. langfun/core/logging.py +284 -0
  87. langfun/core/logging_test.py +125 -0
  88. langfun/core/message.py +319 -9
  89. langfun/core/message_test.py +190 -13
  90. langfun/core/modalities/__init__.py +6 -2
  91. langfun/core/modalities/audio.py +30 -0
  92. langfun/core/modalities/audio_test.py +63 -0
  93. langfun/core/modalities/image.py +39 -20
  94. langfun/core/modalities/image_test.py +52 -9
  95. langfun/core/modalities/mime.py +206 -29
  96. langfun/core/modalities/mime_test.py +90 -9
  97. langfun/core/modalities/ms_office.py +117 -0
  98. langfun/core/modalities/ms_office_test.py +389 -0
  99. langfun/core/modalities/pdf.py +22 -0
  100. langfun/core/modalities/pdf_test.py +57 -0
  101. langfun/core/modalities/video.py +9 -26
  102. langfun/core/modalities/video_test.py +3 -3
  103. langfun/core/modality.py +26 -3
  104. langfun/core/modality_test.py +2 -2
  105. langfun/core/sampling.py +11 -11
  106. langfun/core/structured/__init__.py +12 -16
  107. langfun/core/structured/completion.py +32 -5
  108. langfun/core/structured/completion_test.py +7 -6
  109. langfun/core/structured/description.py +2 -2
  110. langfun/core/structured/description_test.py +3 -3
  111. langfun/core/structured/function_generation.py +60 -27
  112. langfun/core/structured/function_generation_test.py +72 -2
  113. langfun/core/structured/mapping.py +97 -47
  114. langfun/core/structured/mapping_test.py +90 -2
  115. langfun/core/structured/parsing.py +33 -21
  116. langfun/core/structured/parsing_test.py +53 -9
  117. langfun/core/structured/querying.py +746 -0
  118. langfun/core/structured/{prompting_test.py → querying_test.py} +469 -51
  119. langfun/core/structured/schema.py +204 -97
  120. langfun/core/structured/schema_generation.py +1 -1
  121. langfun/core/structured/schema_test.py +130 -29
  122. langfun/core/structured/scoring.py +125 -19
  123. langfun/core/structured/scoring_test.py +30 -0
  124. langfun/core/structured/tokenization.py +64 -0
  125. langfun/core/structured/tokenization_test.py +48 -0
  126. langfun/core/template.py +115 -1
  127. langfun/core/template_test.py +71 -1
  128. langfun/core/templates/conversation.py +9 -0
  129. langfun/core/templates/conversation_test.py +4 -3
  130. langfun/core/templates/selfplay_test.py +10 -2
  131. langfun-0.1.2.dev202501140804.dist-info/METADATA +225 -0
  132. langfun-0.1.2.dev202501140804.dist-info/RECORD +153 -0
  133. {langfun-0.0.2.dev20240429.dist-info → langfun-0.1.2.dev202501140804.dist-info}/WHEEL +1 -1
  134. langfun/core/coding/python/errors.py +0 -108
  135. langfun/core/coding/python/errors_test.py +0 -99
  136. langfun/core/coding/python/permissions.py +0 -90
  137. langfun/core/coding/python/permissions_test.py +0 -86
  138. langfun/core/structured/prompting.py +0 -238
  139. langfun/core/text_formatting.py +0 -162
  140. langfun/core/text_formatting_test.py +0 -47
  141. langfun-0.0.2.dev20240429.dist-info/METADATA +0 -100
  142. langfun-0.0.2.dev20240429.dist-info/RECORD +0 -108
  143. {langfun-0.0.2.dev20240429.dist-info → langfun-0.1.2.dev202501140804.dist-info}/LICENSE +0 -0
  144. {langfun-0.0.2.dev20240429.dist-info → langfun-0.1.2.dev202501140804.dist-info}/top_level.txt +0 -0
langfun/core/template.py CHANGED
@@ -17,7 +17,7 @@ import contextlib
17
17
  import dataclasses
18
18
  import functools
19
19
  import inspect
20
- from typing import Annotated, Any, Callable, Iterator, Set, Tuple, Type
20
+ from typing import Annotated, Any, Callable, Iterator, Set, Tuple, Type, Union
21
21
 
22
22
  import jinja2
23
23
  from jinja2 import meta as jinja2_meta
@@ -47,6 +47,7 @@ class Template(
47
47
  natural_language.NaturalLanguageFormattable,
48
48
  component.Component,
49
49
  pg.typing.CustomTyping,
50
+ pg.views.HtmlTreeView.Extension
50
51
  ):
51
52
  """Langfun string template.
52
53
 
@@ -231,6 +232,16 @@ class Template(
231
232
  """Returns the missing variable names."""
232
233
  return self.vars(closure=True, specified=False)
233
234
 
235
+ @classmethod
236
+ def raw_str(cls, text: str) -> str:
237
+ """Returns a template string that preserve the text as original."""
238
+ return '{% raw %}' + text + '{% endraw %}'
239
+
240
+ @classmethod
241
+ def from_raw_str(cls, text: str) -> 'Template':
242
+ """Returns a template that preserve the text as original."""
243
+ return cls(cls.raw_str(text), clean=False)
244
+
234
245
  def render(
235
246
  self,
236
247
  *,
@@ -495,6 +506,109 @@ class Template(
495
506
  t.sym_setparent(self)
496
507
  return t
497
508
 
509
+ @classmethod
510
+ def from_value(
511
+ cls,
512
+ value: Union[str, message_lib.Message, 'Template'],
513
+ **kwargs
514
+ ) -> 'Template':
515
+ """Create a template object from a string or template."""
516
+ if isinstance(value, cls):
517
+ return value.clone(override=kwargs) if kwargs else value # pylint: disable=no-value-for-parameter
518
+ if isinstance(value, str):
519
+ return cls(template_str=value, **kwargs)
520
+ if isinstance(value, message_lib.Message):
521
+ kwargs.update(value.metadata)
522
+ return cls(template_str=value.text, **kwargs)
523
+ if isinstance(value, Template):
524
+ lfun = cls(template_str=value.template_str, **kwargs)
525
+ # So lfun could acccess all attributes from value.
526
+ lfun.sym_setparent(value)
527
+ return lfun
528
+ return cls(template_str='{{input}}', input=value, **kwargs)
529
+
530
+ def _html_tree_view_content(
531
+ self,
532
+ *,
533
+ view: pg.views.HtmlTreeView,
534
+ root_path: pg.KeyPath | None = None,
535
+ collapse_level: int | None = None,
536
+ extra_flags: dict[str, Any] | None = None,
537
+ debug: bool = False,
538
+ **kwargs,
539
+ ):
540
+ extra_flags = extra_flags if extra_flags is not None else {}
541
+ collapse_template_vars_level: int | None = extra_flags.get(
542
+ 'collapse_template_vars_level', 1
543
+ )
544
+
545
+ def render_template_str():
546
+ return pg.Html.element(
547
+ 'div',
548
+ [
549
+ pg.Html.element('span', [self.template_str])
550
+ ],
551
+ css_classes=['template-str'],
552
+ )
553
+
554
+ def render_fields():
555
+ return view.complex_value(
556
+ {k: v for k, v in self.sym_items()},
557
+ name='fields',
558
+ root_path=root_path,
559
+ parent=self,
560
+ exclude_keys=['template_str', 'clean'],
561
+ collapse_level=max(
562
+ collapse_template_vars_level, collapse_level
563
+ ) if collapse_level is not None else None,
564
+ extra_flags=extra_flags,
565
+ debug=debug,
566
+ **view.get_passthrough_kwargs(
567
+ remove=['exclude_keys'],
568
+ **kwargs,
569
+ )
570
+ )
571
+
572
+ return pg.views.html.controls.TabControl([
573
+ pg.views.html.controls.Tab(
574
+ 'template_str',
575
+ render_template_str(),
576
+ ),
577
+ pg.views.html.controls.Tab(
578
+ 'variables',
579
+ render_fields(),
580
+ ),
581
+ ], selected=1)
582
+
583
+ @classmethod
584
+ def _html_tree_view_css_styles(cls) -> list[str]:
585
+ return super()._html_tree_view_css_styles() + [
586
+ """
587
+ /* Langfun Template styles. */
588
+ .template-str {
589
+ padding: 10px;
590
+ margin: 10px 5px 10px 5px;
591
+ font-style: italic;
592
+ font-size: 1.1em;
593
+ white-space: pre-wrap;
594
+ border: 1px solid #EEE;
595
+ border-radius: 5px;
596
+ background-color: #EEE;
597
+ color: #cc2986;
598
+ }
599
+ """
600
+ ]
601
+
602
+ @classmethod
603
+ @functools.cache
604
+ def _html_tree_view_config(cls) -> dict[str, Any]:
605
+ return pg.views.HtmlTreeView.get_kwargs(
606
+ super()._html_tree_view_config(),
607
+ dict(
608
+ css_classes=['lf-template'],
609
+ )
610
+ )
611
+
498
612
 
499
613
  # Register converter from str to LangFunc, therefore we can always
500
614
  # pass strs to attributes that accept LangFunc.
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
  """Template test."""
15
15
  import inspect
16
+ from typing import Any
16
17
  import unittest
17
18
 
18
19
  from langfun.core import component
@@ -98,6 +99,21 @@ class BasicTest(unittest.TestCase):
98
99
  self.assertEqual(d.z.render(), 'Bye, 1')
99
100
  self.assertEqual(d.p.render(), 'Again Hello, 1')
100
101
 
102
+ def test_raw_text(self):
103
+ self.assertEqual(
104
+ Template(
105
+ '{{a}}' + Template.raw_str('\n{{d}}, {%x%}\n') + '{{b}}',
106
+ a='hi', b=1
107
+ ).render().text,
108
+ 'hi\n{{d}}, {%x%}\n1'
109
+ )
110
+
111
+ def test_from_raw_str(self):
112
+ self.assertEqual(
113
+ Template.from_raw_str('\n{{d}}, {%x%}\n').render().text,
114
+ '\n{{d}}, {%x%}\n'
115
+ )
116
+
101
117
 
102
118
  class DefinitionTest(unittest.TestCase):
103
119
 
@@ -309,7 +325,7 @@ class RenderTest(unittest.TestCase):
309
325
  Template(
310
326
  'This is {{ x }} and {{ a }}', x=1, a=CustomModality('foo')
311
327
  ).render(),
312
- 'This is 1 and {{a}}',
328
+ 'This is 1 and <<[[a]]>>',
313
329
  )
314
330
 
315
331
  def test_render_with_default(self):
@@ -537,5 +553,59 @@ class TemplateRenderEventTest(unittest.TestCase):
537
553
  self.assertEqual(render_stacks, [[l]])
538
554
 
539
555
 
556
+ class HtmlTest(unittest.TestCase):
557
+
558
+ def test_html(self):
559
+
560
+ class Foo(Template):
561
+ """Template Foo.
562
+
563
+ {{x}} + {{y}} = ?
564
+ """
565
+ x: Any
566
+ y: Any
567
+
568
+ class Bar(Template):
569
+ """Template Bar.
570
+
571
+ {{y}} + {{z}}
572
+ """
573
+ y: Any
574
+
575
+ self.assertIn(
576
+ inspect.cleandoc(
577
+ """
578
+ /* Langfun Template styles. */
579
+ .template-str {
580
+ padding: 10px;
581
+ margin: 10px 5px 10px 5px;
582
+ font-style: italic;
583
+ font-size: 1.1em;
584
+ white-space: pre-wrap;
585
+ border: 1px solid #EEE;
586
+ border-radius: 5px;
587
+ background-color: #EEE;
588
+ color: #cc2986;
589
+ }
590
+ """
591
+ ),
592
+ Foo(x=1, y=2).to_html().style_section,
593
+ )
594
+ self.assertIn(
595
+ 'template-str',
596
+ Foo(x=Bar('{{y}} + {{z}}'), y=1).to_html(
597
+ enable_summary_tooltip=False,
598
+ ).content,
599
+ )
600
+ self.assertIn(
601
+ 'template-str',
602
+ Foo(x=Bar('{{y}} + {{z}}'), y=1).to_html(
603
+ enable_summary_tooltip=False,
604
+ collapse_level=0,
605
+ key_style='label',
606
+ ).content,
607
+ )
608
+
609
+
540
610
  if __name__ == '__main__':
541
611
  unittest.main()
@@ -38,6 +38,11 @@ class Conversation(Completion):
38
38
  '(Optional) Preamble before beginning the conversation.',
39
39
  ] = None
40
40
 
41
+ role: Annotated[
42
+ str | None,
43
+ '(Optional) User defined role for the AI response in the conversation.',
44
+ ] = None
45
+
41
46
  conversation_context: Annotated[
42
47
  lf.LangFunc | None,
43
48
  (
@@ -71,6 +76,10 @@ class Conversation(Completion):
71
76
  with lf.context(**kwargs):
72
77
  # Call LM based on the prompt generated from `input_message`.
73
78
  lm_response = super().__call__()
79
+ if self.role is not None:
80
+ lm_response.rebind(
81
+ sender=self.role, skip_notification=True, raise_on_no_change=False
82
+ )
74
83
 
75
84
  # Add current turn to memory.
76
85
  self.add(self.input_message, lm_response)
@@ -83,6 +83,7 @@ class ConversationTest(unittest.TestCase):
83
83
  def test_call(self):
84
84
  c = Conversation(
85
85
  lm=QuestionCounter(),
86
+ role='Agent',
86
87
  preamble="You are a helpful and joyful AI bot. Now let's chat.",
87
88
  )
88
89
  # First round.
@@ -102,7 +103,7 @@ class ConversationTest(unittest.TestCase):
102
103
  inspect.cleandoc("""
103
104
  You are a helpful and joyful AI bot. Now let's chat.
104
105
  User: Hello
105
- AI: Response 1.
106
+ Agent: Response 1.
106
107
  User: How are you?
107
108
  """),
108
109
  )
@@ -114,9 +115,9 @@ class ConversationTest(unittest.TestCase):
114
115
  inspect.cleandoc("""
115
116
  You are a helpful and joyful AI bot. Now let's chat.
116
117
  User: Hello
117
- AI: Response 1.
118
+ Agent: Response 1.
118
119
  User: How are you?
119
- AI: Response 2.
120
+ Agent: Response 2.
120
121
  User: Okay, bye.
121
122
  """),
122
123
  )
@@ -57,7 +57,11 @@ class SelfPlayTest(unittest.TestCase):
57
57
 
58
58
  with lf.context(lm=NumberGuesser(guesses=[50, 20, 5, 10])):
59
59
  self.assertEqual(
60
- g(), lf.AIMessage('10', score=0.0, logprobs=None, usage=None)
60
+ g(),
61
+ lf.AIMessage(
62
+ '10', score=0.0, logprobs=None, is_cached=False,
63
+ usage=lf.UsageNotAvailable()
64
+ )
61
65
  )
62
66
 
63
67
  self.assertEqual(g.num_turns, 4)
@@ -67,7 +71,11 @@ class SelfPlayTest(unittest.TestCase):
67
71
 
68
72
  with lf.context(lm=NumberGuesser(guesses=[50, 20, 5, 2, 5, 4])):
69
73
  self.assertEqual(
70
- g(), lf.AIMessage('2', score=0.0, logprobs=None, usage=None)
74
+ g(),
75
+ lf.AIMessage(
76
+ '2', score=0.0, logprobs=None, is_cached=False,
77
+ usage=lf.UsageNotAvailable()
78
+ )
71
79
  )
72
80
 
73
81
  self.assertEqual(g.num_turns, 10)
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.2
2
+ Name: langfun
3
+ Version: 0.1.2.dev202501140804
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: pyglove>=0.4.5.dev202409110000
25
+ Requires-Dist: jinja2>=3.1.2
26
+ Requires-Dist: requests>=2.31.0
27
+ Provides-Extra: all
28
+ Requires-Dist: pyglove>=0.4.5.dev202409110000; extra == "all"
29
+ Requires-Dist: jinja2>=3.1.2; extra == "all"
30
+ Requires-Dist: requests>=2.31.0; extra == "all"
31
+ Requires-Dist: termcolor==1.1.0; extra == "all"
32
+ Requires-Dist: tqdm>=4.64.1; extra == "all"
33
+ Requires-Dist: google-auth>=2.16.0; extra == "all"
34
+ Requires-Dist: python-magic>=0.4.27; extra == "all"
35
+ Requires-Dist: python-docx>=0.8.11; extra == "all"
36
+ Requires-Dist: pillow>=10.0.0; extra == "all"
37
+ Requires-Dist: openpyxl>=3.1.0; extra == "all"
38
+ Requires-Dist: pandas>=2.0.3; extra == "all"
39
+ Provides-Extra: ui
40
+ Requires-Dist: termcolor==1.1.0; extra == "ui"
41
+ Requires-Dist: tqdm>=4.64.1; extra == "ui"
42
+ Provides-Extra: vertexai
43
+ Requires-Dist: google-auth>=2.16.0; extra == "vertexai"
44
+ Provides-Extra: mime
45
+ Requires-Dist: python-magic>=0.4.27; extra == "mime"
46
+ Requires-Dist: python-docx>=0.8.11; extra == "mime"
47
+ Requires-Dist: pillow>=10.0.0; extra == "mime"
48
+ Requires-Dist: openpyxl>=3.1.0; extra == "mime"
49
+ Requires-Dist: pandas>=2.0.3; extra == "mime"
50
+ Provides-Extra: mime-auto
51
+ Requires-Dist: python-magic>=0.4.27; extra == "mime-auto"
52
+ Provides-Extra: mime-docx
53
+ Requires-Dist: python-docx>=0.8.11; extra == "mime-docx"
54
+ Provides-Extra: mime-pil
55
+ Requires-Dist: pillow>=10.0.0; extra == "mime-pil"
56
+ Provides-Extra: mime-xlsx
57
+ Requires-Dist: openpyxl>=3.1.0; extra == "mime-xlsx"
58
+ Requires-Dist: pandas>=2.0.3; extra == "mime-xlsx"
59
+ Dynamic: author
60
+ Dynamic: author-email
61
+ Dynamic: classifier
62
+ Dynamic: description
63
+ Dynamic: description-content-type
64
+ Dynamic: home-page
65
+ Dynamic: keywords
66
+ Dynamic: license
67
+ Dynamic: provides-extra
68
+ Dynamic: requires-dist
69
+ Dynamic: summary
70
+
71
+ <div align="center">
72
+ <img src="https://raw.githubusercontent.com/google/langfun/main/docs/_static/logo.svg" width="520px" alt="logo"></img>
73
+ </div>
74
+
75
+ # Langfun
76
+
77
+ [![PyPI version](https://badge.fury.io/py/langfun.svg)](https://badge.fury.io/py/langfun)
78
+ [![codecov](https://codecov.io/gh/google/langfun/branch/main/graph/badge.svg)](https://codecov.io/gh/google/langfun)
79
+ ![pytest](https://github.com/google/langfun/actions/workflows/ci.yaml/badge.svg)
80
+
81
+ [**Installation**](#install) | [**Getting started**](#hello-langfun) | [**Tutorial**](https://colab.research.google.com/github/google/langfun/blob/main/docs/notebooks/langfun101.ipynb) | [**Discord community**](https://discord.gg/U6wPN9R68k)
82
+
83
+ ## Introduction
84
+
85
+ Langfun is a [PyGlove](https://github.com/google/pyglove) powered library that
86
+ aims to *make language models (LM) fun to work with*. Its central principle is
87
+ to enable seamless integration between natural language and programming by
88
+ treating language as functions. Through the introduction of *Object-Oriented Prompting*,
89
+ Langfun empowers users to prompt LLMs using objects and types, offering enhanced
90
+ control and simplifying agent development.
91
+
92
+ To unlock the magic of Langfun, you can start with
93
+ [Langfun 101](https://colab.research.google.com/github/google/langfun/blob/main/docs/notebooks/langfun101.ipynb). Notably, Langfun is compatible with popular LLMs such as Gemini, GPT,
94
+ Claude, all without the need for additional fine-tuning.
95
+
96
+ ## Why Langfun?
97
+
98
+ Langfun is *powerful and scalable*:
99
+
100
+ * Seamless integration between natural language and computer programs.
101
+ * Modular prompts, which allows a natural blend of texts and modalities;
102
+ * Efficient for both request-based workflows and batch jobs;
103
+ * A powerful eval framework that thrives dimension explosions.
104
+
105
+ Langfun is *simple and elegant*:
106
+
107
+ * An intuitive programming model, graspable in 5 minutes;
108
+ * Plug-and-play into any Python codebase, making an immediate difference;
109
+ * Comprehensive LLMs under a unified API: Gemini, GPT, Claude, Llama3, and more.
110
+ * Designed for agile developement: offering intellisense, easy debugging, with minimal overhead;
111
+
112
+ ## Hello, Langfun
113
+
114
+ ```python
115
+ import langfun as lf
116
+ import pyglove as pg
117
+
118
+ from IPython import display
119
+
120
+ class Item(pg.Object):
121
+ name: str
122
+ color: str
123
+
124
+ class ImageDescription(pg.Object):
125
+ items: list[Item]
126
+
127
+ image = lf.Image.from_uri('https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Solar_system.jpg/1646px-Solar_system.jpg')
128
+ display.display(image)
129
+
130
+ desc = lf.query(
131
+ 'Describe objects in {{my_image}} from top to bottom.',
132
+ ImageDescription,
133
+ lm=lf.llms.Gpt4o(api_key='<your-openai-api-key>'),
134
+ my_image=image,
135
+ )
136
+ print(desc)
137
+ ```
138
+ *Output:*
139
+
140
+ <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Solar_system.jpg/1646px-Solar_system.jpg" width="520px" alt="my_image"></img>
141
+
142
+ ```
143
+ ImageDescription(
144
+ items = [
145
+ 0 : Item(
146
+ name = 'Mercury',
147
+ color = 'Gray'
148
+ ),
149
+ 1 : Item(
150
+ name = 'Venus',
151
+ color = 'Yellow'
152
+ ),
153
+ 2 : Item(
154
+ name = 'Earth',
155
+ color = 'Blue and white'
156
+ ),
157
+ 3 : Item(
158
+ name = 'Moon',
159
+ color = 'Gray'
160
+ ),
161
+ 4 : Item(
162
+ name = 'Mars',
163
+ color = 'Red'
164
+ ),
165
+ 5 : Item(
166
+ name = 'Jupiter',
167
+ color = 'Brown and white'
168
+ ),
169
+ 6 : Item(
170
+ name = 'Saturn',
171
+ color = 'Yellowish-brown with rings'
172
+ ),
173
+ 7 : Item(
174
+ name = 'Uranus',
175
+ color = 'Light blue'
176
+ ),
177
+ 8 : Item(
178
+ name = 'Neptune',
179
+ color = 'Dark blue'
180
+ )
181
+ ]
182
+ )
183
+ ```
184
+ See [Langfun 101](https://colab.research.google.com/github/google/langfun/blob/main/docs/notebooks/langfun101.ipynb) for more examples.
185
+
186
+ ## Install
187
+
188
+ Langfun offers a range of features through [Extras](https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-extras), allowing users to install only what they need. The minimal installation of Langfun requires only [PyGlove](https://github.com/google/pyglove), [Jinja2](https://github.com/pallets/jinja/), and [requests](https://github.com/psf/requests). To install Langfun with its minimal dependencies, use:
189
+
190
+ ```
191
+ pip install langfun
192
+ ```
193
+
194
+ For a complete installation with all dependencies, use:
195
+
196
+ ```
197
+ pip install langfun[all]
198
+ ```
199
+
200
+ To install a nightly build, include the `--pre` flag, like this:
201
+
202
+ ```
203
+ pip install langfun[all] --pre
204
+ ```
205
+
206
+ If you want to customize your installation, you can select specific features using package names like `langfun[X1, X2, ..., Xn]`, where `Xi` corresponds to a tag from the list below:
207
+
208
+ | Tag | Description |
209
+ | ------------------- | ---------------------------------------- |
210
+ | all | All Langfun features. |
211
+ | vertexai | VertexAI access. |
212
+ | mime | All MIME supports. |
213
+ | mime-auto | Automatic MIME type detection. |
214
+ | mime-docx | DocX format support. |
215
+ | mime-pil | Image support for PIL. |
216
+ | mime-xlsx | XlsX format support. |
217
+ | ui | UI enhancements |
218
+
219
+
220
+ For example, to install a nightly build that includes VertexAI access, full modality support, and UI enhancements, use:
221
+ ```
222
+ pip install langfun[vertexai,mime,ui] --pre
223
+ ```
224
+
225
+ *Disclaimer: this is not an officially supported Google product.*