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.
Files changed (145) hide show
  1. langfun/__init__.py +22 -2
  2. langfun/core/__init__.py +17 -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 -28
  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 +69 -2
  18. langfun/core/component_test.py +54 -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 +17 -0
  24. langfun/core/eval/base.py +767 -140
  25. langfun/core/eval/base_test.py +238 -53
  26. langfun/core/eval/matching.py +80 -76
  27. langfun/core/eval/matching_test.py +19 -9
  28. langfun/core/eval/patching.py +130 -0
  29. langfun/core/eval/patching_test.py +170 -0
  30. langfun/core/eval/scoring.py +37 -28
  31. langfun/core/eval/scoring_test.py +21 -3
  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 +3 -21
  55. langfun/core/langfunc_test.py +26 -8
  56. langfun/core/language_model.py +686 -48
  57. langfun/core/language_model_test.py +681 -44
  58. langfun/core/llms/__init__.py +100 -12
  59. langfun/core/llms/anthropic.py +488 -0
  60. langfun/core/llms/anthropic_test.py +235 -0
  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 +88 -28
  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 +39 -26
  69. langfun/core/llms/fake_test.py +136 -11
  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 -197
  74. langfun/core/llms/groq.py +276 -0
  75. langfun/core/llms/groq_test.py +64 -0
  76. langfun/core/llms/llama_cpp.py +15 -40
  77. langfun/core/llms/llama_cpp_test.py +4 -30
  78. langfun/core/llms/openai.py +436 -226
  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 +35 -174
  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 -23
  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 +15 -16
  107. langfun/core/structured/completion.py +32 -5
  108. langfun/core/structured/completion_test.py +9 -8
  109. langfun/core/structured/description.py +2 -2
  110. langfun/core/structured/description_test.py +3 -3
  111. langfun/core/structured/function_generation.py +278 -0
  112. langfun/core/structured/function_generation_test.py +399 -0
  113. langfun/core/structured/mapping.py +150 -46
  114. langfun/core/structured/mapping_test.py +105 -0
  115. langfun/core/structured/parsing.py +33 -21
  116. langfun/core/structured/parsing_test.py +71 -22
  117. langfun/core/structured/querying.py +746 -0
  118. langfun/core/structured/{prompting_test.py → querying_test.py} +545 -60
  119. langfun/core/structured/schema.py +208 -99
  120. langfun/core/structured/schema_generation.py +1 -1
  121. langfun/core/structured/schema_generation_test.py +2 -2
  122. langfun/core/structured/schema_test.py +133 -34
  123. langfun/core/structured/scoring.py +125 -19
  124. langfun/core/structured/scoring_test.py +30 -0
  125. langfun/core/structured/tokenization.py +64 -0
  126. langfun/core/structured/tokenization_test.py +48 -0
  127. langfun/core/template.py +240 -11
  128. langfun/core/template_test.py +146 -1
  129. langfun/core/templates/conversation.py +9 -0
  130. langfun/core/templates/conversation_test.py +4 -3
  131. langfun/core/templates/selfplay_test.py +14 -2
  132. langfun-0.1.2.dev202501140804.dist-info/METADATA +225 -0
  133. langfun-0.1.2.dev202501140804.dist-info/RECORD +153 -0
  134. {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/WHEEL +1 -1
  135. langfun/core/coding/python/errors.py +0 -108
  136. langfun/core/coding/python/errors_test.py +0 -99
  137. langfun/core/coding/python/permissions.py +0 -90
  138. langfun/core/coding/python/permissions_test.py +0 -86
  139. langfun/core/structured/prompting.py +0 -217
  140. langfun/core/text_formatting.py +0 -162
  141. langfun/core/text_formatting_test.py +0 -47
  142. langfun-0.0.2.dev20240330.dist-info/METADATA +0 -99
  143. langfun-0.0.2.dev20240330.dist-info/RECORD +0 -102
  144. {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/LICENSE +0 -0
  145. {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/top_level.txt +0 -0
@@ -24,31 +24,88 @@ from langfun.core.llms.fake import StaticMapping
24
24
  from langfun.core.llms.fake import StaticResponse
25
25
  from langfun.core.llms.fake import StaticSequence
26
26
 
27
+ # Compositional models.
28
+ from langfun.core.llms.compositional import RandomChoice
29
+
30
+ # REST-based models.
31
+ from langfun.core.llms.rest import REST
32
+
27
33
  # Gemini models.
28
34
  from langfun.core.llms.google_genai import GenAI
29
- from langfun.core.llms.google_genai import GeminiPro
30
- from langfun.core.llms.google_genai import GeminiProVision
31
- from langfun.core.llms.google_genai import Palm2
32
- from langfun.core.llms.google_genai import Palm2_IT
35
+ from langfun.core.llms.google_genai import GeminiFlash2_0ThinkingExp_20241219
36
+ from langfun.core.llms.google_genai import GeminiFlash2_0Exp
37
+ from langfun.core.llms.google_genai import GeminiExp_20241206
38
+ from langfun.core.llms.google_genai import GeminiExp_20241114
39
+ from langfun.core.llms.google_genai import GeminiPro1_5
40
+ from langfun.core.llms.google_genai import GeminiPro1_5_002
41
+ from langfun.core.llms.google_genai import GeminiPro1_5_001
42
+ from langfun.core.llms.google_genai import GeminiFlash1_5
43
+ from langfun.core.llms.google_genai import GeminiFlash1_5_002
44
+ from langfun.core.llms.google_genai import GeminiFlash1_5_001
45
+ from langfun.core.llms.google_genai import GeminiPro1
46
+
47
+ from langfun.core.llms.vertexai import VertexAI
48
+ from langfun.core.llms.vertexai import VertexAIGeminiFlash2_0ThinkingExp_20241219
49
+ from langfun.core.llms.vertexai import VertexAIGeminiFlash2_0Exp
50
+ from langfun.core.llms.vertexai import VertexAIGeminiExp_20241206
51
+ from langfun.core.llms.vertexai import VertexAIGeminiExp_20241114
52
+ from langfun.core.llms.vertexai import VertexAIGeminiPro1_5
53
+ from langfun.core.llms.vertexai import VertexAIGeminiPro1_5_002
54
+ from langfun.core.llms.vertexai import VertexAIGeminiPro1_5_001
55
+ from langfun.core.llms.vertexai import VertexAIGeminiFlash1_5
56
+ from langfun.core.llms.vertexai import VertexAIGeminiFlash1_5_002
57
+ from langfun.core.llms.vertexai import VertexAIGeminiFlash1_5_001
58
+ from langfun.core.llms.vertexai import VertexAIGeminiPro1
59
+
60
+ # Base for OpenAI-compatible models.
61
+ from langfun.core.llms.openai_compatible import OpenAICompatible
33
62
 
34
63
  # OpenAI models.
35
64
  from langfun.core.llms.openai import OpenAI
36
65
 
66
+ from langfun.core.llms.openai import GptO1
67
+ from langfun.core.llms.openai import GptO1Preview
68
+ from langfun.core.llms.openai import GptO1Preview_20240912
69
+ from langfun.core.llms.openai import GptO1Mini
70
+ from langfun.core.llms.openai import GptO1Mini_20240912
71
+
72
+ from langfun.core.llms.openai import Gpt4oMini
73
+ from langfun.core.llms.openai import Gpt4oMini_20240718
74
+ from langfun.core.llms.openai import Gpt4o
75
+ from langfun.core.llms.openai import Gpt4o_20241120
76
+ from langfun.core.llms.openai import Gpt4o_20240806
77
+ from langfun.core.llms.openai import Gpt4o_20240513
78
+
37
79
  from langfun.core.llms.openai import Gpt4Turbo
38
- from langfun.core.llms.openai import Gpt4Turbo_0125
39
- from langfun.core.llms.openai import Gpt4TurboVision
80
+ from langfun.core.llms.openai import Gpt4Turbo_20240409
81
+ from langfun.core.llms.openai import Gpt4TurboPreview
82
+ from langfun.core.llms.openai import Gpt4TurboPreview_20240125
83
+ from langfun.core.llms.openai import Gpt4TurboPreview_20231106
84
+ from langfun.core.llms.openai import Gpt4VisionPreview
85
+ from langfun.core.llms.openai import Gpt4VisionPreview_20231106
40
86
  from langfun.core.llms.openai import Gpt4
41
- from langfun.core.llms.openai import Gpt4_0613
87
+ from langfun.core.llms.openai import Gpt4_20230613
88
+
42
89
  from langfun.core.llms.openai import Gpt4_32K
43
- from langfun.core.llms.openai import Gpt4_32K_0613
90
+ from langfun.core.llms.openai import Gpt4_32K_20230613
44
91
 
45
92
  from langfun.core.llms.openai import Gpt35Turbo
46
- from langfun.core.llms.openai import Gpt35Turbo_0125
47
- from langfun.core.llms.openai import Gpt35Turbo_1106
48
- from langfun.core.llms.openai import Gpt35Turbo_0613
93
+ from langfun.core.llms.openai import Gpt35Turbo_20240125
94
+ from langfun.core.llms.openai import Gpt35Turbo_20231106
95
+ from langfun.core.llms.openai import Gpt35Turbo_20230613
49
96
  from langfun.core.llms.openai import Gpt35Turbo16K
50
- from langfun.core.llms.openai import Gpt35Turbo16K_0613
97
+ from langfun.core.llms.openai import Gpt35Turbo16K_20230613
51
98
 
99
+ # For backward compatibility.
100
+ Gpt4TurboPreview_0125 = Gpt4TurboPreview_20240125
101
+ Gpt4TurboPreview_1106 = Gpt4TurboPreview_20231106
102
+ Gpt4VisionPreview_1106 = Gpt4VisionPreview_20231106
103
+ Gpt4_0613 = Gpt4_20230613
104
+ Gpt4_32K_0613 = Gpt4_32K_20230613
105
+ Gpt35Turbo_0125 = Gpt35Turbo_20240125
106
+ Gpt35Turbo_1106 = Gpt35Turbo_20231106
107
+ Gpt35Turbo_0613 = Gpt35Turbo_20230613
108
+ Gpt35Turbo16K_0613 = Gpt35Turbo16K_20230613
52
109
 
53
110
  from langfun.core.llms.openai import Gpt35
54
111
 
@@ -57,9 +114,40 @@ from langfun.core.llms.openai import Gpt3Curie
57
114
  from langfun.core.llms.openai import Gpt3Babbage
58
115
  from langfun.core.llms.openai import Gpt3Ada
59
116
 
117
+ from langfun.core.llms.anthropic import Anthropic
118
+ from langfun.core.llms.anthropic import Claude35Sonnet
119
+ from langfun.core.llms.anthropic import Claude35Sonnet20241022
120
+ from langfun.core.llms.anthropic import Claude35Sonnet20240620
121
+ from langfun.core.llms.anthropic import Claude3Opus
122
+ from langfun.core.llms.anthropic import Claude3Sonnet
123
+ from langfun.core.llms.anthropic import Claude3Haiku
124
+ from langfun.core.llms.anthropic import VertexAIAnthropic
125
+ from langfun.core.llms.anthropic import VertexAIClaude3_5_Sonnet_20241022
126
+ from langfun.core.llms.anthropic import VertexAIClaude3_5_Sonnet_20240620
127
+ from langfun.core.llms.anthropic import VertexAIClaude3_5_Haiku_20241022
128
+ from langfun.core.llms.anthropic import VertexAIClaude3_Opus_20240229
129
+
130
+ from langfun.core.llms.groq import Groq
131
+ from langfun.core.llms.groq import GroqLlama3_2_3B
132
+ from langfun.core.llms.groq import GroqLlama3_2_1B
133
+ from langfun.core.llms.groq import GroqLlama3_1_70B
134
+ from langfun.core.llms.groq import GroqLlama3_1_8B
135
+ from langfun.core.llms.groq import GroqLlama3_70B
136
+ from langfun.core.llms.groq import GroqLlama3_8B
137
+ from langfun.core.llms.groq import GroqLlama2_70B
138
+ from langfun.core.llms.groq import GroqMistral_8x7B
139
+ from langfun.core.llms.groq import GroqGemma2_9B_IT
140
+ from langfun.core.llms.groq import GroqGemma_7B_IT
141
+ from langfun.core.llms.groq import GroqWhisper_Large_v3
142
+ from langfun.core.llms.groq import GroqWhisper_Large_v3Turbo
143
+
60
144
  # LLaMA C++ models.
61
145
  from langfun.core.llms.llama_cpp import LlamaCppRemote
62
146
 
147
+ # DeepSeek models.
148
+ from langfun.core.llms.deepseek import DeepSeek
149
+ from langfun.core.llms.deepseek import DeepSeekChat
150
+
63
151
  # Placeholder for Google-internal imports.
64
152
 
65
153
  # Include cache as sub-module.
@@ -0,0 +1,488 @@
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
+ """Language models from Anthropic."""
15
+
16
+ import base64
17
+ import functools
18
+ import os
19
+ from typing import Annotated, Any, Literal
20
+
21
+ import langfun.core as lf
22
+ from langfun.core import modalities as lf_modalities
23
+ from langfun.core.llms import rest
24
+ import pyglove as pg
25
+
26
+
27
+ try:
28
+ # pylint: disable=g-import-not-at-top
29
+ from google import auth as google_auth
30
+ from google.auth import credentials as credentials_lib
31
+ from google.auth.transport import requests as auth_requests
32
+ Credentials = credentials_lib.Credentials
33
+ # pylint: enable=g-import-not-at-top
34
+ except ImportError:
35
+ google_auth = None
36
+ auth_requests = None
37
+ credentials_lib = None
38
+ Credentials = Any # pylint: disable=invalid-name
39
+
40
+
41
+ SUPPORTED_MODELS_AND_SETTINGS = {
42
+ # See https://docs.anthropic.com/claude/docs/models-overview
43
+ # Rate limits from https://docs.anthropic.com/claude/reference/rate-limits
44
+ # RPM/TPM for Claude-2.1, Claude-2.0, and Claude-Instant-1.2 estimated
45
+ # as RPM/TPM of the largest-available model (Claude-3-Opus).
46
+ # Price in US dollars at https://www.anthropic.com/pricing
47
+ # as of 2024-10-10.
48
+ # Anthropic models hosted on VertexAI.
49
+ 'claude-3-5-sonnet-v2@20241022': pg.Dict(
50
+ max_tokens=8192,
51
+ rpm=1000,
52
+ tpm=100000,
53
+ cost_per_1k_input_tokens=0.003,
54
+ cost_per_1k_output_tokens=0.015,
55
+ ),
56
+ 'claude-3-5-sonnet@20240620': pg.Dict(
57
+ max_tokens=8192,
58
+ rpm=1000,
59
+ tpm=100000,
60
+ cost_per_1k_input_tokens=0.003,
61
+ cost_per_1k_output_tokens=0.015,
62
+ ),
63
+ 'claude-3-5-haiku@20241022': pg.Dict(
64
+ max_tokens=8192,
65
+ rpm=1000,
66
+ tpm=100000,
67
+ cost_per_1k_input_tokens=0.001,
68
+ cost_per_1k_output_tokens=0.005,
69
+ ),
70
+ 'claude-3-opus@20240229': pg.Dict(
71
+ max_tokens=4096,
72
+ rpm=4000,
73
+ tpm=400000,
74
+ cost_per_1k_input_tokens=0.015,
75
+ cost_per_1k_output_tokens=0.075,
76
+ ),
77
+ # Anthropic hosted models.
78
+ 'claude-3-5-sonnet-20241022': pg.Dict(
79
+ max_tokens=8192,
80
+ rpm=4000,
81
+ tpm=400000,
82
+ cost_per_1k_input_tokens=0.003,
83
+ cost_per_1k_output_tokens=0.015,
84
+ ),
85
+ 'claude-3-5-sonnet-20240620': pg.Dict(
86
+ max_tokens=8192,
87
+ rpm=4000,
88
+ tpm=400000,
89
+ cost_per_1k_input_tokens=0.003,
90
+ cost_per_1k_output_tokens=0.015,
91
+ ),
92
+ 'claude-3-5-haiku-20241022': pg.Dict(
93
+ max_tokens=8192,
94
+ rpm=4000,
95
+ tpm=400000,
96
+ cost_per_1k_input_tokens=0.001,
97
+ cost_per_1k_output_tokens=0.005,
98
+ ),
99
+ 'claude-3-opus-20240229': pg.Dict(
100
+ max_tokens=4096,
101
+ rpm=4000,
102
+ tpm=400000,
103
+ cost_per_1k_input_tokens=0.015,
104
+ cost_per_1k_output_tokens=0.075,
105
+ ),
106
+ 'claude-3-sonnet-20240229': pg.Dict(
107
+ max_tokens=4096,
108
+ rpm=4000,
109
+ tpm=400000,
110
+ cost_per_1k_input_tokens=0.003,
111
+ cost_per_1k_output_tokens=0.015,
112
+ ),
113
+ 'claude-3-haiku-20240307': pg.Dict(
114
+ max_tokens=4096,
115
+ rpm=4000,
116
+ tpm=400000,
117
+ cost_per_1k_input_tokens=0.00025,
118
+ cost_per_1k_output_tokens=0.00125,
119
+ ),
120
+ 'claude-2.1': pg.Dict(
121
+ max_tokens=4096,
122
+ rpm=4000,
123
+ tpm=400000,
124
+ cost_per_1k_input_tokens=0.008,
125
+ cost_per_1k_output_tokens=0.024,
126
+ ),
127
+ 'claude-2.0': pg.Dict(
128
+ max_tokens=4096,
129
+ rpm=4000,
130
+ tpm=400000,
131
+ cost_per_1k_input_tokens=0.008,
132
+ cost_per_1k_output_tokens=0.024,
133
+ ),
134
+ 'claude-instant-1.2': pg.Dict(
135
+ max_tokens=4096,
136
+ rpm=4000,
137
+ tpm=400000,
138
+ cost_per_1k_input_tokens=0.0008,
139
+ cost_per_1k_output_tokens=0.0024,
140
+ ),
141
+ }
142
+
143
+
144
+ @lf.use_init_args(['model'])
145
+ class Anthropic(rest.REST):
146
+ """Anthropic LLMs (Claude) through REST APIs.
147
+
148
+ See https://docs.anthropic.com/claude/reference/messages_post
149
+ """
150
+
151
+ model: pg.typing.Annotated[
152
+ pg.typing.Enum(
153
+ pg.MISSING_VALUE, list(SUPPORTED_MODELS_AND_SETTINGS.keys())
154
+ ),
155
+ 'The name of the model to use.',
156
+ ]
157
+
158
+ multimodal: Annotated[bool, 'Whether this model has multimodal support.'] = (
159
+ True
160
+ )
161
+
162
+ api_key: Annotated[
163
+ str | None,
164
+ (
165
+ 'API key. If None, the key will be read from environment variable '
166
+ "'ANTHROPIC_API_KEY'."
167
+ ),
168
+ ] = None
169
+
170
+ api_endpoint: str = 'https://api.anthropic.com/v1/messages'
171
+
172
+ api_version: Annotated[
173
+ str,
174
+ 'Anthropic API version.'
175
+ ] = '2023-06-01'
176
+
177
+ def _on_bound(self):
178
+ super()._on_bound()
179
+ self._api_key = None
180
+
181
+ def _initialize(self):
182
+ api_key = self.api_key or os.environ.get('ANTHROPIC_API_KEY', None)
183
+ if not api_key:
184
+ raise ValueError(
185
+ 'Please specify `api_key` during `__init__` or set environment '
186
+ 'variable `ANTHROPIC_API_KEY` with your Anthropic API key.'
187
+ )
188
+ self._api_key = api_key
189
+
190
+ @property
191
+ def headers(self) -> dict[str, Any]:
192
+ return {
193
+ 'x-api-key': self._api_key,
194
+ 'anthropic-version': self.api_version,
195
+ 'content-type': 'application/json',
196
+ # TODO(yifenglu): Remove beta flag once the feature is fully supported.
197
+ 'anthropic-beta': 'pdfs-2024-09-25',
198
+ }
199
+
200
+ @property
201
+ def model_id(self) -> str:
202
+ """Returns a string to identify the model."""
203
+ return self.model
204
+
205
+ @property
206
+ def max_concurrency(self) -> int:
207
+ rpm = SUPPORTED_MODELS_AND_SETTINGS[self.model].get('rpm', 0)
208
+ tpm = SUPPORTED_MODELS_AND_SETTINGS[self.model].get('tpm', 0)
209
+ return self.rate_to_max_concurrency(
210
+ requests_per_min=rpm, tokens_per_min=tpm
211
+ )
212
+
213
+ def estimate_cost(
214
+ self,
215
+ num_input_tokens: int,
216
+ num_output_tokens: int
217
+ ) -> float | None:
218
+ """Estimate the cost based on usage."""
219
+ cost_per_1k_input_tokens = SUPPORTED_MODELS_AND_SETTINGS[self.model].get(
220
+ 'cost_per_1k_input_tokens', None
221
+ )
222
+ cost_per_1k_output_tokens = SUPPORTED_MODELS_AND_SETTINGS[self.model].get(
223
+ 'cost_per_1k_output_tokens', None
224
+ )
225
+ if cost_per_1k_output_tokens is None or cost_per_1k_input_tokens is None:
226
+ return None
227
+ return (
228
+ cost_per_1k_input_tokens * num_input_tokens
229
+ + cost_per_1k_output_tokens * num_output_tokens
230
+ ) / 1000
231
+
232
+ def request(
233
+ self,
234
+ prompt: lf.Message,
235
+ sampling_options: lf.LMSamplingOptions
236
+ ) -> dict[str, Any]:
237
+ """Returns the JSON input for a message."""
238
+ request = dict()
239
+ request.update(self._request_args(sampling_options))
240
+ request.update(
241
+ dict(
242
+ messages=[
243
+ dict(role='user', content=self._content_from_message(prompt))
244
+ ]
245
+ )
246
+ )
247
+ return request
248
+
249
+ def _request_args(self, options: lf.LMSamplingOptions) -> dict[str, Any]:
250
+ """Returns a dict as request arguments."""
251
+ # Authropic requires `max_tokens` to be specified.
252
+ max_tokens = (
253
+ options.max_tokens
254
+ or SUPPORTED_MODELS_AND_SETTINGS[self.model].max_tokens
255
+ )
256
+ args = dict(
257
+ model=self.model,
258
+ max_tokens=max_tokens,
259
+ stream=False,
260
+ )
261
+ if options.stop:
262
+ args['stop_sequences'] = options.stop
263
+ if options.temperature is not None:
264
+ args['temperature'] = options.temperature
265
+ if options.top_k is not None:
266
+ args['top_k'] = options.top_k
267
+ if options.top_p is not None:
268
+ args['top_p'] = options.top_p
269
+ return args
270
+
271
+ def _content_from_message(self, prompt: lf.Message) -> list[dict[str, Any]]:
272
+ """Converts an message to Anthropic's content protocol (list of dicts)."""
273
+ # Refer: https://docs.anthropic.com/claude/reference/messages-examples
274
+ if self.multimodal:
275
+ content = []
276
+ for chunk in prompt.chunk():
277
+ if isinstance(chunk, str):
278
+ item = dict(type='text', text=chunk)
279
+ elif isinstance(chunk, lf_modalities.Image):
280
+ item = dict(
281
+ type='image',
282
+ source=dict(
283
+ type='base64',
284
+ media_type=chunk.mime_type,
285
+ data=base64.b64encode(chunk.to_bytes()).decode(),
286
+ ),
287
+ )
288
+ elif isinstance(chunk, lf_modalities.PDF):
289
+ item = dict(
290
+ type='document',
291
+ source=dict(
292
+ type='base64',
293
+ media_type=chunk.mime_type,
294
+ data=base64.b64encode(chunk.to_bytes()).decode(),
295
+ ),
296
+ )
297
+ else:
298
+ raise ValueError(f'Unsupported modality object: {chunk!r}.')
299
+ content.append(item)
300
+ return content
301
+ else:
302
+ return [dict(type='text', text=prompt.text)]
303
+
304
+ def result(self, json: dict[str, Any]) -> lf.LMSamplingResult:
305
+ message = self._message_from_content(json['content'])
306
+ input_tokens = json['usage']['input_tokens']
307
+ output_tokens = json['usage']['output_tokens']
308
+ return lf.LMSamplingResult(
309
+ [lf.LMSample(message)],
310
+ usage=lf.LMSamplingUsage(
311
+ prompt_tokens=input_tokens,
312
+ completion_tokens=output_tokens,
313
+ total_tokens=input_tokens + output_tokens,
314
+ estimated_cost=self.estimate_cost(
315
+ num_input_tokens=input_tokens,
316
+ num_output_tokens=output_tokens,
317
+ ),
318
+ ),
319
+ )
320
+
321
+ def _message_from_content(self, content: list[dict[str, Any]]) -> lf.Message:
322
+ """Converts Anthropic's content protocol to message."""
323
+ # Refer: https://docs.anthropic.com/claude/reference/messages-examples
324
+ return lf.AIMessage.from_chunks(
325
+ [x['text'] for x in content if x['type'] == 'text']
326
+ )
327
+
328
+
329
+ class Claude3(Anthropic):
330
+ """Base class for Claude 3 models. 200K input tokens and 4K output tokens."""
331
+ multimodal = True
332
+
333
+
334
+ class Claude35Sonnet(Claude3):
335
+ """A balance between between Opus and Haiku."""
336
+ model = 'claude-3-5-sonnet-20241022'
337
+
338
+
339
+ class Claude35Sonnet20241022(Claude3):
340
+ """A balance between between Opus and Haiku."""
341
+
342
+ model = 'claude-3-5-sonnet-20241022'
343
+
344
+
345
+ class Claude35Sonnet20240620(Claude3):
346
+ """A balance between between Opus and Haiku."""
347
+
348
+ model = 'claude-3-5-sonnet-20240620'
349
+
350
+
351
+ class Claude3Opus(Claude3):
352
+ """Anthropic's most powerful model."""
353
+
354
+ model = 'claude-3-opus-20240229'
355
+
356
+
357
+ class Claude3Sonnet(Claude3):
358
+ """A balance between between Opus and Haiku."""
359
+
360
+ model = 'claude-3-sonnet-20240229'
361
+
362
+
363
+ class Claude3Haiku(Claude3):
364
+ """Anthropic's most compact model."""
365
+
366
+ model = 'claude-3-haiku-20240307'
367
+
368
+
369
+ class Claude2(Anthropic):
370
+ """Predecessor to Claude 3 with 100K context window.."""
371
+ model = 'claude-2.0'
372
+
373
+
374
+ class Claude21(Anthropic):
375
+ """Updated Claude 2 model with improved accuracy and 200K context window."""
376
+ model = 'claude-2.1'
377
+
378
+
379
+ class ClaudeInstant(Anthropic):
380
+ """Cheapest small and fast model, 100K context window."""
381
+ model = 'claude-instant-1.2'
382
+
383
+
384
+ #
385
+ # Authropic models on VertexAI.
386
+ #
387
+
388
+
389
+ class VertexAIAnthropic(Anthropic):
390
+ """Anthropic models on VertexAI."""
391
+
392
+ project: Annotated[
393
+ str | None,
394
+ 'Google Cloud project ID.',
395
+ ] = None
396
+
397
+ location: Annotated[
398
+ Literal['us-east5', 'europe-west1'],
399
+ 'GCP location with Anthropic models hosted.'
400
+ ] = 'us-east5'
401
+
402
+ credentials: Annotated[
403
+ Credentials | None, # pytype: disable=invalid-annotation
404
+ (
405
+ 'Credentials to use. If None, the default credentials '
406
+ 'to the environment will be used.'
407
+ ),
408
+ ] = None
409
+
410
+ api_version = 'vertex-2023-10-16'
411
+
412
+ def _on_bound(self):
413
+ super()._on_bound()
414
+ if google_auth is None:
415
+ raise ValueError(
416
+ 'Please install "langfun[llm-google-vertex]" to use Vertex AI models.'
417
+ )
418
+ self._project = None
419
+ self._credentials = None
420
+
421
+ def _initialize(self):
422
+ project = self.project or os.environ.get('VERTEXAI_PROJECT', None)
423
+ if not project:
424
+ raise ValueError(
425
+ 'Please specify `project` during `__init__` or set environment '
426
+ 'variable `VERTEXAI_PROJECT` with your Vertex AI project ID.'
427
+ )
428
+ self._project = project
429
+ credentials = self.credentials
430
+ if credentials is None:
431
+ # Use default credentials.
432
+ credentials = google_auth.default(
433
+ scopes=['https://www.googleapis.com/auth/cloud-platform']
434
+ )
435
+ self._credentials = credentials
436
+
437
+ @functools.cached_property
438
+ def _session(self):
439
+ assert self._api_initialized
440
+ assert self._credentials is not None
441
+ assert auth_requests is not None
442
+ s = auth_requests.AuthorizedSession(self._credentials)
443
+ s.headers.update(self.headers or {})
444
+ return s
445
+
446
+ @property
447
+ def headers(self):
448
+ return {
449
+ 'Content-Type': 'application/json; charset=utf-8',
450
+ }
451
+
452
+ @property
453
+ def api_endpoint(self) -> str:
454
+ return (
455
+ f'https://{self.location}-aiplatform.googleapis.com/v1/projects/'
456
+ f'{self._project}/locations/{self.location}/publishers/anthropic/'
457
+ f'models/{self.model}:streamRawPredict'
458
+ )
459
+
460
+ def request(
461
+ self,
462
+ prompt: lf.Message,
463
+ sampling_options: lf.LMSamplingOptions
464
+ ):
465
+ request = super().request(prompt, sampling_options)
466
+ request['anthropic_version'] = self.api_version
467
+ del request['model']
468
+ return request
469
+
470
+
471
+ class VertexAIClaude3_Opus_20240229(VertexAIAnthropic): # pylint: disable=invalid-name
472
+ """Anthropic's Claude 3 Opus model on VertexAI."""
473
+ model = 'claude-3-opus@20240229'
474
+
475
+
476
+ class VertexAIClaude3_5_Sonnet_20241022(VertexAIAnthropic): # pylint: disable=invalid-name
477
+ """Anthropic's Claude 3.5 Sonnet model on VertexAI."""
478
+ model = 'claude-3-5-sonnet-v2@20241022'
479
+
480
+
481
+ class VertexAIClaude3_5_Sonnet_20240620(VertexAIAnthropic): # pylint: disable=invalid-name
482
+ """Anthropic's Claude 3.5 Sonnet model on VertexAI."""
483
+ model = 'claude-3-5-sonnet@20240620'
484
+
485
+
486
+ class VertexAIClaude3_5_Haiku_20241022(VertexAIAnthropic): # pylint: disable=invalid-name
487
+ """Anthropic's Claude 3.5 Haiku model on VertexAI."""
488
+ model = 'claude-3-5-haiku@20241022'