youngjin-langchain-tools 0.1.0__tar.gz → 0.1.2__tar.gz

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.
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: youngjin-langchain-tools
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
7
7
  Project-URL: Repository, https://github.com/yourusername/youngjin-langchain-tools.git
8
8
  Project-URL: Issues, https://github.com/yourusername/youngjin-langchain-tools/issues
9
9
  Project-URL: Changelog, https://github.com/yourusername/youngjin-langchain-tools/releases
10
- Author-email: YoungJin <your-email@example.com>
11
- Maintainer-email: YoungJin <your-email@example.com>
10
+ Author-email: CoCoRoF <gkfua00@gmail.com>
11
+ Maintainer-email: CoCoRoF <gkfua00@gmail.com>
12
12
  License: Apache-2.0
13
13
  License-File: LICENSE
14
14
  Keywords: agents,ai,callback,handler,langchain,langgraph,llm,streamlit
@@ -16,12 +16,13 @@ Classifier: Development Status :: 3 - Alpha
16
16
  Classifier: Intended Audience :: Developers
17
17
  Classifier: License :: OSI Approved :: Apache Software License
18
18
  Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
19
21
  Classifier: Programming Language :: Python :: 3.12
20
22
  Classifier: Programming Language :: Python :: 3.13
21
- Classifier: Programming Language :: Python :: 3.14
22
23
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.12
25
+ Requires-Python: >=3.10
25
26
  Requires-Dist: langchain-core<1.2.7,>=1.0.0
26
27
  Requires-Dist: langchain<1.2.7,>=1.0.0
27
28
  Requires-Dist: pydantic>=2.0.0
@@ -4,16 +4,16 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "youngjin-langchain-tools"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "LangGraph utilities for Streamlit - StreamlitLanggraphHandler and more"
9
9
  readme = "README.md"
10
- requires-python = ">=3.12"
10
+ requires-python = ">=3.10"
11
11
  license = {text = "Apache-2.0"}
12
12
  authors = [
13
- {name = "YoungJin", email = "your-email@example.com"},
13
+ {name = "CoCoRoF", email = "gkfua00@gmail.com"},
14
14
  ]
15
15
  maintainers = [
16
- {name = "YoungJin", email = "your-email@example.com"},
16
+ {name = "CoCoRoF", email = "gkfua00@gmail.com"},
17
17
  ]
18
18
  keywords = [
19
19
  "langchain",
@@ -30,9 +30,10 @@ classifiers = [
30
30
  "Intended Audience :: Developers",
31
31
  "License :: OSI Approved :: Apache Software License",
32
32
  "Programming Language :: Python :: 3",
33
+ "Programming Language :: Python :: 3.10",
34
+ "Programming Language :: Python :: 3.11",
33
35
  "Programming Language :: Python :: 3.12",
34
36
  "Programming Language :: Python :: 3.13",
35
- "Programming Language :: Python :: 3.14",
36
37
  "Topic :: Software Development :: Libraries :: Python Modules",
37
38
  "Topic :: Scientific/Engineering :: Artificial Intelligence",
38
39
  ]
@@ -82,13 +83,13 @@ exclude = [
82
83
 
83
84
  [tool.ruff]
84
85
  line-length = 88
85
- target-version = "py312"
86
+ target-version = "py310"
86
87
 
87
88
  [tool.ruff.lint]
88
89
  select = ["E", "F", "W", "I", "N", "UP", "B", "C4"]
89
90
 
90
91
  [tool.mypy]
91
- python_version = "3.12"
92
+ python_version = "3.10"
92
93
  warn_return_any = true
93
94
  warn_unused_configs = true
94
95
  disallow_untyped_defs = true
@@ -10,6 +10,116 @@ Replaces the deprecated StreamlitCallbackHandler for LangGraph-based agents.
10
10
 
11
11
  from typing import Any, Dict, List, Optional, Union, Generator
12
12
  from dataclasses import dataclass, field
13
+ import logging
14
+ import re
15
+
16
+ # Configure logging
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # ============================================================
21
+ # Error Patterns for User-Friendly Messages
22
+ # ============================================================
23
+ ERROR_PATTERNS = {
24
+ # OpenAI errors
25
+ r"AuthenticationError.*API key|openai.*api.*key|OPENAI_API_KEY": {
26
+ "title": "🔑 OpenAI API Key 오류",
27
+ "message": "OpenAI API 키가 설정되지 않았거나 유효하지 않습니다.",
28
+ "solution": [
29
+ "환경변수 `OPENAI_API_KEY`를 설정하거나 클라이언트 초기화 시 `api_key` 파라미터를 전달하세요.",
30
+ "API 키 발급: https://platform.openai.com/api-keys",
31
+ ],
32
+ },
33
+ r"RateLimitError|rate_limit|429": {
34
+ "title": "⏱️ Rate Limit 초과",
35
+ "message": "API 요청 한도를 초과했습니다.",
36
+ "solution": [
37
+ "잠시 후 다시 시도하거나, API 사용량 및 요금제를 확인하세요.",
38
+ ],
39
+ },
40
+ r"InsufficientQuotaError|insufficient_quota|billing": {
41
+ "title": "💳 크레딧 부족",
42
+ "message": "API 크레딧이 부족합니다.",
43
+ "solution": [
44
+ "API 제공자의 결제 페이지에서 크레딧을 충전하세요.",
45
+ ],
46
+ },
47
+ r"InvalidRequestError|invalid_request": {
48
+ "title": "❌ 잘못된 요청",
49
+ "message": "API 요청 형식이 올바르지 않습니다.",
50
+ "solution": [
51
+ "입력 데이터와 모델명이 올바른지 확인하세요.",
52
+ ],
53
+ },
54
+ # Anthropic errors
55
+ r"anthropic.*authentication|ANTHROPIC_API_KEY": {
56
+ "title": "🔑 Anthropic API Key 오류",
57
+ "message": "Anthropic API 키가 설정되지 않았거나 유효하지 않습니다.",
58
+ "solution": [
59
+ "환경변수 `ANTHROPIC_API_KEY`를 설정하세요.",
60
+ "API 키 발급: https://console.anthropic.com/",
61
+ ],
62
+ },
63
+ # Google errors
64
+ r"google.*api.*key|GOOGLE_API_KEY": {
65
+ "title": "🔑 Google API Key 오류",
66
+ "message": "Google API 키가 설정되지 않았거나 유효하지 않습니다.",
67
+ "solution": [
68
+ "환경변수 `GOOGLE_API_KEY`를 설정하세요.",
69
+ "API 키 발급: https://aistudio.google.com/apikey",
70
+ ],
71
+ },
72
+ # Network errors
73
+ r"ConnectionError|connection.*refused|network": {
74
+ "title": "🌐 네트워크 오류",
75
+ "message": "API 서버에 연결할 수 없습니다.",
76
+ "solution": [
77
+ "인터넷 연결 및 방화벽/프록시 설정을 확인하세요.",
78
+ ],
79
+ },
80
+ r"TimeoutError|timeout|timed out": {
81
+ "title": "⏰ 시간 초과",
82
+ "message": "API 요청이 시간 초과되었습니다.",
83
+ "solution": [
84
+ "네트워크 연결을 확인하고 잠시 후 다시 시도하세요.",
85
+ ],
86
+ },
87
+ # Model errors
88
+ r"model.*not.*found|does not exist|invalid.*model": {
89
+ "title": "🤖 모델 오류",
90
+ "message": "지정된 모델을 찾을 수 없습니다.",
91
+ "solution": [
92
+ "모델명과 접근 권한을 확인하세요.",
93
+ ],
94
+ },
95
+ }
96
+
97
+
98
+ def _parse_error(error: Exception) -> Dict[str, Any]:
99
+ """Parse an exception and return user-friendly error information."""
100
+ error_str = str(error)
101
+ error_type = type(error).__name__
102
+ full_error = f"{error_type}: {error_str}"
103
+
104
+ # Try to match known error patterns
105
+ for pattern, info in ERROR_PATTERNS.items():
106
+ if re.search(pattern, full_error, re.IGNORECASE):
107
+ return {
108
+ "matched": True,
109
+ "title": info["title"],
110
+ "message": info["message"],
111
+ "solution": info["solution"],
112
+ "original_error": error_str[:500], # Truncate for display
113
+ }
114
+
115
+ # Unknown error - return generic info
116
+ return {
117
+ "matched": False,
118
+ "title": "❗ 오류 발생",
119
+ "message": f"{error_type}",
120
+ "solution": ["에러 메시지를 확인하고 문제를 해결해주세요."],
121
+ "original_error": error_str[:500],
122
+ }
13
123
 
14
124
 
15
125
  @dataclass
@@ -231,25 +341,63 @@ class StreamlitLanggraphHandler:
231
341
  )
232
342
  self._response_placeholder = st.empty()
233
343
 
234
- # Stream from agent
344
+ # Stream from agent with error handling
235
345
  config = config or {}
236
346
 
237
- for stream_mode, data in agent.stream(
238
- input,
239
- config=config,
240
- stream_mode=["messages", "updates"]
241
- ):
242
- if stream_mode == "updates":
243
- yield from self._handle_updates(data)
244
- elif stream_mode == "messages":
245
- yield from self._handle_messages(data)
246
-
247
- # Mark as complete
248
- self._status_container.update(
249
- label=self._config.complete_label,
250
- state="complete",
251
- expanded=False
252
- )
347
+ try:
348
+ for stream_mode, data in agent.stream(
349
+ input,
350
+ config=config,
351
+ stream_mode=["messages", "updates"]
352
+ ):
353
+ if stream_mode == "updates":
354
+ yield from self._handle_updates(data)
355
+ elif stream_mode == "messages":
356
+ yield from self._handle_messages(data)
357
+
358
+ # Mark as complete
359
+ self._status_container.update(
360
+ label=self._config.complete_label,
361
+ state="complete",
362
+ expanded=False
363
+ )
364
+
365
+ except Exception as e:
366
+ # Parse error and display user-friendly message
367
+ error_info = _parse_error(e)
368
+
369
+ # Update status to show error
370
+ self._status_container.update(
371
+ label="❌ 오류 발생",
372
+ state="error",
373
+ expanded=True
374
+ )
375
+
376
+ # Display error in status container
377
+ with self._status_container:
378
+ st.error(f"**{error_info['title']}**")
379
+ st.markdown(f"_{error_info['message']}_")
380
+
381
+ st.markdown("**해결 방법:**")
382
+ for solution in error_info["solution"]:
383
+ st.markdown(f" {solution}")
384
+
385
+ with st.expander("🔍 상세 에러 메시지", expanded=False):
386
+ st.code(error_info["original_error"], language="text")
387
+
388
+ # Log the full error for debugging
389
+ logger.error(f"Agent execution error: {e}", exc_info=True)
390
+
391
+ # Yield error event
392
+ yield {
393
+ "type": "error",
394
+ "data": {
395
+ "error_type": type(e).__name__,
396
+ "error_info": error_info,
397
+ "original_error": str(e),
398
+ }
399
+ }
400
+ return # Stop further processing
253
401
 
254
402
  # Final render without cursor
255
403
  if self._final_response: