lionagi 0.13.0__py3-none-any.whl → 0.13.2__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.
lionagi/_errors.py CHANGED
@@ -33,3 +33,7 @@ class OperationError(LionError):
33
33
 
34
34
  class ExecutionError(LionError):
35
35
  pass
36
+
37
+
38
+ class ValidationError(LionError):
39
+ pass
@@ -3,10 +3,90 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  import json
6
+ import sys
6
7
  from typing import Any
7
8
 
8
9
  from lionagi.utils import to_dict
9
10
 
11
+ # Try to import rich for enhanced console output
12
+ try:
13
+ from rich.align import Align
14
+ from rich.box import ROUNDED
15
+ from rich.console import Console
16
+ from rich.padding import Padding
17
+ from rich.panel import Panel
18
+ from rich.syntax import Syntax
19
+ from rich.theme import Theme
20
+
21
+ RICH_AVAILABLE = True
22
+ except ImportError:
23
+ RICH_AVAILABLE = False
24
+
25
+ # Custom themes for dark and light modes
26
+ DARK_THEME = Theme(
27
+ {
28
+ "info": "bright_cyan",
29
+ "warning": "bright_yellow",
30
+ "error": "bold bright_red",
31
+ "success": "bold bright_green",
32
+ "panel.border": "bright_blue",
33
+ "panel.title": "bold bright_cyan",
34
+ "markdown.h1": "bold bright_magenta",
35
+ "markdown.h2": "bold bright_blue",
36
+ "markdown.h3": "bold bright_cyan",
37
+ "markdown.h4": "bold bright_green",
38
+ "markdown.code": "bright_yellow on grey23",
39
+ "markdown.code_block": "bright_white on grey15",
40
+ "markdown.paragraph": "bright_white",
41
+ "markdown.text": "bright_white",
42
+ "markdown.emph": "italic bright_yellow",
43
+ "markdown.strong": "bold bright_white",
44
+ "markdown.item": "bright_cyan",
45
+ "markdown.item.bullet": "bright_blue",
46
+ "json.key": "bright_cyan",
47
+ "json.string": "bright_green",
48
+ "json.number": "bright_yellow",
49
+ "json.boolean": "bright_magenta",
50
+ "json.null": "bright_red",
51
+ "yaml.key": "bright_cyan",
52
+ "yaml.string": "bright_green",
53
+ "yaml.number": "bright_yellow",
54
+ "yaml.boolean": "bright_magenta",
55
+ }
56
+ )
57
+
58
+ LIGHT_THEME = Theme(
59
+ {
60
+ "info": "blue",
61
+ "warning": "dark_orange",
62
+ "error": "bold red",
63
+ "success": "bold green4",
64
+ "panel.border": "blue",
65
+ "panel.title": "bold blue",
66
+ "markdown.h1": "bold dark_magenta",
67
+ "markdown.h2": "bold dark_blue",
68
+ "markdown.h3": "bold dark_cyan",
69
+ "markdown.h4": "bold dark_green",
70
+ "markdown.code": "dark_orange on grey93",
71
+ "markdown.code_block": "black on grey82",
72
+ "markdown.paragraph": "black",
73
+ "markdown.text": "black",
74
+ "markdown.emph": "italic dark_orange",
75
+ "markdown.strong": "bold black",
76
+ "markdown.item": "dark_blue",
77
+ "markdown.item.bullet": "blue",
78
+ "json.key": "dark_blue",
79
+ "json.string": "dark_green",
80
+ "json.number": "dark_orange",
81
+ "json.boolean": "dark_magenta",
82
+ "json.null": "dark_red",
83
+ "yaml.key": "dark_blue",
84
+ "yaml.string": "dark_green",
85
+ "yaml.number": "dark_orange",
86
+ "yaml.boolean": "dark_magenta",
87
+ }
88
+ )
89
+
10
90
 
11
91
  def in_notebook() -> bool:
12
92
  """
@@ -22,6 +102,18 @@ def in_notebook() -> bool:
22
102
  return False
23
103
 
24
104
 
105
+ def in_console() -> bool:
106
+ """
107
+ Checks if we're running in a console/terminal environment.
108
+ Returns True if stdout is a TTY and not in a notebook.
109
+ """
110
+ return (
111
+ hasattr(sys.stdout, "isatty")
112
+ and sys.stdout.isatty()
113
+ and not in_notebook()
114
+ )
115
+
116
+
25
117
  def format_dict(data: Any, indent: int = 0) -> str:
26
118
  """
27
119
  Recursively format Python data (dicts, lists, strings, etc.) into a
@@ -77,6 +169,9 @@ def as_readable(
77
169
  format_curly: bool = False,
78
170
  display_str: bool = False,
79
171
  max_chars: int | None = None,
172
+ use_rich: bool = True,
173
+ theme: str = "dark",
174
+ max_panel_width: int = 140,
80
175
  ) -> str:
81
176
  """
82
177
  Convert `input_` into a human-readable string. If `format_curly=True`, uses
@@ -84,14 +179,21 @@ def as_readable(
84
179
 
85
180
  - For Pydantic models or nested data, uses `to_dict` to get a dictionary.
86
181
  - If the result is a list of items, each is processed and concatenated.
182
+ - When in console and rich is available, provides syntax highlighting.
87
183
 
88
184
  Args:
89
185
  input_: The data to convert (could be a single item or list).
90
186
  md: If True, wraps the final output in code fences for Markdown display.
91
187
  format_curly: If True, use `format_dict`. Otherwise, produce JSON text.
188
+ display_str: If True, prints the output instead of returning it.
189
+ max_chars: If set, truncates output to this many characters.
190
+ use_rich: If True and rich is available, uses rich for console output.
191
+ theme: Color theme - "dark" (default) or "light". Dark uses GitHub Dark Dimmed,
192
+ light uses Solarized Light inspired colors.
193
+ max_panel_width: Maximum width for panels and code blocks in characters.
92
194
 
93
195
  Returns:
94
- A formatted string representation of `input_`.
196
+ A formatted string representation of `input_` (unless display_str=True).
95
197
  """
96
198
 
97
199
  # 1) Convert the input to a Python dict/list structure
@@ -108,6 +210,7 @@ def as_readable(
108
210
  return to_dict(obj, **to_dict_kwargs)
109
211
 
110
212
  def _inner(i_: Any) -> Any:
213
+ items = []
111
214
  try:
112
215
  if isinstance(i_, list):
113
216
  # Already a list. Convert each item
@@ -167,8 +270,127 @@ def as_readable(
167
270
  from IPython.display import Markdown, display
168
271
 
169
272
  display(Markdown(str_))
273
+ elif RICH_AVAILABLE and in_console() and use_rich:
274
+ # Use rich for enhanced console output
275
+ # Select theme and syntax highlighting based on user preference
276
+ console_theme = DARK_THEME if theme == "dark" else LIGHT_THEME
277
+ syntax_theme = (
278
+ "github-dark" if theme == "dark" else "solarized-light"
279
+ )
280
+ panel_style = "bright_blue" if theme == "dark" else "blue"
281
+
282
+ console = Console(theme=console_theme)
283
+
284
+ # Check if content looks like markdown prose (not code)
285
+ is_markdown_prose = isinstance(str_, str) and (
286
+ str_.startswith("#")
287
+ or str_.startswith("**")
288
+ or str_.startswith("- ")
289
+ or str_.startswith("1.")
290
+ or "<multi_reasoning>" in str_
291
+ or "\n### " in str_
292
+ or "\n## " in str_
293
+ or "\n# " in str_
294
+ or "│" in str_ # Rich table content
295
+ )
296
+
297
+ if md and is_markdown_prose:
298
+ # Display as formatted markdown
299
+ # Create markdown with max width
300
+ from rich.markdown import Markdown as RichMarkdown
301
+
302
+ md_content = RichMarkdown(str_, code_theme=syntax_theme)
303
+
304
+ # Calculate appropriate width
305
+ console_width = console.width
306
+ panel_width = min(console_width - 4, max_panel_width)
307
+
308
+ # Add left margin padding for better alignment
309
+ panel = Panel(
310
+ Padding(md_content, (0, 2)),
311
+ border_style=panel_style,
312
+ box=ROUNDED,
313
+ width=panel_width,
314
+ expand=False,
315
+ )
316
+
317
+ # Left align with margin
318
+ aligned_panel = Align.left(panel, pad=True)
319
+ console.print(Padding(aligned_panel, (0, 0, 0, 4)))
320
+
321
+ elif md:
322
+ # Extract content from markdown code blocks if present
323
+ content = str_
324
+ if content.startswith("```") and content.endswith("```"):
325
+ # Remove code fences
326
+ lines = content.split("\n")
327
+ if len(lines) > 2:
328
+ lang = lines[0][3:].strip() or "json"
329
+ content = "\n".join(lines[1:-1])
330
+ else:
331
+ lang = "json"
332
+ else:
333
+ lang = "yaml" if format_curly else "json"
334
+
335
+ # Calculate appropriate width
336
+ console_width = console.width
337
+ panel_width = min(console_width - 4, max_panel_width)
338
+
339
+ # Create syntax highlighted output
340
+ syntax = Syntax(
341
+ content,
342
+ lang,
343
+ theme=syntax_theme,
344
+ line_numbers=True,
345
+ background_color="default",
346
+ word_wrap=True,
347
+ )
348
+
349
+ # Add left margin padding for better alignment
350
+ panel = Panel(
351
+ syntax,
352
+ border_style=panel_style,
353
+ box=ROUNDED,
354
+ width=panel_width,
355
+ expand=False,
356
+ )
357
+
358
+ # Left align with margin
359
+ aligned_panel = Align.left(panel, pad=True)
360
+ console.print(Padding(aligned_panel, (0, 0, 0, 4)))
361
+
362
+ else:
363
+ # Plain text output with rich formatting
364
+ if format_curly:
365
+ syntax = Syntax(
366
+ str_,
367
+ "yaml",
368
+ theme=syntax_theme,
369
+ background_color="default",
370
+ word_wrap=True,
371
+ )
372
+ else:
373
+ syntax = Syntax(
374
+ str_,
375
+ "json",
376
+ theme=syntax_theme,
377
+ background_color="default",
378
+ word_wrap=True,
379
+ )
380
+
381
+ # For plain syntax, add left margin
382
+ # Create a constrained width container if console is too wide
383
+ if console.width > max_panel_width:
384
+ content = Align.left(
385
+ syntax, width=max_panel_width, pad=False
386
+ )
387
+ # Add left margin
388
+ console.print(Padding(content, (0, 0, 0, 4)))
389
+ else:
390
+ # Just add left margin
391
+ console.print(Padding(syntax, (0, 0, 0, 4)))
170
392
  else:
171
- # Otherwise, just print the string
393
+ # Fallback to regular print
172
394
  print(str_)
173
395
  else:
174
396
  return str_