langfun 0.1.2.dev202508250805__py3-none-any.whl → 0.1.2.dev202511110805__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 +1 -1
- langfun/core/__init__.py +6 -1
- langfun/core/agentic/__init__.py +4 -0
- langfun/core/agentic/action.py +412 -103
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +68 -6
- langfun/core/async_support.py +104 -5
- langfun/core/async_support_test.py +23 -0
- langfun/core/coding/python/correction.py +19 -9
- langfun/core/coding/python/execution.py +14 -12
- langfun/core/coding/python/generation.py +21 -16
- langfun/core/coding/python/sandboxing.py +23 -3
- langfun/core/component.py +42 -3
- langfun/core/concurrent.py +70 -6
- langfun/core/concurrent_test.py +9 -2
- langfun/core/console.py +1 -1
- langfun/core/data/conversion/anthropic.py +12 -3
- langfun/core/data/conversion/anthropic_test.py +8 -6
- langfun/core/data/conversion/gemini.py +9 -2
- langfun/core/data/conversion/gemini_test.py +12 -9
- langfun/core/data/conversion/openai.py +145 -31
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base.py +47 -43
- langfun/core/eval/base_test.py +4 -4
- langfun/core/eval/matching.py +5 -2
- langfun/core/eval/patching.py +3 -3
- langfun/core/eval/scoring.py +4 -3
- langfun/core/eval/v2/__init__.py +1 -0
- langfun/core/eval/v2/checkpointing.py +30 -4
- langfun/core/eval/v2/eval_test_helper.py +1 -1
- langfun/core/eval/v2/evaluation.py +60 -14
- langfun/core/eval/v2/example.py +22 -11
- langfun/core/eval/v2/experiment.py +51 -8
- langfun/core/eval/v2/metric_values.py +31 -3
- langfun/core/eval/v2/metric_values_test.py +32 -0
- langfun/core/eval/v2/metrics.py +39 -4
- langfun/core/eval/v2/metrics_test.py +14 -0
- langfun/core/eval/v2/progress.py +30 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking_test.py +6 -0
- langfun/core/eval/v2/reporting.py +90 -71
- langfun/core/eval/v2/reporting_test.py +20 -6
- langfun/core/eval/v2/runners.py +27 -7
- langfun/core/eval/v2/runners_test.py +3 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +6 -4
- langfun/core/language_model.py +151 -31
- langfun/core/language_model_test.py +9 -3
- langfun/core/llms/__init__.py +12 -1
- langfun/core/llms/anthropic.py +157 -2
- langfun/core/llms/azure_openai.py +29 -17
- langfun/core/llms/cache/base.py +25 -3
- langfun/core/llms/cache/in_memory.py +48 -7
- langfun/core/llms/cache/in_memory_test.py +14 -4
- langfun/core/llms/compositional.py +25 -1
- langfun/core/llms/deepseek.py +30 -2
- langfun/core/llms/fake.py +39 -1
- langfun/core/llms/fake_test.py +9 -0
- langfun/core/llms/gemini.py +43 -7
- langfun/core/llms/google_genai.py +34 -1
- langfun/core/llms/groq.py +28 -3
- langfun/core/llms/llama_cpp.py +23 -4
- langfun/core/llms/openai.py +93 -3
- langfun/core/llms/openai_compatible.py +148 -27
- langfun/core/llms/openai_compatible_test.py +207 -20
- langfun/core/llms/openai_test.py +0 -2
- langfun/core/llms/rest.py +16 -1
- langfun/core/llms/vertexai.py +59 -8
- langfun/core/logging.py +1 -1
- langfun/core/mcp/__init__.py +10 -0
- langfun/core/mcp/client.py +177 -0
- langfun/core/mcp/client_test.py +71 -0
- langfun/core/mcp/session.py +241 -0
- langfun/core/mcp/session_test.py +54 -0
- langfun/core/mcp/testing/simple_mcp_client.py +33 -0
- langfun/core/mcp/testing/simple_mcp_server.py +33 -0
- langfun/core/mcp/tool.py +256 -0
- langfun/core/mcp/tool_test.py +197 -0
- langfun/core/memory.py +1 -0
- langfun/core/message.py +160 -55
- langfun/core/message_test.py +65 -81
- langfun/core/modalities/__init__.py +8 -0
- langfun/core/modalities/audio.py +21 -1
- langfun/core/modalities/image.py +19 -1
- langfun/core/modalities/mime.py +62 -3
- langfun/core/modalities/pdf.py +19 -1
- langfun/core/modalities/video.py +21 -1
- langfun/core/modality.py +167 -29
- langfun/core/modality_test.py +42 -12
- langfun/core/natural_language.py +1 -1
- langfun/core/sampling.py +4 -4
- langfun/core/sampling_test.py +20 -4
- langfun/core/structured/completion.py +34 -44
- langfun/core/structured/completion_test.py +23 -43
- langfun/core/structured/description.py +54 -50
- langfun/core/structured/function_generation.py +29 -12
- langfun/core/structured/mapping.py +74 -28
- langfun/core/structured/parsing.py +90 -74
- langfun/core/structured/parsing_test.py +0 -3
- langfun/core/structured/querying.py +242 -156
- langfun/core/structured/querying_test.py +95 -64
- langfun/core/structured/schema.py +70 -10
- langfun/core/structured/schema_generation.py +33 -14
- langfun/core/structured/scoring.py +45 -34
- langfun/core/structured/tokenization.py +24 -9
- langfun/core/subscription.py +2 -2
- langfun/core/template.py +175 -50
- langfun/core/template_test.py +123 -17
- langfun/env/__init__.py +43 -0
- langfun/env/base_environment.py +827 -0
- langfun/env/base_environment_test.py +473 -0
- langfun/env/base_feature.py +304 -0
- langfun/env/base_feature_test.py +228 -0
- langfun/env/base_sandbox.py +842 -0
- langfun/env/base_sandbox_test.py +1235 -0
- langfun/env/event_handlers/__init__.py +14 -0
- langfun/env/event_handlers/chain.py +233 -0
- langfun/env/event_handlers/chain_test.py +253 -0
- langfun/env/event_handlers/event_logger.py +472 -0
- langfun/env/event_handlers/event_logger_test.py +304 -0
- langfun/env/event_handlers/metric_writer.py +726 -0
- langfun/env/event_handlers/metric_writer_test.py +214 -0
- langfun/env/interface.py +1640 -0
- langfun/env/interface_test.py +151 -0
- langfun/env/load_balancers.py +59 -0
- langfun/env/load_balancers_test.py +139 -0
- langfun/env/test_utils.py +497 -0
- {langfun-0.1.2.dev202508250805.dist-info → langfun-0.1.2.dev202511110805.dist-info}/METADATA +7 -3
- langfun-0.1.2.dev202511110805.dist-info/RECORD +200 -0
- langfun-0.1.2.dev202508250805.dist-info/RECORD +0 -172
- {langfun-0.1.2.dev202508250805.dist-info → langfun-0.1.2.dev202511110805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202508250805.dist-info → langfun-0.1.2.dev202511110805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202508250805.dist-info → langfun-0.1.2.dev202511110805.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
|
|
|
@@ -525,6 +500,12 @@ class MessageConverterTest(unittest.TestCase):
|
|
|
525
500
|
self.assertIn('test_format2', message.Message.convertible_formats())
|
|
526
501
|
self.assertIn('test_format3', message.Message.convertible_formats())
|
|
527
502
|
|
|
503
|
+
self.assertTrue(message.Message.is_convertible(int))
|
|
504
|
+
self.assertFalse(message.Message.is_convertible(dict))
|
|
505
|
+
self.assertTrue(message.Message.is_convertible('test_format1'))
|
|
506
|
+
self.assertTrue(message.Message.is_convertible('test_format2'))
|
|
507
|
+
self.assertTrue(message.Message.is_convertible('test_format3'))
|
|
508
|
+
self.assertFalse(message.Message.is_convertible('test_format4'))
|
|
528
509
|
self.assertIn(int, message.Message.convertible_types())
|
|
529
510
|
self.assertIn(tuple, message.Message.convertible_types())
|
|
530
511
|
self.assertEqual(
|
|
@@ -565,6 +546,9 @@ class MessageConverterTest(unittest.TestCase):
|
|
|
565
546
|
message.Message.from_value((1, 2, 3)),
|
|
566
547
|
message.UserMessage('1,2,3')
|
|
567
548
|
)
|
|
549
|
+
message.MessageConverter._REGISTRY.unregister(TestConverter)
|
|
550
|
+
message.MessageConverter._REGISTRY.unregister(TestConverter2)
|
|
551
|
+
message.MessageConverter._REGISTRY.unregister(TestConverter3)
|
|
568
552
|
|
|
569
553
|
def test_get_role(self):
|
|
570
554
|
self.assertEqual(
|
|
@@ -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/audio.py
CHANGED
|
@@ -18,7 +18,27 @@ from langfun.core.modalities import mime
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Audio(mime.Mime):
|
|
21
|
-
"""
|
|
21
|
+
"""Represents audio for communicating with language models.
|
|
22
|
+
|
|
23
|
+
`lf.Audio` can be initialized from a URI (HTTP/HTTPS URL or local path)
|
|
24
|
+
using `lf.Audio.from_uri()` or from raw bytes using `lf.Audio.from_bytes()`.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import langfun as lf
|
|
30
|
+
|
|
31
|
+
# Load audio from path
|
|
32
|
+
audio = lf.Audio.from_path('/path/to/audio.mp3')
|
|
33
|
+
|
|
34
|
+
# Use audio in a prompt
|
|
35
|
+
prompt = lf.Template(
|
|
36
|
+
'What is being said in this audio? {{audio}}', audio=audio
|
|
37
|
+
)
|
|
38
|
+
response = lf.query(prompt, lm=lf.llms.Gemini25Flash())
|
|
39
|
+
print(response)
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
22
42
|
|
|
23
43
|
MIME_PREFIX = 'audio'
|
|
24
44
|
|
langfun/core/modalities/image.py
CHANGED
|
@@ -33,7 +33,25 @@ except ImportError:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class Image(mime.Mime):
|
|
36
|
-
"""
|
|
36
|
+
"""Represents an image for communicating with language models.
|
|
37
|
+
|
|
38
|
+
`lf.Image` can be initialized from a URI (HTTP/HTTPS URL or local path)
|
|
39
|
+
using `lf.Image.from_uri()` or from raw bytes using `lf.Image.from_bytes()`.
|
|
40
|
+
|
|
41
|
+
**Example:**
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import langfun as lf
|
|
45
|
+
|
|
46
|
+
# Load image from path
|
|
47
|
+
image = lf.Image.from_path('/path/to/image.png')
|
|
48
|
+
|
|
49
|
+
# Use image in a prompt
|
|
50
|
+
prompt = lf.Template('Describe this image: {{image}}', image=image)
|
|
51
|
+
response = lf.query(prompt, lm=lf.llms.Gemini25Flash())
|
|
52
|
+
print(response)
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
37
55
|
|
|
38
56
|
MIME_PREFIX = 'image'
|
|
39
57
|
|
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.
|
|
@@ -36,7 +37,33 @@ def _detect_mime_type(content: bytes) -> str:
|
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class Mime(lf.Modality):
|
|
39
|
-
"""Base for MIME
|
|
40
|
+
"""Base class for representing modality data based on MIME types.
|
|
41
|
+
|
|
42
|
+
`lf.Mime` is a subclass of `lf.Modality` that serves as a base for
|
|
43
|
+
handling various data types like images, audio, video, and PDFs,
|
|
44
|
+
identified by their MIME types. It provides unified methods for
|
|
45
|
+
loading data from URIs or bytes (`.from_uri()`, `.from_bytes()`) and
|
|
46
|
+
for accessing content (`.to_bytes()`).
|
|
47
|
+
|
|
48
|
+
Subclasses like `lf.Image`, `lf.Audio`, `lf.Video`, and `lf.PDF`
|
|
49
|
+
specialize in handling specific MIME type prefixes (e.g., 'image/', 'audio/').
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import langfun as lf
|
|
55
|
+
|
|
56
|
+
# Load an image from a path
|
|
57
|
+
image = lf.Image.from_path('/path/to/image.png')
|
|
58
|
+
print(image.mime_type)
|
|
59
|
+
# Output: image/png
|
|
60
|
+
|
|
61
|
+
# Create a text document
|
|
62
|
+
text = lf.Custom.from_bytes(b'hello world', mime='text/plain')
|
|
63
|
+
print(text.mime_type)
|
|
64
|
+
# Output: text/plain
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
40
67
|
|
|
41
68
|
# The regular expression that describes the MIME type str.
|
|
42
69
|
# If None, the MIME type is dynamic. Subclass could override.
|
|
@@ -48,6 +75,10 @@ class Mime(lf.Modality):
|
|
|
48
75
|
Union[str, bytes, None], 'The raw content of the MIME type.'
|
|
49
76
|
] = None
|
|
50
77
|
|
|
78
|
+
metadata: Annotated[
|
|
79
|
+
dict[str, Any], 'Additional metadata attached to this object.'
|
|
80
|
+
] = {}
|
|
81
|
+
|
|
51
82
|
@functools.cached_property
|
|
52
83
|
def mime_type(self) -> str:
|
|
53
84
|
"""Returns the MIME type."""
|
|
@@ -87,6 +118,17 @@ class Mime(lf.Modality):
|
|
|
87
118
|
"""Returns True if the MIME type is a binary type."""
|
|
88
119
|
return not self.is_text
|
|
89
120
|
|
|
121
|
+
@property
|
|
122
|
+
def hash(self) -> str:
|
|
123
|
+
"""Returns the hash of the MIME content."""
|
|
124
|
+
# Hash the URI to avoid downloading the content.
|
|
125
|
+
if self.uri is not None:
|
|
126
|
+
return hashlib.md5(self.uri.encode()).hexdigest()[:8]
|
|
127
|
+
if self.content is not None:
|
|
128
|
+
return super().hash
|
|
129
|
+
assert self.metadata
|
|
130
|
+
return hashlib.md5(str(self.metadata).encode()).hexdigest()[:8]
|
|
131
|
+
|
|
90
132
|
def to_text(self) -> str:
|
|
91
133
|
"""Returns the text content of the MIME type."""
|
|
92
134
|
if not self.is_text:
|
|
@@ -132,7 +174,7 @@ class Mime(lf.Modality):
|
|
|
132
174
|
|
|
133
175
|
def _on_bound(self):
|
|
134
176
|
super()._on_bound()
|
|
135
|
-
if self.uri is None and self.content is None:
|
|
177
|
+
if self.uri is None and self.content is None and not self.metadata:
|
|
136
178
|
raise ValueError('Either uri or content must be provided.')
|
|
137
179
|
|
|
138
180
|
def to_bytes(self) -> bytes:
|
|
@@ -272,7 +314,24 @@ class Mime(lf.Modality):
|
|
|
272
314
|
|
|
273
315
|
@pg.use_init_args(['mime', 'content', 'uri'])
|
|
274
316
|
class Custom(Mime):
|
|
275
|
-
"""
|
|
317
|
+
"""Represents content of a custom MIME type.
|
|
318
|
+
|
|
319
|
+
`lf.modalities.Custom` is useful for representing data with MIME types
|
|
320
|
+
that do not have dedicated classes like `lf.Image` or `lf.Audio`.
|
|
321
|
+
|
|
322
|
+
**Example:**
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
import langfun as lf
|
|
326
|
+
|
|
327
|
+
# Create a custom MIME object for plain text
|
|
328
|
+
text_data = lf.Custom.from_bytes(
|
|
329
|
+
b'This is a text document.', mime='text/plain'
|
|
330
|
+
)
|
|
331
|
+
print(text_data.mime_type)
|
|
332
|
+
# Output: text/plain
|
|
333
|
+
```
|
|
334
|
+
"""
|
|
276
335
|
|
|
277
336
|
mime: Annotated[
|
|
278
337
|
str, 'The MIME type of the data. E.g. text/plain, or image/png. '
|
langfun/core/modalities/pdf.py
CHANGED
|
@@ -17,6 +17,24 @@ from langfun.core.modalities import mime
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class PDF(mime.Mime):
|
|
20
|
-
"""PDF document.
|
|
20
|
+
"""Represents a PDF document for communicating with language models.
|
|
21
|
+
|
|
22
|
+
`lf.PDF` can be initialized from a URI (HTTP/HTTPS URL or local path)
|
|
23
|
+
using `lf.PDF.from_uri()` or from raw bytes using `lf.PDF.from_bytes()`.
|
|
24
|
+
|
|
25
|
+
**Example:**
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import langfun as lf
|
|
29
|
+
|
|
30
|
+
# Load PDF from path
|
|
31
|
+
pdf = lf.PDF.from_path('/path/to/document.pdf')
|
|
32
|
+
|
|
33
|
+
# Use PDF in a prompt
|
|
34
|
+
prompt = lf.Template('Summarize this document: {{pdf}}', pdf=pdf)
|
|
35
|
+
response = lf.query(prompt, lm=lf.llms.Gemini25Flash())
|
|
36
|
+
print(response)
|
|
37
|
+
```
|
|
38
|
+
"""
|
|
21
39
|
|
|
22
40
|
MIME_PREFIX = 'application/pdf'
|
langfun/core/modalities/video.py
CHANGED
|
@@ -18,7 +18,27 @@ from langfun.core.modalities import mime
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Video(mime.Mime):
|
|
21
|
-
"""
|
|
21
|
+
"""Represents a video for communicating with language models.
|
|
22
|
+
|
|
23
|
+
`lf.Video` can be initialized from a URI (HTTP/HTTPS URL or local path)
|
|
24
|
+
using `lf.Video.from_uri()` or from raw bytes using `lf.Video.from_bytes()`.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import langfun as lf
|
|
30
|
+
|
|
31
|
+
# Load video from path
|
|
32
|
+
video = lf.Video.from_path('/path/to/video.mp4')
|
|
33
|
+
|
|
34
|
+
# Use video in a prompt
|
|
35
|
+
prompt = lf.Template(
|
|
36
|
+
'What is happening in this video? {{video}}', video=video
|
|
37
|
+
)
|
|
38
|
+
response = lf.query(prompt, lm=lf.llms.Gemini25Flash())
|
|
39
|
+
print(response)
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
22
42
|
|
|
23
43
|
MIME_PREFIX = 'video'
|
|
24
44
|
|