lionagi 0.13.1__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/libs/schema/as_readable.py +224 -2
- lionagi/service/connections/providers/claude_code_.py +194 -20
- lionagi/version.py +1 -1
- {lionagi-0.13.1.dist-info → lionagi-0.13.2.dist-info}/METADATA +4 -1
- {lionagi-0.13.1.dist-info → lionagi-0.13.2.dist-info}/RECORD +7 -7
- {lionagi-0.13.1.dist-info → lionagi-0.13.2.dist-info}/WHEEL +0 -0
- {lionagi-0.13.1.dist-info → lionagi-0.13.2.dist-info}/licenses/LICENSE +0 -0
@@ -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
|
-
#
|
393
|
+
# Fallback to regular print
|
172
394
|
print(str_)
|
173
395
|
else:
|
174
396
|
return str_
|
@@ -10,11 +10,13 @@ from typing import Any, Literal
|
|
10
10
|
|
11
11
|
from claude_code_sdk import ClaudeCodeOptions
|
12
12
|
from claude_code_sdk import query as sdk_query
|
13
|
+
from claude_code_sdk import types as cc_types
|
13
14
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
14
15
|
|
16
|
+
from lionagi.libs.schema.as_readable import as_readable
|
15
17
|
from lionagi.service.connections.endpoint import Endpoint
|
16
18
|
from lionagi.service.connections.endpoint_config import EndpointConfig
|
17
|
-
from lionagi.utils import to_dict
|
19
|
+
from lionagi.utils import to_dict, to_list
|
18
20
|
|
19
21
|
# --------------------------------------------------------------------------- constants
|
20
22
|
ClaudePermission = Literal[
|
@@ -67,6 +69,14 @@ class ClaudeCodeRequest(BaseModel):
|
|
67
69
|
permission_prompt_tool_name: str | None = None
|
68
70
|
disallowed_tools: list[str] = Field(default_factory=list)
|
69
71
|
|
72
|
+
# -- internal use --------------------------------------------------------
|
73
|
+
auto_finish: bool = Field(
|
74
|
+
default=True,
|
75
|
+
exclude=True,
|
76
|
+
description="Automatically finish the conversation after the first response",
|
77
|
+
)
|
78
|
+
verbose_output: bool = Field(default=False, exclude=True)
|
79
|
+
|
70
80
|
# ------------------------ validators & helpers --------------------------
|
71
81
|
@field_validator("permission_mode", mode="before")
|
72
82
|
def _norm_perm(cls, v):
|
@@ -182,27 +192,46 @@ class ClaudeCodeRequest(BaseModel):
|
|
182
192
|
if not messages:
|
183
193
|
raise ValueError("messages may not be empty")
|
184
194
|
|
185
|
-
prompt =
|
186
|
-
if isinstance(prompt, (dict, list)):
|
187
|
-
prompt = json.dumps(prompt)
|
195
|
+
prompt = ""
|
188
196
|
|
189
|
-
if resume
|
197
|
+
# 1. if resume or continue_conversation, use the last message
|
198
|
+
if resume or continue_conversation:
|
190
199
|
continue_conversation = True
|
191
|
-
|
200
|
+
prompt = messages[-1]["content"]
|
201
|
+
if isinstance(prompt, (dict, list)):
|
202
|
+
prompt = json.dumps(prompt)
|
203
|
+
|
204
|
+
# 2. else, use entire messages except system message
|
205
|
+
else:
|
206
|
+
prompts = []
|
207
|
+
continue_conversation = False
|
208
|
+
for message in messages:
|
209
|
+
if message["role"] != "system":
|
210
|
+
content = message["content"]
|
211
|
+
prompts.append(
|
212
|
+
json.dumps(content)
|
213
|
+
if isinstance(content, (dict, list))
|
214
|
+
else content
|
215
|
+
)
|
216
|
+
|
217
|
+
prompt = "\n".join(prompts)
|
218
|
+
|
219
|
+
# 3. assemble the request data
|
192
220
|
data: dict[str, Any] = dict(
|
193
221
|
prompt=prompt,
|
194
222
|
resume=resume,
|
195
223
|
continue_conversation=bool(continue_conversation),
|
196
224
|
)
|
197
225
|
|
226
|
+
# 4. extract system prompt if available
|
198
227
|
if (messages[0]["role"] == "system") and (
|
199
228
|
resume or continue_conversation
|
200
229
|
):
|
201
230
|
data["system_prompt"] = messages[0]["content"]
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
231
|
+
if kwargs.get("append_system_prompt"):
|
232
|
+
data["append_system_prompt"] = str(
|
233
|
+
kwargs.get("append_system_prompt")
|
234
|
+
)
|
206
235
|
|
207
236
|
data.update(kwargs)
|
208
237
|
return cls.model_validate(data, strict=False)
|
@@ -238,7 +267,7 @@ class ClaudeCodeEndpoint(Endpoint):
|
|
238
267
|
)
|
239
268
|
|
240
269
|
async def stream(self, request: dict | BaseModel, **kwargs):
|
241
|
-
payload = self.create_payload(request, **kwargs)["request"]
|
270
|
+
payload, _ = self.create_payload(request, **kwargs)["request"]
|
242
271
|
async for chunk in self._stream_claude_code(payload):
|
243
272
|
yield chunk
|
244
273
|
|
@@ -258,6 +287,7 @@ class ClaudeCodeEndpoint(Endpoint):
|
|
258
287
|
"session_id": None,
|
259
288
|
"model": "claude-code",
|
260
289
|
"result": "",
|
290
|
+
"tool_uses": [],
|
261
291
|
"tool_results": [],
|
262
292
|
"is_error": False,
|
263
293
|
"num_turns": None,
|
@@ -269,17 +299,31 @@ class ClaudeCodeEndpoint(Endpoint):
|
|
269
299
|
},
|
270
300
|
}
|
271
301
|
|
272
|
-
from claude_code_sdk import types
|
273
|
-
|
274
302
|
for response in responses:
|
275
|
-
if isinstance(response,
|
303
|
+
if isinstance(response, cc_types.SystemMessage):
|
276
304
|
results["session_id"] = response.data.get("session_id")
|
277
305
|
results["model"] = response.data.get("model", "claude-code")
|
278
|
-
if isinstance(
|
279
|
-
|
280
|
-
|
306
|
+
if isinstance(
|
307
|
+
response, cc_types.AssistantMessage | cc_types.UserMessage
|
308
|
+
):
|
309
|
+
for block in to_list(
|
310
|
+
response.content,
|
311
|
+
flatten=True,
|
312
|
+
flatten_tuple_set=True,
|
313
|
+
dropna=True,
|
314
|
+
):
|
315
|
+
if isinstance(block, cc_types.TextBlock):
|
281
316
|
results["result"] += block.text.strip() + "\n"
|
282
|
-
|
317
|
+
|
318
|
+
if isinstance(block, cc_types.ToolUseBlock):
|
319
|
+
entry = {
|
320
|
+
"id": block.id,
|
321
|
+
"name": block.name,
|
322
|
+
"input": block.input,
|
323
|
+
}
|
324
|
+
results["tool_uses"].append(entry)
|
325
|
+
|
326
|
+
if isinstance(block, cc_types.ToolResultBlock):
|
283
327
|
results["tool_results"].append(
|
284
328
|
{
|
285
329
|
"tool_use_id": block.tool_use_id,
|
@@ -287,8 +331,10 @@ class ClaudeCodeEndpoint(Endpoint):
|
|
287
331
|
"is_error": block.is_error,
|
288
332
|
}
|
289
333
|
)
|
290
|
-
|
291
|
-
|
334
|
+
|
335
|
+
if isinstance(response, cc_types.ResultMessage):
|
336
|
+
if response.result:
|
337
|
+
results["result"] = str(response.result).strip()
|
292
338
|
results["usage"] = response.usage
|
293
339
|
results["is_error"] = response.is_error
|
294
340
|
results["total_cost_usd"] = response.total_cost_usd
|
@@ -305,7 +351,135 @@ class ClaudeCodeEndpoint(Endpoint):
|
|
305
351
|
**kwargs,
|
306
352
|
):
|
307
353
|
responses = []
|
354
|
+
request: ClaudeCodeRequest = payload["request"]
|
355
|
+
system: cc_types.SystemMessage = None
|
356
|
+
|
357
|
+
# 1. stream the Claude Code response
|
308
358
|
async for chunk in self._stream_claude_code(**payload):
|
359
|
+
|
360
|
+
if request.verbose_output:
|
361
|
+
_display_message(chunk)
|
362
|
+
|
363
|
+
if isinstance(chunk, cc_types.SystemMessage):
|
364
|
+
system = chunk
|
309
365
|
responses.append(chunk)
|
310
366
|
|
367
|
+
# 2. If the last response is not a ResultMessage and auto_finish is True,
|
368
|
+
# we need to query Claude Code again to get the final result message.
|
369
|
+
if request.auto_finish and not isinstance(
|
370
|
+
responses[-1], cc_types.ResultMessage
|
371
|
+
):
|
372
|
+
options = request.as_claude_options()
|
373
|
+
options.continue_conversation = True
|
374
|
+
options.max_turns = 1
|
375
|
+
if system:
|
376
|
+
options.resume = (
|
377
|
+
system.data.get("session_id", None) if system else None
|
378
|
+
)
|
379
|
+
|
380
|
+
async for chunk in sdk_query(
|
381
|
+
prompt="Please provide a the final result message only",
|
382
|
+
options=options,
|
383
|
+
):
|
384
|
+
if isinstance(chunk, cc_types.ResultMessage):
|
385
|
+
if request.verbose_output:
|
386
|
+
str_ = _verbose_output(chunk)
|
387
|
+
if str_:
|
388
|
+
as_readable(
|
389
|
+
str_,
|
390
|
+
md=True,
|
391
|
+
display_str=True,
|
392
|
+
format_curly=True,
|
393
|
+
max_panel_width=100,
|
394
|
+
theme="light",
|
395
|
+
)
|
396
|
+
|
397
|
+
responses.append(chunk)
|
398
|
+
|
399
|
+
# 3. Parse the responses into a clean format
|
311
400
|
return self._parse_claude_code_response(responses)
|
401
|
+
|
402
|
+
|
403
|
+
def _display_message(chunk):
|
404
|
+
if isinstance(
|
405
|
+
chunk,
|
406
|
+
cc_types.SystemMessage
|
407
|
+
| cc_types.AssistantMessage
|
408
|
+
| cc_types.UserMessage,
|
409
|
+
):
|
410
|
+
str_ = _verbose_output(chunk)
|
411
|
+
if str_:
|
412
|
+
if str_.startswith("Claude:"):
|
413
|
+
as_readable(
|
414
|
+
str_,
|
415
|
+
md=True,
|
416
|
+
display_str=True,
|
417
|
+
max_panel_width=100,
|
418
|
+
theme="light",
|
419
|
+
)
|
420
|
+
else:
|
421
|
+
as_readable(
|
422
|
+
str_,
|
423
|
+
format_curly=True,
|
424
|
+
display_str=True,
|
425
|
+
max_panel_width=100,
|
426
|
+
theme="light",
|
427
|
+
)
|
428
|
+
|
429
|
+
if isinstance(chunk, cc_types.ResultMessage):
|
430
|
+
str_ = _verbose_output(chunk)
|
431
|
+
as_readable(
|
432
|
+
str_,
|
433
|
+
md=True,
|
434
|
+
display_str=True,
|
435
|
+
format_curly=True,
|
436
|
+
max_panel_width=100,
|
437
|
+
theme="light",
|
438
|
+
)
|
439
|
+
|
440
|
+
|
441
|
+
def _verbose_output(res: cc_types.Message) -> str:
|
442
|
+
str_ = ""
|
443
|
+
if isinstance(res, cc_types.SystemMessage):
|
444
|
+
str_ = f"Claude Code Session Started: {res.data.get('session_id', 'unknown')}"
|
445
|
+
str_ += f"\nModel: {res.data.get('model', 'claude-code')}\n---"
|
446
|
+
return str_
|
447
|
+
|
448
|
+
if isinstance(res, cc_types.AssistantMessage | cc_types.UserMessage):
|
449
|
+
for block in to_list(
|
450
|
+
res.content, flatten=True, flatten_tuple_set=True, dropna=True
|
451
|
+
):
|
452
|
+
if isinstance(block, cc_types.TextBlock):
|
453
|
+
text = (
|
454
|
+
block.text.strip() if isinstance(block.text, str) else ""
|
455
|
+
)
|
456
|
+
str_ += f"Claude:\n{text}"
|
457
|
+
|
458
|
+
if isinstance(block, cc_types.ToolUseBlock):
|
459
|
+
input = (
|
460
|
+
json.dumps(block.input, indent=2)
|
461
|
+
if isinstance(block.input, dict)
|
462
|
+
else str(block.input)
|
463
|
+
)
|
464
|
+
input = input[:200] + "..." if len(input) > 200 else input
|
465
|
+
str_ += (
|
466
|
+
f"Tool Use: {block.name} - {block.id}\n - Input: {input}"
|
467
|
+
)
|
468
|
+
|
469
|
+
if isinstance(block, cc_types.ToolResultBlock):
|
470
|
+
content = str(block.content)
|
471
|
+
content = (
|
472
|
+
content[:200] + "..." if len(content) > 200 else content
|
473
|
+
)
|
474
|
+
str_ += (
|
475
|
+
f"Tool Result: {block.tool_use_id}\n - Content: {content}"
|
476
|
+
)
|
477
|
+
return str_
|
478
|
+
|
479
|
+
if isinstance(res, cc_types.ResultMessage):
|
480
|
+
str_ += f"Session Completion - {res.session_id}"
|
481
|
+
str_ += f"\nResult: {res.result or 'No result'}"
|
482
|
+
str_ += f"\n- Cost: ${res.total_cost_usd:.4f} USD"
|
483
|
+
str_ += f"\n- Duration: {res.duration_ms} ms (API: {res.duration_api_ms} ms)"
|
484
|
+
str_ += f"\n- Turns: {res.num_turns}"
|
485
|
+
return str_
|
lionagi/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.13.
|
1
|
+
__version__ = "0.13.2"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lionagi
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.2
|
4
4
|
Summary: An Intelligence Operating System.
|
5
5
|
Author-email: HaiyangLi <quantocean.li@gmail.com>, Liangbingyan Luo <llby_luo@outlook.com>
|
6
6
|
License: Apache License
|
@@ -235,6 +235,7 @@ Requires-Dist: claude-code-sdk>=0.0.14; extra == 'all'
|
|
235
235
|
Requires-Dist: docling>=2.15.1; extra == 'all'
|
236
236
|
Requires-Dist: fastmcp>=2.10.5; extra == 'all'
|
237
237
|
Requires-Dist: ollama>=0.5.0; extra == 'all'
|
238
|
+
Requires-Dist: rich>=13.0.0; extra == 'all'
|
238
239
|
Provides-Extra: claude-code
|
239
240
|
Requires-Dist: claude-code-sdk>=0.0.14; extra == 'claude-code'
|
240
241
|
Provides-Extra: docs
|
@@ -251,6 +252,8 @@ Provides-Extra: ollama
|
|
251
252
|
Requires-Dist: ollama>=0.5.0; extra == 'ollama'
|
252
253
|
Provides-Extra: reader
|
253
254
|
Requires-Dist: docling>=2.15.1; extra == 'reader'
|
255
|
+
Provides-Extra: rich
|
256
|
+
Requires-Dist: rich>=13.0.0; extra == 'rich'
|
254
257
|
Provides-Extra: test
|
255
258
|
Requires-Dist: pytest-asyncio>=1.0.0; extra == 'test'
|
256
259
|
Requires-Dist: pytest>=8.3.4; extra == 'test'
|
@@ -6,7 +6,7 @@ lionagi/config.py,sha256=dAhDFKtaaSfn6WT9dwX9Vd4TWWs6-Su1FgYIrFgYcgc,3709
|
|
6
6
|
lionagi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
lionagi/settings.py,sha256=W52mM34E6jXF3GyqCFzVREKZrmnUqtZm_BVDsUiDI_s,1627
|
8
8
|
lionagi/utils.py,sha256=uLTJKl7aTnFXV6ehA6zwiwEB7G2nQYKsO2pZ6mqFzUk,78908
|
9
|
-
lionagi/version.py,sha256=
|
9
|
+
lionagi/version.py,sha256=blu6md2c3Nnj5gDBi8U36sYO3k8HcND8s7UoQBjfn3g,23
|
10
10
|
lionagi/fields/__init__.py,sha256=8oU7Vfk-fKiULFKqhM6VpJMqdZcVXPTM7twVfNDN_SQ,603
|
11
11
|
lionagi/fields/action.py,sha256=iWSApCM77jS0Oc28lb7G601Etkp-yjx5U1hfI_FQgfA,5792
|
12
12
|
lionagi/fields/base.py,sha256=5CJc7j8kTTWzXwpYzkSAFzx4BglABfx3AElIATKB7bg,3857
|
@@ -41,7 +41,7 @@ lionagi/libs/package/management.py,sha256=zgzZ1lNpUhunu_QExiobSYWlvBbR0EORXW4jrt
|
|
41
41
|
lionagi/libs/package/params.py,sha256=4dJiuaTlnhho6OHmBv-02cHx89XRCjnKqpMhVRvTse8,1056
|
42
42
|
lionagi/libs/package/system.py,sha256=UW8Y6tEPRCy_pBv_Q8CXJAIbuo7CJDDoWEDdnP0ixp4,564
|
43
43
|
lionagi/libs/schema/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
|
44
|
-
lionagi/libs/schema/as_readable.py,sha256=
|
44
|
+
lionagi/libs/schema/as_readable.py,sha256=Sd-SBxOwHaEPw2fBuY0vpAoYqUvatDYnoPWsJO6S8BE,14160
|
45
45
|
lionagi/libs/schema/extract_code_block.py,sha256=PuJbJj1JnqR5fSZudowPcVPpEoKISLr0MjTOOVXSzwY,2394
|
46
46
|
lionagi/libs/schema/extract_docstring.py,sha256=aYyLSRlB8lTH9QF9-6a56uph3AAkNuTyZ0S_duf5-fw,5729
|
47
47
|
lionagi/libs/schema/function_to_schema.py,sha256=XAB031WbYu3a7eFJyYjXVMAjmtWYSYr5kC_DYgjiuyM,5604
|
@@ -177,7 +177,7 @@ lionagi/service/connections/header_factory.py,sha256=22sG4ian3MiNklF6SdQqkEYgtWK
|
|
177
177
|
lionagi/service/connections/match_endpoint.py,sha256=mEZPDkK1qtvjTGN9-PZsK7w_yB7642nZiJsb0l5QUx4,1827
|
178
178
|
lionagi/service/connections/providers/__init__.py,sha256=3lzOakDoBWmMaNnT2g-YwktPKa_Wme4lnPRSmOQfayY,105
|
179
179
|
lionagi/service/connections/providers/anthropic_.py,sha256=SUPnw2UqjY5wuHXLHas6snMTzhQ-UuixvPYbkVnXn34,3083
|
180
|
-
lionagi/service/connections/providers/claude_code_.py,sha256=
|
180
|
+
lionagi/service/connections/providers/claude_code_.py,sha256=pqi_CfWdAnDnKt2ktTMn_62TZ0vgXv6ey4wxbGh-jjU,17393
|
181
181
|
lionagi/service/connections/providers/exa_.py,sha256=GGWaD9jd5gKM257OfUaIBBKIqR1NrNcBE67p_7JbK7g,938
|
182
182
|
lionagi/service/connections/providers/oai_.py,sha256=cUI5En8irtKnEbwApgkyg2cPReS8c4Gxtcv65WnGhas,4776
|
183
183
|
lionagi/service/connections/providers/ollama_.py,sha256=jdx6dGeChwVk5TFfFRbpnrpKzj8YQZw6D5iWJ6zYmfk,4096
|
@@ -203,7 +203,7 @@ lionagi/traits/base.py,sha256=ww1qOTrKz6p0rf52OFSWluUNldhs8_S2O-ers4KEUqQ,7114
|
|
203
203
|
lionagi/traits/composer.py,sha256=7qqISOhN3ZhtL0J6fvU3Ic1vHXS2-SSI6kuwQn2tF3E,12780
|
204
204
|
lionagi/traits/protocols.py,sha256=S4-EwTjpQwAZJcBbROn9QtJyXw4srnyMGOfabzqMh34,12210
|
205
205
|
lionagi/traits/registry.py,sha256=hsQaDE4INsHTwD2ZvYpmPZ92k0i-grgC6PQXbscF5Qc,38465
|
206
|
-
lionagi-0.13.
|
207
|
-
lionagi-0.13.
|
208
|
-
lionagi-0.13.
|
209
|
-
lionagi-0.13.
|
206
|
+
lionagi-0.13.2.dist-info/METADATA,sha256=OSa_9xutEW5Eb8UhrMmMoV247VDjVtJHX2ay0QJI4VA,20457
|
207
|
+
lionagi-0.13.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
208
|
+
lionagi-0.13.2.dist-info/licenses/LICENSE,sha256=VXFWsdoN5AAknBCgFqQNgPWYx7OPp-PFEP961zGdOjc,11288
|
209
|
+
lionagi-0.13.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|