langfun 0.0.2.dev20240531__py3-none-any.whl → 0.0.2.dev20240601__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/__init__.py +2 -0
- langfun/core/__init__.py +1 -0
- langfun/core/llms/google_genai.py +66 -13
- langfun/core/llms/google_genai_test.py +1 -1
- langfun/core/llms/vertexai.py +67 -14
- langfun/core/llms/vertexai_test.py +1 -1
- langfun/core/modalities/__init__.py +1 -1
- langfun/core/modalities/audio.py +1 -1
- langfun/core/modalities/image.py +1 -1
- langfun/core/modalities/image_test.py +23 -6
- langfun/core/modalities/mime.py +105 -16
- langfun/core/modalities/mime_test.py +18 -3
- langfun/core/modalities/ms_office.py +38 -10
- langfun/core/modalities/ms_office_test.py +93 -16
- langfun/core/modalities/pdf.py +1 -1
- langfun/core/modalities/video.py +1 -1
- langfun/core/modality.py +4 -0
- {langfun-0.0.2.dev20240531.dist-info → langfun-0.0.2.dev20240601.dist-info}/METADATA +4 -3
- {langfun-0.0.2.dev20240531.dist-info → langfun-0.0.2.dev20240601.dist-info}/RECORD +22 -22
- {langfun-0.0.2.dev20240531.dist-info → langfun-0.0.2.dev20240601.dist-info}/LICENSE +0 -0
- {langfun-0.0.2.dev20240531.dist-info → langfun-0.0.2.dev20240601.dist-info}/WHEEL +0 -0
- {langfun-0.0.2.dev20240531.dist-info → langfun-0.0.2.dev20240601.dist-info}/top_level.txt +0 -0
langfun/__init__.py
CHANGED
langfun/core/__init__.py
CHANGED
@@ -94,6 +94,7 @@ from langfun.core.message import MemoryRecord
|
|
94
94
|
# Interface for modality.
|
95
95
|
from langfun.core.modality import Modality
|
96
96
|
from langfun.core.modality import ModalityRef
|
97
|
+
from langfun.core.modality import ModalityError
|
97
98
|
|
98
99
|
# Interfaces for languge models.
|
99
100
|
from langfun.core.language_model import LanguageModel
|
@@ -49,9 +49,10 @@ class GenAI(lf.LanguageModel):
|
|
49
49
|
),
|
50
50
|
] = None
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
supported_modalities: Annotated[
|
53
|
+
list[str],
|
54
|
+
'A list of MIME types for supported modalities'
|
55
|
+
] = []
|
55
56
|
|
56
57
|
# Set the default max concurrency to 8 workers.
|
57
58
|
max_concurrency = 8
|
@@ -118,14 +119,27 @@ class GenAI(lf.LanguageModel):
|
|
118
119
|
chunks = []
|
119
120
|
for lf_chunk in formatted.chunk():
|
120
121
|
if isinstance(lf_chunk, str):
|
121
|
-
|
122
|
-
elif
|
123
|
-
|
124
|
-
|
125
|
-
|
122
|
+
chunks.append(lf_chunk)
|
123
|
+
elif isinstance(lf_chunk, lf_modalities.Mime):
|
124
|
+
try:
|
125
|
+
modalities = lf_chunk.make_compatible(
|
126
|
+
self.supported_modalities + ['text/plain']
|
127
|
+
)
|
128
|
+
if isinstance(modalities, lf_modalities.Mime):
|
129
|
+
modalities = [modalities]
|
130
|
+
for modality in modalities:
|
131
|
+
if modality.is_text:
|
132
|
+
chunk = modality.to_text()
|
133
|
+
else:
|
134
|
+
chunk = genai.types.BlobDict(
|
135
|
+
data=modality.to_bytes(),
|
136
|
+
mime_type=modality.mime_type
|
137
|
+
)
|
138
|
+
chunks.append(chunk)
|
139
|
+
except lf.ModalityError as e:
|
140
|
+
raise lf.ModalityError(f'Unsupported modality: {lf_chunk!r}') from e
|
126
141
|
else:
|
127
|
-
raise
|
128
|
-
chunks.append(chunk)
|
142
|
+
raise lf.ModalityError(f'Unsupported modality: {lf_chunk!r}')
|
129
143
|
return chunks
|
130
144
|
|
131
145
|
def _response_to_result(
|
@@ -264,18 +278,57 @@ _GOOGLE_GENAI_MODEL_HUB = _ModelHub()
|
|
264
278
|
#
|
265
279
|
|
266
280
|
|
281
|
+
_IMAGE_TYPES = [
|
282
|
+
'image/png',
|
283
|
+
'image/jpeg',
|
284
|
+
'image/webp',
|
285
|
+
'image/heic',
|
286
|
+
'image/heif',
|
287
|
+
]
|
288
|
+
|
289
|
+
_AUDIO_TYPES = [
|
290
|
+
'audio/aac',
|
291
|
+
'audio/flac',
|
292
|
+
'audio/mp3',
|
293
|
+
'audio/m4a',
|
294
|
+
'audio/mpeg',
|
295
|
+
'audio/mpga',
|
296
|
+
'audio/mp4',
|
297
|
+
'audio/opus',
|
298
|
+
'audio/pcm',
|
299
|
+
'audio/wav',
|
300
|
+
'audio/webm'
|
301
|
+
]
|
302
|
+
|
303
|
+
_VIDEO_TYPES = [
|
304
|
+
'video/mov',
|
305
|
+
'video/mpeg',
|
306
|
+
'video/mpegps',
|
307
|
+
'video/mpg',
|
308
|
+
'video/mp4',
|
309
|
+
'video/webm',
|
310
|
+
'video/wmv',
|
311
|
+
'video/x-flv',
|
312
|
+
'video/3gpp',
|
313
|
+
]
|
314
|
+
|
315
|
+
_PDF = [
|
316
|
+
'application/pdf',
|
317
|
+
]
|
318
|
+
|
319
|
+
|
267
320
|
class GeminiPro1_5(GenAI): # pylint: disable=invalid-name
|
268
321
|
"""Gemini Pro latest model."""
|
269
322
|
|
270
323
|
model = 'gemini-1.5-pro-latest'
|
271
|
-
|
324
|
+
supported_modalities = _PDF + _IMAGE_TYPES + _AUDIO_TYPES + _VIDEO_TYPES
|
272
325
|
|
273
326
|
|
274
327
|
class GeminiFlash1_5(GenAI): # pylint: disable=invalid-name
|
275
328
|
"""Gemini Flash latest model."""
|
276
329
|
|
277
330
|
model = 'gemini-1.5-flash-latest'
|
278
|
-
|
331
|
+
supported_modalities = _PDF + _IMAGE_TYPES + _AUDIO_TYPES + _VIDEO_TYPES
|
279
332
|
|
280
333
|
|
281
334
|
class GeminiPro(GenAI):
|
@@ -288,7 +341,7 @@ class GeminiProVision(GenAI):
|
|
288
341
|
"""Gemini Pro vision model."""
|
289
342
|
|
290
343
|
model = 'gemini-pro-vision'
|
291
|
-
|
344
|
+
supported_modalities = _IMAGE_TYPES + _VIDEO_TYPES
|
292
345
|
|
293
346
|
|
294
347
|
class Palm2(GenAI):
|
@@ -107,7 +107,7 @@ class GenAITest(unittest.TestCase):
|
|
107
107
|
)
|
108
108
|
|
109
109
|
# Non-multimodal model.
|
110
|
-
with self.assertRaisesRegex(
|
110
|
+
with self.assertRaisesRegex(lf.ModalityError, 'Unsupported modality'):
|
111
111
|
google_genai.GeminiPro()._content_from_message(message)
|
112
112
|
|
113
113
|
model = google_genai.GeminiProVision()
|
langfun/core/llms/vertexai.py
CHANGED
@@ -75,9 +75,10 @@ class VertexAI(lf.LanguageModel):
|
|
75
75
|
),
|
76
76
|
] = None
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
supported_modalities: Annotated[
|
79
|
+
list[str],
|
80
|
+
'A list of MIME types for supported modalities'
|
81
|
+
] = []
|
81
82
|
|
82
83
|
def _on_bound(self):
|
83
84
|
super()._on_bound()
|
@@ -142,16 +143,29 @@ class VertexAI(lf.LanguageModel):
|
|
142
143
|
"""Gets generation input from langfun message."""
|
143
144
|
from vertexai import generative_models
|
144
145
|
chunks = []
|
146
|
+
|
145
147
|
for lf_chunk in prompt.chunk():
|
146
148
|
if isinstance(lf_chunk, str):
|
147
|
-
|
148
|
-
elif
|
149
|
-
|
150
|
-
|
151
|
-
|
149
|
+
chunks.append(lf_chunk)
|
150
|
+
elif isinstance(lf_chunk, lf_modalities.Mime):
|
151
|
+
try:
|
152
|
+
modalities = lf_chunk.make_compatible(
|
153
|
+
self.supported_modalities + ['text/plain']
|
154
|
+
)
|
155
|
+
if isinstance(modalities, lf_modalities.Mime):
|
156
|
+
modalities = [modalities]
|
157
|
+
for modality in modalities:
|
158
|
+
if modality.is_text:
|
159
|
+
chunk = modality.to_text()
|
160
|
+
else:
|
161
|
+
chunk = generative_models.Part.from_data(
|
162
|
+
modality.to_bytes(), modality.mime_type
|
163
|
+
)
|
164
|
+
chunks.append(chunk)
|
165
|
+
except lf.ModalityError as e:
|
166
|
+
raise lf.ModalityError(f'Unsupported modality: {lf_chunk!r}') from e
|
152
167
|
else:
|
153
|
-
raise
|
154
|
-
chunks.append(chunk)
|
168
|
+
raise lf.ModalityError(f'Unsupported modality: {lf_chunk!r}')
|
155
169
|
return chunks
|
156
170
|
|
157
171
|
def _generation_response_to_message(
|
@@ -265,25 +279,64 @@ class _ModelHub:
|
|
265
279
|
_VERTEXAI_MODEL_HUB = _ModelHub()
|
266
280
|
|
267
281
|
|
282
|
+
_IMAGE_TYPES = [
|
283
|
+
'image/png',
|
284
|
+
'image/jpeg',
|
285
|
+
'image/webp',
|
286
|
+
'image/heic',
|
287
|
+
'image/heif',
|
288
|
+
]
|
289
|
+
|
290
|
+
_AUDIO_TYPES = [
|
291
|
+
'audio/aac',
|
292
|
+
'audio/flac',
|
293
|
+
'audio/mp3',
|
294
|
+
'audio/m4a',
|
295
|
+
'audio/mpeg',
|
296
|
+
'audio/mpga',
|
297
|
+
'audio/mp4',
|
298
|
+
'audio/opus',
|
299
|
+
'audio/pcm',
|
300
|
+
'audio/wav',
|
301
|
+
'audio/webm'
|
302
|
+
]
|
303
|
+
|
304
|
+
_VIDEO_TYPES = [
|
305
|
+
'video/mov',
|
306
|
+
'video/mpeg',
|
307
|
+
'video/mpegps',
|
308
|
+
'video/mpg',
|
309
|
+
'video/mp4',
|
310
|
+
'video/webm',
|
311
|
+
'video/wmv',
|
312
|
+
'video/x-flv',
|
313
|
+
'video/3gpp',
|
314
|
+
]
|
315
|
+
|
316
|
+
_PDF = [
|
317
|
+
'application/pdf',
|
318
|
+
]
|
319
|
+
|
320
|
+
|
268
321
|
class VertexAIGeminiPro1_5(VertexAI): # pylint: disable=invalid-name
|
269
322
|
"""Vertex AI Gemini 1.5 Pro model."""
|
270
323
|
|
271
324
|
model = 'gemini-1.5-pro-preview-0514'
|
272
|
-
|
325
|
+
supported_modalities = _PDF + _IMAGE_TYPES + _AUDIO_TYPES + _VIDEO_TYPES
|
273
326
|
|
274
327
|
|
275
328
|
class VertexAIGeminiPro1_5_0409(VertexAI): # pylint: disable=invalid-name
|
276
329
|
"""Vertex AI Gemini 1.5 Pro model."""
|
277
330
|
|
278
331
|
model = 'gemini-1.5-pro-preview-0409'
|
279
|
-
|
332
|
+
supported_modalities = _PDF + _IMAGE_TYPES + _AUDIO_TYPES + _VIDEO_TYPES
|
280
333
|
|
281
334
|
|
282
335
|
class VertexAIGeminiFlash1_5(VertexAI): # pylint: disable=invalid-name
|
283
336
|
"""Vertex AI Gemini 1.5 Flash model."""
|
284
337
|
|
285
338
|
model = 'gemini-1.5-flash-preview-0514'
|
286
|
-
|
339
|
+
supported_modalities = _PDF + _IMAGE_TYPES + _AUDIO_TYPES + _VIDEO_TYPES
|
287
340
|
|
288
341
|
|
289
342
|
class VertexAIGeminiPro1(VertexAI): # pylint: disable=invalid-name
|
@@ -296,7 +349,7 @@ class VertexAIGeminiPro1Vision(VertexAI): # pylint: disable=invalid-name
|
|
296
349
|
"""Vertex AI Gemini 1.0 Pro model."""
|
297
350
|
|
298
351
|
model = 'gemini-1.0-pro-vision'
|
299
|
-
|
352
|
+
supported_modalities = _IMAGE_TYPES + _VIDEO_TYPES
|
300
353
|
|
301
354
|
|
302
355
|
class VertexAIPalm2(VertexAI): # pylint: disable=invalid-name
|
@@ -79,7 +79,7 @@ class VertexAITest(unittest.TestCase):
|
|
79
79
|
)
|
80
80
|
|
81
81
|
# Non-multimodal model.
|
82
|
-
with self.assertRaisesRegex(
|
82
|
+
with self.assertRaisesRegex(lf.ModalityError, 'Unsupported modality'):
|
83
83
|
vertexai.VertexAIGeminiPro1()._content_from_message(message)
|
84
84
|
|
85
85
|
model = vertexai.VertexAIGeminiPro1Vision()
|
@@ -18,7 +18,7 @@
|
|
18
18
|
# pylint: disable=g-import-not-at-top
|
19
19
|
|
20
20
|
from langfun.core.modalities.audio import Audio
|
21
|
-
from langfun.core.modalities.mime import
|
21
|
+
from langfun.core.modalities.mime import Mime
|
22
22
|
from langfun.core.modalities.mime import Custom
|
23
23
|
from langfun.core.modalities.ms_office import Docx
|
24
24
|
from langfun.core.modalities.ms_office import Pptx
|
langfun/core/modalities/audio.py
CHANGED
langfun/core/modalities/image.py
CHANGED
@@ -15,7 +15,9 @@
|
|
15
15
|
import unittest
|
16
16
|
from unittest import mock
|
17
17
|
|
18
|
+
import langfun.core as lf
|
18
19
|
from langfun.core.modalities import image as image_lib
|
20
|
+
from langfun.core.modalities import mime as mime_lib
|
19
21
|
import pyglove as pg
|
20
22
|
|
21
23
|
|
@@ -36,23 +38,29 @@ def mock_request(*args, **kwargs):
|
|
36
38
|
return pg.Dict(content=image_content)
|
37
39
|
|
38
40
|
|
39
|
-
class
|
41
|
+
class ImageTest(unittest.TestCase):
|
40
42
|
|
41
|
-
def
|
43
|
+
def test_from_bytes(self):
|
42
44
|
image = image_lib.Image.from_bytes(image_content)
|
43
45
|
self.assertEqual(image.image_format, 'png')
|
44
46
|
self.assertIn('data:image/png;base64,', image._repr_html_())
|
45
47
|
self.assertEqual(image.to_bytes(), image_content)
|
48
|
+
with self.assertRaisesRegex(
|
49
|
+
lf.ModalityError, '.* cannot be converted to text'
|
50
|
+
):
|
51
|
+
image.to_text()
|
46
52
|
|
47
|
-
def
|
53
|
+
def test_from_bytes_invalid(self):
|
48
54
|
image = image_lib.Image.from_bytes(b'bad')
|
49
55
|
with self.assertRaisesRegex(ValueError, 'Expected MIME type'):
|
50
56
|
_ = image.image_format
|
51
57
|
|
58
|
+
def test_from_bytes_base_cls(self):
|
59
|
+
self.assertIsInstance(
|
60
|
+
mime_lib.Mime.from_bytes(image_content), image_lib.Image
|
61
|
+
)
|
52
62
|
|
53
|
-
|
54
|
-
|
55
|
-
def test_image_file(self):
|
63
|
+
def test_from_uri(self):
|
56
64
|
image = image_lib.Image.from_uri('http://mock/web/a.png')
|
57
65
|
with mock.patch('requests.get') as mock_requests_get:
|
58
66
|
mock_requests_get.side_effect = mock_request
|
@@ -60,6 +68,15 @@ class ImageFileTest(unittest.TestCase):
|
|
60
68
|
self.assertEqual(image._repr_html_(), '<img src="http://mock/web/a.png">')
|
61
69
|
self.assertEqual(image.to_bytes(), image_content)
|
62
70
|
|
71
|
+
def test_from_uri_base_cls(self):
|
72
|
+
with mock.patch('requests.get') as mock_requests_get:
|
73
|
+
mock_requests_get.side_effect = mock_request
|
74
|
+
image = mime_lib.Mime.from_uri('http://mock/web/a.png')
|
75
|
+
self.assertIsInstance(image, image_lib.Image)
|
76
|
+
self.assertEqual(image.image_format, 'png')
|
77
|
+
self.assertEqual(image._repr_html_(), '<img src="http://mock/web/a.png">')
|
78
|
+
self.assertEqual(image.to_bytes(), image_content)
|
79
|
+
|
63
80
|
|
64
81
|
if __name__ == '__main__':
|
65
82
|
unittest.main()
|
langfun/core/modalities/mime.py
CHANGED
@@ -15,15 +15,15 @@
|
|
15
15
|
|
16
16
|
import base64
|
17
17
|
import functools
|
18
|
-
from typing import Annotated, Union
|
18
|
+
from typing import Annotated, Iterable, Type, Union
|
19
19
|
import langfun.core as lf
|
20
20
|
import magic
|
21
21
|
import pyglove as pg
|
22
22
|
import requests
|
23
23
|
|
24
24
|
|
25
|
-
class
|
26
|
-
"""Base for MIME
|
25
|
+
class Mime(lf.Modality):
|
26
|
+
"""Base for MIME data."""
|
27
27
|
|
28
28
|
# The regular expression that describes the MIME type str.
|
29
29
|
# If None, the MIME type is dynamic. Subclass could override.
|
@@ -39,12 +39,80 @@ class MimeType(lf.Modality):
|
|
39
39
|
def mime_type(self) -> str:
|
40
40
|
"""Returns the MIME type."""
|
41
41
|
mime = magic.from_buffer((self.to_bytes()), mime=True)
|
42
|
-
if
|
42
|
+
if (
|
43
|
+
self.MIME_PREFIX
|
44
|
+
and not mime.lower().startswith(self.MIME_PREFIX)
|
45
|
+
# NOTE(daiyip): libmagic fails to detect the MIME type of some binary
|
46
|
+
# files.
|
47
|
+
and mime != 'application/octet-stream'
|
48
|
+
):
|
43
49
|
raise ValueError(
|
44
50
|
f'Expected MIME type: {self.MIME_PREFIX}, Encountered: {mime}'
|
45
51
|
)
|
46
52
|
return mime
|
47
53
|
|
54
|
+
@functools.cached_property
|
55
|
+
def is_text(self) -> bool:
|
56
|
+
return self.mime_type.startswith(
|
57
|
+
(
|
58
|
+
'text/',
|
59
|
+
'application/javascript',
|
60
|
+
'application/json',
|
61
|
+
'application/ld+json',
|
62
|
+
'application/plain',
|
63
|
+
'application/xhtml+xml',
|
64
|
+
'application/xml',
|
65
|
+
'application/x-tex',
|
66
|
+
'application/x-yaml',
|
67
|
+
)
|
68
|
+
)
|
69
|
+
|
70
|
+
@property
|
71
|
+
def is_binary(self) -> bool:
|
72
|
+
"""Returns True if the MIME type is a binary type."""
|
73
|
+
return not self.is_text
|
74
|
+
|
75
|
+
def to_text(self) -> str:
|
76
|
+
"""Returns the text content of the MIME type."""
|
77
|
+
if not self.is_text:
|
78
|
+
raise lf.ModalityError(
|
79
|
+
f'MIME type {self.mime_type!r} cannot be converted to text.'
|
80
|
+
)
|
81
|
+
return self.to_bytes().decode()
|
82
|
+
|
83
|
+
def is_compatible(
|
84
|
+
self, mime_types: str | Iterable[str]
|
85
|
+
) -> bool:
|
86
|
+
"""Returns True if this object is compatible to any of the MIME types."""
|
87
|
+
if isinstance(mime_types, str):
|
88
|
+
mime_types = {mime_types}
|
89
|
+
return self._is_compatible(mime_types)
|
90
|
+
|
91
|
+
def _is_compatible(self, mime_types: Iterable[str]):
|
92
|
+
return self.mime_type in mime_types
|
93
|
+
|
94
|
+
def make_compatible(
|
95
|
+
self,
|
96
|
+
mime_types: str | Iterable[str]
|
97
|
+
) -> Union['Mime', list['Mime']]:
|
98
|
+
"""Makes compatible MIME objects from this object."""
|
99
|
+
if isinstance(mime_types, str):
|
100
|
+
mime_types = {mime_types}
|
101
|
+
if not self._is_compatible(mime_types):
|
102
|
+
raise lf.ModalityError(
|
103
|
+
f'MIME type {self.mime_type!r} cannot be converted to supported '
|
104
|
+
f'types: {mime_types!r}.'
|
105
|
+
)
|
106
|
+
return self._make_compatible(mime_types)
|
107
|
+
|
108
|
+
def _make_compatible(
|
109
|
+
self,
|
110
|
+
mime_types: Iterable[str]
|
111
|
+
) -> Union['Mime', list['Mime']]:
|
112
|
+
"""Makes compatbile MIME objects from this object."""
|
113
|
+
del mime_types
|
114
|
+
return self
|
115
|
+
|
48
116
|
def _on_bound(self):
|
49
117
|
super()._on_bound()
|
50
118
|
if self.uri is None and self.content is None:
|
@@ -54,15 +122,7 @@ class MimeType(lf.Modality):
|
|
54
122
|
if self.content is not None:
|
55
123
|
return self.content
|
56
124
|
|
57
|
-
|
58
|
-
if self.uri.lower().startswith(('http:', 'https:', 'ftp:')):
|
59
|
-
content = requests.get(
|
60
|
-
self.uri,
|
61
|
-
headers={'User-Agent': 'Langfun'},
|
62
|
-
).content
|
63
|
-
else:
|
64
|
-
content = pg.io.readfile(self.uri, mode='rb')
|
65
|
-
self.rebind(content=content, skip_notification=True)
|
125
|
+
self.rebind(content=self.download(self.uri), skip_notification=True)
|
66
126
|
return self.content
|
67
127
|
|
68
128
|
@property
|
@@ -71,13 +131,42 @@ class MimeType(lf.Modality):
|
|
71
131
|
return f'data:{self.mime_type};base64,{base64_content}'
|
72
132
|
|
73
133
|
@classmethod
|
74
|
-
def from_uri(cls, uri: str, **kwargs) -> '
|
134
|
+
def from_uri(cls, uri: str, **kwargs) -> 'Mime':
|
135
|
+
if cls is Mime:
|
136
|
+
content = cls.download(uri)
|
137
|
+
mime = magic.from_buffer(content, mime=True).lower()
|
138
|
+
return cls.class_from_mime_type(mime)(uri=uri, content=content, **kwargs)
|
75
139
|
return cls(uri=uri, content=None, **kwargs)
|
76
140
|
|
77
141
|
@classmethod
|
78
|
-
def from_bytes(cls, content: bytes | str, **kwargs) -> '
|
142
|
+
def from_bytes(cls, content: bytes | str, **kwargs) -> 'Mime':
|
143
|
+
if cls is Mime:
|
144
|
+
mime = magic.from_buffer(content, mime=True).lower()
|
145
|
+
return cls.class_from_mime_type(mime)(content=content, **kwargs)
|
79
146
|
return cls(content=content, **kwargs)
|
80
147
|
|
148
|
+
@classmethod
|
149
|
+
def class_from_mime_type(cls, mime_type: str) -> Type['Mime']:
|
150
|
+
"""Subclass from the given MIME type."""
|
151
|
+
for subcls in cls.__subclasses__():
|
152
|
+
if subcls.MIME_PREFIX is not None and mime_type.startswith(
|
153
|
+
subcls.MIME_PREFIX):
|
154
|
+
return subcls
|
155
|
+
return cls
|
156
|
+
|
157
|
+
@classmethod
|
158
|
+
def download(cls, uri: str) -> bytes | str:
|
159
|
+
"""Downloads the content of the given URI."""
|
160
|
+
if uri.lower().startswith(('http:', 'https:', 'ftp:')):
|
161
|
+
return requests.get(
|
162
|
+
uri,
|
163
|
+
headers={'User-Agent': 'Mozilla/5.0'},
|
164
|
+
).content
|
165
|
+
else:
|
166
|
+
content = pg.io.readfile(uri, mode='rb')
|
167
|
+
assert content is not None
|
168
|
+
return content
|
169
|
+
|
81
170
|
def _repr_html_(self) -> str:
|
82
171
|
if self.uri and self.uri.lower().startswith(('http:', 'https:', 'ftp:')):
|
83
172
|
uri = self.uri
|
@@ -90,7 +179,7 @@ class MimeType(lf.Modality):
|
|
90
179
|
|
91
180
|
|
92
181
|
@pg.use_init_args(['mime', 'content', 'uri'])
|
93
|
-
class Custom(
|
182
|
+
class Custom(Mime):
|
94
183
|
"""Custom MIME data."""
|
95
184
|
|
96
185
|
mime: Annotated[
|
@@ -15,6 +15,7 @@
|
|
15
15
|
import unittest
|
16
16
|
from unittest import mock
|
17
17
|
|
18
|
+
import langfun.core as lf
|
18
19
|
from langfun.core.modalities import mime
|
19
20
|
import pyglove as pg
|
20
21
|
|
@@ -31,10 +32,24 @@ def mock_readfile(*args, **kwargs):
|
|
31
32
|
|
32
33
|
class CustomMimeTest(unittest.TestCase):
|
33
34
|
|
34
|
-
def
|
35
|
-
content = mime.
|
36
|
-
self.
|
35
|
+
def test_from_byes(self):
|
36
|
+
content = mime.Mime.from_bytes(b'hello')
|
37
|
+
self.assertIs(content.__class__, mime.Mime)
|
38
|
+
|
39
|
+
content = mime.Custom('text/plain', b'foo')
|
40
|
+
self.assertEqual(content.to_bytes(), b'foo')
|
37
41
|
self.assertEqual(content.mime_type, 'text/plain')
|
42
|
+
self.assertTrue(content.is_text)
|
43
|
+
self.assertFalse(content.is_binary)
|
44
|
+
self.assertEqual(content.to_text(), 'foo')
|
45
|
+
self.assertTrue(content.is_compatible('text/plain'))
|
46
|
+
self.assertFalse(content.is_compatible('text/xml'))
|
47
|
+
self.assertIs(content.make_compatible('text/plain'), content)
|
48
|
+
|
49
|
+
with self.assertRaisesRegex(
|
50
|
+
lf.ModalityError, '.* cannot be converted to supported types'
|
51
|
+
):
|
52
|
+
content.make_compatible('application/pdf')
|
38
53
|
|
39
54
|
with self.assertRaisesRegex(
|
40
55
|
ValueError, 'Either uri or content must be provided.'
|
@@ -16,12 +16,13 @@
|
|
16
16
|
import base64
|
17
17
|
import io
|
18
18
|
import os
|
19
|
+
from typing import Iterable
|
19
20
|
from langfun.core.modalities import mime
|
20
21
|
from langfun.core.modalities import pdf
|
21
22
|
import requests
|
22
23
|
|
23
24
|
|
24
|
-
class Xlsx(mime.
|
25
|
+
class Xlsx(mime.Mime):
|
25
26
|
"""Xlsx file type."""
|
26
27
|
|
27
28
|
MIME_PREFIX = (
|
@@ -37,8 +38,19 @@ class Xlsx(mime.MimeType):
|
|
37
38
|
def _repr_html_(self) -> str:
|
38
39
|
return self.to_html()
|
39
40
|
|
41
|
+
def _is_compatible(self, mime_types: Iterable[str]) -> bool:
|
42
|
+
return bool(set(mime_types).intersection([
|
43
|
+
'text/html',
|
44
|
+
'text/plain',
|
45
|
+
]))
|
40
46
|
|
41
|
-
|
47
|
+
def _make_compatible(self, mime_types: Iterable[str]) -> mime.Mime:
|
48
|
+
"""Returns the MimeType of the converted file."""
|
49
|
+
del mime_types
|
50
|
+
return mime.Mime(uri=self.uri, content=self.to_html())
|
51
|
+
|
52
|
+
|
53
|
+
class Docx(mime.Mime):
|
42
54
|
"""Docx file type."""
|
43
55
|
|
44
56
|
MIME_PREFIX = (
|
@@ -54,17 +66,26 @@ class Docx(mime.MimeType):
|
|
54
66
|
def _repr_html_(self) -> str:
|
55
67
|
return self.to_xml()
|
56
68
|
|
69
|
+
def _is_compatible(self, mime_types: Iterable[str]) -> bool:
|
70
|
+
return bool(set(mime_types).intersection([
|
71
|
+
'application/xml',
|
72
|
+
'text/xml',
|
73
|
+
'text/plain',
|
74
|
+
]))
|
75
|
+
|
76
|
+
def _make_compatible(self, mime_types: Iterable[str]) -> mime.Mime:
|
77
|
+
"""Returns the MimeType of the converted file."""
|
78
|
+
del mime_types
|
79
|
+
return mime.Mime(uri=self.uri, content=self.to_xml())
|
80
|
+
|
57
81
|
|
58
|
-
class Pptx(mime.
|
82
|
+
class Pptx(mime.Mime):
|
59
83
|
"""Pptx file type."""
|
60
84
|
|
61
85
|
MIME_PREFIX = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
62
86
|
API_URL = 'https://v2.convertapi.com/convert/pptx/to/pdf'
|
63
87
|
|
64
88
|
def to_pdf(self, convert_api_key: str | None = None) -> pdf.PDF:
|
65
|
-
filename = os.path.basename(self.uri)
|
66
|
-
file_bytes = self.to_bytes()
|
67
|
-
|
68
89
|
api_key = convert_api_key or os.environ.get('CONVERT_API_KEY')
|
69
90
|
url = f'{self.API_URL}?Secret={api_key}'
|
70
91
|
|
@@ -72,12 +93,19 @@ class Pptx(mime.MimeType):
|
|
72
93
|
'Parameters': [{
|
73
94
|
'Name': 'File',
|
74
95
|
'FileValue': {
|
75
|
-
'Name':
|
76
|
-
'Data': base64.b64encode(
|
96
|
+
'Name': os.path.basename(self.uri) if self.uri else 'tmp.pptx',
|
97
|
+
'Data': base64.b64encode(self.to_bytes()).decode('utf-8'),
|
77
98
|
},
|
78
99
|
}]
|
79
100
|
}
|
80
101
|
response = requests.post(url, json=json).json()
|
81
102
|
base64_pdf = response['Files'][0]['FileData']
|
82
|
-
|
83
|
-
|
103
|
+
return pdf.PDF.from_bytes(base64.b64decode(base64_pdf))
|
104
|
+
|
105
|
+
def _is_compatible(self, mime_types: Iterable[str]) -> bool:
|
106
|
+
return 'application/pdf' in mime_types
|
107
|
+
|
108
|
+
def _make_compatible(self, mime_types: Iterable[str]) -> mime.Mime:
|
109
|
+
"""Returns the MimeType of the converted file."""
|
110
|
+
del mime_types
|
111
|
+
return self.to_pdf()
|
@@ -12,11 +12,13 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
"""Video tests."""
|
15
|
+
import base64
|
15
16
|
import io
|
16
17
|
import unittest
|
17
18
|
from unittest import mock
|
18
19
|
|
19
20
|
from langfun.core.modalities import ms_office as ms_office_lib
|
21
|
+
from langfun.core.modalities import pdf as pdf_lib
|
20
22
|
import pyglove as pg
|
21
23
|
|
22
24
|
|
@@ -243,23 +245,72 @@ def pptx_mock_request(*args, **kwargs):
|
|
243
245
|
return pg.Dict(content=pptx_bytes)
|
244
246
|
|
245
247
|
|
248
|
+
pdf_bytes = (
|
249
|
+
b'%PDF-1.1\n%\xc2\xa5\xc2\xb1\xc3\xab\n\n1 0 obj\n'
|
250
|
+
b'<< /Type /Catalog\n /Pages 2 0 R\n >>\nendobj\n\n2 0 obj\n '
|
251
|
+
b'<< /Type /Pages\n /Kids [3 0 R]\n '
|
252
|
+
b'/Count 1\n /MediaBox [0 0 300 144]\n '
|
253
|
+
b'>>\nendobj\n\n3 0 obj\n '
|
254
|
+
b'<< /Type /Page\n /Parent 2 0 R\n /Resources\n '
|
255
|
+
b'<< /Font\n'
|
256
|
+
b'<< /F1\n'
|
257
|
+
b'<< /Type /Font\n'
|
258
|
+
b'/Subtype /Type1\n'
|
259
|
+
b'/BaseFont /Times-Roman\n'
|
260
|
+
b'>>\n>>\n>>\n '
|
261
|
+
b'/Contents 4 0 R\n >>\nendobj\n\n4 0 obj\n '
|
262
|
+
b'<< /Length 55 >>\nstream\n BT\n /F1 18 Tf\n 0 0 Td\n '
|
263
|
+
b'(Hello World) Tj\n ET\nendstream\nendobj\n\nxref\n0 5\n0000000000 '
|
264
|
+
b'65535 f \n0000000018 00000 n \n0000000077 00000 n \n0000000178 00000 n '
|
265
|
+
b'\n0000000457 00000 n \ntrailer\n << /Root 1 0 R\n /Size 5\n '
|
266
|
+
b'>>\nstartxref\n565\n%%EOF\n'
|
267
|
+
)
|
268
|
+
|
269
|
+
|
270
|
+
def convert_mock_request(*args, **kwargs):
|
271
|
+
del args, kwargs
|
272
|
+
|
273
|
+
class Result:
|
274
|
+
def json(self):
|
275
|
+
return {
|
276
|
+
'Files': [
|
277
|
+
{
|
278
|
+
'FileData': base64.b64encode(pdf_bytes).decode()
|
279
|
+
}
|
280
|
+
]
|
281
|
+
}
|
282
|
+
return Result()
|
283
|
+
|
284
|
+
|
246
285
|
class DocxTest(unittest.TestCase):
|
247
286
|
|
248
|
-
def
|
287
|
+
def test_from_bytes(self):
|
249
288
|
content = ms_office_lib.Docx.from_bytes(docx_bytes)
|
250
|
-
self.
|
289
|
+
self.assertIn(
|
251
290
|
content.mime_type,
|
252
|
-
|
291
|
+
(
|
292
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
293
|
+
'application/octet-stream',
|
294
|
+
),
|
253
295
|
)
|
254
296
|
self.assertEqual(content.to_bytes(), docx_bytes)
|
297
|
+
self.assertTrue(content.is_compatible('text/plain'))
|
298
|
+
self.assertFalse(content.is_compatible('application/pdf'))
|
299
|
+
self.assertEqual(
|
300
|
+
content.make_compatible(['image/png', 'text/plain']).mime_type,
|
301
|
+
'text/plain'
|
302
|
+
)
|
255
303
|
|
256
|
-
def
|
304
|
+
def test_from_uri(self):
|
257
305
|
content = ms_office_lib.Docx.from_uri('http://mock/web/a.docx')
|
258
306
|
with mock.patch('requests.get') as mock_requests_get:
|
259
307
|
mock_requests_get.side_effect = docx_mock_request
|
260
|
-
self.
|
308
|
+
self.assertIn(
|
261
309
|
content.mime_type,
|
262
|
-
|
310
|
+
(
|
311
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
312
|
+
'application/octet-stream',
|
313
|
+
),
|
263
314
|
)
|
264
315
|
self.assertEqual(content.to_bytes(), docx_bytes)
|
265
316
|
self.assertEqual(content.to_xml(), expected_docx_xml)
|
@@ -267,21 +318,33 @@ class DocxTest(unittest.TestCase):
|
|
267
318
|
|
268
319
|
class XlsxTest(unittest.TestCase):
|
269
320
|
|
270
|
-
def
|
321
|
+
def test_from_bytes(self):
|
271
322
|
content = ms_office_lib.Xlsx.from_bytes(xlsx_bytes)
|
272
|
-
self.
|
323
|
+
self.assertIn(
|
273
324
|
content.mime_type,
|
274
|
-
|
325
|
+
(
|
326
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
327
|
+
'application/octet-stream',
|
328
|
+
),
|
275
329
|
)
|
276
330
|
self.assertEqual(content.to_bytes(), xlsx_bytes)
|
331
|
+
self.assertTrue(content.is_compatible('text/plain'))
|
332
|
+
self.assertFalse(content.is_compatible('application/pdf'))
|
333
|
+
self.assertEqual(
|
334
|
+
content.make_compatible('text/plain').mime_type,
|
335
|
+
'text/html'
|
336
|
+
)
|
277
337
|
|
278
|
-
def
|
338
|
+
def test_from_uri(self):
|
279
339
|
content = ms_office_lib.Xlsx.from_uri('http://mock/web/a.xlsx')
|
280
340
|
with mock.patch('requests.get') as mock_requests_get:
|
281
341
|
mock_requests_get.side_effect = xlsx_mock_request
|
282
|
-
self.
|
342
|
+
self.assertIn(
|
283
343
|
content.mime_type,
|
284
|
-
|
344
|
+
(
|
345
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
346
|
+
'application/octet-stream',
|
347
|
+
),
|
285
348
|
)
|
286
349
|
self.assertEqual(content.to_bytes(), xlsx_bytes)
|
287
350
|
self.assertEqual(content.to_html(), expected_xlsx_html)
|
@@ -291,22 +354,36 @@ class PptxTest(unittest.TestCase):
|
|
291
354
|
|
292
355
|
def test_content(self):
|
293
356
|
content = ms_office_lib.Pptx.from_bytes(pptx_bytes)
|
294
|
-
self.
|
357
|
+
self.assertIn(
|
295
358
|
content.mime_type,
|
296
|
-
|
359
|
+
(
|
360
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
361
|
+
'application/octet-stream',
|
362
|
+
),
|
297
363
|
)
|
298
364
|
self.assertEqual(content.to_bytes(), pptx_bytes)
|
299
365
|
|
300
366
|
def test_file(self):
|
301
367
|
content = ms_office_lib.Pptx.from_uri('http://mock/web/a.pptx')
|
368
|
+
self.assertFalse(content.is_compatible('text/plain'))
|
369
|
+
self.assertTrue(content.is_compatible('application/pdf'))
|
302
370
|
with mock.patch('requests.get') as mock_requests_get:
|
303
371
|
mock_requests_get.side_effect = pptx_mock_request
|
304
|
-
self.
|
372
|
+
self.assertIn(
|
305
373
|
content.mime_type,
|
306
|
-
|
374
|
+
(
|
375
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
376
|
+
'application/octet-stream',
|
377
|
+
),
|
307
378
|
)
|
308
379
|
self.assertEqual(content.to_bytes(), pptx_bytes)
|
309
380
|
|
381
|
+
with mock.patch('requests.post') as mock_requests_post:
|
382
|
+
mock_requests_post.side_effect = convert_mock_request
|
383
|
+
self.assertIsInstance(
|
384
|
+
content.make_compatible('application/pdf'), pdf_lib.PDF
|
385
|
+
)
|
386
|
+
|
310
387
|
|
311
388
|
if __name__ == '__main__':
|
312
389
|
unittest.main()
|
langfun/core/modalities/pdf.py
CHANGED
langfun/core/modalities/video.py
CHANGED
langfun/core/modality.py
CHANGED
@@ -108,3 +108,7 @@ class ModalityRef(pg.Object, pg.typing.CustomTyping):
|
|
108
108
|
return ModalityRef(name=value.sym_path + k)
|
109
109
|
return v
|
110
110
|
return value.clone().rebind(_placehold, raise_on_no_change=False)
|
111
|
+
|
112
|
+
|
113
|
+
class ModalityError(RuntimeError): # pylint: disable=g-bad-exception-name
|
114
|
+
"""Exception raised when modality is not supported."""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langfun
|
3
|
-
Version: 0.0.2.
|
3
|
+
Version: 0.0.2.dev20240601
|
4
4
|
Summary: Langfun: Language as Functions.
|
5
5
|
Home-page: https://github.com/google/langfun
|
6
6
|
Author: Langfun Authors
|
@@ -25,13 +25,14 @@ Requires-Dist: google-cloud-aiplatform >=1.5.0
|
|
25
25
|
Requires-Dist: google-generativeai >=0.3.2
|
26
26
|
Requires-Dist: jinja2 >=3.1.2
|
27
27
|
Requires-Dist: openai ==0.27.2
|
28
|
+
Requires-Dist: openpyxl >=3.1.0
|
29
|
+
Requires-Dist: pandas >=2.1.4
|
28
30
|
Requires-Dist: pyglove >=0.4.5.dev20240423
|
31
|
+
Requires-Dist: python-docx >=0.8.11
|
29
32
|
Requires-Dist: python-magic >=0.4.27
|
30
33
|
Requires-Dist: requests >=2.31.0
|
31
34
|
Requires-Dist: termcolor ==1.1.0
|
32
35
|
Requires-Dist: tqdm >=4.64.1
|
33
|
-
Requires-Dist: python-docx >=0.8.11
|
34
|
-
Requires-Dist: pandas >=2.1.4
|
35
36
|
|
36
37
|
<div align="center">
|
37
38
|
<img src="https://raw.githubusercontent.com/google/langfun/main/docs/_static/logo.svg" width="520px" alt="logo"></img>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
langfun/__init__.py,sha256=
|
2
|
-
langfun/core/__init__.py,sha256=
|
1
|
+
langfun/__init__.py,sha256=LFsDp22pTeJHmzzKEg2OLmSVOPAym00DyF38LmrL2n4,2263
|
2
|
+
langfun/core/__init__.py,sha256=nFJx6X7oB7IIWsAQqjbgZ_ScH-gsKg53YgAkuDvY0cw,4296
|
3
3
|
langfun/core/component.py,sha256=oxesbC0BoE_TbtxwW5x-BAZWxZyyJbuPiX5S38RqCv0,9909
|
4
4
|
langfun/core/component_test.py,sha256=uR-_Sz_42Jxc5qzLIB-f5_pXmNwnC01Xlbv5NOQSeSU,8021
|
5
5
|
langfun/core/concurrent.py,sha256=TRc49pJ3HQro2kb5FtcWkHjhBm8UcgE8RJybU5cU3-0,24537
|
@@ -13,7 +13,7 @@ langfun/core/language_model_test.py,sha256=NZaSUls6cZdtxiqkqumWbtkx9zgNiJlsviYZO
|
|
13
13
|
langfun/core/memory.py,sha256=f-asN1F7Vehgdn_fK84v73GrEUOxRtaW934keutTKjk,2416
|
14
14
|
langfun/core/message.py,sha256=Rw3yC9HyGRjMhfDgyNjGlSCALEyDDbJ0_o6qTXeeDiQ,15738
|
15
15
|
langfun/core/message_test.py,sha256=b6DDRoQ5j3uK-dc0QPSLelNTKaXX10MxJrRiI61iGX4,9574
|
16
|
-
langfun/core/modality.py,sha256
|
16
|
+
langfun/core/modality.py,sha256=Tla4t86DUYHpbZ2G7dy1r19fTj_Ga5XOvlYp6lbWa-Q,3512
|
17
17
|
langfun/core/modality_test.py,sha256=HyZ5xONKQ0Fw18SzoWAq-Ob9njOXIIjBo1hNtw-rudw,2400
|
18
18
|
langfun/core/natural_language.py,sha256=3ynSnaYQnjE60LIPK5fyMgdIjubnPYZwzGq4rWPeloE,1177
|
19
19
|
langfun/core/natural_language_test.py,sha256=LHGU_1ytbkGuSZQFIFP7vP3dBlcY4-A12fT6dbjUA0E,1424
|
@@ -53,16 +53,16 @@ langfun/core/llms/anthropic.py,sha256=7W9YdPN3SlAFhAIQlihMkrpo7tTY_4NvD0KIlCrqcs
|
|
53
53
|
langfun/core/llms/anthropic_test.py,sha256=TMM30myyEhwF99Le4RvJEXOn8RYl0q1FRkt9Q9nl1jk,5540
|
54
54
|
langfun/core/llms/fake.py,sha256=Dd7-6ka9pFf3fcWZyczamjOqQ91MOI-m7We3Oc9Ffmo,2927
|
55
55
|
langfun/core/llms/fake_test.py,sha256=ipKfdOcuqVcJ8lDXVpnBVb9HHG0hAVkFkMoHpWjC2cI,7212
|
56
|
-
langfun/core/llms/google_genai.py,sha256=
|
57
|
-
langfun/core/llms/google_genai_test.py,sha256=
|
56
|
+
langfun/core/llms/google_genai.py,sha256=Rl5a5CyF_6Y0BYYArKk8yMaenv1rH3MUQLy6b3dfMRI,10202
|
57
|
+
langfun/core/llms/google_genai_test.py,sha256=iTISk3tJ4-3gjWmzcKQhEbH3ke4AkEiCu8rAGtB7SvU,7535
|
58
58
|
langfun/core/llms/groq.py,sha256=NaGItVL_pkOpqPpI4bPGU27xLFRoaeizZ49v2s-4ERs,7844
|
59
59
|
langfun/core/llms/groq_test.py,sha256=M6GtlrsOvDun_j-sR8cPh4W_moHWZNSTiThu3kuwbbc,5281
|
60
60
|
langfun/core/llms/llama_cpp.py,sha256=Y_KkMUf3Xfac49koMUtUslKl3h-HWp3-ntq7Jaa3bdo,2385
|
61
61
|
langfun/core/llms/llama_cpp_test.py,sha256=ZxC6defGd_HX9SFRU9U4cJiQnBKundbOrchbXuC1Z2M,1683
|
62
62
|
langfun/core/llms/openai.py,sha256=IN46gIqfY6aEEfxCPNmyH1hrep3oWBhJDwVFilfqNkM,13657
|
63
63
|
langfun/core/llms/openai_test.py,sha256=QWDzTgi8F2Z9u9ip6alK4rDEp_YraVTxWlDX5XOsKJk,14858
|
64
|
-
langfun/core/llms/vertexai.py,sha256=
|
65
|
-
langfun/core/llms/vertexai_test.py,sha256=
|
64
|
+
langfun/core/llms/vertexai.py,sha256=eILbXoMSza5r4FLGlIdH6-eD8Ggy9Z4PdjLaBDxy29A,11162
|
65
|
+
langfun/core/llms/vertexai_test.py,sha256=G18BG36h5KvmX2zutDTLjtYCRjTuP_nWIFm4FMnLnyY,7651
|
66
66
|
langfun/core/llms/cache/__init__.py,sha256=QAo3InUMDM_YpteNnVCSejI4zOsnjSMWKJKzkb3VY64,993
|
67
67
|
langfun/core/llms/cache/base.py,sha256=cFfYvOIUae842pncqCAsRvqXCk2AnAsRYVx0mcIoAeY,3338
|
68
68
|
langfun/core/llms/cache/in_memory.py,sha256=YfFyJEhLs73cUiB0ZfhMxYpdE8Iuxxw-dvMFwGHTSHw,4742
|
@@ -70,18 +70,18 @@ langfun/core/llms/cache/in_memory_test.py,sha256=D-n26h__rVXQO51WRFhRfq5sw1oifRL
|
|
70
70
|
langfun/core/memories/__init__.py,sha256=HpghfZ-w1NQqzJXBx8Lz0daRhB2rcy2r9Xm491SBhC4,773
|
71
71
|
langfun/core/memories/conversation_history.py,sha256=c9amD8hCxGFiZuVAzkP0dOMWSp8L90uvwkOejjuBqO0,1835
|
72
72
|
langfun/core/memories/conversation_history_test.py,sha256=AaW8aNoFjxNusanwJDV0r3384Mg0eAweGmPx5DIkM0Y,2052
|
73
|
-
langfun/core/modalities/__init__.py,sha256=
|
74
|
-
langfun/core/modalities/audio.py,sha256=
|
73
|
+
langfun/core/modalities/__init__.py,sha256=F8P72IwFiTpEseTR2tYEJyQMlDW7fd9csvGJquLKJNg,1269
|
74
|
+
langfun/core/modalities/audio.py,sha256=Qxo7bYjLKQ1gVJVomr9RqR2SvxY826QgXhTzzk437Sk,952
|
75
75
|
langfun/core/modalities/audio_test.py,sha256=gWCB9h3FyrdGqro3ajBXqkw0lU0W1sBjOOq6wZbl7Fg,2027
|
76
|
-
langfun/core/modalities/image.py,sha256=
|
77
|
-
langfun/core/modalities/image_test.py,sha256=
|
78
|
-
langfun/core/modalities/mime.py,sha256=
|
79
|
-
langfun/core/modalities/mime_test.py,sha256=
|
80
|
-
langfun/core/modalities/ms_office.py,sha256=
|
81
|
-
langfun/core/modalities/ms_office_test.py,sha256=
|
82
|
-
langfun/core/modalities/pdf.py,sha256=
|
76
|
+
langfun/core/modalities/image.py,sha256=qi7B9uYLxBoKvMzApdOQNpVcp_dKaRwLzeshg2nvo9k,926
|
77
|
+
langfun/core/modalities/image_test.py,sha256=qU7G4ucUihIQ9ZB453FsUfcOipUYx5TnnuoMB1GIMfE,3034
|
78
|
+
langfun/core/modalities/mime.py,sha256=yMpbBAhf7MmEPJm9qj7tTn7_XionZQ4XkgTT8StA7io,5836
|
79
|
+
langfun/core/modalities/mime_test.py,sha256=ruEro7Joima2r-zOuQfO0NzBvmaweSQ6F6jsf-w4Bns,2468
|
80
|
+
langfun/core/modalities/ms_office.py,sha256=jOidMSdWCaV9RILpGz8VJkpTSpHJNoirD53jzQvcytM,3388
|
81
|
+
langfun/core/modalities/ms_office_test.py,sha256=d_NZ0QU23NydenYZgNj6YxgO5ZYzjg-HCbglsVJGp04,87866
|
82
|
+
langfun/core/modalities/pdf.py,sha256=mfaeCbUA4JslFVTARiJh8hW7imvL4tLVw9gUhO5bAZA,727
|
83
83
|
langfun/core/modalities/pdf_test.py,sha256=KE40zJD3Whe6ty2OULkp1J8jwLmB4ZjGXlGekluTP48,1952
|
84
|
-
langfun/core/modalities/video.py,sha256=
|
84
|
+
langfun/core/modalities/video.py,sha256=sKcXxbx9S1ERjH8yEzkbtySpcRJD40QiPIQiIBy-U5I,955
|
85
85
|
langfun/core/modalities/video_test.py,sha256=GbsoefSeO7y8kCYhTtp4s9E3ah_eYrb6Z-MXpS01RFc,2046
|
86
86
|
langfun/core/structured/__init__.py,sha256=yp60yeDSVlyT0ElmLwbpBHnQtk_JX5udnjG1UGcsXKA,3776
|
87
87
|
langfun/core/structured/completion.py,sha256=RzWdHyaqKj-tj6mGwpHXk0s8YbM0UEHSpyT2axmj-o8,7343
|
@@ -111,8 +111,8 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
|
|
111
111
|
langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
|
112
112
|
langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
|
113
113
|
langfun/core/templates/selfplay_test.py,sha256=DYVrkk7uNKCqJGEHH31HssU2BPuMItU1vJLzfcXIlYg,2156
|
114
|
-
langfun-0.0.2.
|
115
|
-
langfun-0.0.2.
|
116
|
-
langfun-0.0.2.
|
117
|
-
langfun-0.0.2.
|
118
|
-
langfun-0.0.2.
|
114
|
+
langfun-0.0.2.dev20240601.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
115
|
+
langfun-0.0.2.dev20240601.dist-info/METADATA,sha256=V6qAAPX1gt2gDEInFQKJIvYe48IbEztRw5qwpgq_QH0,3550
|
116
|
+
langfun-0.0.2.dev20240601.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
117
|
+
langfun-0.0.2.dev20240601.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
|
118
|
+
langfun-0.0.2.dev20240601.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|