youngjin-langchain-tools 0.1.2__py3-none-any.whl → 0.1.4__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.
- youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py +121 -44
- {youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/METADATA +1 -1
- {youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/RECORD +5 -5
- {youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/WHEEL +0 -0
- {youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,8 +8,8 @@ LangGraph agent responses in Streamlit applications.
|
|
|
8
8
|
Replaces the deprecated StreamlitCallbackHandler for LangGraph-based agents.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
from typing import Any, Dict, List, Optional,
|
|
12
|
-
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Dict, List, Optional, Generator
|
|
12
|
+
from dataclasses import dataclass
|
|
13
13
|
import logging
|
|
14
14
|
import re
|
|
15
15
|
|
|
@@ -129,6 +129,13 @@ class StreamlitLanggraphHandlerConfig:
|
|
|
129
129
|
expand_new_thoughts: bool = True
|
|
130
130
|
"""Whether to expand the status container to show tool calls."""
|
|
131
131
|
|
|
132
|
+
collapse_completed_thoughts: bool = True
|
|
133
|
+
"""Whether to automatically collapse completed thought containers."""
|
|
134
|
+
|
|
135
|
+
max_thought_containers: int = 4
|
|
136
|
+
"""Maximum number of thought containers to show at once.
|
|
137
|
+
When exceeded, oldest thoughts are moved to a 'History' expander."""
|
|
138
|
+
|
|
132
139
|
max_tool_content_length: int = 2000
|
|
133
140
|
"""Maximum length of tool output to display before truncating."""
|
|
134
141
|
|
|
@@ -202,6 +209,8 @@ class StreamlitLanggraphHandler:
|
|
|
202
209
|
container: Any,
|
|
203
210
|
*,
|
|
204
211
|
expand_new_thoughts: bool = True,
|
|
212
|
+
collapse_completed_thoughts: bool = True,
|
|
213
|
+
max_thought_containers: int = 4,
|
|
205
214
|
max_tool_content_length: int = 2000,
|
|
206
215
|
show_tool_calls: bool = True,
|
|
207
216
|
show_tool_results: bool = True,
|
|
@@ -217,6 +226,11 @@ class StreamlitLanggraphHandler:
|
|
|
217
226
|
Usually st.container() or similar.
|
|
218
227
|
expand_new_thoughts: Whether to expand status container
|
|
219
228
|
to show tool calls. Defaults to True.
|
|
229
|
+
collapse_completed_thoughts: Whether to collapse completed
|
|
230
|
+
thought containers. Defaults to True.
|
|
231
|
+
max_thought_containers: Maximum number of thought containers
|
|
232
|
+
to show at once. Oldest are moved to
|
|
233
|
+
'History' expander. Defaults to 4.
|
|
220
234
|
max_tool_content_length: Maximum characters of tool output
|
|
221
235
|
to display. Defaults to 2000.
|
|
222
236
|
show_tool_calls: Whether to show tool call info. Defaults to True.
|
|
@@ -230,6 +244,8 @@ class StreamlitLanggraphHandler:
|
|
|
230
244
|
else:
|
|
231
245
|
self._config = StreamlitLanggraphHandlerConfig(
|
|
232
246
|
expand_new_thoughts=expand_new_thoughts,
|
|
247
|
+
collapse_completed_thoughts=collapse_completed_thoughts,
|
|
248
|
+
max_thought_containers=max_thought_containers,
|
|
233
249
|
max_tool_content_length=max_tool_content_length,
|
|
234
250
|
show_tool_calls=show_tool_calls,
|
|
235
251
|
show_tool_results=show_tool_results,
|
|
@@ -241,6 +257,9 @@ class StreamlitLanggraphHandler:
|
|
|
241
257
|
self._final_response: str = ""
|
|
242
258
|
self._status_container: Any = None
|
|
243
259
|
self._response_placeholder: Any = None
|
|
260
|
+
self._thoughts_placeholder: Any = None # Placeholder for thoughts area
|
|
261
|
+
self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
|
|
262
|
+
self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
|
|
244
263
|
|
|
245
264
|
@property
|
|
246
265
|
def config(self) -> StreamlitLanggraphHandlerConfig:
|
|
@@ -332,9 +351,13 @@ class StreamlitLanggraphHandler:
|
|
|
332
351
|
|
|
333
352
|
# Reset state
|
|
334
353
|
self._final_response = ""
|
|
354
|
+
self._thought_history = []
|
|
355
|
+
self._current_thoughts = []
|
|
335
356
|
|
|
336
357
|
# Create UI components
|
|
337
358
|
with self._container:
|
|
359
|
+
# Thoughts area placeholder (manages history + current thoughts)
|
|
360
|
+
self._thoughts_placeholder = st.empty()
|
|
338
361
|
self._status_container = st.status(
|
|
339
362
|
self._config.thinking_label,
|
|
340
363
|
expanded=self._config.expand_new_thoughts
|
|
@@ -405,16 +428,75 @@ class StreamlitLanggraphHandler:
|
|
|
405
428
|
|
|
406
429
|
yield {"type": "complete", "data": {"response": self._final_response}}
|
|
407
430
|
|
|
431
|
+
def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
|
|
432
|
+
"""Add a thought and manage history if max_thought_containers exceeded."""
|
|
433
|
+
thought = {"type": thought_type, "data": data}
|
|
434
|
+
self._current_thoughts.append(thought)
|
|
435
|
+
|
|
436
|
+
# Check if we need to move old thoughts to history
|
|
437
|
+
max_containers = self._config.max_thought_containers
|
|
438
|
+
if len(self._current_thoughts) > max_containers:
|
|
439
|
+
# Move oldest thoughts to history
|
|
440
|
+
while len(self._current_thoughts) > max_containers:
|
|
441
|
+
old_thought = self._current_thoughts.pop(0)
|
|
442
|
+
self._thought_history.append(old_thought)
|
|
443
|
+
|
|
444
|
+
# Re-render the entire thoughts area
|
|
445
|
+
self._render_thoughts()
|
|
446
|
+
|
|
447
|
+
def _render_thought_item(self, thought: Dict[str, Any], in_history: bool = False) -> None:
|
|
448
|
+
"""Render a single thought item."""
|
|
449
|
+
import streamlit as st
|
|
450
|
+
|
|
451
|
+
if thought["type"] == "tool_call":
|
|
452
|
+
if self._config.show_tool_calls:
|
|
453
|
+
st.markdown(
|
|
454
|
+
f"{self._config.tool_call_emoji} "
|
|
455
|
+
f"**{thought['data']['name']}**: `{thought['data']['args']}`"
|
|
456
|
+
)
|
|
457
|
+
elif thought["type"] == "tool_result":
|
|
458
|
+
if self._config.show_tool_results:
|
|
459
|
+
tool_name = thought['data']['name']
|
|
460
|
+
content = thought['data']['content']
|
|
461
|
+
|
|
462
|
+
st.markdown(
|
|
463
|
+
f"{self._config.tool_complete_emoji} "
|
|
464
|
+
f"**{tool_name}** 완료"
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if in_history:
|
|
468
|
+
# Shorter preview in history
|
|
469
|
+
max_len = 200
|
|
470
|
+
else:
|
|
471
|
+
max_len = self._config.max_tool_content_length
|
|
472
|
+
|
|
473
|
+
expanded = not self._config.collapse_completed_thoughts
|
|
474
|
+
with st.expander(f"📋 {tool_name} 결과 보기", expanded=expanded if not in_history else False):
|
|
475
|
+
if len(content) > max_len:
|
|
476
|
+
st.code(content[:max_len] + "\n... (truncated)", language="text")
|
|
477
|
+
else:
|
|
478
|
+
st.code(content, language="text")
|
|
479
|
+
|
|
480
|
+
def _render_thoughts(self) -> None:
|
|
481
|
+
"""Render all thoughts (history + current) in the placeholder."""
|
|
482
|
+
import streamlit as st
|
|
483
|
+
|
|
484
|
+
with self._thoughts_placeholder.container():
|
|
485
|
+
# Render history expander if there are old thoughts
|
|
486
|
+
if self._thought_history:
|
|
487
|
+
with st.expander(f"📜 History ({len(self._thought_history)} items)", expanded=False):
|
|
488
|
+
for thought in self._thought_history:
|
|
489
|
+
self._render_thought_item(thought, in_history=True)
|
|
490
|
+
|
|
491
|
+
# Render current thoughts
|
|
492
|
+
for thought in self._current_thoughts:
|
|
493
|
+
self._render_thought_item(thought, in_history=False)
|
|
494
|
+
|
|
408
495
|
def _handle_updates(
|
|
409
496
|
self,
|
|
410
497
|
data: Dict[str, Any]
|
|
411
498
|
) -> Generator[Dict[str, Any], None, None]:
|
|
412
499
|
"""Handle 'updates' stream mode events."""
|
|
413
|
-
try:
|
|
414
|
-
import streamlit as st
|
|
415
|
-
except ImportError:
|
|
416
|
-
return
|
|
417
|
-
|
|
418
500
|
for source, update in data.items():
|
|
419
501
|
if not isinstance(update, dict):
|
|
420
502
|
continue
|
|
@@ -423,48 +505,43 @@ class StreamlitLanggraphHandler:
|
|
|
423
505
|
for msg in messages:
|
|
424
506
|
# Handle tool calls
|
|
425
507
|
if hasattr(msg, 'tool_calls') and msg.tool_calls:
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
tool_args = tc.get('args', {})
|
|
430
|
-
|
|
431
|
-
with self._status_container:
|
|
432
|
-
st.write(
|
|
433
|
-
f"{self._config.tool_call_emoji} "
|
|
434
|
-
f"**{tool_name}**: `{tool_args}`"
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
yield {
|
|
438
|
-
"type": "tool_call",
|
|
439
|
-
"data": {"name": tool_name, "args": tool_args}
|
|
440
|
-
}
|
|
508
|
+
for tc in msg.tool_calls:
|
|
509
|
+
tool_name = tc.get('name', 'tool')
|
|
510
|
+
tool_args = tc.get('args', {})
|
|
441
511
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
f"{self._config.tool_complete_emoji} "
|
|
451
|
-
f"**{tool_name}** 완료"
|
|
452
|
-
)
|
|
453
|
-
with st.expander(f"📋 {tool_name} 결과 보기", expanded=False):
|
|
454
|
-
if len(tool_content) > self._config.max_tool_content_length:
|
|
455
|
-
st.code(
|
|
456
|
-
tool_content[:self._config.max_tool_content_length]
|
|
457
|
-
+ "\n... (truncated)",
|
|
458
|
-
language="text"
|
|
459
|
-
)
|
|
460
|
-
else:
|
|
461
|
-
st.code(tool_content, language="text")
|
|
512
|
+
# Add to thought tracking (this triggers re-render)
|
|
513
|
+
self._add_thought("tool_call", {"name": tool_name, "args": tool_args})
|
|
514
|
+
|
|
515
|
+
# Update status label to show current action
|
|
516
|
+
self._status_container.update(
|
|
517
|
+
label=f"{self._config.tool_call_emoji} {tool_name}...",
|
|
518
|
+
state="running"
|
|
519
|
+
)
|
|
462
520
|
|
|
463
521
|
yield {
|
|
464
|
-
"type": "
|
|
465
|
-
"data": {"name": tool_name, "
|
|
522
|
+
"type": "tool_call",
|
|
523
|
+
"data": {"name": tool_name, "args": tool_args}
|
|
466
524
|
}
|
|
467
525
|
|
|
526
|
+
# Handle tool results
|
|
527
|
+
if source == "tools" and hasattr(msg, 'name'):
|
|
528
|
+
tool_name = msg.name
|
|
529
|
+
tool_content = str(msg.content) if hasattr(msg, 'content') else ""
|
|
530
|
+
|
|
531
|
+
# Add to thought tracking (this triggers re-render)
|
|
532
|
+
self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
|
|
533
|
+
|
|
534
|
+
# Update status to show thinking again
|
|
535
|
+
self._status_container.update(
|
|
536
|
+
label=self._config.thinking_label,
|
|
537
|
+
state="running"
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
yield {
|
|
541
|
+
"type": "tool_result",
|
|
542
|
+
"data": {"name": tool_name, "content": tool_content}
|
|
543
|
+
}
|
|
544
|
+
|
|
468
545
|
def _handle_messages(
|
|
469
546
|
self,
|
|
470
547
|
data: tuple
|
{youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youngjin-langchain-tools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: LangGraph utilities for Streamlit - StreamlitLanggraphHandler and more
|
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/youngjin-langchain-tools
|
|
6
6
|
Project-URL: Documentation, https://github.com/yourusername/youngjin-langchain-tools#readme
|
{youngjin_langchain_tools-0.1.2.dist-info → youngjin_langchain_tools-0.1.4.dist-info}/RECORD
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
youngjin_langchain_tools/__init__.py,sha256=S5GJtbYymhDuGtTibEG61Li9UvwvoFtnKm4mxjWNgnU,1310
|
|
2
2
|
youngjin_langchain_tools/handlers/__init__.py,sha256=-vGk-m1fqOipJSe02ogcScE0K3pdVwO9A_EqBovPRxo,397
|
|
3
|
-
youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py,sha256=
|
|
3
|
+
youngjin_langchain_tools/handlers/streamlit_langgraph_handler.py,sha256=5CvUm2kN9peE3v3dqboqfXMVtQ2EthwOaJkO_T2l5J8,21390
|
|
4
4
|
youngjin_langchain_tools/utils/__init__.py,sha256=LgSd7Gz2n5WgIhxaKHqpmQVUaBUDT_TTTw63hzoWGgM,272
|
|
5
5
|
youngjin_langchain_tools/utils/config.py,sha256=hHvdxsn5ZFWRJvR6N7ODy94JbCkeJocsS58hmvxoK5I,1640
|
|
6
|
-
youngjin_langchain_tools-0.1.
|
|
7
|
-
youngjin_langchain_tools-0.1.
|
|
8
|
-
youngjin_langchain_tools-0.1.
|
|
9
|
-
youngjin_langchain_tools-0.1.
|
|
6
|
+
youngjin_langchain_tools-0.1.4.dist-info/METADATA,sha256=MDDYlAZ4SjXGtWT1YQl3JB8Gt66mnDmy1BdaizzhIDA,6959
|
|
7
|
+
youngjin_langchain_tools-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
youngjin_langchain_tools-0.1.4.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
|
|
9
|
+
youngjin_langchain_tools-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|