lionagi 0.14.5__py3-none-any.whl → 0.14.7__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.
@@ -1,352 +1,23 @@
1
+ # Copyright (c) 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
- import asyncio
4
- import codecs
5
- import contextlib
6
- import dataclasses
7
- import json
8
- import logging
9
- import shutil
10
- from collections.abc import AsyncIterator, Callable
11
- from datetime import datetime
12
- from functools import partial
13
- from textwrap import shorten
14
- from typing import Any
7
+ from collections.abc import AsyncIterator
15
8
 
16
- from json_repair import repair_json
17
9
  from pydantic import BaseModel
18
10
 
19
- from lionagi.libs.schema.as_readable import as_readable
20
11
  from lionagi.service.connections.endpoint import Endpoint, EndpointConfig
21
12
  from lionagi.utils import to_dict
22
13
 
23
- from .claude_code_ import ClaudeCodeRequest
24
-
25
- CLAUDE = shutil.which("claude") or "claude"
26
- if not shutil.which(CLAUDE):
27
- raise RuntimeError(
28
- "Claude CLI binary not found (npm i -g @anthropic-ai/claude-code)"
29
- )
30
- logging.basicConfig(level=logging.INFO)
31
- log = logging.getLogger("claude-cli")
32
-
33
-
34
- @dataclasses.dataclass
35
- class ClaudeChunk:
36
- """Low-level wrapper around every NDJSON object coming from the CLI."""
37
-
38
- raw: dict[str, Any]
39
- type: str
40
- # convenience views
41
- thinking: str | None = None
42
- text: str | None = None
43
- tool_use: dict[str, Any] | None = None
44
- tool_result: dict[str, Any] | None = None
45
-
46
-
47
- @dataclasses.dataclass
48
- class ClaudeSession:
49
- """Aggregated view of a whole CLI conversation."""
50
-
51
- session_id: str | None = None
52
- model: str | None = None
53
-
54
- # chronological log
55
- chunks: list[ClaudeChunk] = dataclasses.field(default_factory=list)
56
-
57
- # materialised views
58
- thinking_log: list[str] = dataclasses.field(default_factory=list)
59
- messages: list[dict[str, Any]] = dataclasses.field(default_factory=list)
60
- tool_uses: list[dict[str, Any]] = dataclasses.field(default_factory=list)
61
- tool_results: list[dict[str, Any]] = dataclasses.field(
62
- default_factory=list
63
- )
64
-
65
- # final summary
66
- result: str = ""
67
- usage: dict[str, Any] = dataclasses.field(default_factory=dict)
68
- total_cost_usd: float | None = None
69
- num_turns: int | None = None
70
- duration_ms: int | None = None
71
- duration_api_ms: int | None = None
72
- is_error: bool = False
73
-
74
-
75
- # --------------------------------------------------------------------------- helpers
76
-
77
-
78
- async def ndjson_from_cli(request: ClaudeCodeRequest):
79
- """
80
- Yields each JSON object emitted by the *claude-code* CLI.
81
-
82
- • Robust against UTF‑8 splits across chunks (incremental decoder).
83
- • Robust against braces inside strings (uses json.JSONDecoder.raw_decode)
84
- • Falls back to `json_repair.repair_json` when necessary.
85
- """
86
- workspace = request.cwd()
87
- workspace.mkdir(parents=True, exist_ok=True)
88
-
89
- proc = await asyncio.create_subprocess_exec(
90
- CLAUDE,
91
- *request.as_cmd_args(),
92
- cwd=str(workspace),
93
- stdout=asyncio.subprocess.PIPE,
94
- stderr=asyncio.subprocess.PIPE,
95
- )
96
-
97
- decoder = codecs.getincrementaldecoder("utf-8")()
98
- json_decoder = json.JSONDecoder()
99
- buffer: str = "" # text buffer that may hold >1 JSON objects
100
-
101
- try:
102
- while True:
103
- chunk = await proc.stdout.read(4096)
104
- if not chunk:
105
- break
106
-
107
- # 1) decode *incrementally* so we never split multibyte chars
108
- buffer += decoder.decode(chunk)
109
-
110
- # 2) try to peel off as many complete JSON objs as possible
111
- while buffer:
112
- buffer = buffer.lstrip() # remove leading spaces/newlines
113
- if not buffer:
114
- break
115
- try:
116
- obj, idx = json_decoder.raw_decode(buffer)
117
- yield obj
118
- buffer = buffer[idx:] # keep remainder for next round
119
- except json.JSONDecodeError:
120
- # incomplete → need more bytes
121
- break
122
-
123
- # 3) flush any tail bytes in the incremental decoder
124
- buffer += decoder.decode(b"", final=True)
125
- buffer = buffer.strip()
126
- if buffer:
127
- try:
128
- obj, idx = json_decoder.raw_decode(buffer)
129
- yield obj
130
- except json.JSONDecodeError:
131
- try:
132
- fixed = repair_json(buffer)
133
- yield json.loads(fixed)
134
- log.warning(
135
- "Repaired malformed JSON fragment at stream end"
136
- )
137
- except Exception:
138
- log.error(
139
- "Skipped unrecoverable JSON tail: %.120s…", buffer
140
- )
141
-
142
- # 4) propagate non‑zero exit code
143
- if await proc.wait() != 0:
144
- err = (await proc.stderr.read()).decode().strip()
145
- raise RuntimeError(err or "CLI exited non‑zero")
146
-
147
- finally:
148
- with contextlib.suppress(ProcessLookupError):
149
- proc.terminate()
150
- await proc.wait()
151
-
152
-
153
- # --------------------------------------------------------------------------- SSE route
154
- async def stream_events(request: ClaudeCodeRequest):
155
- async for obj in ndjson_from_cli(request):
156
- yield obj
157
- yield {"type": "done"}
158
-
159
-
160
- print_readable = partial(as_readable, md=True, display_str=True)
161
-
162
-
163
- def _pp_system(sys_obj: dict[str, Any], theme) -> None:
164
- txt = (
165
- f"◼️ **Claude Code Session** \n"
166
- f"- id: `{sys_obj.get('session_id', '?')}` \n"
167
- f"- model: `{sys_obj.get('model', '?')}` \n"
168
- f"- tools: {', '.join(sys_obj.get('tools', [])[:8])}"
169
- + ("…" if len(sys_obj.get("tools", [])) > 8 else "")
170
- )
171
- print_readable(txt, border=False, theme=theme)
172
-
173
-
174
- def _pp_thinking(thought: str, theme) -> None:
175
- text = f"""
176
- 🧠 Thinking:
177
- {thought}
178
- """
179
- print_readable(text, border=True, theme=theme)
180
-
181
-
182
- def _pp_assistant_text(text: str, theme) -> None:
183
- txt = f"""
184
- > 🗣️ Claude:
185
- {text}
186
- """
187
- print_readable(txt, theme=theme)
188
-
189
-
190
- def _pp_tool_use(tu: dict[str, Any], theme) -> None:
191
- preview = shorten(str(tu["input"]).replace("\n", " "), 130)
192
- body = f"- 🔧 Tool Use — {tu['name']}({tu['id']}) - input: {preview}"
193
- print_readable(body, border=False, panel=False, theme=theme)
194
-
195
-
196
- def _pp_tool_result(tr: dict[str, Any], theme) -> None:
197
- body_preview = shorten(str(tr["content"]).replace("\n", " "), 130)
198
- status = "ERR" if tr.get("is_error") else "OK"
199
- body = f"- 📄 Tool Result({tr['tool_use_id']}) - {status}\n\n\tcontent: {body_preview}"
200
- print_readable(body, border=False, panel=False, theme=theme)
201
-
202
-
203
- def _pp_final(sess: ClaudeSession, theme) -> None:
204
- usage = sess.usage or {}
205
- txt = (
206
- f"### ✅ Session complete - {datetime.utcnow().isoformat(timespec='seconds')} UTC\n"
207
- f"**Result:**\n\n{sess.result or ''}\n\n"
208
- f"- cost: **${sess.total_cost_usd:.4f}** \n"
209
- f"- turns: **{sess.num_turns}** \n"
210
- f"- duration: **{sess.duration_ms} ms** (API {sess.duration_api_ms} ms) \n"
211
- f"- tokens in/out: {usage.get('input_tokens', 0)}/{usage.get('output_tokens', 0)}"
212
- )
213
- print_readable(txt, theme=theme)
214
-
215
-
216
- # --------------------------------------------------------------------------- internal utils
217
-
218
-
219
- async def _maybe_await(func, *args, **kw):
220
- """Call func which may be sync or async."""
221
- res = func(*args, **kw) if func else None
222
- if asyncio.iscoroutine(res):
223
- await res
224
-
225
-
226
- # --------------------------------------------------------------------------- main parser
227
-
228
-
229
- async def stream_claude_code_cli( # noqa: C901 (complexity from branching is fine here)
230
- request: ClaudeCodeRequest,
231
- session: ClaudeSession = ClaudeSession(),
232
- *,
233
- on_system: Callable[[dict[str, Any]], None] | None = None,
234
- on_thinking: Callable[[str], None] | None = None,
235
- on_text: Callable[[str], None] | None = None,
236
- on_tool_use: Callable[[dict[str, Any]], None] | None = None,
237
- on_tool_result: Callable[[dict[str, Any]], None] | None = None,
238
- on_final: Callable[[ClaudeSession], None] | None = None,
239
- ) -> AsyncIterator[ClaudeChunk | dict | ClaudeSession]:
240
- """
241
- Consume the ND‑JSON stream produced by ndjson_from_cli()
242
- and return a fully‑populated ClaudeSession.
243
-
244
- If callbacks are omitted a default pretty‑print is emitted.
245
- """
246
- stream = ndjson_from_cli(request)
247
- theme = request.cli_display_theme or "light"
248
-
249
- async for obj in stream:
250
- typ = obj.get("type", "unknown")
251
- chunk = ClaudeChunk(raw=obj, type=typ)
252
- session.chunks.append(chunk)
253
-
254
- # ------------------------ SYSTEM -----------------------------------
255
- if typ == "system":
256
- data = obj
257
- session.session_id = data.get("session_id", session.session_id)
258
- session.model = data.get("model", session.model)
259
- await _maybe_await(on_system, data)
260
- if request.verbose_output and on_system is None:
261
- _pp_system(data, theme)
262
- yield data
263
-
264
- # ------------------------ ASSISTANT --------------------------------
265
- elif typ == "assistant":
266
- msg = obj["message"]
267
- session.messages.append(msg)
268
-
269
- for blk in msg.get("content", []):
270
- btype = blk.get("type")
271
- if btype == "thinking":
272
- thought = blk.get("thinking", "").strip()
273
- chunk.thinking = thought
274
- session.thinking_log.append(thought)
275
- await _maybe_await(on_thinking, thought)
276
- if request.verbose_output and on_thinking is None:
277
- _pp_thinking(thought, theme)
278
-
279
- elif btype == "text":
280
- text = blk.get("text", "")
281
- chunk.text = text
282
- await _maybe_await(on_text, text)
283
- if request.verbose_output and on_text is None:
284
- _pp_assistant_text(text, theme)
285
-
286
- elif btype == "tool_use":
287
- tu = {
288
- "id": blk["id"],
289
- "name": blk["name"],
290
- "input": blk["input"],
291
- }
292
- chunk.tool_use = tu
293
- session.tool_uses.append(tu)
294
- await _maybe_await(on_tool_use, tu)
295
- if request.verbose_output and on_tool_use is None:
296
- _pp_tool_use(tu, theme)
297
-
298
- elif btype == "tool_result":
299
- tr = {
300
- "tool_use_id": blk["tool_use_id"],
301
- "content": blk["content"],
302
- "is_error": blk.get("is_error", False),
303
- }
304
- chunk.tool_result = tr
305
- session.tool_results.append(tr)
306
- await _maybe_await(on_tool_result, tr)
307
- if request.verbose_output and on_tool_result is None:
308
- _pp_tool_result(tr, theme)
309
- yield chunk
310
-
311
- # ------------------------ USER (tool_result containers) ------------
312
- elif typ == "user":
313
- msg = obj["message"]
314
- session.messages.append(msg)
315
- for blk in msg.get("content", []):
316
- if blk.get("type") == "tool_result":
317
- tr = {
318
- "tool_use_id": blk["tool_use_id"],
319
- "content": blk["content"],
320
- "is_error": blk.get("is_error", False),
321
- }
322
- chunk.tool_result = tr
323
- session.tool_results.append(tr)
324
- await _maybe_await(on_tool_result, tr)
325
- if request.verbose_output and on_tool_result is None:
326
- _pp_tool_result(tr, theme)
327
- yield chunk
328
-
329
- # ------------------------ RESULT -----------------------------------
330
- elif typ == "result":
331
- session.result = obj.get("result", "").strip()
332
- session.usage = obj.get("usage", {})
333
- session.total_cost_usd = obj.get("total_cost_usd")
334
- session.num_turns = obj.get("num_turns")
335
- session.duration_ms = obj.get("duration_ms")
336
- session.duration_api_ms = obj.get("duration_api_ms")
337
- session.is_error = obj.get("is_error", False)
338
-
339
- # ------------------------ DONE -------------------------------------
340
- elif typ == "done":
341
- break
342
-
343
- # final pretty print
344
- await _maybe_await(on_final, session)
345
- if request.verbose_output and on_final is None:
346
- _pp_final(session, theme)
347
-
348
- yield session
349
-
14
+ from ._claude_code.models import ClaudeCodeRequest
15
+ from ._claude_code.stream_cli import (
16
+ ClaudeChunk,
17
+ ClaudeSession,
18
+ log,
19
+ stream_claude_code_cli,
20
+ )
350
21
 
351
22
  ENDPOINT_CONFIG = EndpointConfig(
352
23
  name="claude_code_cli",
@@ -363,13 +34,27 @@ class ClaudeCodeCLIEndpoint(Endpoint):
363
34
  def __init__(self, config: EndpointConfig = ENDPOINT_CONFIG, **kwargs):
364
35
  super().__init__(config=config, **kwargs)
365
36
 
37
+ @property
38
+ def claude_handlers(self):
39
+ handlers = {
40
+ "on_thinking": None,
41
+ "on_text": None,
42
+ "on_tool_use": None,
43
+ "on_tool_result": None,
44
+ "on_system": None,
45
+ "on_final": None,
46
+ }
47
+ return self.config.kwargs.get("claude_handlers", handlers)
48
+
366
49
  def create_payload(self, request: dict | BaseModel, **kwargs):
367
50
  req_dict = {**self.config.kwargs, **to_dict(request), **kwargs}
368
51
  messages = req_dict.pop("messages")
369
52
  req_obj = ClaudeCodeRequest.create(messages=messages, **req_dict)
370
53
  return {"request": req_obj}, {}
371
54
 
372
- async def stream(self, request: dict | BaseModel, **kwargs):
55
+ async def stream(
56
+ self, request: dict | BaseModel, **kwargs
57
+ ) -> AsyncIterator[ClaudeChunk | dict | ClaudeSession]:
373
58
  payload, _ = self.create_payload(request, **kwargs)["request"]
374
59
  async for chunk in stream_claude_code_cli(payload):
375
60
  yield chunk
@@ -386,7 +71,9 @@ class ClaudeCodeCLIEndpoint(Endpoint):
386
71
  system: dict = None
387
72
 
388
73
  # 1. stream the Claude Code response
389
- async for chunk in stream_claude_code_cli(request, session, **kwargs):
74
+ async for chunk in stream_claude_code_cli(
75
+ request, session, **self.claude_handlers, **kwargs
76
+ ):
390
77
  if isinstance(chunk, dict):
391
78
  system = chunk
392
79
  responses.append(chunk)
@@ -395,6 +82,7 @@ class ClaudeCodeCLIEndpoint(Endpoint):
395
82
  responses[-1], ClaudeSession
396
83
  ):
397
84
  req2 = request.model_copy(deep=True)
85
+ req2.prompt = "Please provide a the final result message only"
398
86
  req2.max_turns = 1
399
87
  req2.continue_conversation = True
400
88
  if system:
@@ -407,4 +95,11 @@ class ClaudeCodeCLIEndpoint(Endpoint):
407
95
  log.info(
408
96
  f"Session {session.session_id} finished with {len(responses)} chunks"
409
97
  )
98
+ texts = []
99
+ for i in session.chunks:
100
+ if i.text is not None:
101
+ texts.append(i.text)
102
+
103
+ texts.append(session.result)
104
+ session.result = "\n".join(texts)
410
105
  return to_dict(session, recursive=True)
lionagi/service/types.py CHANGED
@@ -3,7 +3,7 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  from .connections.api_calling import APICalling
6
- from .connections.endpoint import Endpoint
6
+ from .connections.endpoint import Endpoint, EndpointConfig
7
7
  from .imodel import iModel
8
8
  from .manager import iModelManager
9
9
  from .rate_limited_processor import RateLimitedAPIExecutor
@@ -12,6 +12,7 @@ from .token_calculator import TokenCalculator
12
12
  __all__ = (
13
13
  "APICalling",
14
14
  "Endpoint",
15
+ "EndpointConfig",
15
16
  "RateLimitedAPIExecutor",
16
17
  "TokenCalculator",
17
18
  "iModel",
lionagi/session/branch.py CHANGED
@@ -917,9 +917,7 @@ class Branch(Element, Communicatable, Relational):
917
917
  actions: bool = False,
918
918
  reason: bool = False,
919
919
  action_kwargs: dict = None,
920
- action_strategy: Literal[
921
- "sequential", "concurrent", "batch"
922
- ] = "concurrent",
920
+ action_strategy: Literal["sequential", "concurrent"] = "concurrent",
923
921
  verbose_action: bool = False,
924
922
  field_models: list[FieldModel] = None,
925
923
  exclude_fields: list | dict | None = None,
@@ -987,7 +985,7 @@ class Branch(Element, Communicatable, Relational):
987
985
  If `True`, signals that the LLM should provide chain-of-thought or reasoning (where applicable).
988
986
  action_kwargs (dict | None, optional):
989
987
  Additional parameters for the `branch.act()` call if tools are invoked.
990
- action_strategy (Literal["sequential","concurrent","batch"], optional):
988
+ action_strategy (Literal["sequential","concurrent"], optional):
991
989
  The strategy for invoking tools (default: "concurrent").
992
990
  verbose_action (bool, optional):
993
991
  If `True`, logs detailed information about tool invocation.
@@ -1174,9 +1172,8 @@ class Branch(Element, Communicatable, Relational):
1174
1172
  self,
1175
1173
  action_request: list | ActionRequest | BaseModel | dict,
1176
1174
  *,
1177
- strategy: Literal["concurrent", "sequential", "batch"] = "concurrent",
1175
+ strategy: Literal["concurrent", "sequential"] = "concurrent",
1178
1176
  verbose_action: bool = False,
1179
- batch_size: int = None,
1180
1177
  suppress_errors: bool = True,
1181
1178
  sanitize_input: bool = False,
1182
1179
  unique_input: bool = False,
@@ -1223,7 +1220,7 @@ class Branch(Element, Communicatable, Relational):
1223
1220
  retry_timeout (float|None):
1224
1221
  Overall timeout for all attempts (None = no limit).
1225
1222
  max_concurrent (int|None):
1226
- Maximum concurrent tasks (if batching).
1223
+ Maximum concurrent tasks.
1227
1224
  throttle_period (float|None):
1228
1225
  Minimum spacing (in seconds) between requests.
1229
1226
  flatten (bool):
@@ -1239,11 +1236,6 @@ class Branch(Element, Communicatable, Relational):
1239
1236
  Any:
1240
1237
  The result or results from the invoked tool(s).
1241
1238
  """
1242
- if batch_size and not strategy == "batch":
1243
- raise ValueError(
1244
- "Batch size is only applicable for 'batch' strategy."
1245
- )
1246
-
1247
1239
  match strategy:
1248
1240
  case "concurrent":
1249
1241
  return await self._concurrent_act(
@@ -1271,27 +1263,8 @@ class Branch(Element, Communicatable, Relational):
1271
1263
  verbose_action=verbose_action,
1272
1264
  suppress_errors=suppress_errors,
1273
1265
  )
1274
- case "batch":
1275
- return await self._batch_act(
1276
- action_request,
1277
- verbose_action=verbose_action,
1278
- batch_size=batch_size or 1,
1279
- max_concurrent=max_concurrent,
1280
- suppress_errors=suppress_errors,
1281
- sanitize_input=sanitize_input,
1282
- unique_input=unique_input,
1283
- num_retries=num_retries,
1284
- initial_delay=initial_delay,
1285
- retry_delay=retry_delay,
1286
- backoff_factor=backoff_factor,
1287
- retry_default=retry_default,
1288
- retry_timeout=retry_timeout,
1289
- throttle_period=throttle_period,
1290
- flatten=flatten,
1291
- dropna=dropna,
1292
- unique_output=unique_output,
1293
- flatten_tuple_set=flatten_tuple_set,
1294
- )
1266
+ case _:
1267
+ raise
1295
1268
 
1296
1269
  async def _concurrent_act(
1297
1270
  self,
@@ -1322,19 +1295,6 @@ class Branch(Element, Communicatable, Relational):
1322
1295
  )
1323
1296
  return results
1324
1297
 
1325
- async def _batch_act(
1326
- self,
1327
- action_request: list[ActionRequest | BaseModel | dict],
1328
- batch_size: int = None,
1329
- **kwargs,
1330
- ) -> list:
1331
- result = []
1332
- async for i in bcall(
1333
- action_request, self._act, batch_size=batch_size, **kwargs
1334
- ):
1335
- result.extend(i)
1336
- return result
1337
-
1338
1298
  async def translate(
1339
1299
  self,
1340
1300
  text: str,
@@ -2,6 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ import contextlib
5
6
  from collections.abc import Callable
6
7
  from typing import Any
7
8
 
@@ -46,7 +47,7 @@ class Session(Node, Communicatable, Relational):
46
47
  mail_manager (MailManager | None): Manages mail operations.
47
48
  """
48
49
 
49
- branches: Pile[Any] = Field(
50
+ branches: Pile[Branch] = Field(
50
51
  default_factory=lambda: Pile(item_type={Branch}, strict_type=False)
51
52
  )
52
53
  default_branch: Any = Field(default=None, exclude=True)
@@ -57,21 +58,38 @@ class Session(Node, Communicatable, Relational):
57
58
  name: str = Field(default="Session")
58
59
 
59
60
  @model_validator(mode="after")
60
- def _initialize_default_branch(self) -> Self:
61
+ def _add_mail_sources(self) -> Self:
61
62
  if self.default_branch is None:
62
- from .branch import Branch
63
-
64
63
  self.default_branch = Branch()
65
- return self
66
-
67
- @model_validator(mode="after")
68
- def _add_mail_sources(self) -> Self:
69
64
  if self.default_branch not in self.branches:
70
65
  self.branches.include(self.default_branch)
71
66
  if self.branches:
72
67
  self.mail_manager.add_sources(self.branches)
73
68
  return self
74
69
 
70
+ def _lookup_branch_by_name(self, name: str) -> Branch | None:
71
+ for branch in self.branches:
72
+ if branch.name == name:
73
+ return branch
74
+ return None
75
+
76
+ def get_branch(
77
+ self, branch: ID.Ref | str, default: Any = ..., /
78
+ ) -> Branch:
79
+ """Get a branch by its ID or name."""
80
+
81
+ with contextlib.suppress(ItemNotFoundError, ValueError):
82
+ id = ID.get_id(branch)
83
+ return self.branches[id]
84
+
85
+ if isinstance(branch, str):
86
+ if b := self._lookup_branch_by_name(branch):
87
+ return b
88
+
89
+ if default is ...:
90
+ raise ItemNotFoundError(f"Branch '{branch}' not found.")
91
+ return default
92
+
75
93
  def new_branch(
76
94
  self,
77
95
  system: System | JsonValue = None,
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.14.5"
1
+ __version__ = "0.14.7"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.14.5
3
+ Version: 0.14.7
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
@@ -234,9 +234,9 @@ Requires-Dist: tiktoken>=0.8.0
234
234
  Requires-Dist: toml>=0.9.0
235
235
  Provides-Extra: all
236
236
  Requires-Dist: aiosqlite>=0.21.0; extra == 'all'
237
- Requires-Dist: claude-code-sdk>=0.0.14; extra == 'all'
237
+ Requires-Dist: claude-code-sdk>=0.0.15; extra == 'all'
238
238
  Requires-Dist: datamodel-code-generator>=0.31.2; extra == 'all'
239
- Requires-Dist: docling>=2.15.1; extra == 'all'
239
+ Requires-Dist: docling>=2.15.0; extra == 'all'
240
240
  Requires-Dist: fastmcp>=2.10.5; extra == 'all'
241
241
  Requires-Dist: matplotlib>=3.7.0; extra == 'all'
242
242
  Requires-Dist: networkx>=3.0.0; extra == 'all'
@@ -244,36 +244,26 @@ Requires-Dist: ollama>=0.4.0; extra == 'all'
244
244
  Requires-Dist: pydapter[postgres]; extra == 'all'
245
245
  Requires-Dist: rich>=13.0.0; extra == 'all'
246
246
  Provides-Extra: claude-code
247
- Requires-Dist: claude-code-sdk>=0.0.14; extra == 'claude-code'
248
- Provides-Extra: docs
249
- Requires-Dist: furo>=2024.8.6; extra == 'docs'
250
- Requires-Dist: sphinx-autobuild>=2024.10.3; extra == 'docs'
251
- Requires-Dist: sphinx>=8.1.3; extra == 'docs'
247
+ Requires-Dist: claude-code-sdk>=0.0.15; extra == 'claude-code'
252
248
  Provides-Extra: graph
253
249
  Requires-Dist: matplotlib>=3.7.0; extra == 'graph'
254
250
  Requires-Dist: networkx>=3.0.0; extra == 'graph'
255
- Provides-Extra: lint
256
- Requires-Dist: black[jupyter]>=24.10.0; extra == 'lint'
257
- Requires-Dist: isort>=5.13.2; extra == 'lint'
258
- Requires-Dist: pre-commit>=4.0.1; extra == 'lint'
259
251
  Provides-Extra: mcp
260
252
  Requires-Dist: fastmcp>=2.10.5; extra == 'mcp'
261
253
  Provides-Extra: ollama
262
254
  Requires-Dist: ollama>=0.4.0; extra == 'ollama'
263
255
  Provides-Extra: postgres
264
- Requires-Dist: aiosqlite>=0.21.0; extra == 'postgres'
265
256
  Requires-Dist: pydapter[postgres]; extra == 'postgres'
266
257
  Provides-Extra: reader
267
- Requires-Dist: docling>=2.15.1; extra == 'reader'
258
+ Requires-Dist: docling>=2.15.0; extra == 'reader'
268
259
  Provides-Extra: rich
269
260
  Requires-Dist: rich>=13.0.0; extra == 'rich'
270
261
  Provides-Extra: schema
271
262
  Requires-Dist: datamodel-code-generator>=0.31.2; extra == 'schema'
272
- Provides-Extra: test
273
- Requires-Dist: pytest-asyncio>=1.0.0; extra == 'test'
274
- Requires-Dist: pytest>=8.3.4; extra == 'test'
263
+ Provides-Extra: sqlite
264
+ Requires-Dist: aiosqlite>=0.21.0; extra == 'sqlite'
275
265
  Provides-Extra: tools
276
- Requires-Dist: docling>=2.15.1; extra == 'tools'
266
+ Requires-Dist: docling>=2.15.0; extra == 'tools'
277
267
  Description-Content-Type: text/markdown
278
268
 
279
269
  ![PyPI - Version](https://img.shields.io/pypi/v/lionagi?labelColor=233476aa&color=231fc935)
@@ -416,7 +406,7 @@ Seamlessly route to different models in the same workflow.
416
406
 
417
407
  ### Claude Code Integration
418
408
 
419
- LionAGI now supports Anthropic's [Claude Code SDK](https://github.com/anthropics/claude-code-sdk), enabling autonomous coding capabilities with persistent session management:
409
+ LionAGI now supports Anthropic's [Claude Code Python SDK](https://github.com/anthropics/claude-code-sdk-python), enabling autonomous coding capabilities with persistent session management:
420
410
 
421
411
  ```python
422
412
  from lionagi import iModel, Branch