langfun 0.1.2.dev202510230805__py3-none-any.whl → 0.1.2.dev202510250803__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of langfun might be problematic. Click here for more details.
- langfun/core/concurrent_test.py +1 -0
- langfun/core/data/conversion/anthropic_test.py +8 -6
- langfun/core/data/conversion/gemini_test.py +12 -9
- langfun/core/data/conversion/openai.py +134 -30
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base_test.py +4 -4
- langfun/core/eval/v2/progress_tracking_test.py +3 -0
- langfun/core/langfunc_test.py +6 -4
- langfun/core/language_model.py +15 -6
- langfun/core/language_model_test.py +9 -3
- langfun/core/llms/__init__.py +7 -1
- langfun/core/llms/anthropic.py +130 -0
- langfun/core/llms/cache/base.py +3 -1
- langfun/core/llms/cache/in_memory_test.py +14 -4
- langfun/core/llms/deepseek.py +1 -1
- langfun/core/llms/gemini.py +2 -5
- langfun/core/llms/groq.py +1 -1
- langfun/core/llms/llama_cpp.py +1 -1
- langfun/core/llms/openai.py +7 -2
- langfun/core/llms/openai_compatible.py +136 -27
- langfun/core/llms/openai_compatible_test.py +207 -20
- langfun/core/llms/openai_test.py +0 -2
- langfun/core/llms/vertexai.py +12 -2
- langfun/core/message.py +78 -44
- langfun/core/message_test.py +56 -81
- langfun/core/modalities/__init__.py +8 -0
- langfun/core/modalities/mime.py +9 -0
- langfun/core/modality.py +104 -27
- langfun/core/modality_test.py +42 -12
- langfun/core/sampling_test.py +20 -4
- langfun/core/structured/completion.py +2 -7
- langfun/core/structured/completion_test.py +23 -43
- langfun/core/structured/mapping.py +4 -13
- langfun/core/structured/querying.py +13 -11
- langfun/core/structured/querying_test.py +65 -29
- langfun/core/template.py +39 -13
- langfun/core/template_test.py +83 -17
- langfun/env/event_handlers/metric_writer_test.py +3 -3
- langfun/env/load_balancers_test.py +2 -2
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202510250803.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202510250803.dist-info}/RECORD +44 -44
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202510250803.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202510250803.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202510230805.dist-info → langfun-0.1.2.dev202510250803.dist-info}/top_level.txt +0 -0
langfun/core/message_test.py
CHANGED
|
@@ -60,10 +60,19 @@ class MessageTest(unittest.TestCase):
|
|
|
60
60
|
self.assertTrue(
|
|
61
61
|
pg.eq(message.UserMessage.from_value('hi'), message.UserMessage('hi'))
|
|
62
62
|
)
|
|
63
|
+
foo = CustomModality('foo')
|
|
63
64
|
self.assertTrue(
|
|
64
65
|
pg.eq(
|
|
65
|
-
message.UserMessage.from_value(
|
|
66
|
-
message.UserMessage('<<[[
|
|
66
|
+
message.UserMessage.from_value(foo),
|
|
67
|
+
message.UserMessage(f'<<[[{foo.id}]]>>', referred_modalities=[foo]),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
self.assertTrue(
|
|
71
|
+
pg.eq(
|
|
72
|
+
message.UserMessage.from_value(foo),
|
|
73
|
+
message.UserMessage(
|
|
74
|
+
f'<<[[{foo.id}]]>>', referred_modalities={foo.id: foo}
|
|
75
|
+
),
|
|
67
76
|
)
|
|
68
77
|
)
|
|
69
78
|
m = message.UserMessage('hi')
|
|
@@ -224,76 +233,39 @@ class MessageTest(unittest.TestCase):
|
|
|
224
233
|
self.assertEqual(str(m), m.text)
|
|
225
234
|
|
|
226
235
|
def test_get_modality(self):
|
|
236
|
+
foo = CustomModality('foo')
|
|
237
|
+
bar = CustomModality('bar')
|
|
227
238
|
m1 = message.UserMessage(
|
|
228
|
-
'hi
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
'hi',
|
|
240
|
+
referred_modalities={
|
|
241
|
+
foo.id: foo,
|
|
242
|
+
bar.id: pg.Ref(bar),
|
|
243
|
+
},
|
|
231
244
|
)
|
|
232
|
-
self.assertIs(m1.get_modality(
|
|
233
|
-
self.assertIs(m1.get_modality(
|
|
245
|
+
self.assertIs(m1.get_modality(foo.id), foo)
|
|
246
|
+
self.assertIs(m1.get_modality(bar.id), bar)
|
|
234
247
|
self.assertIsNone(m1.get_modality('video'))
|
|
248
|
+
self.assertEqual(len(m1.modalities()), 2)
|
|
249
|
+
self.assertEqual(len(m1.modalities(CustomModality)), 2)
|
|
235
250
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
# We could get the modality object even it's not directly used by current
|
|
239
|
-
# message.
|
|
240
|
-
self.assertIs(m2.get_modality('x.img2'), m1.x.img2)
|
|
241
|
-
self.assertIsNone(m2.get_modality('video'))
|
|
242
|
-
|
|
243
|
-
m3 = message.AIMessage(
|
|
244
|
-
'This is the {{output_image}} based on {{x.img2}}',
|
|
245
|
-
output_image=CustomModality('bar'),
|
|
246
|
-
source=m2,
|
|
247
|
-
)
|
|
248
|
-
self.assertIs(m3.get_modality('x.img2'), m1.x.img2)
|
|
249
|
-
self.assertIs(m3.get_modality('output_image'), m3.output_image)
|
|
250
|
-
self.assertIsNone(m3.get_modality('video'))
|
|
251
|
-
|
|
252
|
-
def test_referred_modalities(self):
|
|
253
|
-
m1 = message.UserMessage(
|
|
254
|
-
'hi, this is a <<[[img1]]>> and <<[[x.img2]]>>',
|
|
255
|
-
img1=CustomModality('foo'),
|
|
256
|
-
x=dict(img2=CustomModality('bar')),
|
|
257
|
-
)
|
|
258
|
-
m2 = message.SystemMessage('class Question:\n image={{img1}}', source=m1)
|
|
259
|
-
m3 = message.AIMessage(
|
|
260
|
-
(
|
|
261
|
-
'This is the <<[[output_image]]>> based on <<[[x.img2]]>>, '
|
|
262
|
-
'{{unknown_var}}'
|
|
263
|
-
),
|
|
264
|
-
output_image=CustomModality('bar'),
|
|
265
|
-
source=m2,
|
|
266
|
-
)
|
|
267
|
-
self.assertEqual(
|
|
268
|
-
m3.referred_modalities(),
|
|
269
|
-
{
|
|
270
|
-
'output_image': m3.output_image,
|
|
271
|
-
'x.img2': m1.x.img2,
|
|
272
|
-
},
|
|
273
|
-
)
|
|
251
|
+
class MyModality(modality.Modality):
|
|
252
|
+
pass
|
|
274
253
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
'hi, this is a <<[[img1]]>> and <<[[x.img2]]>>',
|
|
278
|
-
img1=CustomModality('foo'),
|
|
279
|
-
x=dict(img2=CustomModality('bar')),
|
|
280
|
-
)
|
|
281
|
-
self.assertEqual(
|
|
282
|
-
m.text_with_modality_hash,
|
|
283
|
-
(
|
|
284
|
-
'hi, this is a <<[[img1]]>> and <<[[x.img2]]>>'
|
|
285
|
-
'<img1>acbd18db</img1><x.img2>37b51d19</x.img2>'
|
|
286
|
-
)
|
|
287
|
-
)
|
|
254
|
+
self.assertEqual(len(m1.modalities(MyModality)), 0)
|
|
255
|
+
self.assertEqual(len(m1.modalities(lambda x: x.content == 'foo')), 1)
|
|
288
256
|
|
|
289
257
|
def test_chunking(self):
|
|
258
|
+
foo = CustomModality('foo')
|
|
259
|
+
bar = CustomModality('bar')
|
|
290
260
|
m = message.UserMessage(
|
|
291
|
-
inspect.cleandoc("""
|
|
292
|
-
Hi, this is <<[[
|
|
293
|
-
<<[[
|
|
261
|
+
inspect.cleandoc(f"""
|
|
262
|
+
Hi, this is <<[[{foo.id}]]>> and this is {{b}}.
|
|
263
|
+
<<[[{bar.id}]]>> something else
|
|
294
264
|
"""),
|
|
295
|
-
|
|
296
|
-
|
|
265
|
+
referred_modalities={
|
|
266
|
+
foo.id: pg.Ref(foo),
|
|
267
|
+
bar.id: pg.Ref(bar),
|
|
268
|
+
},
|
|
297
269
|
)
|
|
298
270
|
chunks = m.chunk()
|
|
299
271
|
self.assertTrue(
|
|
@@ -301,10 +273,10 @@ class MessageTest(unittest.TestCase):
|
|
|
301
273
|
chunks,
|
|
302
274
|
[
|
|
303
275
|
'Hi, this is',
|
|
304
|
-
|
|
305
|
-
'and this is {
|
|
306
|
-
|
|
307
|
-
'
|
|
276
|
+
foo,
|
|
277
|
+
'and this is {b}.\n',
|
|
278
|
+
bar,
|
|
279
|
+
'something else',
|
|
308
280
|
],
|
|
309
281
|
)
|
|
310
282
|
)
|
|
@@ -312,15 +284,17 @@ class MessageTest(unittest.TestCase):
|
|
|
312
284
|
pg.eq(
|
|
313
285
|
message.AIMessage.from_chunks(chunks),
|
|
314
286
|
message.AIMessage(
|
|
315
|
-
|
|
316
|
-
Hi, this is <<[[
|
|
317
|
-
<<[[
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
287
|
+
(
|
|
288
|
+
f'Hi, this is <<[[{foo.id}]]>> and this '
|
|
289
|
+
f'is {{b}}.\n<<[[{bar.id}]]>> '
|
|
290
|
+
'something else'
|
|
291
|
+
),
|
|
292
|
+
referred_modalities=[foo, bar],
|
|
321
293
|
),
|
|
322
294
|
)
|
|
323
295
|
)
|
|
296
|
+
with self.assertRaisesRegex(ValueError, 'Unknown modality reference'):
|
|
297
|
+
message.UserMessage('<<[[abc]]>>').chunk()
|
|
324
298
|
|
|
325
299
|
def assert_html_content(self, html, expected):
|
|
326
300
|
expected = inspect.cleandoc(expected).strip()
|
|
@@ -404,25 +378,26 @@ class MessageTest(unittest.TestCase):
|
|
|
404
378
|
<details open class="pyglove user-message lf-message"><summary><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"></div><div class="message-text">what is a <div></div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div></div></details>
|
|
405
379
|
"""
|
|
406
380
|
)
|
|
381
|
+
image = CustomModality('bird')
|
|
407
382
|
self.assert_html_content(
|
|
408
383
|
message.UserMessage(
|
|
409
|
-
'what is this <<[[image]]>>',
|
|
384
|
+
f'what is this <<[[{image.id}]]>>',
|
|
410
385
|
tags=['lm-input'],
|
|
411
|
-
image
|
|
386
|
+
referred_modalities=[image],
|
|
412
387
|
).to_html(
|
|
413
388
|
enable_summary_tooltip=False,
|
|
414
389
|
extra_flags=dict(include_message_metadata=False)
|
|
415
390
|
),
|
|
416
391
|
"""
|
|
417
|
-
<details open class="pyglove user-message lf-message"><summary><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">what is this<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">
|
|
392
|
+
<details open class="pyglove user-message lf-message"><summary><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">what is this<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">custom_modality:abaecf8c<span class="tooltip"></span></div><div class="summary-title">CustomModality(...)</div></summary><div class="complex-value custom-modality"><details open class="pyglove str"><summary><div class="summary-name">content<span class="tooltip">content</span></div><div class="summary-title">str</div></summary><span class="simple-value str">'bird'</span></details></div></details></div></div></div></details>
|
|
418
393
|
"""
|
|
419
394
|
)
|
|
420
395
|
|
|
421
396
|
def test_html_ai_message(self):
|
|
422
397
|
image = CustomModality('foo')
|
|
423
398
|
user_message = message.UserMessage(
|
|
424
|
-
'What is in this image? <<[[image]]>> this is a test',
|
|
425
|
-
|
|
399
|
+
f'What is in this image? <<[[{image.id}]]>> this is a test',
|
|
400
|
+
referred_modalities=[image],
|
|
426
401
|
source=message.UserMessage('User input'),
|
|
427
402
|
tags=['lm-input']
|
|
428
403
|
)
|
|
@@ -438,7 +413,7 @@ class MessageTest(unittest.TestCase):
|
|
|
438
413
|
self.assert_html_content(
|
|
439
414
|
ai_message.to_html(enable_summary_tooltip=False),
|
|
440
415
|
"""
|
|
441
|
-
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details open class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><details open class="pyglove int"><summary><div class="summary-name">x<span class="tooltip">metadata.result.x</span></div><div class="summary-title">int</div></summary><span class="simple-value int">1</span></details><details open class="pyglove int"><summary><div class="summary-name">y<span class="tooltip">metadata.result.y</span></div><div class="summary-title">int</div></summary><span class="simple-value int">2</span></details><details class="pyglove dict"><summary><div class="summary-name">z<span class="tooltip">metadata.result.z</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><details class="pyglove list"><summary><div class="summary-name">a<span class="tooltip">metadata.result.z.a</span></div><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></div></details></div></details></div><div class="message-usage"><details open class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details open class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">
|
|
416
|
+
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details open class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><details open class="pyglove int"><summary><div class="summary-name">x<span class="tooltip">metadata.result.x</span></div><div class="summary-title">int</div></summary><span class="simple-value int">1</span></details><details open class="pyglove int"><summary><div class="summary-name">y<span class="tooltip">metadata.result.y</span></div><div class="summary-title">int</div></summary><span class="simple-value int">2</span></details><details class="pyglove dict"><summary><div class="summary-name">z<span class="tooltip">metadata.result.z</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><details class="pyglove list"><summary><div class="summary-name">a<span class="tooltip">metadata.result.z.a</span></div><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></div></details></div></details></div><div class="message-usage"><details open class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details open class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">custom_modality:acbd18db<span class="tooltip"></span></div><div class="summary-title">CustomModality(...)</div></summary><div class="complex-value custom-modality"><details open class="pyglove str"><summary><div class="summary-name">content<span class="tooltip">content</span></div><div class="summary-title">str</div></summary><span class="simple-value str">'foo'</span></details></div></details></div>this is a test</div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">source.metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div></div></details></div></details>
|
|
442
417
|
"""
|
|
443
418
|
)
|
|
444
419
|
self.assert_html_content(
|
|
@@ -455,7 +430,7 @@ class MessageTest(unittest.TestCase):
|
|
|
455
430
|
),
|
|
456
431
|
),
|
|
457
432
|
"""
|
|
458
|
-
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">x</span><span class="tooltip">metadata.result.x</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">y</span><span class="tooltip">metadata.result.y</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">z</span><span class="tooltip">metadata.result.z</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">a</span><span class="tooltip">metadata.result.z.a</span></td><td><details class="pyglove list"><summary><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></td></tr></table></div></details></td></tr></table></div></details></div><div class="message-usage"><details class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details open class="pyglove custom-modality"><summary><div class="summary-name">
|
|
433
|
+
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">x</span><span class="tooltip">metadata.result.x</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">y</span><span class="tooltip">metadata.result.y</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">z</span><span class="tooltip">metadata.result.z</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">a</span><span class="tooltip">metadata.result.z.a</span></td><td><details class="pyglove list"><summary><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></td></tr></table></div></details></td></tr></table></div></details></div><div class="message-usage"><details class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details open class="pyglove custom-modality"><summary><div class="summary-name">custom_modality:acbd18db<span class="tooltip"></span></div><div class="summary-title">CustomModality(...)</div></summary><div class="complex-value custom-modality"><table><tr><td><span class="object-key str">content</span><span class="tooltip">content</span></td><td><span class="simple-value str">'foo'</span></td></tr></table></div></details></div>this is a test</div><div class="message-metadata"><details class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">source.metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source.source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"></div><div class="message-text">User input</div><div class="message-metadata"><details class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">source.source.metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div></div></details></div></details></div></details>
|
|
459
434
|
"""
|
|
460
435
|
)
|
|
461
436
|
self.assert_html_content(
|
|
@@ -472,7 +447,7 @@ class MessageTest(unittest.TestCase):
|
|
|
472
447
|
),
|
|
473
448
|
),
|
|
474
449
|
"""
|
|
475
|
-
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details open class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">x</span><span class="tooltip">metadata.result.x</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">y</span><span class="tooltip">metadata.result.y</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">z</span><span class="tooltip">metadata.result.z</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">a</span><span class="tooltip">metadata.result.z.a</span></td><td><details class="pyglove list"><summary><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></td></tr></table></div></details></td></tr></table></div></details></div><div class="message-usage"><details open class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details open class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">
|
|
450
|
+
<details open class="pyglove ai-message lf-message"><summary><div class="summary-title lf-message">AIMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-response</span><span>lm-output</span></div><div class="message-text">My name is Gemini</div><div class="message-result"><details open class="pyglove dict"><summary><div class="summary-name">result<span class="tooltip">metadata.result</span></div><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">x</span><span class="tooltip">metadata.result.x</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">y</span><span class="tooltip">metadata.result.y</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">z</span><span class="tooltip">metadata.result.z</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><table><tr><td><span class="object-key str">a</span><span class="tooltip">metadata.result.z.a</span></td><td><details class="pyglove list"><summary><div class="summary-title">List(...)</div></summary><div class="complex-value list"><table><tr><td><span class="object-key int">0</span><span class="tooltip">metadata.result.z.a[0]</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key int">1</span><span class="tooltip">metadata.result.z.a[1]</span></td><td><span class="simple-value int">323</span></td></tr></table></div></details></td></tr></table></div></details></td></tr></table></div></details></div><div class="message-usage"><details open class="pyglove lm-sampling-usage"><summary><div class="summary-name">llm usage<span class="tooltip">metadata.usage</span></div><div class="summary-title">LMSamplingUsage(...)</div></summary><div class="complex-value lm-sampling-usage"><table><tr><td><span class="object-key str">prompt_tokens</span><span class="tooltip">metadata.usage.prompt_tokens</span></td><td><span class="simple-value int">10</span></td></tr><tr><td><span class="object-key str">completion_tokens</span><span class="tooltip">metadata.usage.completion_tokens</span></td><td><span class="simple-value int">2</span></td></tr><tr><td><span class="object-key str">total_tokens</span><span class="tooltip">metadata.usage.total_tokens</span></td><td><span class="simple-value int">12</span></td></tr><tr><td><span class="object-key str">num_requests</span><span class="tooltip">metadata.usage.num_requests</span></td><td><span class="simple-value int">1</span></td></tr><tr><td><span class="object-key str">estimated_cost</span><span class="tooltip">metadata.usage.estimated_cost</span></td><td><span class="simple-value none-type">None</span></td></tr><tr><td><span class="object-key str">retry_stats</span><span class="tooltip">metadata.usage.retry_stats</span></td><td><details class="pyglove retry-stats"><summary><div class="summary-title">RetryStats(...)</div></summary><div class="complex-value retry-stats"><table><tr><td><span class="object-key str">num_occurences</span><span class="tooltip">metadata.usage.retry_stats.num_occurences</span></td><td><span class="simple-value int">0</span></td></tr><tr><td><span class="object-key str">total_wait_interval</span><span class="tooltip">metadata.usage.retry_stats.total_wait_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">total_call_interval</span><span class="tooltip">metadata.usage.retry_stats.total_call_interval</span></td><td><span class="simple-value float">0.0</span></td></tr><tr><td><span class="object-key str">errors</span><span class="tooltip">metadata.usage.retry_stats.errors</span></td><td><details class="pyglove dict"><summary><div class="summary-title">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></td></tr></table></div></details></td></tr><tr><td><span class="object-key str">completion_tokens_details</span><span class="tooltip">metadata.usage.completion_tokens_details</span></td><td><span class="simple-value none-type">None</span></td></tr></table></div></details></div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details open class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"><span>lm-input</span></div><div class="message-text">What is in this image?<div class="modality-in-text"><details class="pyglove custom-modality"><summary><div class="summary-name">custom_modality:acbd18db<span class="tooltip"></span></div><div class="summary-title">CustomModality(...)</div></summary><div class="complex-value custom-modality"><table><tr><td><span class="object-key str">content</span><span class="tooltip">content</span></td><td><span class="simple-value str">'foo'</span></td></tr></table></div></details></div>this is a test</div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">source.metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div><details open class="pyglove user-message lf-message"><summary><div class="summary-name lf-message">source<span class="tooltip lf-message">source.source</span></div><div class="summary-title lf-message">UserMessage(...)</div></summary><div class="complex_value"><div class="message-tags"></div><div class="message-text">User input</div><div class="message-metadata"><details open class="pyglove dict message-metadata"><summary><div class="summary-name message-metadata">metadata<span class="tooltip message-metadata">source.source.metadata</span></div><div class="summary-title message-metadata">Dict(...)</div></summary><div class="complex-value dict"><span class="empty-container"></span></div></details></div></div></details></div></details></div></details>
|
|
476
451
|
"""
|
|
477
452
|
)
|
|
478
453
|
|
|
@@ -24,6 +24,14 @@ from langfun.core.modalities.image import Image
|
|
|
24
24
|
from langfun.core.modalities.pdf import PDF
|
|
25
25
|
from langfun.core.modalities.video import Video
|
|
26
26
|
|
|
27
|
+
from langfun.core import message as _message_lib
|
|
28
|
+
|
|
29
|
+
# Override the `images`, `videos` and `audios` properties of `Message` to
|
|
30
|
+
# return the modalities of the corresponding types.
|
|
31
|
+
_message_lib.Message.images = property(lambda self: self.modalities(Image))
|
|
32
|
+
_message_lib.Message.videos = property(lambda self: self.modalities(Video))
|
|
33
|
+
_message_lib.Message.audios = property(lambda self: self.modalities(Audio))
|
|
34
|
+
|
|
27
35
|
# pylint: enable=g-import-not-at-top
|
|
28
36
|
# pylint: enable=g-bad-import-order
|
|
29
37
|
# pylint: enable=g-importing-member
|
langfun/core/modalities/mime.py
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import functools
|
|
18
|
+
import hashlib
|
|
18
19
|
from typing import Annotated, Any, Iterable, Type, Union
|
|
19
20
|
import langfun.core as lf
|
|
20
21
|
# Placeholder for Google-internal internet access import.
|
|
@@ -87,6 +88,14 @@ class Mime(lf.Modality):
|
|
|
87
88
|
"""Returns True if the MIME type is a binary type."""
|
|
88
89
|
return not self.is_text
|
|
89
90
|
|
|
91
|
+
@property
|
|
92
|
+
def hash(self) -> str:
|
|
93
|
+
"""Returns the hash of the MIME content."""
|
|
94
|
+
# Hash the URI to avoid downloading the content.
|
|
95
|
+
if self.uri is not None:
|
|
96
|
+
return hashlib.md5(self.uri.encode()).hexdigest()[:8]
|
|
97
|
+
return super().hash
|
|
98
|
+
|
|
90
99
|
def to_text(self) -> str:
|
|
91
100
|
"""Returns the text content of the MIME type."""
|
|
92
101
|
if not self.is_text:
|
langfun/core/modality.py
CHANGED
|
@@ -14,23 +14,15 @@
|
|
|
14
14
|
"""Interface for modality (e.g. Image, Video, etc.)."""
|
|
15
15
|
|
|
16
16
|
import abc
|
|
17
|
+
import contextlib
|
|
17
18
|
import functools
|
|
18
19
|
import hashlib
|
|
19
|
-
|
|
20
|
+
import re
|
|
21
|
+
from typing import Any, ContextManager, Iterator
|
|
20
22
|
from langfun.core import component
|
|
21
23
|
import pyglove as pg
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
_TLS_MODALITY_AS_REF = '__format_modality_as_ref__'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def format_modality_as_ref(enabled: bool = True) -> ContextManager[None]:
|
|
28
|
-
"""A context manager that formats modality objects as references."""
|
|
29
|
-
return pg.object_utils.thread_local_value_scope(
|
|
30
|
-
_TLS_MODALITY_AS_REF, enabled, False
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
26
|
class Modality(component.Component, pg.views.HtmlTreeView.Extension):
|
|
35
27
|
"""Base class for multimodal object."""
|
|
36
28
|
|
|
@@ -39,15 +31,18 @@ class Modality(component.Component, pg.views.HtmlTreeView.Extension):
|
|
|
39
31
|
|
|
40
32
|
def _on_bound(self):
|
|
41
33
|
super()._on_bound()
|
|
42
|
-
# Invalidate cached hash if modality member is changed.
|
|
34
|
+
# Invalidate cached hash and id if modality member is changed.
|
|
43
35
|
self.__dict__.pop('hash', None)
|
|
36
|
+
self.__dict__.pop('id', None)
|
|
44
37
|
|
|
45
38
|
def format(self, *args, **kwargs) -> str:
|
|
46
|
-
if
|
|
47
|
-
_TLS_MODALITY_AS_REF, False
|
|
48
|
-
):
|
|
39
|
+
if not pg.object_utils.thread_local_get(_TLS_MODALITY_AS_REF, False):
|
|
49
40
|
return super().format(*args, **kwargs)
|
|
50
|
-
|
|
41
|
+
|
|
42
|
+
capture_scope = get_modality_capture_context()
|
|
43
|
+
if capture_scope is not None:
|
|
44
|
+
capture_scope.capture(self)
|
|
45
|
+
return Modality.text_marker(self.id)
|
|
51
46
|
|
|
52
47
|
def __str_kwargs__(self) -> dict[str, Any]:
|
|
53
48
|
# For modality objects, we don't want to use markdown format when they
|
|
@@ -70,14 +65,11 @@ class Modality(component.Component, pg.views.HtmlTreeView.Extension):
|
|
|
70
65
|
"""Returns a marker in the text for this object."""
|
|
71
66
|
return Modality.REF_START + var_name + Modality.REF_END
|
|
72
67
|
|
|
73
|
-
@
|
|
74
|
-
def
|
|
68
|
+
@functools.cached_property
|
|
69
|
+
def id(self) -> str | None:
|
|
75
70
|
"""Returns the referred name of this object in its template."""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# Strip the metadata prefix under message.
|
|
79
|
-
path = str(self.sym_path)
|
|
80
|
-
return path[9:] if path.startswith('metadata.') else path
|
|
71
|
+
modality_type = _camel_to_snake(self.__class__.__name__)
|
|
72
|
+
return f'{modality_type}:{self.hash}'
|
|
81
73
|
|
|
82
74
|
@classmethod
|
|
83
75
|
def from_value(cls, value: pg.Symbolic) -> dict[str, 'Modality']:
|
|
@@ -86,7 +78,7 @@ class Modality(component.Component, pg.views.HtmlTreeView.Extension):
|
|
|
86
78
|
def _visit(k, v, p):
|
|
87
79
|
del k, p
|
|
88
80
|
if isinstance(v, Modality):
|
|
89
|
-
modalities[v.
|
|
81
|
+
modalities[v.id] = v
|
|
90
82
|
return pg.TraverseAction.CONTINUE
|
|
91
83
|
return pg.TraverseAction.ENTER
|
|
92
84
|
|
|
@@ -102,7 +94,7 @@ class ModalityRef(pg.Object, pg.typing.CustomTyping):
|
|
|
102
94
|
structure.
|
|
103
95
|
"""
|
|
104
96
|
|
|
105
|
-
|
|
97
|
+
id: str
|
|
106
98
|
|
|
107
99
|
def custom_apply(
|
|
108
100
|
self, path: pg.KeyPath, value_spec: pg.ValueSpec, *args, **kwargs
|
|
@@ -122,12 +114,97 @@ class ModalityRef(pg.Object, pg.typing.CustomTyping):
|
|
|
122
114
|
"""
|
|
123
115
|
|
|
124
116
|
def _placehold(k, v, p):
|
|
125
|
-
del p
|
|
117
|
+
del k, p
|
|
126
118
|
if isinstance(v, Modality):
|
|
127
|
-
return ModalityRef(
|
|
119
|
+
return ModalityRef(id=v.id)
|
|
128
120
|
return v
|
|
129
121
|
return value.clone().rebind(_placehold, raise_on_no_change=False)
|
|
130
122
|
|
|
123
|
+
@classmethod
|
|
124
|
+
def restore(cls, value: pg.Symbolic, modalities: dict[str, Modality]) -> Any:
|
|
125
|
+
"""Returns a copy of value by replacing refs with modality objects."""
|
|
126
|
+
def _restore(k, v, p):
|
|
127
|
+
del k, p
|
|
128
|
+
if isinstance(v, ModalityRef):
|
|
129
|
+
modality_object = modalities.get(v.id)
|
|
130
|
+
if modality_object is None:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f'Modality {v.id} not found in modalities {modalities.keys()}'
|
|
133
|
+
)
|
|
134
|
+
return modality_object
|
|
135
|
+
return v
|
|
136
|
+
return value.rebind(_restore, raise_on_no_change=False)
|
|
137
|
+
|
|
131
138
|
|
|
132
139
|
class ModalityError(RuntimeError): # pylint: disable=g-bad-exception-name
|
|
133
140
|
"""Exception raised when modality is not supported."""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
#
|
|
144
|
+
# Context managers to deal with modality objects.
|
|
145
|
+
#
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
_TLS_MODALITY_CAPTURE_SCOPE = '__modality_capture_scope__'
|
|
149
|
+
_TLS_MODALITY_AS_REF = '__format_modality_as_ref__'
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def format_modality_as_ref(enabled: bool = True) -> ContextManager[None]:
|
|
153
|
+
"""A context manager that formats modality objects as references."""
|
|
154
|
+
return pg.object_utils.thread_local_value_scope(
|
|
155
|
+
_TLS_MODALITY_AS_REF, enabled, False
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class _ModalityCaptureContext:
|
|
160
|
+
"""A context to capture modality objects when being rendered."""
|
|
161
|
+
|
|
162
|
+
def __init__(self):
|
|
163
|
+
self._references: dict[str, pg.Ref[Modality]] = {}
|
|
164
|
+
|
|
165
|
+
def capture(self, modality: Modality) -> None:
|
|
166
|
+
"""Captures the modality object."""
|
|
167
|
+
self._references[modality.id] = pg.Ref(modality)
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def references(self) -> dict[str, pg.Ref[Modality]]:
|
|
171
|
+
"""Returns the modality references captured in this context."""
|
|
172
|
+
return self._references
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@contextlib.contextmanager
|
|
176
|
+
def capture_rendered_modalities() -> Iterator[dict[str, pg.Ref[Modality]]]:
|
|
177
|
+
"""Capture modality objects whose references is being rendered.
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
```
|
|
181
|
+
image = lf.Image.from_url(...)
|
|
182
|
+
with lf.modality.capture_rendered_modalities() as rendered_modalities:
|
|
183
|
+
with lf.modality.format_modality_as_ref():
|
|
184
|
+
print(f'Hello {image}')
|
|
185
|
+
self.assertEqual(rendered_modalities, {'image:<hash>': pg.Ref(image)})
|
|
186
|
+
```
|
|
187
|
+
"""
|
|
188
|
+
context = get_modality_capture_context()
|
|
189
|
+
top_level = context is None
|
|
190
|
+
if top_level:
|
|
191
|
+
context = _ModalityCaptureContext()
|
|
192
|
+
pg.object_utils.thread_local_set(_TLS_MODALITY_CAPTURE_SCOPE, context)
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
yield context.references # pylint: disable=attribute-error
|
|
196
|
+
finally:
|
|
197
|
+
if top_level:
|
|
198
|
+
pg.object_utils.thread_local_del(_TLS_MODALITY_CAPTURE_SCOPE)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_modality_capture_context() -> _ModalityCaptureContext | None:
|
|
202
|
+
"""Returns the current modality capture context."""
|
|
203
|
+
return pg.object_utils.thread_local_get(_TLS_MODALITY_CAPTURE_SCOPE, None)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _camel_to_snake(name: str) -> str:
|
|
207
|
+
"""Converts a camelCase name to snake_case."""
|
|
208
|
+
return re.sub(
|
|
209
|
+
pattern=r'([A-Z]+)', repl=r'_\1', string=name
|
|
210
|
+
).lower().lstrip('_')
|