youngjin-langchain-tools 0.1.3__py3-none-any.whl → 0.2.0__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 +128 -91
- {youngjin_langchain_tools-0.1.3.dist-info → youngjin_langchain_tools-0.2.0.dist-info}/METADATA +1 -1
- {youngjin_langchain_tools-0.1.3.dist-info → youngjin_langchain_tools-0.2.0.dist-info}/RECORD +5 -5
- {youngjin_langchain_tools-0.1.3.dist-info → youngjin_langchain_tools-0.2.0.dist-info}/WHEEL +0 -0
- {youngjin_langchain_tools-0.1.3.dist-info → youngjin_langchain_tools-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -124,13 +124,22 @@ def _parse_error(error: Exception) -> Dict[str, Any]:
|
|
|
124
124
|
|
|
125
125
|
@dataclass
|
|
126
126
|
class StreamlitLanggraphHandlerConfig:
|
|
127
|
-
"""Configuration for StreamlitLanggraphHandler.
|
|
127
|
+
"""Configuration for StreamlitLanggraphHandler.
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
expand_new_thoughts: 도구 결과 expander를 펼친 상태로 표시할지 여부.
|
|
131
|
+
collapse_completed_thoughts: 완료 시 status container를 접을지 여부.
|
|
132
|
+
max_thought_containers: 표시할 최대 사고 과정 수 (초과 시 History로 이동).
|
|
133
|
+
max_tool_content_length: 도구 결과 최대 표시 문자 수.
|
|
134
|
+
show_tool_calls: 도구 호출 정보 표시 여부.
|
|
135
|
+
show_tool_results: 도구 실행 결과 표시 여부.
|
|
136
|
+
"""
|
|
128
137
|
|
|
129
138
|
expand_new_thoughts: bool = True
|
|
130
|
-
"""Whether to
|
|
139
|
+
"""Whether to show tool result expanders in expanded state."""
|
|
131
140
|
|
|
132
141
|
collapse_completed_thoughts: bool = True
|
|
133
|
-
"""Whether to
|
|
142
|
+
"""Whether to collapse the status container when agent completes."""
|
|
134
143
|
|
|
135
144
|
max_thought_containers: int = 4
|
|
136
145
|
"""Maximum number of thought containers to show at once.
|
|
@@ -257,9 +266,9 @@ class StreamlitLanggraphHandler:
|
|
|
257
266
|
self._final_response: str = ""
|
|
258
267
|
self._status_container: Any = None
|
|
259
268
|
self._response_placeholder: Any = None
|
|
260
|
-
self._history_placeholder: Any = None # Placeholder for history expander
|
|
261
269
|
self._thought_history: List[Dict[str, Any]] = [] # History of old thoughts
|
|
262
270
|
self._current_thoughts: List[Dict[str, Any]] = [] # Current visible thoughts
|
|
271
|
+
self._thought_counter: int = 0 # Unique ID counter for thoughts
|
|
263
272
|
|
|
264
273
|
@property
|
|
265
274
|
def config(self) -> StreamlitLanggraphHandlerConfig:
|
|
@@ -353,24 +362,23 @@ class StreamlitLanggraphHandler:
|
|
|
353
362
|
self._final_response = ""
|
|
354
363
|
self._thought_history = []
|
|
355
364
|
self._current_thoughts = []
|
|
365
|
+
self._thought_counter = 0
|
|
356
366
|
|
|
357
367
|
# Create UI components
|
|
358
368
|
with self._container:
|
|
359
|
-
# History expander placeholder (will be shown when needed)
|
|
360
|
-
self._history_placeholder = st.empty()
|
|
361
369
|
self._status_container = st.status(
|
|
362
370
|
self._config.thinking_label,
|
|
363
|
-
expanded=
|
|
371
|
+
expanded=True # Always start expanded during processing
|
|
364
372
|
)
|
|
365
373
|
self._response_placeholder = st.empty()
|
|
366
374
|
|
|
367
375
|
# Stream from agent with error handling
|
|
368
|
-
|
|
376
|
+
agent_config = config or {}
|
|
369
377
|
|
|
370
378
|
try:
|
|
371
379
|
for stream_mode, data in agent.stream(
|
|
372
380
|
input,
|
|
373
|
-
config=
|
|
381
|
+
config=agent_config,
|
|
374
382
|
stream_mode=["messages", "updates"]
|
|
375
383
|
):
|
|
376
384
|
if stream_mode == "updates":
|
|
@@ -378,11 +386,11 @@ class StreamlitLanggraphHandler:
|
|
|
378
386
|
elif stream_mode == "messages":
|
|
379
387
|
yield from self._handle_messages(data)
|
|
380
388
|
|
|
381
|
-
# Mark as complete
|
|
389
|
+
# Mark as complete - collapse based on config
|
|
382
390
|
self._status_container.update(
|
|
383
391
|
label=self._config.complete_label,
|
|
384
392
|
state="complete",
|
|
385
|
-
expanded=
|
|
393
|
+
expanded=not self._config.collapse_completed_thoughts
|
|
386
394
|
)
|
|
387
395
|
|
|
388
396
|
except Exception as e:
|
|
@@ -430,58 +438,90 @@ class StreamlitLanggraphHandler:
|
|
|
430
438
|
|
|
431
439
|
def _add_thought(self, thought_type: str, data: Dict[str, Any]) -> None:
|
|
432
440
|
"""Add a thought and manage history if max_thought_containers exceeded."""
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
441
|
+
self._thought_counter += 1
|
|
442
|
+
thought = {
|
|
443
|
+
"id": self._thought_counter,
|
|
444
|
+
"type": thought_type,
|
|
445
|
+
"data": data
|
|
446
|
+
}
|
|
436
447
|
self._current_thoughts.append(thought)
|
|
437
448
|
|
|
438
449
|
# Check if we need to move old thoughts to history
|
|
439
450
|
max_containers = self._config.max_thought_containers
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
451
|
+
while len(self._current_thoughts) > max_containers:
|
|
452
|
+
old_thought = self._current_thoughts.pop(0)
|
|
453
|
+
self._thought_history.append(old_thought)
|
|
454
|
+
|
|
455
|
+
def _render_thought_item(
|
|
456
|
+
self,
|
|
457
|
+
thought: Dict[str, Any],
|
|
458
|
+
st_module: Any,
|
|
459
|
+
in_history: bool = False
|
|
460
|
+
) -> None:
|
|
461
|
+
"""Render a single thought item.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
thought: The thought dict with id, type, data.
|
|
465
|
+
st_module: The streamlit module reference.
|
|
466
|
+
in_history: Whether this thought is in history section.
|
|
467
|
+
"""
|
|
468
|
+
thought_type = thought["type"]
|
|
469
|
+
thought_data = thought["data"]
|
|
470
|
+
|
|
471
|
+
if thought_type == "tool_call":
|
|
472
|
+
# Tool call display: 🔧 tool_name: {args}
|
|
473
|
+
st_module.markdown(
|
|
474
|
+
f"{self._config.tool_call_emoji} "
|
|
475
|
+
f"**{thought_data['name']}**: `{thought_data['args']}`"
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
elif thought_type == "tool_result":
|
|
479
|
+
# Tool result display: ✅ tool_name 완료 + expander
|
|
480
|
+
tool_name = thought_data['name']
|
|
481
|
+
content = thought_data['content']
|
|
445
482
|
|
|
446
|
-
|
|
447
|
-
|
|
483
|
+
st_module.markdown(
|
|
484
|
+
f"{self._config.tool_complete_emoji} "
|
|
485
|
+
f"**{tool_name}** 완료"
|
|
486
|
+
)
|
|
448
487
|
|
|
449
|
-
|
|
450
|
-
|
|
488
|
+
# Determine max length and expand state
|
|
489
|
+
if in_history:
|
|
490
|
+
max_len = 200
|
|
491
|
+
should_expand = False # History items always collapsed
|
|
492
|
+
else:
|
|
493
|
+
max_len = self._config.max_tool_content_length
|
|
494
|
+
should_expand = self._config.expand_new_thoughts
|
|
495
|
+
|
|
496
|
+
with st_module.expander(f"📋 {tool_name} 결과 보기", expanded=should_expand):
|
|
497
|
+
if len(content) > max_len:
|
|
498
|
+
st_module.code(content[:max_len] + "\n... (truncated)", language="text")
|
|
499
|
+
else:
|
|
500
|
+
st_module.code(content, language="text")
|
|
501
|
+
|
|
502
|
+
def _render_thoughts_in_status(self) -> None:
|
|
503
|
+
"""Render all thoughts (history + current) inside the status container."""
|
|
451
504
|
import streamlit as st
|
|
452
505
|
|
|
453
|
-
|
|
454
|
-
|
|
506
|
+
with self._status_container:
|
|
507
|
+
# Render history expander if there are old thoughts
|
|
508
|
+
if self._thought_history:
|
|
509
|
+
with st.expander(
|
|
510
|
+
f"📜 History ({len(self._thought_history)} items)",
|
|
511
|
+
expanded=False
|
|
512
|
+
):
|
|
513
|
+
for thought in self._thought_history:
|
|
514
|
+
self._render_thought_item(thought, st, in_history=True)
|
|
455
515
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
if thought["type"] == "tool_call":
|
|
460
|
-
st.markdown(
|
|
461
|
-
f"{self._config.tool_call_emoji} "
|
|
462
|
-
f"**{thought['data']['name']}**: `{thought['data']['args']}`"
|
|
463
|
-
)
|
|
464
|
-
elif thought["type"] == "tool_result":
|
|
465
|
-
st.markdown(
|
|
466
|
-
f"{self._config.tool_complete_emoji} "
|
|
467
|
-
f"**{thought['data']['name']}** 완료"
|
|
468
|
-
)
|
|
469
|
-
content = thought['data']['content']
|
|
470
|
-
if len(content) > 200: # Shorter preview in history
|
|
471
|
-
st.code(content[:200] + "\n... (truncated)", language="text")
|
|
472
|
-
else:
|
|
473
|
-
st.code(content, language="text")
|
|
516
|
+
# Render current thoughts
|
|
517
|
+
for thought in self._current_thoughts:
|
|
518
|
+
self._render_thought_item(thought, st, in_history=False)
|
|
474
519
|
|
|
475
520
|
def _handle_updates(
|
|
476
521
|
self,
|
|
477
522
|
data: Dict[str, Any]
|
|
478
523
|
) -> Generator[Dict[str, Any], None, None]:
|
|
479
524
|
"""Handle 'updates' stream mode events."""
|
|
480
|
-
try:
|
|
481
|
-
import streamlit as st
|
|
482
|
-
except ImportError:
|
|
483
|
-
return
|
|
484
|
-
|
|
485
525
|
for source, update in data.items():
|
|
486
526
|
if not isinstance(update, dict):
|
|
487
527
|
continue
|
|
@@ -490,55 +530,52 @@ class StreamlitLanggraphHandler:
|
|
|
490
530
|
for msg in messages:
|
|
491
531
|
# Handle tool calls
|
|
492
532
|
if hasattr(msg, 'tool_calls') and msg.tool_calls:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
self._add_thought(
|
|
533
|
+
for tc in msg.tool_calls:
|
|
534
|
+
tool_name = tc.get('name', 'tool')
|
|
535
|
+
tool_args = tc.get('args', {})
|
|
536
|
+
|
|
537
|
+
# Only add to thoughts if show_tool_calls is True
|
|
538
|
+
if self._config.show_tool_calls:
|
|
539
|
+
self._add_thought(
|
|
540
|
+
"tool_call",
|
|
541
|
+
{"name": tool_name, "args": tool_args}
|
|
542
|
+
)
|
|
543
|
+
self._render_thoughts_in_status()
|
|
500
544
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
545
|
+
# Update status label to show current action
|
|
546
|
+
self._status_container.update(
|
|
547
|
+
label=f"{self._config.tool_call_emoji} {tool_name}...",
|
|
548
|
+
state="running"
|
|
549
|
+
)
|
|
506
550
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
551
|
+
yield {
|
|
552
|
+
"type": "tool_call",
|
|
553
|
+
"data": {"name": tool_name, "args": tool_args}
|
|
554
|
+
}
|
|
511
555
|
|
|
512
556
|
# Handle tool results
|
|
513
557
|
if source == "tools" and hasattr(msg, 'name'):
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
tool_content = str(msg.content) if hasattr(msg, 'content') else ""
|
|
517
|
-
|
|
518
|
-
# Add to thought tracking
|
|
519
|
-
self._add_thought("tool_result", {"name": tool_name, "content": tool_content})
|
|
520
|
-
|
|
521
|
-
with self._status_container:
|
|
522
|
-
st.write(
|
|
523
|
-
f"{self._config.tool_complete_emoji} "
|
|
524
|
-
f"**{tool_name}** 완료"
|
|
525
|
-
)
|
|
526
|
-
# Collapse based on config
|
|
527
|
-
expanded = not self._config.collapse_completed_thoughts
|
|
528
|
-
with st.expander(f"📋 {tool_name} 결과 보기", expanded=expanded):
|
|
529
|
-
if len(tool_content) > self._config.max_tool_content_length:
|
|
530
|
-
st.code(
|
|
531
|
-
tool_content[:self._config.max_tool_content_length]
|
|
532
|
-
+ "\n... (truncated)",
|
|
533
|
-
language="text"
|
|
534
|
-
)
|
|
535
|
-
else:
|
|
536
|
-
st.code(tool_content, language="text")
|
|
558
|
+
tool_name = msg.name
|
|
559
|
+
tool_content = str(msg.content) if hasattr(msg, 'content') else ""
|
|
537
560
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
561
|
+
# Only add to thoughts if show_tool_results is True
|
|
562
|
+
if self._config.show_tool_results:
|
|
563
|
+
self._add_thought(
|
|
564
|
+
"tool_result",
|
|
565
|
+
{"name": tool_name, "content": tool_content}
|
|
566
|
+
)
|
|
567
|
+
self._render_thoughts_in_status()
|
|
568
|
+
|
|
569
|
+
# Update status to show thinking again
|
|
570
|
+
self._status_container.update(
|
|
571
|
+
label=self._config.thinking_label,
|
|
572
|
+
state="running"
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
yield {
|
|
576
|
+
"type": "tool_result",
|
|
577
|
+
"data": {"name": tool_name, "content": tool_content}
|
|
578
|
+
}
|
|
542
579
|
|
|
543
580
|
def _handle_messages(
|
|
544
581
|
self,
|
{youngjin_langchain_tools-0.1.3.dist-info → youngjin_langchain_tools-0.2.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youngjin-langchain-tools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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.3.dist-info → youngjin_langchain_tools-0.2.0.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=O2_B5I7E0OWqh1JdS014aGw_smQsCpT7NykxcH16Ql8,22599
|
|
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.
|
|
7
|
-
youngjin_langchain_tools-0.
|
|
8
|
-
youngjin_langchain_tools-0.
|
|
9
|
-
youngjin_langchain_tools-0.
|
|
6
|
+
youngjin_langchain_tools-0.2.0.dist-info/METADATA,sha256=3zKlWZoqhkSKRxhFV89VL08pgwtfYxZgORGKWZM3N4Q,6959
|
|
7
|
+
youngjin_langchain_tools-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
youngjin_langchain_tools-0.2.0.dist-info/licenses/LICENSE,sha256=fENUlkDDxJEn5c0u8mhVcz5ek-rTg2L4Kby1tMNchI8,11342
|
|
9
|
+
youngjin_langchain_tools-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|