langfun 0.1.2.dev202509120804__py3-none-any.whl → 0.1.2.dev202512040805__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.
- langfun/__init__.py +1 -1
- langfun/core/__init__.py +7 -1
- langfun/core/agentic/__init__.py +8 -1
- langfun/core/agentic/action.py +740 -112
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +189 -24
- 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 +11 -2
- langfun/core/data/conversion/gemini_test.py +48 -9
- langfun/core/data/conversion/openai.py +145 -31
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base.py +48 -44
- langfun/core/eval/base_test.py +5 -5
- 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 +2 -0
- langfun/core/eval/v2/checkpointing.py +76 -7
- langfun/core/eval/v2/checkpointing_test.py +9 -2
- langfun/core/eval/v2/config_saver.py +37 -0
- langfun/core/eval/v2/config_saver_test.py +36 -0
- langfun/core/eval/v2/eval_test_helper.py +104 -3
- langfun/core/eval/v2/evaluation.py +92 -17
- langfun/core/eval/v2/evaluation_test.py +9 -3
- langfun/core/eval/v2/example.py +50 -40
- langfun/core/eval/v2/example_test.py +16 -8
- langfun/core/eval/v2/experiment.py +84 -15
- langfun/core/eval/v2/experiment_test.py +19 -0
- 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 +157 -44
- langfun/core/eval/v2/metrics_test.py +39 -18
- langfun/core/eval/v2/progress.py +31 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking.py +13 -5
- langfun/core/eval/v2/progress_tracking_test.py +9 -1
- langfun/core/eval/v2/reporting.py +90 -71
- langfun/core/eval/v2/reporting_test.py +24 -6
- langfun/core/eval/v2/runners/__init__.py +30 -0
- langfun/core/eval/v2/{runners.py → runners/base.py} +72 -180
- langfun/core/eval/v2/runners/beam.py +354 -0
- langfun/core/eval/v2/runners/beam_test.py +153 -0
- langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
- langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
- langfun/core/eval/v2/runners/debug.py +40 -0
- langfun/core/eval/v2/runners/debug_test.py +76 -0
- langfun/core/eval/v2/runners/parallel.py +243 -0
- langfun/core/eval/v2/runners/parallel_test.py +182 -0
- langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun/core/eval/v2/runners/sequential_test.py +169 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +7 -5
- langfun/core/language_model.py +189 -36
- langfun/core/language_model_test.py +54 -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 +32 -1
- langfun/core/llms/gemini.py +64 -12
- langfun/core/llms/gemini_test.py +110 -0
- 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 +120 -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 +58 -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 +254 -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 +73 -3
- langfun/core/modalities/image_test.py +116 -0
- langfun/core/modalities/mime.py +64 -3
- langfun/core/modalities/mime_test.py +11 -0
- 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/__init__.py +2 -24
- 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 +81 -37
- langfun/core/structured/parsing.py +95 -79
- langfun/core/structured/parsing_test.py +0 -3
- langfun/core/structured/querying.py +230 -154
- langfun/core/structured/querying_test.py +69 -33
- langfun/core/structured/schema/__init__.py +49 -0
- langfun/core/structured/schema/base.py +664 -0
- langfun/core/structured/schema/base_test.py +531 -0
- langfun/core/structured/schema/json.py +174 -0
- langfun/core/structured/schema/json_test.py +121 -0
- langfun/core/structured/schema/python.py +316 -0
- langfun/core/structured/schema/python_test.py +410 -0
- langfun/core/structured/schema_generation.py +33 -14
- langfun/core/structured/scoring.py +47 -36
- langfun/core/structured/tokenization.py +26 -11
- 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 +153 -0
- langfun/env/load_balancers.py +59 -0
- langfun/env/load_balancers_test.py +141 -0
- langfun/env/test_utils.py +507 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/METADATA +7 -3
- langfun-0.1.2.dev202512040805.dist-info/RECORD +217 -0
- langfun/core/eval/v2/runners_test.py +0 -343
- langfun/core/structured/schema.py +0 -987
- langfun/core/structured/schema_test.py +0 -982
- langfun-0.1.2.dev202509120804.dist-info/RECORD +0 -172
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.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
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
import functools
|
|
17
17
|
import io
|
|
18
|
-
|
|
18
|
+
import os
|
|
19
|
+
from typing import Any, Iterable
|
|
19
20
|
|
|
20
21
|
from langfun.core.modalities import mime
|
|
21
22
|
|
|
@@ -33,7 +34,25 @@ except ImportError:
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class Image(mime.Mime):
|
|
36
|
-
"""
|
|
37
|
+
"""Represents an image for communicating with language models.
|
|
38
|
+
|
|
39
|
+
`lf.Image` can be initialized from a URI (HTTP/HTTPS URL or local path)
|
|
40
|
+
using `lf.Image.from_uri()` or from raw bytes using `lf.Image.from_bytes()`.
|
|
41
|
+
|
|
42
|
+
**Example:**
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import langfun as lf
|
|
46
|
+
|
|
47
|
+
# Load image from path
|
|
48
|
+
image = lf.Image.from_path('/path/to/image.png')
|
|
49
|
+
|
|
50
|
+
# Use image in a prompt
|
|
51
|
+
prompt = lf.Template('Describe this image: {{image}}', image=image)
|
|
52
|
+
response = lf.query(prompt, lm=lf.llms.Gemini25Flash())
|
|
53
|
+
print(response)
|
|
54
|
+
```
|
|
55
|
+
"""
|
|
37
56
|
|
|
38
57
|
MIME_PREFIX = 'image'
|
|
39
58
|
|
|
@@ -53,8 +72,59 @@ class Image(mime.Mime):
|
|
|
53
72
|
def to_pil_image(self) -> PILImage: # pytype: disable=invalid-annotation
|
|
54
73
|
return pil_open(io.BytesIO(self.to_bytes()))
|
|
55
74
|
|
|
75
|
+
def _is_compatible(self, mime_types: Iterable[str]) -> bool:
|
|
76
|
+
"""Returns True if this image is compatible with any of the MIME types."""
|
|
77
|
+
mime_types = set(mime_types)
|
|
78
|
+
if self.mime_type in mime_types:
|
|
79
|
+
return True
|
|
80
|
+
if self.mime_type == 'image/gif':
|
|
81
|
+
return bool(mime_types & {'image/png', 'image/jpeg', 'image/webp'})
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def _make_compatible(self, mime_types: Iterable[str]) -> 'Image':
|
|
85
|
+
"""Converts this image to a compatible format if needed."""
|
|
86
|
+
mime_types = set(mime_types)
|
|
87
|
+
if self.mime_type in mime_types:
|
|
88
|
+
return self
|
|
89
|
+
if self.mime_type == 'image/gif':
|
|
90
|
+
# Convert to first supported format
|
|
91
|
+
for target_format, pil_format in [
|
|
92
|
+
('image/png', 'PNG'),
|
|
93
|
+
('image/jpeg', 'JPEG'),
|
|
94
|
+
('image/webp', 'WEBP'),
|
|
95
|
+
]:
|
|
96
|
+
if target_format in mime_types:
|
|
97
|
+
return self._convert_to_format(pil_format)
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def _convert_to_format(self, pil_format: str) -> 'Image':
|
|
101
|
+
"""Converts this image to the specified PIL format."""
|
|
102
|
+
buf = io.BytesIO()
|
|
103
|
+
img = self.to_pil_image()
|
|
104
|
+
# JPEG doesn't support transparency, convert RGBA to RGB
|
|
105
|
+
if pil_format == 'JPEG' and img.mode in ('RGBA', 'P'):
|
|
106
|
+
img = img.convert('RGB')
|
|
107
|
+
try:
|
|
108
|
+
img.save(buf, format=pil_format)
|
|
109
|
+
except OSError:
|
|
110
|
+
cwd = os.getcwd()
|
|
111
|
+
try:
|
|
112
|
+
os.chdir('/tmp')
|
|
113
|
+
img.save(buf, format=pil_format)
|
|
114
|
+
finally:
|
|
115
|
+
os.chdir(cwd)
|
|
116
|
+
return self.from_bytes(buf.getvalue())
|
|
117
|
+
|
|
56
118
|
@classmethod
|
|
57
119
|
def from_pil_image(cls, img: PILImage) -> 'Image': # pytype: disable=invalid-annotation
|
|
58
120
|
buf = io.BytesIO()
|
|
59
|
-
|
|
121
|
+
try:
|
|
122
|
+
img.save(buf, format='PNG')
|
|
123
|
+
except OSError:
|
|
124
|
+
cwd = os.getcwd()
|
|
125
|
+
try:
|
|
126
|
+
os.chdir('/tmp')
|
|
127
|
+
img.save(buf, format='PNG')
|
|
128
|
+
finally:
|
|
129
|
+
os.chdir(cwd)
|
|
60
130
|
return cls.from_bytes(buf.getvalue())
|