lite-agent 0.2.0__py3-none-any.whl → 0.3.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.

Potentially problematic release.


This version of lite-agent might be problematic. Click here for more details.

lite_agent/__init__.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  from .agent import Agent
4
4
  from .message_transfers import consolidate_history_transfer
5
+ from .rich_helpers import print_chat_history, print_chat_summary
5
6
  from .runner import Runner
6
7
 
7
- __all__ = ["Agent", "Runner", "consolidate_history_transfer"]
8
+ __all__ = ["Agent", "Runner", "consolidate_history_transfer", "print_chat_history", "print_chat_summary"]
lite_agent/agent.py CHANGED
@@ -2,51 +2,55 @@ from collections.abc import AsyncGenerator, Callable, Sequence
2
2
  from pathlib import Path
3
3
  from typing import Any, Optional
4
4
 
5
- import litellm
6
5
  from funcall import Funcall
6
+ from jinja2 import Environment, FileSystemLoader
7
7
  from litellm import CustomStreamWrapper
8
8
  from pydantic import BaseModel
9
9
 
10
+ from lite_agent.client import BaseLLMClient, LiteLLMClient
10
11
  from lite_agent.loggers import logger
11
12
  from lite_agent.stream_handlers import litellm_stream_handler
12
13
  from lite_agent.types import AgentChunk, AgentSystemMessage, RunnerMessages, ToolCall, ToolCallChunk, ToolCallResultChunk
13
14
 
14
- HANDOFFS_SOURCE_INSTRUCTIONS = """<ExtraGuide>
15
- You are a parent agent that can assign tasks to sub-agents.
15
+ TEMPLATES_DIR = Path(__file__).parent / "templates"
16
+ jinja_env = Environment(loader=FileSystemLoader(str(TEMPLATES_DIR)), autoescape=True)
16
17
 
17
- You can transfer conversations to other agents for specific tasks.
18
- If you need to assign tasks to multiple agents, you should break down the tasks and assign them one by one.
19
- You need to wait for one sub-agent to finish before assigning the task to the next sub-agent.
20
- </ExtraGuide>"""
21
-
22
- HANDOFFS_TARGET_INSTRUCTIONS = """<ExtraGuide>
23
- You are a sub-agent that is assigned to a specific task by your parent agent.
24
-
25
- Everything you output is intended for your parent agent to read.
26
- When you finish your task, you should call `transfer_to_parent` to transfer back to parent agent.
27
- </ExtraGuide>"""
18
+ HANDOFFS_SOURCE_INSTRUCTIONS_TEMPLATE = jinja_env.get_template("handoffs_source_instructions.xml.j2")
19
+ HANDOFFS_TARGET_INSTRUCTIONS_TEMPLATE = jinja_env.get_template("handoffs_target_instructions.xml.j2")
20
+ WAIT_FOR_USER_INSTRUCTIONS_TEMPLATE = jinja_env.get_template("wait_for_user_instructions.xml.j2")
28
21
 
29
22
 
30
23
  class Agent:
31
24
  def __init__( # noqa: PLR0913
32
25
  self,
33
26
  *,
34
- model: str,
27
+ model: str | BaseLLMClient,
35
28
  name: str,
36
29
  instructions: str,
37
30
  tools: list[Callable] | None = None,
38
31
  handoffs: list["Agent"] | None = None,
39
32
  message_transfer: Callable[[RunnerMessages], RunnerMessages] | None = None,
33
+ completion_condition: str = "stop",
40
34
  ) -> None:
41
35
  self.name = name
42
36
  self.instructions = instructions
43
- self.model = model
37
+ if isinstance(model, BaseLLMClient):
38
+ # If model is a BaseLLMClient instance, use it directly
39
+ self.client = model
40
+ else:
41
+ # Otherwise, create a LitellmClient instance
42
+ self.client = LiteLLMClient(model=model)
43
+ self.completion_condition = completion_condition
44
44
  self.handoffs = handoffs if handoffs else []
45
45
  self._parent: Agent | None = None
46
46
  self.message_transfer = message_transfer
47
47
  # Initialize Funcall with regular tools
48
48
  self.fc = Funcall(tools)
49
49
 
50
+ # Add wait_for_user tool if completion condition is "call"
51
+ if completion_condition == "call":
52
+ self._add_wait_for_user_tool()
53
+
50
54
  # Set parent for handoff agents
51
55
  if handoffs:
52
56
  for handoff_agent in handoffs:
@@ -167,11 +171,15 @@ class Agent:
167
171
 
168
172
  # Add source instructions if this agent can handoff to others
169
173
  if self.handoffs:
170
- instructions = HANDOFFS_SOURCE_INSTRUCTIONS + "\n\n" + instructions
174
+ instructions = HANDOFFS_SOURCE_INSTRUCTIONS_TEMPLATE.render(extra_instructions=None) + "\n\n" + instructions
171
175
 
172
176
  # Add target instructions if this agent can be handed off to (has a parent)
173
177
  if self.parent:
174
- instructions = HANDOFFS_TARGET_INSTRUCTIONS + "\n\n" + instructions
178
+ instructions = HANDOFFS_TARGET_INSTRUCTIONS_TEMPLATE.render(extra_instructions=None) + "\n\n" + instructions
179
+
180
+ # Add wait_for_user instructions if completion condition is "call"
181
+ if self.completion_condition == "call":
182
+ instructions = WAIT_FOR_USER_INSTRUCTIONS_TEMPLATE.render(extra_instructions=None) + "\n\n" + instructions
175
183
 
176
184
  return [
177
185
  AgentSystemMessage(
@@ -190,12 +198,10 @@ class Agent:
190
198
 
191
199
  self.message_histories = self.prepare_completion_messages(processed_messages)
192
200
  tools = self.fc.get_tools(target="completion")
193
- resp = await litellm.acompletion(
194
- model=self.model,
201
+ resp = await self.client.completion(
195
202
  messages=self.message_histories,
196
203
  tools=tools,
197
204
  tool_choice="auto", # TODO: make this configurable
198
- stream=True,
199
205
  )
200
206
 
201
207
  # Ensure resp is a CustomStreamWrapper
@@ -275,11 +281,11 @@ class Agent:
275
281
 
276
282
  if next_dict.get("type") == "function_call":
277
283
  tool_call = {
278
- "id": next_dict["function_call_id"],
284
+ "id": next_dict["function_call_id"], # type: ignore
279
285
  "type": "function",
280
286
  "function": {
281
- "name": next_dict["name"],
282
- "arguments": next_dict["arguments"],
287
+ "name": next_dict["name"], # type: ignore
288
+ "arguments": next_dict["arguments"], # type: ignore
283
289
  },
284
290
  "index": len(tool_calls),
285
291
  }
@@ -291,7 +297,7 @@ class Agent:
291
297
  # Create assistant message with tool_calls if any
292
298
  assistant_msg = message_dict.copy()
293
299
  if tool_calls:
294
- assistant_msg["tool_calls"] = tool_calls
300
+ assistant_msg["tool_calls"] = tool_calls # type: ignore
295
301
 
296
302
  converted_messages.append(assistant_msg)
297
303
  i = j # Skip the function_call messages we've processed
@@ -301,8 +307,8 @@ class Agent:
301
307
  converted_messages.append(
302
308
  {
303
309
  "role": "tool",
304
- "tool_call_id": message_dict["call_id"],
305
- "content": message_dict["output"],
310
+ "tool_call_id": message_dict["call_id"], # type: ignore
311
+ "content": message_dict["output"], # type: ignore
306
312
  },
307
313
  )
308
314
  i += 1
@@ -314,11 +320,67 @@ class Agent:
314
320
 
315
321
  else:
316
322
  # Regular message (user, system)
317
- converted_messages.append(message_dict)
323
+ converted_msg = message_dict.copy()
324
+
325
+ # Handle new Response API format for user messages
326
+ content = message_dict.get("content")
327
+ if role == "user" and isinstance(content, list):
328
+ converted_msg["content"] = self._convert_user_content_to_completions_format(content) # type: ignore
329
+
330
+ converted_messages.append(converted_msg)
318
331
  i += 1
319
332
 
320
333
  return converted_messages
321
334
 
335
+ def _convert_user_content_to_completions_format(self, content: list) -> list:
336
+ """Convert user message content from Response API format to Completion API format."""
337
+ # Handle the case where content might not actually be a list due to test mocking
338
+ if type(content) is not list: # Use type() instead of isinstance() to avoid test mocking issues
339
+ return content
340
+
341
+ converted_content = []
342
+ for item in content:
343
+ if isinstance(item, dict):
344
+ item_type = item.get("type")
345
+ if item_type == "input_text":
346
+ # Convert ResponseInputText to completion API format
347
+ converted_content.append(
348
+ {
349
+ "type": "text",
350
+ "text": item["text"],
351
+ },
352
+ )
353
+ elif item_type == "input_image":
354
+ # Convert ResponseInputImage to completion API format
355
+ if item.get("file_id"):
356
+ msg = "File ID input is not supported for Completion API. Please use image_url instead of file_id for image input."
357
+ raise ValueError(msg)
358
+
359
+ if not item.get("image_url"):
360
+ msg = "ResponseInputImage must have either file_id or image_url, but image_url is required for Completion API."
361
+ raise ValueError(msg)
362
+
363
+ # Build image_url object with detail inside
364
+ image_data = {"url": item["image_url"]}
365
+ detail = item.get("detail", "auto")
366
+ if detail: # Include detail if provided
367
+ image_data["detail"] = detail
368
+
369
+ converted_content.append(
370
+ {
371
+ "type": "image_url",
372
+ "image_url": image_data,
373
+ },
374
+ )
375
+ else:
376
+ # Keep existing format (text, image_url)
377
+ converted_content.append(item)
378
+ else:
379
+ # Handle non-dict items (shouldn't happen, but just in case)
380
+ converted_content.append(item)
381
+
382
+ return converted_content
383
+
322
384
  def set_message_transfer(self, message_transfer: Callable[[RunnerMessages], RunnerMessages] | None) -> None:
323
385
  """Set or update the message transfer callback function.
324
386
 
@@ -328,3 +390,22 @@ class Agent:
328
390
  called before making API calls to allow preprocessing of messages.
329
391
  """
330
392
  self.message_transfer = message_transfer
393
+
394
+ def _add_wait_for_user_tool(self) -> None:
395
+ """Add wait_for_user tool for agents with completion_condition='call'.
396
+
397
+ This tool allows the agent to signal when it has completed its task.
398
+ """
399
+
400
+ def wait_for_user_handler() -> str:
401
+ """Handler for wait_for_user function."""
402
+ return "Waiting for user input."
403
+
404
+ # Add dynamic tool for task completion
405
+ self.fc.add_dynamic_tool(
406
+ name="wait_for_user",
407
+ description="Call this function when you have completed your assigned task or need more information from the user.",
408
+ parameters={},
409
+ required=[],
410
+ handler=wait_for_user_handler,
411
+ )
lite_agent/client.py ADDED
@@ -0,0 +1,34 @@
1
+ import abc
2
+ from typing import Any
3
+
4
+ import litellm
5
+ from openai.types.chat import ChatCompletionToolParam
6
+
7
+
8
+ class BaseLLMClient(abc.ABC):
9
+ """Base class for LLM clients."""
10
+
11
+ def __init__(self, *, model: str, api_key: str | None = None, api_base: str | None = None, api_version: str | None = None):
12
+ self.model = model
13
+ self.api_key = api_key
14
+ self.api_base = api_base
15
+ self.api_version = api_version
16
+
17
+ @abc.abstractmethod
18
+ async def completion(self, messages: list[Any], tools: list[ChatCompletionToolParam] | None = None, tool_choice: str = "auto") -> Any: # noqa: ANN401
19
+ """Perform a completion request to the LLM."""
20
+
21
+
22
+ class LiteLLMClient(BaseLLMClient):
23
+ async def completion(self, messages: list[Any], tools: list[ChatCompletionToolParam] | None = None, tool_choice: str = "auto") -> Any: # noqa: ANN401
24
+ """Perform a completion request to the Litellm API."""
25
+ return await litellm.acompletion(
26
+ model=self.model,
27
+ messages=messages,
28
+ tools=tools,
29
+ tool_choice=tool_choice,
30
+ api_version=self.api_version,
31
+ api_key=self.api_key,
32
+ api_base=self.api_base,
33
+ stream=True,
34
+ )
@@ -0,0 +1,503 @@
1
+ """
2
+ Rich chat history renderer for lite-agent.
3
+
4
+ This module provides utilities to beautifully render chat history using the rich library.
5
+ It supports all message types including user messages, assistant messages, function calls,
6
+ and function call outputs.
7
+ """
8
+
9
+ import json
10
+ from datetime import datetime, timezone
11
+
12
+ from rich.console import Console
13
+ from rich.panel import Panel
14
+ from rich.syntax import Syntax
15
+ from rich.table import Table
16
+
17
+ from lite_agent.types import (
18
+ AgentAssistantMessage,
19
+ AgentFunctionCallOutput,
20
+ AgentFunctionToolCallMessage,
21
+ AgentSystemMessage,
22
+ AgentUserMessage,
23
+ RunnerMessages,
24
+ )
25
+
26
+
27
+ def print_chat_history(
28
+ messages: RunnerMessages,
29
+ *,
30
+ console: Console | None = None,
31
+ show_timestamps: bool = True,
32
+ show_indices: bool = True,
33
+ chat_width: int = 80,
34
+ ) -> None:
35
+ """
36
+ 使用 rich 库美观地渲染聊天记录。
37
+
38
+ Args:
39
+ messages: 要渲染的消息列表
40
+ console: Rich Console 实例,如果为 None 则创建新的
41
+ show_timestamps: 是否显示时间戳
42
+ show_indices: 是否显示消息索引
43
+ chat_width: 聊天气泡的最大宽度
44
+
45
+ Example:
46
+ >>> from lite_agent.runner import Runner
47
+ >>> from lite_agent.rich_helpers import render_chat_history
48
+ >>>
49
+ >>> runner = Runner(agent=my_agent)
50
+ >>> # ... add some messages ...
51
+ >>> render_chat_history(runner.messages)
52
+ """
53
+ if console is None:
54
+ console = Console()
55
+
56
+ if not messages:
57
+ console.print("[dim]No messages to display[/dim]")
58
+ return
59
+
60
+ console.print(f"\n[bold blue]Chat History[/bold blue] ([dim]{len(messages)} messages[/dim])\n")
61
+
62
+ for i, message in enumerate(messages):
63
+ _render_single_message(
64
+ message,
65
+ index=i if show_indices else None,
66
+ console=console,
67
+ show_timestamp=show_timestamps,
68
+ chat_width=chat_width,
69
+ )
70
+
71
+
72
+ def _render_single_message(
73
+ message: object,
74
+ *,
75
+ index: int | None = None,
76
+ console: Console,
77
+ show_timestamp: bool = True,
78
+ chat_width: int = 80,
79
+ ) -> None:
80
+ """渲染单个消息。"""
81
+ timestamp = datetime.now(timezone.utc).strftime("%H:%M:%S") if show_timestamp else None
82
+
83
+ # 处理不同类型的消息
84
+ if isinstance(message, AgentUserMessage):
85
+ _render_user_message(message, index, console, timestamp, chat_width)
86
+ elif isinstance(message, AgentAssistantMessage):
87
+ _render_assistant_message(message, index, console, timestamp, chat_width)
88
+ elif isinstance(message, AgentSystemMessage):
89
+ _render_system_message(message, index, console, timestamp, chat_width)
90
+ elif isinstance(message, AgentFunctionToolCallMessage):
91
+ _render_function_call_message(message, index, console, timestamp, chat_width)
92
+ elif isinstance(message, AgentFunctionCallOutput):
93
+ _render_function_output_message(message, index, console, timestamp, chat_width)
94
+ elif isinstance(message, dict):
95
+ _render_dict_message(message, index, console, timestamp, chat_width)
96
+ else:
97
+ _render_unknown_message(message, index, console, timestamp, chat_width)
98
+
99
+
100
+ def _render_user_message(
101
+ message: AgentUserMessage,
102
+ index: int | None,
103
+ console: Console,
104
+ timestamp: str | None,
105
+ chat_width: int,
106
+ ) -> None:
107
+ """渲染用户消息 - 靠右显示的蓝色气泡。"""
108
+ content = str(message.content) # 显示完整内容,不截断
109
+
110
+ title_parts = ["👤 User"]
111
+ if index is not None:
112
+ title_parts.append(f"#{index}")
113
+ if timestamp:
114
+ title_parts.append(f"[dim]{timestamp}[/dim]")
115
+
116
+ title = " ".join(title_parts)
117
+
118
+ # 计算内容的实际宽度,用于气泡大小
119
+ content_width = min(len(content) + 4, chat_width) # +4 for padding
120
+ bubble_width = max(content_width, 20) # 最小宽度
121
+
122
+ # 创建用户消息气泡 - 靠右
123
+ panel = Panel(
124
+ content,
125
+ title=title,
126
+ title_align="left",
127
+ border_style="blue",
128
+ padding=(0, 1),
129
+ width=bubble_width,
130
+ )
131
+
132
+ # 用户消息靠右
133
+ console.print(panel, justify="right")
134
+
135
+
136
+ def _render_assistant_message(
137
+ message: AgentAssistantMessage,
138
+ index: int | None,
139
+ console: Console,
140
+ timestamp: str | None,
141
+ chat_width: int,
142
+ ) -> None:
143
+ """渲染助手消息 - 靠左显示的绿色气泡。"""
144
+ content = message.content # 显示完整内容,不截断
145
+
146
+ title_parts = ["🤖 Assistant"]
147
+ if index is not None:
148
+ title_parts.append(f"#{index}")
149
+ if timestamp:
150
+ title_parts.append(f"[dim]{timestamp}[/dim]")
151
+
152
+ title = " ".join(title_parts)
153
+
154
+ # 计算内容的实际宽度,用于气泡大小
155
+ content_width = min(len(content) + 4, chat_width) # +4 for padding
156
+ bubble_width = max(content_width, 20) # 最小宽度
157
+
158
+ # 创建助手消息气泡 - 靠左
159
+ panel = Panel(
160
+ content,
161
+ title=title,
162
+ title_align="left",
163
+ border_style="green",
164
+ padding=(0, 1),
165
+ width=bubble_width,
166
+ )
167
+
168
+ # 助手消息靠左
169
+ console.print(panel)
170
+
171
+
172
+ def _render_system_message(
173
+ message: AgentSystemMessage,
174
+ index: int | None,
175
+ console: Console,
176
+ timestamp: str | None,
177
+ chat_width: int,
178
+ ) -> None:
179
+ """渲染系统消息 - 居中显示的黄色气泡。"""
180
+ content = message.content # 显示完整内容,不截断
181
+
182
+ title_parts = ["⚙️ System"]
183
+ if index is not None:
184
+ title_parts.append(f"#{index}")
185
+ if timestamp:
186
+ title_parts.append(f"[dim]{timestamp}[/dim]")
187
+
188
+ title = " ".join(title_parts)
189
+
190
+ # 系统消息居中显示,使用较小的宽度
191
+ console.print(
192
+ Panel(
193
+ content,
194
+ title=title,
195
+ title_align="center",
196
+ border_style="yellow",
197
+ padding=(0, 1),
198
+ width=min(len(content) + 10, chat_width),
199
+ ),
200
+ justify="center",
201
+ )
202
+
203
+
204
+ def _render_function_call_message(
205
+ message: AgentFunctionToolCallMessage,
206
+ index: int | None,
207
+ console: Console,
208
+ timestamp: str | None,
209
+ chat_width: int,
210
+ ) -> None:
211
+ """渲染函数调用消息 - 靠左显示的紫色气泡。"""
212
+ title_parts = ["🛠️ Function Call"]
213
+ if index is not None:
214
+ title_parts.append(f"#{index}")
215
+ if timestamp:
216
+ title_parts.append(f"[dim]{timestamp}[/dim]")
217
+
218
+ title = " ".join(title_parts)
219
+
220
+ # 创建表格显示函数调用详情
221
+ table = Table(show_header=False, box=None, padding=0)
222
+ table.add_column("Field", style="cyan", width=12)
223
+ table.add_column("Value", style="white")
224
+
225
+ table.add_row("Name:", f"[bold]{message.name}[/bold]")
226
+ table.add_row("Call ID:", f"[dim]{message.function_call_id}[/dim]")
227
+
228
+ if message.arguments:
229
+ # 尝试格式化 JSON 参数 - 显示完整内容
230
+ try:
231
+ parsed_args = json.loads(message.arguments)
232
+ formatted_args = json.dumps(parsed_args, indent=2, ensure_ascii=False)
233
+ syntax = Syntax(formatted_args, "json", theme="monokai", line_numbers=False)
234
+ table.add_row("Arguments:", syntax)
235
+ except (json.JSONDecodeError, TypeError):
236
+ table.add_row("Arguments:", message.arguments)
237
+
238
+ # 函数调用消息靠左
239
+ console.print(
240
+ Panel(
241
+ table,
242
+ title=title,
243
+ title_align="left",
244
+ border_style="magenta",
245
+ padding=(0, 1),
246
+ width=min(chat_width, 100),
247
+ ),
248
+ )
249
+
250
+
251
+ def _render_function_output_message(
252
+ message: AgentFunctionCallOutput,
253
+ index: int | None,
254
+ console: Console,
255
+ timestamp: str | None,
256
+ chat_width: int,
257
+ ) -> None:
258
+ """渲染函数输出消息 - 靠左显示的青色气泡。"""
259
+ title_parts = ["📤 Function Output"]
260
+ if index is not None:
261
+ title_parts.append(f"#{index}")
262
+ if timestamp:
263
+ title_parts.append(f"[dim]{timestamp}[/dim]")
264
+
265
+ title = " ".join(title_parts)
266
+
267
+ output_content = message.output # 显示完整内容,不截断
268
+
269
+ # 创建表格显示函数输出详情
270
+ table = Table(show_header=False, box=None, padding=0)
271
+ table.add_column("Field", style="cyan", width=12)
272
+ table.add_column("Value", style="white")
273
+
274
+ table.add_row("Call ID:", f"[dim]{message.call_id}[/dim]")
275
+ table.add_row("Output:", output_content)
276
+
277
+ # 函数输出消息靠左
278
+ console.print(
279
+ Panel(
280
+ table,
281
+ title=title,
282
+ title_align="left",
283
+ border_style="cyan",
284
+ padding=(0, 1),
285
+ width=min(chat_width, 100),
286
+ ),
287
+ )
288
+
289
+
290
+ def _render_role_based_dict_message( # noqa: PLR0913
291
+ *,
292
+ message: dict[str, object],
293
+ role: str,
294
+ index: int | None,
295
+ console: Console,
296
+ timestamp: str | None,
297
+ chat_width: int,
298
+ ) -> None:
299
+ """渲染基于角色的字典消息。"""
300
+ content = str(message.get("content", "")) # 显示完整内容,不截断
301
+
302
+ title_parts = []
303
+ if role == "user":
304
+ title_parts = ["👤 User"]
305
+ border_style = "blue"
306
+ # 用户消息靠右
307
+ content_width = min(len(content) + 4, chat_width)
308
+ bubble_width = max(content_width, 20)
309
+ if index is not None:
310
+ title_parts.append(f"#{index}")
311
+ if timestamp:
312
+ title_parts.append(f"[dim]{timestamp}[/dim]")
313
+
314
+ panel = Panel(
315
+ content,
316
+ title=" ".join(title_parts),
317
+ title_align="left",
318
+ border_style=border_style,
319
+ padding=(0, 1),
320
+ width=bubble_width,
321
+ )
322
+ console.print(panel, justify="right")
323
+ elif role == "assistant":
324
+ title_parts = ["🤖 Assistant"]
325
+ border_style = "green"
326
+ # 助手消息靠左
327
+ content_width = min(len(content) + 4, chat_width)
328
+ bubble_width = max(content_width, 20)
329
+ if index is not None:
330
+ title_parts.append(f"#{index}")
331
+ if timestamp:
332
+ title_parts.append(f"[dim]{timestamp}[/dim]")
333
+
334
+ panel = Panel(
335
+ content,
336
+ title=" ".join(title_parts),
337
+ title_align="left",
338
+ border_style=border_style,
339
+ padding=(0, 1),
340
+ width=bubble_width,
341
+ )
342
+ # 助手消息靠左
343
+ console.print(panel)
344
+ else: # system
345
+ title_parts = ["⚙️ System"]
346
+ border_style = "yellow"
347
+ if index is not None:
348
+ title_parts.append(f"#{index}")
349
+ if timestamp:
350
+ title_parts.append(f"[dim]{timestamp}[/dim]")
351
+
352
+ # 系统消息居中
353
+ console.print(
354
+ Panel(
355
+ content,
356
+ title=" ".join(title_parts),
357
+ title_align="center",
358
+ border_style=border_style,
359
+ padding=(0, 1),
360
+ width=min(len(content) + 10, chat_width),
361
+ ),
362
+ justify="center",
363
+ )
364
+
365
+
366
+ def _render_dict_message(
367
+ message: dict[str, object],
368
+ index: int | None,
369
+ console: Console,
370
+ timestamp: str | None,
371
+ chat_width: int,
372
+ ) -> None:
373
+ """渲染字典格式的消息。"""
374
+ message_type = message.get("type")
375
+ role = message.get("role")
376
+
377
+ if message_type == "function_call":
378
+ # 创建临时 AgentFunctionToolCallMessage 对象进行渲染
379
+ temp_message = AgentFunctionToolCallMessage(
380
+ type="function_call",
381
+ function_call_id=str(message.get("function_call_id", "")),
382
+ name=str(message.get("name", "unknown")),
383
+ arguments=str(message.get("arguments", "")),
384
+ content=str(message.get("content", "")),
385
+ )
386
+ _render_function_call_message(temp_message, index, console, timestamp, chat_width)
387
+ elif message_type == "function_call_output":
388
+ # 创建临时 AgentFunctionCallOutput 对象进行渲染
389
+ temp_message = AgentFunctionCallOutput(
390
+ type="function_call_output",
391
+ call_id=str(message.get("call_id", "")),
392
+ output=str(message.get("output", "")),
393
+ )
394
+ _render_function_output_message(temp_message, index, console, timestamp, chat_width)
395
+ elif role in ["user", "assistant", "system"]:
396
+ _render_role_based_dict_message(
397
+ message=message,
398
+ role=str(role),
399
+ index=index,
400
+ console=console,
401
+ timestamp=timestamp,
402
+ chat_width=chat_width,
403
+ )
404
+ else:
405
+ _render_unknown_message(message, index, console, timestamp, chat_width)
406
+
407
+
408
+ def _render_unknown_message(
409
+ message: object,
410
+ index: int | None,
411
+ console: Console,
412
+ timestamp: str | None,
413
+ chat_width: int,
414
+ ) -> None:
415
+ """渲染未知类型的消息 - 居中显示的红色气泡。"""
416
+ title_parts = ["❓ Unknown"]
417
+ if index is not None:
418
+ title_parts.append(f"#{index}")
419
+ if timestamp:
420
+ title_parts.append(f"[dim]{timestamp}[/dim]")
421
+
422
+ title = " ".join(title_parts)
423
+
424
+ # 尝试将消息转换为可读格式 - 显示完整内容
425
+ try:
426
+ content = str(message.model_dump()) if hasattr(message, "model_dump") else str(message) # type: ignore[attr-defined]
427
+ except Exception:
428
+ content = str(message)
429
+
430
+ console.print(
431
+ Panel(
432
+ content,
433
+ title=title,
434
+ title_align="center",
435
+ border_style="red",
436
+ padding=(0, 1),
437
+ width=min(len(content) + 10, chat_width),
438
+ ),
439
+ justify="center",
440
+ )
441
+
442
+
443
+ def create_chat_summary_table(messages: RunnerMessages) -> Table:
444
+ """
445
+ 创建聊天记录摘要表格。
446
+
447
+ Args:
448
+ messages: 要汇总的消息列表
449
+
450
+ Returns:
451
+ Rich Table 对象,包含消息统计信息
452
+ """
453
+ table = Table(title="Chat Summary")
454
+ table.add_column("Message Type", style="cyan")
455
+ table.add_column("Count", justify="right", style="green")
456
+
457
+ # 统计各种消息类型
458
+ counts = {
459
+ "User": 0,
460
+ "Assistant": 0,
461
+ "System": 0,
462
+ "Function Call": 0,
463
+ "Function Output": 0,
464
+ "Unknown": 0,
465
+ }
466
+
467
+ for message in messages:
468
+ if isinstance(message, AgentUserMessage) or (isinstance(message, dict) and message.get("role") == "user"):
469
+ counts["User"] += 1
470
+ elif isinstance(message, AgentAssistantMessage) or (isinstance(message, dict) and message.get("role") == "assistant"):
471
+ counts["Assistant"] += 1
472
+ elif isinstance(message, AgentSystemMessage) or (isinstance(message, dict) and message.get("role") == "system"):
473
+ counts["System"] += 1
474
+ elif isinstance(message, AgentFunctionToolCallMessage) or (isinstance(message, dict) and message.get("type") == "function_call"):
475
+ counts["Function Call"] += 1
476
+ elif isinstance(message, AgentFunctionCallOutput) or (isinstance(message, dict) and message.get("type") == "function_call_output"):
477
+ counts["Function Output"] += 1
478
+ else:
479
+ counts["Unknown"] += 1
480
+
481
+ # 只显示计数大于0的类型
482
+ for msg_type, count in counts.items():
483
+ if count > 0:
484
+ table.add_row(msg_type, str(count))
485
+
486
+ table.add_row("[bold]Total[/bold]", f"[bold]{len(messages)}[/bold]")
487
+
488
+ return table
489
+
490
+
491
+ def print_chat_summary(messages: RunnerMessages, *, console: Console | None = None) -> None:
492
+ """
493
+ 打印聊天记录摘要。
494
+
495
+ Args:
496
+ messages: 要汇总的消息列表
497
+ console: Rich Console 实例,如果为 None 则创建新的
498
+ """
499
+ if console is None:
500
+ console = Console()
501
+
502
+ summary_table = create_chat_summary_table(messages)
503
+ console.print(summary_table)
lite_agent/runner.py CHANGED
@@ -14,10 +14,12 @@ from lite_agent.types import (
14
14
  AgentFunctionToolCallMessage,
15
15
  AgentSystemMessage,
16
16
  AgentUserMessage,
17
+ FlexibleRunnerMessage,
18
+ MessageDict,
17
19
  RunnerMessage,
18
- RunnerMessages,
19
20
  ToolCall,
20
21
  ToolCallFunction,
22
+ UserInput,
21
23
  )
22
24
 
23
25
  if TYPE_CHECKING:
@@ -70,6 +72,7 @@ class Runner:
70
72
  ),
71
73
  )
72
74
  return # Stop processing other tool calls after transfer
75
+
73
76
  return_parent_calls = [tc for tc in tool_calls if tc.function.name == "transfer_to_parent"]
74
77
  if return_parent_calls:
75
78
  # Handle multiple transfer_to_parent calls (only execute the first one)
@@ -87,6 +90,7 @@ class Runner:
87
90
  ),
88
91
  )
89
92
  return # Stop processing other tool calls after transfer
93
+
90
94
  async for tool_call_chunk in self.agent.handle_tool_calls(tool_calls, context=context):
91
95
  if tool_call_chunk.type == "tool_call" and tool_call_chunk.type in includes:
92
96
  yield tool_call_chunk
@@ -108,7 +112,7 @@ class Runner:
108
112
 
109
113
  def run(
110
114
  self,
111
- user_input: RunnerMessages | str,
115
+ user_input: UserInput,
112
116
  max_steps: int = 20,
113
117
  includes: Sequence[AgentChunkType] | None = None,
114
118
  context: "Any | None" = None, # noqa: ANN401
@@ -118,18 +122,32 @@ class Runner:
118
122
  includes = self._normalize_includes(includes)
119
123
  if isinstance(user_input, str):
120
124
  self.messages.append(AgentUserMessage(role="user", content=user_input))
121
- else:
125
+ elif isinstance(user_input, (list, tuple)):
126
+ # Handle sequence of messages
122
127
  for message in user_input:
123
128
  self.append_message(message)
129
+ else:
130
+ # Handle single message (BaseModel, TypedDict, or dict)
131
+ # Type assertion needed due to the complex union type
132
+ self.append_message(user_input) # type: ignore[arg-type]
124
133
  return self._run(max_steps, includes, self._normalize_record_path(record_to), context=context)
125
134
 
126
- async def _run(self, max_steps: int, includes: Sequence[AgentChunkType], record_to: Path | None = None, context: "Any | None" = None) -> AsyncGenerator[AgentChunk, None]: # noqa: ANN401
135
+ async def _run(self, max_steps: int, includes: Sequence[AgentChunkType], record_to: Path | None = None, context: "Any | None" = None) -> AsyncGenerator[AgentChunk, None]: # noqa: ANN401, C901
127
136
  """Run the agent and return a RunResponse object that can be asynchronously iterated for each chunk."""
128
137
  logger.debug(f"Running agent with messages: {self.messages}")
129
138
  steps = 0
130
139
  finish_reason = None
131
140
 
132
- while finish_reason != "stop" and steps < max_steps:
141
+ # Determine completion condition based on agent configuration
142
+ completion_condition = getattr(self.agent, "completion_condition", "stop")
143
+
144
+ def is_finish() -> bool:
145
+ if completion_condition == "call":
146
+ function_calls = self._find_pending_function_calls()
147
+ return any(getattr(fc, "name", None) == "wait_for_user" for fc in function_calls)
148
+ return finish_reason == "stop"
149
+
150
+ while not is_finish() and steps < max_steps:
133
151
  resp = await self.agent.completion(self.messages, record_to_file=record_to)
134
152
  async for chunk in resp:
135
153
  if chunk.type in includes:
@@ -202,14 +220,13 @@ class Runner:
202
220
  msg = "Cannot continue running without a valid last message from the assistant."
203
221
  raise ValueError(msg)
204
222
 
205
- # If we have an assistant message but no pending function calls,
206
- # that means there's nothing to continue
207
- msg = "Cannot continue running without pending function calls."
208
- raise ValueError(msg)
223
+ resp = self._run(max_steps=max_steps, includes=includes, record_to=self._normalize_record_path(record_to), context=context)
224
+ async for chunk in resp:
225
+ yield chunk
209
226
 
210
227
  async def run_until_complete(
211
228
  self,
212
- user_input: RunnerMessages | str,
229
+ user_input: UserInput,
213
230
  max_steps: int = 20,
214
231
  includes: list[AgentChunkType] | None = None,
215
232
  record_to: PathLike | str | None = None,
@@ -223,12 +240,13 @@ class Runner:
223
240
  # The final message from the stream handler might still contain tool_calls
224
241
  # We need to convert it to responses format
225
242
  if hasattr(message, "tool_calls") and message.tool_calls:
226
- # Add the assistant message without tool_calls
227
- assistant_msg = AgentAssistantMessage(
228
- role="assistant",
229
- content=message.content,
230
- )
231
- self.messages.append(assistant_msg)
243
+ if message.content:
244
+ # Add the assistant message without tool_calls
245
+ assistant_msg = AgentAssistantMessage(
246
+ role="assistant",
247
+ content=message.content,
248
+ )
249
+ self.messages.append(assistant_msg)
232
250
 
233
251
  # Add function call messages
234
252
  for tool_call in message.tool_calls:
@@ -285,7 +303,134 @@ class Runner:
285
303
  tool_calls.append(tool_call)
286
304
  return tool_calls
287
305
 
288
- def append_message(self, message: RunnerMessage | dict) -> None:
306
+ def set_chat_history(self, messages: Sequence[FlexibleRunnerMessage], root_agent: Agent | None = None) -> None:
307
+ """Set the entire chat history and track the current agent based on function calls.
308
+
309
+ This method analyzes the message history to determine which agent should be active
310
+ based on transfer_to_agent and transfer_to_parent function calls.
311
+
312
+ Args:
313
+ messages: List of messages to set as the chat history
314
+ root_agent: The root agent to use if no transfers are found. If None, uses self.agent
315
+ """
316
+ # Clear current messages
317
+ self.messages.clear()
318
+
319
+ # Set initial agent
320
+ current_agent = root_agent if root_agent is not None else self.agent
321
+
322
+ # Add each message and track agent transfers
323
+ for message in messages:
324
+ self.append_message(message)
325
+ current_agent = self._track_agent_transfer_in_message(message, current_agent)
326
+
327
+ # Set the current agent based on the tracked transfers
328
+ self.agent = current_agent
329
+ logger.info(f"Chat history set with {len(self.messages)} messages. Current agent: {self.agent.name}")
330
+
331
+ def get_messages_dict(self) -> list[dict[str, Any]]:
332
+ """Get the messages in JSONL format."""
333
+ return [msg.model_dump(mode="json") for msg in self.messages]
334
+
335
+ def _track_agent_transfer_in_message(self, message: FlexibleRunnerMessage, current_agent: Agent) -> Agent:
336
+ """Track agent transfers in a single message.
337
+
338
+ Args:
339
+ message: The message to analyze for transfers
340
+ current_agent: The currently active agent
341
+
342
+ Returns:
343
+ The agent that should be active after processing this message
344
+ """
345
+ if isinstance(message, dict):
346
+ return self._track_transfer_from_dict_message(message, current_agent)
347
+
348
+ if isinstance(message, AgentFunctionToolCallMessage):
349
+ return self._track_transfer_from_function_call_message(message, current_agent)
350
+
351
+ return current_agent
352
+
353
+ def _track_transfer_from_dict_message(self, message: dict[str, Any] | MessageDict, current_agent: Agent) -> Agent:
354
+ """Track transfers from dictionary-format messages."""
355
+ message_type = message.get("type")
356
+ if message_type != "function_call":
357
+ return current_agent
358
+
359
+ function_name = message.get("name", "")
360
+ if function_name == "transfer_to_agent":
361
+ return self._handle_transfer_to_agent_tracking(message.get("arguments", ""), current_agent)
362
+
363
+ if function_name == "transfer_to_parent":
364
+ return self._handle_transfer_to_parent_tracking(current_agent)
365
+
366
+ return current_agent
367
+
368
+ def _track_transfer_from_function_call_message(self, message: AgentFunctionToolCallMessage, current_agent: Agent) -> Agent:
369
+ """Track transfers from AgentFunctionToolCallMessage objects."""
370
+ if message.name == "transfer_to_agent":
371
+ return self._handle_transfer_to_agent_tracking(message.arguments, current_agent)
372
+
373
+ if message.name == "transfer_to_parent":
374
+ return self._handle_transfer_to_parent_tracking(current_agent)
375
+
376
+ return current_agent
377
+
378
+ def _handle_transfer_to_agent_tracking(self, arguments: str | dict, current_agent: Agent) -> Agent:
379
+ """Handle transfer_to_agent function call tracking."""
380
+ try:
381
+ args_dict = json.loads(arguments) if isinstance(arguments, str) else arguments
382
+
383
+ target_agent_name = args_dict.get("name")
384
+ if target_agent_name:
385
+ target_agent = self._find_agent_by_name(current_agent, target_agent_name)
386
+ if target_agent:
387
+ logger.debug(f"History tracking: Transferring from {current_agent.name} to {target_agent_name}")
388
+ return target_agent
389
+
390
+ logger.warning(f"Target agent '{target_agent_name}' not found in handoffs during history setup")
391
+ except (json.JSONDecodeError, KeyError, TypeError) as e:
392
+ logger.warning(f"Failed to parse transfer_to_agent arguments during history setup: {e}")
393
+
394
+ return current_agent
395
+
396
+ def _handle_transfer_to_parent_tracking(self, current_agent: Agent) -> Agent:
397
+ """Handle transfer_to_parent function call tracking."""
398
+ if current_agent.parent:
399
+ logger.debug(f"History tracking: Transferring from {current_agent.name} back to parent {current_agent.parent.name}")
400
+ return current_agent.parent
401
+
402
+ logger.warning(f"Agent {current_agent.name} has no parent to transfer back to during history setup")
403
+ return current_agent
404
+
405
+ def _find_agent_by_name(self, root_agent: Agent, target_name: str) -> Agent | None:
406
+ """Find an agent by name in the handoffs tree starting from root_agent.
407
+
408
+ Args:
409
+ root_agent: The root agent to start searching from
410
+ target_name: The name of the agent to find
411
+
412
+ Returns:
413
+ The agent if found, None otherwise
414
+ """
415
+ # Check direct handoffs from current agent
416
+ if root_agent.handoffs:
417
+ for agent in root_agent.handoffs:
418
+ if agent.name == target_name:
419
+ return agent
420
+
421
+ # If not found in direct handoffs, check if we need to look in parent's handoffs
422
+ # This handles cases where agents can transfer to siblings
423
+ current = root_agent
424
+ while current.parent is not None:
425
+ current = current.parent
426
+ if current.handoffs:
427
+ for agent in current.handoffs:
428
+ if agent.name == target_name:
429
+ return agent
430
+
431
+ return None
432
+
433
+ def append_message(self, message: FlexibleRunnerMessage) -> None:
289
434
  if isinstance(message, RunnerMessage):
290
435
  self.messages.append(message)
291
436
  elif isinstance(message, dict):
@@ -0,0 +1,10 @@
1
+ <HandoffsGuide>
2
+ You are a parent agent that can assign tasks to sub-agents.
3
+
4
+ You can transfer conversations to other agents for specific tasks.
5
+ If you need to assign tasks to multiple agents, you should break down the tasks and assign them one by one.
6
+ You need to wait for one sub-agent to finish before assigning the task to the next sub-agent.
7
+ {% if extra_instructions %}
8
+ {{ extra_instructions }}
9
+ {% endif %}
10
+ </HandoffsGuide>
@@ -0,0 +1,9 @@
1
+ <TransferToParentGuide>
2
+ You are a sub-agent that is assigned to a specific task by your parent agent.
3
+
4
+ Everything you output is intended for your parent agent to read.
5
+ When you finish your task, you should call `transfer_to_parent` to transfer back to parent agent.
6
+ {% if extra_instructions %}
7
+ {{ extra_instructions }}
8
+ {% endif %}
9
+ </TransferToParentGuide>
@@ -0,0 +1,6 @@
1
+ <WaitForUserGuide>
2
+ When you have completed your assigned task or need more information from the user, you must call the `wait_for_user` function.
3
+ {% if extra_instructions %}
4
+ {{ extra_instructions }}
5
+ {% endif %}
6
+ </WaitForUserGuide>
@@ -18,12 +18,22 @@ from .messages import (
18
18
  AgentSystemMessage,
19
19
  AgentUserMessage,
20
20
  AssistantMessage,
21
+ AssistantMessageDict,
22
+ FlexibleRunnerMessage,
23
+ FunctionCallDict,
24
+ FunctionCallOutputDict,
21
25
  Message,
26
+ MessageDict,
27
+ ResponseInputImage,
28
+ ResponseInputText,
22
29
  RunnerMessage,
23
30
  RunnerMessages,
31
+ SystemMessageDict,
32
+ UserInput,
24
33
  UserMessageContentItemImageURL,
25
34
  UserMessageContentItemImageURLImageURL,
26
35
  UserMessageContentItemText,
36
+ UserMessageDict,
27
37
  )
28
38
  from .tool_calls import ToolCall, ToolCallFunction
29
39
 
@@ -37,19 +47,29 @@ __all__ = [
37
47
  "AgentSystemMessage",
38
48
  "AgentUserMessage",
39
49
  "AssistantMessage",
50
+ "AssistantMessageDict",
40
51
  "CompletionRawChunk",
41
52
  "ContentDeltaChunk",
42
53
  "FinalMessageChunk",
54
+ "FlexibleRunnerMessage",
55
+ "FunctionCallDict",
56
+ "FunctionCallOutputDict",
43
57
  "Message",
58
+ "MessageDict",
59
+ "ResponseInputImage",
60
+ "ResponseInputText",
44
61
  "RunnerMessage",
45
62
  "RunnerMessages",
63
+ "SystemMessageDict",
46
64
  "ToolCall",
47
65
  "ToolCallChunk",
48
66
  "ToolCallDeltaChunk",
49
67
  "ToolCallFunction",
50
68
  "ToolCallResultChunk",
51
69
  "UsageChunk",
70
+ "UserInput",
52
71
  "UserMessageContentItemImageURL",
53
72
  "UserMessageContentItemImageURLImageURL",
54
73
  "UserMessageContentItemText",
74
+ "UserMessageDict",
55
75
  ]
@@ -1,25 +1,71 @@
1
1
  from collections.abc import Sequence
2
- from typing import Literal
2
+ from typing import Any, Literal, NotRequired, TypedDict
3
3
 
4
4
  from pydantic import BaseModel
5
- from rich import Any
6
5
 
7
6
  from .tool_calls import ToolCall
8
7
 
9
8
 
10
- class AssistantMessage(BaseModel):
11
- id: str
12
- index: int
13
- role: Literal["assistant"] = "assistant"
14
- content: str = ""
15
- tool_calls: list[ToolCall] | None = None
9
+ class ResponseInputImageDict(TypedDict):
10
+ detail: NotRequired[Literal["low", "high", "auto"]]
11
+ type: Literal["input_image"]
12
+ file_id: str | None
13
+ image_url: str | None
16
14
 
17
15
 
18
- class Message(BaseModel):
19
- role: str
16
+ class ResponseInputTextDict(TypedDict):
17
+ text: str
18
+ type: Literal["input_text"]
19
+
20
+
21
+ # TypedDict definitions for better type hints
22
+ class UserMessageDict(TypedDict):
23
+ role: Literal["user"]
24
+ content: str | Sequence[ResponseInputTextDict | ResponseInputImageDict]
25
+
26
+
27
+ class AssistantMessageDict(TypedDict):
28
+ role: Literal["assistant"]
20
29
  content: str
21
30
 
22
31
 
32
+ class SystemMessageDict(TypedDict):
33
+ role: Literal["system"]
34
+ content: str
35
+
36
+
37
+ class FunctionCallDict(TypedDict):
38
+ type: Literal["function_call"]
39
+ function_call_id: str
40
+ name: str
41
+ arguments: str
42
+ content: str
43
+
44
+
45
+ class FunctionCallOutputDict(TypedDict):
46
+ type: Literal["function_call_output"]
47
+ call_id: str
48
+ output: str
49
+
50
+
51
+ # Union type for all supported message dictionary formats
52
+ MessageDict = UserMessageDict | AssistantMessageDict | SystemMessageDict | FunctionCallDict | FunctionCallOutputDict
53
+
54
+
55
+ # Response API format input types
56
+ class ResponseInputText(BaseModel):
57
+ text: str
58
+ type: Literal["input_text"]
59
+
60
+
61
+ class ResponseInputImage(BaseModel):
62
+ detail: Literal["low", "high", "auto"] = "auto"
63
+ type: Literal["input_image"]
64
+ file_id: str | None = None
65
+ image_url: str | None = None
66
+
67
+
68
+ # Compatibility types for old completion API format
23
69
  class UserMessageContentItemText(BaseModel):
24
70
  type: Literal["text"]
25
71
  text: str
@@ -34,9 +80,23 @@ class UserMessageContentItemImageURL(BaseModel):
34
80
  image_url: UserMessageContentItemImageURLImageURL
35
81
 
36
82
 
83
+ # Legacy types - keeping for compatibility
84
+ class AssistantMessage(BaseModel):
85
+ id: str
86
+ index: int
87
+ role: Literal["assistant"] = "assistant"
88
+ content: str = ""
89
+ tool_calls: list[ToolCall] | None = None
90
+
91
+
92
+ class Message(BaseModel):
93
+ role: str
94
+ content: str
95
+
96
+
37
97
  class AgentUserMessage(BaseModel):
38
98
  role: Literal["user"]
39
- content: str | Sequence[UserMessageContentItemText | UserMessageContentItemImageURL]
99
+ content: str | Sequence[ResponseInputText | ResponseInputImage | UserMessageContentItemText | UserMessageContentItemImageURL]
40
100
 
41
101
 
42
102
  class AgentAssistantMessage(BaseModel):
@@ -65,4 +125,11 @@ class AgentFunctionCallOutput(BaseModel):
65
125
 
66
126
  RunnerMessage = AgentUserMessage | AgentAssistantMessage | AgentSystemMessage | AgentFunctionToolCallMessage | AgentFunctionCallOutput
67
127
  AgentMessage = RunnerMessage | AgentSystemMessage
68
- RunnerMessages = Sequence[RunnerMessage | dict[str, Any]]
128
+
129
+ # Enhanced type definitions for better type hints
130
+ # Supports BaseModel instances, TypedDict, and plain dict
131
+ FlexibleRunnerMessage = RunnerMessage | MessageDict | dict[str, Any]
132
+ RunnerMessages = Sequence[FlexibleRunnerMessage]
133
+
134
+ # Type alias for user input - supports string, single message, or sequence of messages
135
+ UserInput = str | FlexibleRunnerMessage | RunnerMessages
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lite-agent
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A lightweight, extensible framework for building AI agent.
5
5
  Author-email: Jianqi Pan <jannchie@gmail.com>
6
6
  License: MIT
@@ -0,0 +1,22 @@
1
+ lite_agent/__init__.py,sha256=Xaex4kVGxZzg_hhO17b8_tVXf63xFbSzBOlVdhRf-Ng,338
2
+ lite_agent/agent.py,sha256=MsdAnM2pqh9RLTGOFuynowkS52QXUb7vHaMHQyiOpoQ,17627
3
+ lite_agent/client.py,sha256=e_BsXo6KUgleRFkSPSESUoIPvyLXWyJ9E1AzExYXXsk,1236
4
+ lite_agent/loggers.py,sha256=XkNkdqwD_nQGfhQJ-bBWT7koci_mMkNw3aBpyMhOICw,57
5
+ lite_agent/message_transfers.py,sha256=nT7-tID20RK2yoN-rDiEE6sSclluSlhYSkayCzmPwk8,3984
6
+ lite_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ lite_agent/rich_helpers.py,sha256=6HdjMwR2U7P5ieZ5KHQNUsQbW-apG67GzQ_nvJm774E,15583
8
+ lite_agent/runner.py,sha256=LWhtmxrYxL4sFi3ZKeWWRymK3tGCvWfIh1-EcWf8P0g,27822
9
+ lite_agent/processors/__init__.py,sha256=X78GKL_IWwW2lg8w4DD6GOFWLzAR2wTfKxHlvOkcuUQ,114
10
+ lite_agent/processors/stream_chunk_processor.py,sha256=nMA_cW7FDpXwJvm4F8vFwBXmHHsSELQFcoNEjH3xvn8,4751
11
+ lite_agent/stream_handlers/__init__.py,sha256=2GSiG0VUgcQlFMl6JkGAqikXMII1a43Hr-J5NIct6dk,115
12
+ lite_agent/stream_handlers/litellm.py,sha256=NNMAl8Bvoc2xe-qWKtfqvJQA2yr3sz1IUU90rQ_9iBw,3976
13
+ lite_agent/templates/handoffs_source_instructions.xml.j2,sha256=2XsXQlBzk38qbxGrfyt8y2b0KlZmsV_1xavLufcdkHc,428
14
+ lite_agent/templates/handoffs_target_instructions.xml.j2,sha256=gSbWVYYcovPKbGpFc0kqGSJ5Y5UC3fOHyUmZfcrDgSE,356
15
+ lite_agent/templates/wait_for_user_instructions.xml.j2,sha256=wXbcYD5Q1FaCGVBm3Hz_Cp7nnoK7KzloP0ao-jYMwPk,231
16
+ lite_agent/types/__init__.py,sha256=8l2RL-55sRHQW-sTmtKkKzCQGLrENaJT7Cgy5iA5xCo,1767
17
+ lite_agent/types/chunks.py,sha256=Ro5BtrrdsYGkKrEekIhs9vIrBM7HljtgOkHherH8B3k,1697
18
+ lite_agent/types/messages.py,sha256=A66YVl2IYMMTlnEdGlbCXqMztSSMSjS9F2yyebBlKR0,3364
19
+ lite_agent/types/tool_calls.py,sha256=Xnut8-2-Ld9vgA2GKJY6BbFlBaAv_n4W7vo7Jx21A-E,260
20
+ lite_agent-0.3.0.dist-info/METADATA,sha256=l4SLUuFQlcrlr-CHOIaWqQ3WOyYnPVIsHjjxKjR_g4E,3455
21
+ lite_agent-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
+ lite_agent-0.3.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- lite_agent/__init__.py,sha256=T-jxY0aHwqPz5gs-ZzH1zvFgFdYVmOd15fnEc5A2H6U,229
2
- lite_agent/agent.py,sha256=UE06H1Huk4gHGovbh9DuAJAH2zC0hqf1YXgrVa59_Os,13546
3
- lite_agent/loggers.py,sha256=XkNkdqwD_nQGfhQJ-bBWT7koci_mMkNw3aBpyMhOICw,57
4
- lite_agent/message_transfers.py,sha256=nT7-tID20RK2yoN-rDiEE6sSclluSlhYSkayCzmPwk8,3984
5
- lite_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- lite_agent/runner.py,sha256=lO8Z5R2RxjjgYzDAeYQbO2niQeieXOEzqUEN484G7QU,21367
7
- lite_agent/processors/__init__.py,sha256=X78GKL_IWwW2lg8w4DD6GOFWLzAR2wTfKxHlvOkcuUQ,114
8
- lite_agent/processors/stream_chunk_processor.py,sha256=nMA_cW7FDpXwJvm4F8vFwBXmHHsSELQFcoNEjH3xvn8,4751
9
- lite_agent/stream_handlers/__init__.py,sha256=2GSiG0VUgcQlFMl6JkGAqikXMII1a43Hr-J5NIct6dk,115
10
- lite_agent/stream_handlers/litellm.py,sha256=NNMAl8Bvoc2xe-qWKtfqvJQA2yr3sz1IUU90rQ_9iBw,3976
11
- lite_agent/types/__init__.py,sha256=sc2cdX1tPisfQwu2-apZtUa3u_Q6WDEqzNglfXhwCJo,1295
12
- lite_agent/types/chunks.py,sha256=Ro5BtrrdsYGkKrEekIhs9vIrBM7HljtgOkHherH8B3k,1697
13
- lite_agent/types/messages.py,sha256=cmMcj_r1_R9Pgu6ixmBIq2uwwqi5KzIllxGoxpcxF3w,1531
14
- lite_agent/types/tool_calls.py,sha256=Xnut8-2-Ld9vgA2GKJY6BbFlBaAv_n4W7vo7Jx21A-E,260
15
- lite_agent-0.2.0.dist-info/METADATA,sha256=egjWZCc9UgPAHMCJqZrjBXlXrG39f9LoW9k6DzntMas,3455
16
- lite_agent-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- lite_agent-0.2.0.dist-info/RECORD,,