langfun 0.1.2.dev202410080804__py3-none-any.whl → 0.1.2.dev202410110804__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/core/logging.py +144 -43
- langfun/core/logging_test.py +33 -0
- langfun/core/message.py +238 -70
- langfun/core/message_test.py +70 -45
- langfun/core/modalities/audio.py +1 -1
- langfun/core/modalities/audio_test.py +1 -1
- langfun/core/modalities/image.py +1 -1
- langfun/core/modalities/image_test.py +9 -3
- langfun/core/modalities/mime.py +39 -3
- langfun/core/modalities/mime_test.py +39 -0
- langfun/core/modalities/ms_office.py +2 -5
- langfun/core/modalities/ms_office_test.py +1 -1
- langfun/core/modalities/pdf_test.py +1 -1
- langfun/core/modalities/video.py +1 -1
- langfun/core/modalities/video_test.py +2 -2
- langfun/core/structured/mapping.py +38 -0
- langfun/core/structured/mapping_test.py +55 -0
- langfun/core/structured/schema.py +34 -0
- langfun/core/template.py +107 -1
- langfun/core/template_test.py +37 -0
- {langfun-0.1.2.dev202410080804.dist-info → langfun-0.1.2.dev202410110804.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202410080804.dist-info → langfun-0.1.2.dev202410110804.dist-info}/RECORD +25 -25
- {langfun-0.1.2.dev202410080804.dist-info → langfun-0.1.2.dev202410110804.dist-info}/LICENSE +0 -0
- {langfun-0.1.2.dev202410080804.dist-info → langfun-0.1.2.dev202410110804.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202410080804.dist-info → langfun-0.1.2.dev202410110804.dist-info}/top_level.txt +0 -0
langfun/core/logging.py
CHANGED
@@ -13,16 +13,13 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Langfun event logging."""
|
15
15
|
|
16
|
-
from collections.abc import Iterator
|
17
16
|
import contextlib
|
18
17
|
import datetime
|
19
|
-
import io
|
20
18
|
import typing
|
21
|
-
from typing import Any, Literal
|
19
|
+
from typing import Any, Iterator, Literal, Sequence
|
22
20
|
|
23
21
|
from langfun.core import component
|
24
22
|
from langfun.core import console
|
25
|
-
from langfun.core import repr_utils
|
26
23
|
import pyglove as pg
|
27
24
|
|
28
25
|
|
@@ -56,49 +53,153 @@ class LogEntry(pg.Object):
|
|
56
53
|
def should_output(self, min_log_level: LogLevel) -> bool:
|
57
54
|
return _LOG_LEVELS.index(self.level) >= _LOG_LEVELS.index(min_log_level)
|
58
55
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
alt_text = self.time.strftime('%Y-%m-%d %H:%M:%S.%f')
|
89
|
-
return (
|
90
|
-
'<span style="background-color: #BBBBBB; color: white; '
|
91
|
-
'border-radius:5px; padding:0px 5px 0px 5px;" '
|
92
|
-
f'title="{alt_text}">{display_text}</span>'
|
56
|
+
def _html_tree_view_summary(
|
57
|
+
self,
|
58
|
+
view: pg.views.HtmlTreeView,
|
59
|
+
title: str | pg.Html | None = None,
|
60
|
+
max_str_len_for_summary: int = pg.View.PresetArgValue(80), # pytype: disable=annotation-type-mismatch
|
61
|
+
**kwargs
|
62
|
+
) -> str:
|
63
|
+
if len(self.message) > max_str_len_for_summary:
|
64
|
+
message = self.message[:max_str_len_for_summary] + '...'
|
65
|
+
else:
|
66
|
+
message = self.message
|
67
|
+
|
68
|
+
s = pg.Html(
|
69
|
+
pg.Html.element(
|
70
|
+
'span',
|
71
|
+
[self.time.strftime('%H:%M:%S')],
|
72
|
+
css_class=['log-time']
|
73
|
+
),
|
74
|
+
pg.Html.element(
|
75
|
+
'span',
|
76
|
+
[pg.Html.escape(message)],
|
77
|
+
css_class=['log-summary'],
|
78
|
+
),
|
79
|
+
)
|
80
|
+
return view.summary(
|
81
|
+
self,
|
82
|
+
title=title or s,
|
83
|
+
max_str_len_for_summary=max_str_len_for_summary,
|
84
|
+
**kwargs,
|
93
85
|
)
|
94
86
|
|
95
|
-
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
87
|
+
# pytype: disable=annotation-type-mismatch
|
88
|
+
def _html_tree_view_content(
|
89
|
+
self,
|
90
|
+
view: pg.views.HtmlTreeView,
|
91
|
+
root_path: pg.KeyPath,
|
92
|
+
collapse_log_metadata_level: int = pg.View.PresetArgValue(0),
|
93
|
+
max_str_len_for_summary: int = pg.View.PresetArgValue(80),
|
94
|
+
**kwargs
|
95
|
+
) -> pg.Html:
|
96
|
+
# pytype: enable=annotation-type-mismatch
|
97
|
+
def render_message_text():
|
98
|
+
if len(self.message) < max_str_len_for_summary:
|
99
|
+
return None
|
100
|
+
return pg.Html.element(
|
101
|
+
'span',
|
102
|
+
[pg.Html.escape(self.message)],
|
103
|
+
css_class=['log-text'],
|
104
|
+
)
|
105
|
+
|
106
|
+
def render_metadata():
|
107
|
+
if not self.metadata:
|
108
|
+
return None
|
109
|
+
return pg.Html.element(
|
110
|
+
'div',
|
111
|
+
[
|
112
|
+
view.render(
|
113
|
+
self.metadata,
|
114
|
+
name='metadata',
|
115
|
+
root_path=root_path + 'metadata',
|
116
|
+
parent=self,
|
117
|
+
collapse_level=(
|
118
|
+
root_path.depth + collapse_log_metadata_level + 1
|
119
|
+
)
|
120
|
+
)
|
121
|
+
],
|
122
|
+
css_class=['log-metadata'],
|
123
|
+
)
|
124
|
+
|
125
|
+
return pg.Html.element(
|
126
|
+
'div',
|
127
|
+
[
|
128
|
+
render_message_text(),
|
129
|
+
render_metadata(),
|
130
|
+
],
|
131
|
+
css_class=['complex_value'],
|
100
132
|
)
|
101
133
|
|
134
|
+
def _html_style(self) -> list[str]:
|
135
|
+
return super()._html_style() + [
|
136
|
+
"""
|
137
|
+
.log-time {
|
138
|
+
color: #222;
|
139
|
+
font-size: 12px;
|
140
|
+
padding-right: 10px;
|
141
|
+
}
|
142
|
+
.log-summary {
|
143
|
+
font-weight: normal;
|
144
|
+
font-style: italic;
|
145
|
+
padding: 4px;
|
146
|
+
}
|
147
|
+
.log-debug > summary > .summary_title::before {
|
148
|
+
content: '🛠️ '
|
149
|
+
}
|
150
|
+
.log-info > summary > .summary_title::before {
|
151
|
+
content: '💡 '
|
152
|
+
}
|
153
|
+
.log-warning > summary > .summary_title::before {
|
154
|
+
content: '❗ '
|
155
|
+
}
|
156
|
+
.log-error > summary > .summary_title::before {
|
157
|
+
content: '❌ '
|
158
|
+
}
|
159
|
+
.log-fatal > summary > .summary_title::before {
|
160
|
+
content: '💀 '
|
161
|
+
}
|
162
|
+
.log-text {
|
163
|
+
display: block;
|
164
|
+
color: black;
|
165
|
+
font-style: italic;
|
166
|
+
padding: 20px;
|
167
|
+
border-radius: 5px;
|
168
|
+
background: rgba(255, 255, 255, 0.5);
|
169
|
+
white-space: pre-wrap;
|
170
|
+
}
|
171
|
+
details.log-entry {
|
172
|
+
margin: 0px 0px 10px;
|
173
|
+
border: 0px;
|
174
|
+
}
|
175
|
+
div.log-metadata {
|
176
|
+
margin: 10px 0px 0px 0px;
|
177
|
+
}
|
178
|
+
.log-metadata > details {
|
179
|
+
background-color: rgba(255, 255, 255, 0.5);
|
180
|
+
border: 1px solid transparent;
|
181
|
+
}
|
182
|
+
.log-debug {
|
183
|
+
background-color: #EEEEEE
|
184
|
+
}
|
185
|
+
.log-warning {
|
186
|
+
background-color: #F8C471
|
187
|
+
}
|
188
|
+
.log-info {
|
189
|
+
background-color: #A3E4D7
|
190
|
+
}
|
191
|
+
.log-error {
|
192
|
+
background-color: #F5C6CB
|
193
|
+
}
|
194
|
+
.log-fatal {
|
195
|
+
background-color: #F19CBB
|
196
|
+
}
|
197
|
+
"""
|
198
|
+
]
|
199
|
+
|
200
|
+
def _html_element_class(self) -> Sequence[str] | None:
|
201
|
+
return super()._html_element_class() + [f'log-{self.level}']
|
202
|
+
|
102
203
|
|
103
204
|
def log(level: LogLevel,
|
104
205
|
message: str,
|
langfun/core/logging_test.py
CHANGED
@@ -13,6 +13,8 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Tests for langfun.core.logging."""
|
15
15
|
|
16
|
+
import datetime
|
17
|
+
import inspect
|
16
18
|
import unittest
|
17
19
|
|
18
20
|
from langfun.core import logging
|
@@ -52,6 +54,37 @@ class LoggingTest(unittest.TestCase):
|
|
52
54
|
assert_color(logging.error('hi', indent=2, x=1, y=2), '#F5C6CB')
|
53
55
|
assert_color(logging.fatal('hi', indent=2, x=1, y=2), '#F19CBB')
|
54
56
|
|
57
|
+
def assert_html_content(self, html, expected):
|
58
|
+
expected = inspect.cleandoc(expected).strip()
|
59
|
+
actual = html.content.strip()
|
60
|
+
if actual != expected:
|
61
|
+
print(actual)
|
62
|
+
self.assertEqual(actual, expected)
|
63
|
+
|
64
|
+
def test_html(self):
|
65
|
+
time = datetime.datetime(2024, 10, 10, 12, 30, 45)
|
66
|
+
self.assert_html_content(
|
67
|
+
logging.LogEntry(
|
68
|
+
level='info', message='5 + 2 > 3',
|
69
|
+
time=time, metadata={}
|
70
|
+
).to_html(enable_summary_tooltip=False),
|
71
|
+
"""
|
72
|
+
<details open class="pyglove log-entry log-info"><summary><div class="summary_title"><span class="log-time">12:30:45</span><span class="log-summary">5 + 2 > 3</span></div></summary><div class="complex_value"></div></details>
|
73
|
+
"""
|
74
|
+
)
|
75
|
+
self.assert_html_content(
|
76
|
+
logging.LogEntry(
|
77
|
+
level='error', message='This is a longer message: 5 + 2 > 3',
|
78
|
+
time=time, metadata=dict(x=1, y=2)
|
79
|
+
).to_html(
|
80
|
+
max_str_len_for_summary=10,
|
81
|
+
enable_summary_tooltip=False,
|
82
|
+
collapse_log_metadata_level=1
|
83
|
+
),
|
84
|
+
"""
|
85
|
+
<details open class="pyglove log-entry log-error"><summary><div class="summary_title"><span class="log-time">12:30:45</span><span class="log-summary">This is a ...</span></div></summary><div class="complex_value"><span class="log-text">This is a longer message: 5 + 2 > 3</span><div class="log-metadata"><details open class="pyglove dict"><summary><div class="summary_name">metadata</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 key-path">metadata.x</span></td><td><div><span class="simple_value int">1</span></div></td></tr><tr><td><span class="object_key str">y</span><span class="tooltip key-path">metadata.y</span></td><td><div><span class="simple_value int">2</span></div></td></tr></table></div></details></div></div></details>
|
86
|
+
"""
|
87
|
+
)
|
55
88
|
|
56
89
|
if __name__ == '__main__':
|
57
90
|
unittest.main()
|
langfun/core/message.py
CHANGED
@@ -14,13 +14,11 @@
|
|
14
14
|
"""Messages that are exchanged between users and agents."""
|
15
15
|
|
16
16
|
import contextlib
|
17
|
-
import html
|
18
17
|
import io
|
19
|
-
from typing import Annotated, Any, Optional, Union
|
18
|
+
from typing import Annotated, Any, Optional, Sequence, Union
|
20
19
|
|
21
20
|
from langfun.core import modality
|
22
21
|
from langfun.core import natural_language
|
23
|
-
from langfun.core import repr_utils
|
24
22
|
import pyglove as pg
|
25
23
|
|
26
24
|
|
@@ -406,6 +404,11 @@ class Message(natural_language.NaturalLanguageFormattable, pg.Object):
|
|
406
404
|
with pg.notify_on_change(False):
|
407
405
|
self.tags.append(tag)
|
408
406
|
|
407
|
+
def has_tag(self, tag: str | tuple[str, ...]) -> bool:
|
408
|
+
if isinstance(tag, str):
|
409
|
+
return tag in self.tags
|
410
|
+
return any(t in self.tags for t in tag)
|
411
|
+
|
409
412
|
#
|
410
413
|
# Message source chain.
|
411
414
|
#
|
@@ -503,79 +506,244 @@ class Message(natural_language.NaturalLanguageFormattable, pg.Object):
|
|
503
506
|
v = self.metadata[key]
|
504
507
|
return v.value if isinstance(v, pg.Ref) else v
|
505
508
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
def to_html(
|
509
|
+
# pytype: disable=annotation-type-mismatch
|
510
|
+
def _html_tree_view_content(
|
510
511
|
self,
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
512
|
+
*,
|
513
|
+
view: pg.views.HtmlTreeView,
|
514
|
+
root_path: pg.KeyPath,
|
515
|
+
source_tag: str | Sequence[str] | None = pg.View.PresetArgValue(
|
516
|
+
('lm-input', 'lm-output')
|
517
|
+
),
|
518
|
+
include_message_metadata: bool = pg.View.PresetArgValue(True),
|
519
|
+
collapse_modalities_in_text: bool = pg.View.PresetArgValue(True),
|
520
|
+
collapse_llm_usage: bool = pg.View.PresetArgValue(False),
|
521
|
+
collapse_message_result_level: int = pg.View.PresetArgValue(1),
|
522
|
+
collapse_message_metadata_level: int = pg.View.PresetArgValue(0),
|
523
|
+
collapse_source_message_level: int = pg.View.PresetArgValue(1),
|
524
|
+
**kwargs,
|
525
|
+
) -> pg.Html:
|
526
|
+
# pytype: enable=annotation-type-mismatch
|
527
|
+
"""Returns the HTML representation of the message.
|
528
|
+
|
529
|
+
Args:
|
530
|
+
view: The HTML tree view.
|
531
|
+
root_path: The root path of the message.
|
532
|
+
source_tag: tags to filter source messages. If None, the entire
|
533
|
+
source chain will be included.
|
534
|
+
include_message_metadata: Whether to include the metadata of the message.
|
535
|
+
collapse_modalities_in_text: Whether to collapse the modalities in the
|
536
|
+
message text.
|
537
|
+
collapse_llm_usage: Whether to collapse the usage in the message.
|
538
|
+
collapse_message_result_level: The level to collapse the result in the
|
539
|
+
message.
|
540
|
+
collapse_message_metadata_level: The level to collapse the metadata in the
|
541
|
+
message.
|
542
|
+
collapse_source_message_level: The level to collapse the source in the
|
543
|
+
message.
|
544
|
+
**kwargs: Other keyword arguments.
|
545
|
+
|
546
|
+
Returns:
|
547
|
+
The HTML representation of the message content.
|
548
|
+
"""
|
549
|
+
def render_tags():
|
550
|
+
return pg.Html.element(
|
551
|
+
'div',
|
552
|
+
[pg.Html.element('span', [tag]) for tag in self.tags],
|
553
|
+
css_class=['message-tags'],
|
554
|
+
)
|
555
|
+
|
556
|
+
def render_message_text():
|
557
|
+
maybe_reformatted = self.get('formatted_text')
|
558
|
+
referred_chunks = {}
|
559
|
+
s = pg.Html('<div class="message-text">')
|
560
|
+
for chunk in self.chunk(maybe_reformatted):
|
561
|
+
if isinstance(chunk, str):
|
562
|
+
s.write(s.escape(chunk))
|
563
|
+
else:
|
564
|
+
assert isinstance(chunk, modality.Modality), chunk
|
565
|
+
child_path = root_path + 'metadata' + chunk.referred_name
|
566
|
+
s.write(
|
567
|
+
pg.Html.element(
|
568
|
+
'div',
|
569
|
+
[
|
570
|
+
view.render(
|
571
|
+
chunk,
|
572
|
+
name=chunk.referred_name,
|
573
|
+
root_path=child_path,
|
574
|
+
collapse_level=child_path.depth + (
|
575
|
+
0 if collapse_modalities_in_text else 1
|
576
|
+
)
|
577
|
+
)
|
578
|
+
],
|
579
|
+
css_class=['modality-in-text'],
|
580
|
+
)
|
523
581
|
)
|
582
|
+
referred_chunks[chunk.referred_name] = chunk
|
583
|
+
s.write('</div>')
|
584
|
+
return s
|
585
|
+
|
586
|
+
def render_result():
|
587
|
+
if 'result' not in self.metadata:
|
588
|
+
return None
|
589
|
+
child_path = root_path + 'metadata' + 'result'
|
590
|
+
return pg.Html.element(
|
591
|
+
'div',
|
592
|
+
[
|
593
|
+
view.render(
|
594
|
+
self.result,
|
595
|
+
name='result',
|
596
|
+
root_path=child_path,
|
597
|
+
collapse_level=(
|
598
|
+
child_path.depth + collapse_message_result_level
|
599
|
+
)
|
600
|
+
)
|
601
|
+
],
|
602
|
+
css_class=['message-result'],
|
603
|
+
)
|
604
|
+
|
605
|
+
def render_usage():
|
606
|
+
if 'usage' not in self.metadata:
|
607
|
+
return None
|
608
|
+
child_path = root_path + 'metadata' + 'usage'
|
609
|
+
return pg.Html.element(
|
610
|
+
'div',
|
611
|
+
[
|
612
|
+
view.render(
|
613
|
+
self.usage,
|
614
|
+
name='llm usage',
|
615
|
+
root_path=child_path,
|
616
|
+
collapse_level=child_path.depth + (
|
617
|
+
0 if collapse_llm_usage else 1
|
618
|
+
)
|
619
|
+
)
|
620
|
+
],
|
621
|
+
css_class=['message-usage'],
|
622
|
+
)
|
623
|
+
|
624
|
+
def render_source_message():
|
625
|
+
source = self.source
|
626
|
+
while (source is not None
|
627
|
+
and source_tag is not None
|
628
|
+
and not source.has_tag(source_tag)):
|
629
|
+
source = source.source
|
630
|
+
if source is not None:
|
631
|
+
return view.render(
|
632
|
+
self.source,
|
633
|
+
name='source',
|
634
|
+
root_path=root_path + 'source',
|
635
|
+
include_metadata=include_message_metadata,
|
636
|
+
collapse_level=(
|
637
|
+
root_path.depth + 1 + collapse_source_message_level
|
638
|
+
),
|
639
|
+
collapse_source_level=max(0, collapse_source_message_level - 1),
|
640
|
+
collapse_modalities=collapse_modalities_in_text,
|
641
|
+
collapse_usage=collapse_llm_usage,
|
642
|
+
collapse_metadata_level=collapse_message_metadata_level,
|
643
|
+
collapse_result_level=collapse_message_result_level,
|
644
|
+
)
|
645
|
+
return None
|
646
|
+
|
647
|
+
def render_metadata():
|
648
|
+
if not include_message_metadata:
|
649
|
+
return None
|
650
|
+
child_path = root_path + 'metadata'
|
651
|
+
return pg.Html.element(
|
652
|
+
'div',
|
653
|
+
[
|
654
|
+
view.render(
|
655
|
+
self.metadata,
|
656
|
+
css_class=['message-metadata'],
|
657
|
+
name='metadata',
|
658
|
+
root_path=child_path,
|
659
|
+
collapse_level=(
|
660
|
+
child_path.depth + collapse_message_metadata_level
|
661
|
+
)
|
662
|
+
)
|
663
|
+
],
|
664
|
+
css_class=['message-metadata'],
|
524
665
|
)
|
525
|
-
s.write('<hr>')
|
526
666
|
|
527
|
-
|
528
|
-
|
529
|
-
|
667
|
+
return pg.Html.element(
|
668
|
+
'div',
|
669
|
+
[
|
670
|
+
render_tags(),
|
671
|
+
render_message_text(),
|
672
|
+
render_result(),
|
673
|
+
render_usage(),
|
674
|
+
render_metadata(),
|
675
|
+
render_source_message(),
|
676
|
+
],
|
677
|
+
css_class=['complex_value'],
|
530
678
|
)
|
531
679
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
680
|
+
def _html_style(self) -> list[str]:
|
681
|
+
return super()._html_style() + [
|
682
|
+
"""
|
683
|
+
/* Langfun Message styles.*/
|
684
|
+
[class^="message-"] > details {
|
685
|
+
margin: 0px 0px 5px 0px;
|
686
|
+
border: 1px solid #EEE;
|
687
|
+
}
|
688
|
+
details.lf-message > summary > .summary_title::after {
|
689
|
+
content: ' 💬';
|
690
|
+
}
|
691
|
+
details.pyglove.ai-message {
|
692
|
+
border: 1px solid blue;
|
693
|
+
color: blue;
|
694
|
+
}
|
695
|
+
details.pyglove.user-message {
|
696
|
+
border: 1px solid green;
|
697
|
+
color: green;
|
698
|
+
}
|
699
|
+
.message-tags {
|
700
|
+
margin: 5px 0px 5px 0px;
|
701
|
+
font-size: .8em;
|
702
|
+
}
|
703
|
+
.message-tags > span {
|
704
|
+
border-radius: 5px;
|
705
|
+
background-color: #CCC;
|
706
|
+
padding: 3px;
|
707
|
+
margin: 0px 2px 0px 2px;
|
708
|
+
color: white;
|
709
|
+
}
|
710
|
+
.message-text {
|
711
|
+
padding: 20px;
|
712
|
+
margin: 10px 5px 10px 5px;
|
713
|
+
font-style: italic;
|
714
|
+
font-size: 1.1em;
|
715
|
+
white-space: pre-wrap;
|
716
|
+
border: 1px solid #EEE;
|
717
|
+
border-radius: 5px;
|
718
|
+
background-color: #EEE;
|
719
|
+
}
|
720
|
+
.modality-in-text {
|
721
|
+
display: inline-block;
|
722
|
+
}
|
723
|
+
.modality-in-text > details {
|
724
|
+
display: inline-block;
|
725
|
+
font-size: 0.8em;
|
726
|
+
border: 0;
|
727
|
+
background-color: #A6F1A6;
|
728
|
+
margin: 0px 5px 0px 5px;
|
729
|
+
}
|
730
|
+
.message-result {
|
731
|
+
color: purple;
|
732
|
+
}
|
733
|
+
.message-usage {
|
734
|
+
color: orange;
|
735
|
+
}
|
736
|
+
.message-usage .object_key.str {
|
737
|
+
border: 1px solid orange;
|
738
|
+
background-color: orange;
|
739
|
+
color: white;
|
740
|
+
}
|
741
|
+
"""
|
742
|
+
]
|
743
|
+
|
744
|
+
def _html_element_class(self) -> list[str]:
|
745
|
+
return super()._html_element_class() + ['lf-message']
|
746
|
+
|
579
747
|
|
580
748
|
#
|
581
749
|
# Messages of different roles.
|