lite-agent 0.5.0__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (109) hide show
  1. {lite_agent-0.5.0 → lite_agent-0.8.0}/.claude/settings.local.json +5 -1
  2. {lite_agent-0.5.0 → lite_agent-0.8.0}/CHANGELOG.md +39 -0
  3. {lite_agent-0.5.0 → lite_agent-0.8.0}/PKG-INFO +2 -1
  4. lite_agent-0.8.0/examples/basic.py +43 -0
  5. lite_agent-0.5.0/examples/basic.py → lite_agent-0.8.0/examples/basic_model.py +7 -1
  6. lite_agent-0.8.0/examples/cancel_and_transfer_demo.py +299 -0
  7. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/confirm_and_continue.py +1 -1
  8. lite_agent-0.8.0/examples/debug_non_streaming.py +57 -0
  9. lite_agent-0.8.0/examples/debug_with_logging.py +39 -0
  10. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/handoffs.py +1 -1
  11. lite_agent-0.8.0/examples/llm_config_demo.py +90 -0
  12. lite_agent-0.8.0/examples/non_streaming.py +66 -0
  13. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/reasoning_example.py +22 -22
  14. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/response_api_example.py +3 -1
  15. lite_agent-0.8.0/examples/simple_debug.py +38 -0
  16. lite_agent-0.8.0/examples/simple_debug2.py +31 -0
  17. lite_agent-0.8.0/examples/stop_before_functions.py +114 -0
  18. lite_agent-0.8.0/examples/streaming_demo.py +74 -0
  19. {lite_agent-0.5.0 → lite_agent-0.8.0}/pyproject.toml +4 -1
  20. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/agent.py +188 -49
  21. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/chat_display.py +22 -14
  22. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/client.py +65 -3
  23. lite_agent-0.8.0/src/lite_agent/constants.py +30 -0
  24. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/message_transfers.py +3 -3
  25. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/processors/completion_event_processor.py +14 -20
  26. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/processors/response_event_processor.py +21 -15
  27. lite_agent-0.8.0/src/lite_agent/response_handlers/__init__.py +11 -0
  28. lite_agent-0.8.0/src/lite_agent/response_handlers/base.py +54 -0
  29. lite_agent-0.8.0/src/lite_agent/response_handlers/completion.py +78 -0
  30. lite_agent-0.8.0/src/lite_agent/response_handlers/responses.py +76 -0
  31. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/runner.py +312 -247
  32. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/types/__init__.py +2 -0
  33. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/types/messages.py +6 -5
  34. lite_agent-0.8.0/src/lite_agent/utils/__init__.py +0 -0
  35. lite_agent-0.8.0/src/lite_agent/utils/message_builder.py +211 -0
  36. lite_agent-0.8.0/src/lite_agent/utils/metrics.py +50 -0
  37. lite_agent-0.8.0/temp/main.py +36 -0
  38. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/mocks/confirm_and_continue/1.jsonl +21 -21
  39. lite_agent-0.8.0/tests/mocks/confirm_and_continue/2.jsonl +48 -0
  40. lite_agent-0.8.0/tests/performance/test_set_chat_history_performance.py +142 -0
  41. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_agent.py +8 -12
  42. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_agent_handoffs.py +17 -4
  43. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_append_message.py +18 -51
  44. lite_agent-0.8.0/tests/unit/test_cancel_pending_tools.py +233 -0
  45. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_message_transfer.py +2 -2
  46. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_message_transfers.py +19 -10
  47. lite_agent-0.8.0/tests/unit/test_response_api_format.py +130 -0
  48. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_runner.py +5 -5
  49. lite_agent-0.8.0/tests/unit/test_set_chat_history.py +198 -0
  50. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_stream_handlers_additional.py +1 -0
  51. lite_agent-0.8.0/tests/unit/test_streaming_config.py +102 -0
  52. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/utils/mock_litellm.py +1 -2
  53. {lite_agent-0.5.0 → lite_agent-0.8.0}/uv.lock +230 -227
  54. lite_agent-0.5.0/tests/mocks/confirm_and_continue/2.jsonl +0 -37
  55. lite_agent-0.5.0/tests/performance/test_set_chat_history_performance.py +0 -130
  56. lite_agent-0.5.0/tests/unit/test_response_api_format.py +0 -290
  57. lite_agent-0.5.0/tests/unit/test_set_chat_history.py +0 -354
  58. {lite_agent-0.5.0 → lite_agent-0.8.0}/.github/workflows/ci.yml +0 -0
  59. {lite_agent-0.5.0 → lite_agent-0.8.0}/.gitignore +0 -0
  60. {lite_agent-0.5.0 → lite_agent-0.8.0}/.python-version +0 -0
  61. {lite_agent-0.5.0 → lite_agent-0.8.0}/.vscode/launch.json +0 -0
  62. {lite_agent-0.5.0 → lite_agent-0.8.0}/CLAUDE.md +0 -0
  63. {lite_agent-0.5.0 → lite_agent-0.8.0}/README.md +0 -0
  64. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/basic_agent.py +0 -0
  65. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/channels/rich_channel.py +0 -0
  66. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/chat_display_demo.py +0 -0
  67. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/consolidate_history.py +0 -0
  68. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/context.py +0 -0
  69. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/image.py +0 -0
  70. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/message_transfer_example.py +0 -0
  71. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/message_transfer_example_new.py +0 -0
  72. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/new_message_structure_demo.py +0 -0
  73. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/responses.py +0 -0
  74. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/set_chat_history_example.py +0 -0
  75. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/stop_with_tool_call.py +0 -0
  76. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/terminal.py +0 -0
  77. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/translate/main.py +0 -0
  78. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/translate/prompts/translation_system.md.j2 +0 -0
  79. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/translate.py +0 -0
  80. {lite_agent-0.5.0 → lite_agent-0.8.0}/examples/type_system_example.py +0 -0
  81. {lite_agent-0.5.0 → lite_agent-0.8.0}/scripts/record_chat_messages.py +0 -0
  82. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/__init__.py +0 -0
  83. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/loggers.py +0 -0
  84. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/processors/__init__.py +0 -0
  85. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/py.typed +0 -0
  86. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/stream_handlers/__init__.py +0 -0
  87. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/stream_handlers/litellm.py +0 -0
  88. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/templates/handoffs_source_instructions.xml.j2 +0 -0
  89. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/templates/handoffs_target_instructions.xml.j2 +0 -0
  90. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/templates/wait_for_user_instructions.xml.j2 +0 -0
  91. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/types/events.py +0 -0
  92. {lite_agent-0.5.0 → lite_agent-0.8.0}/src/lite_agent/types/tool_calls.py +0 -0
  93. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/integration/test_agent_with_mocks.py +0 -0
  94. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/integration/test_basic.py +0 -0
  95. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/integration/test_mock_litellm.py +0 -0
  96. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/mocks/basic/1.jsonl +0 -0
  97. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/mocks/context/1.jsonl +0 -0
  98. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/mocks/handoffs/1.jsonl +0 -0
  99. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/test_new_messages.py +0 -0
  100. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_agent_additional.py +0 -0
  101. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_chat_display.py +0 -0
  102. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_chat_display_additional.py +0 -0
  103. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_completion_condition.py +0 -0
  104. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_file_recording.py +0 -0
  105. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_litellm_stream_handler.py +0 -0
  106. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_message_transfers_additional.py +0 -0
  107. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_response_event_processor.py +0 -0
  108. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_simple_stream_handlers.py +0 -0
  109. {lite_agent-0.5.0 → lite_agent-0.8.0}/tests/unit/test_stream_chunk_processor.py +0 -0
@@ -14,7 +14,11 @@
14
14
  "Bash(uv run pytest:*)",
15
15
  "Bash(timeout:*)",
16
16
  "Bash(pyright:*)",
17
- "Bash(rg:*)"
17
+ "Bash(rg:*)",
18
+ "Bash(uv run:*)",
19
+ "Bash(mkdir:*)",
20
+ "Bash(sed:*)",
21
+ "Bash(mv:*)"
18
22
  ],
19
23
  "deny": []
20
24
  }
@@ -1,3 +1,42 @@
1
+ ## v0.8.0
2
+
3
+ [v0.7.0...v0.8.0](https://github.com/Jannchie/lite-agent/compare/v0.7.0...v0.8.0)
4
+
5
+ ### :rocket: Breaking Changes
6
+
7
+ - **runner**: remove dict-format and legacy support from message history and enforce newmessage format - By [Jannchie](mailto:jannchie@gmail.com) in [44b435c](https://github.com/Jannchie/lite-agent/commit/44b435c)
8
+
9
+ ### :sparkles: Features
10
+
11
+ - **agent**: add dynamic stop-before-functions support - By [Jannchie](mailto:jannchie@gmail.com) in [e7de181](https://github.com/Jannchie/lite-agent/commit/e7de181)
12
+ - **agent**: improve tool call extraction and meta preservation - By [Jannchie](mailto:jannchie@gmail.com) in [1b10296](https://github.com/Jannchie/lite-agent/commit/1b10296)
13
+ - **runner**: add cancellation events for pending tool calls and implement function_call_output for agent transfers - By [Jannchie](mailto:jannchie@gmail.com) in [4c298b7](https://github.com/Jannchie/lite-agent/commit/4c298b7)
14
+ - **runner**: preserve original dict message structure in chat history - By [Jannchie](mailto:jannchie@gmail.com) in [da4ad59](https://github.com/Jannchie/lite-agent/commit/da4ad59)
15
+
16
+ ## v0.7.0
17
+
18
+ [v0.6.0...v0.7.0](https://github.com/Jannchie/lite-agent/compare/v0.6.0...v0.7.0)
19
+
20
+ ### :sparkles: Features
21
+
22
+ - **assistant-meta**: add model and usage support to assistant message meta and refactor token accounting - By [Jannchie](mailto:jannchie@gmail.com) in [b50ed0e](https://github.com/Jannchie/lite-agent/commit/b50ed0e)
23
+ - **message-builder**: support array content in assistant messages - By [Jannchie](mailto:jannchie@gmail.com) in [1b7a790](https://github.com/Jannchie/lite-agent/commit/1b7a790)
24
+ - **runner**: add tool call handling and usage events for completion and responses api - By [Jannchie](mailto:jannchie@gmail.com) in [1a82a18](https://github.com/Jannchie/lite-agent/commit/1a82a18)
25
+
26
+ ### :art: Refactors
27
+
28
+ - **messages**: replace type checks with isinstance for assistant content - By [Jannchie](mailto:jannchie@gmail.com) in [ec506a8](https://github.com/Jannchie/lite-agent/commit/ec506a8)
29
+ - **utils**: extract constants and message utilities && centralize timing metrics - By [Jannchie](mailto:jannchie@gmail.com) in [d928637](https://github.com/Jannchie/lite-agent/commit/d928637)
30
+
31
+ ## v0.6.0
32
+
33
+ [v0.5.0...v0.6.0](https://github.com/Jannchie/lite-agent/compare/v0.5.0...v0.6.0)
34
+
35
+ ### :sparkles: Features
36
+
37
+ - **examples**: add basic and llm config usage examples - By [Jannchie](mailto:jannchie@gmail.com) in [ea4112f](https://github.com/Jannchie/lite-agent/commit/ea4112f)
38
+ - **streaming**: add unified streaming and non-streaming response handling - By [Jannchie](mailto:jannchie@gmail.com) in [53daf42](https://github.com/Jannchie/lite-agent/commit/53daf42)
39
+
1
40
  ## v0.5.0
2
41
 
3
42
  [v0.4.1...v0.5.0](https://github.com/Jannchie/lite-agent/compare/v0.4.1...v0.5.0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lite-agent
3
- Version: 0.5.0
3
+ Version: 0.8.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
@@ -19,6 +19,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Requires-Python: >=3.10
20
20
  Requires-Dist: aiofiles>=24.1.0
21
21
  Requires-Dist: funcall>=0.10.0
22
+ Requires-Dist: openai<=1.99.5
22
23
  Requires-Dist: prompt-toolkit>=3.0.51
23
24
  Requires-Dist: rich>=14.0.0
24
25
  Description-Content-Type: text/markdown
@@ -0,0 +1,43 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ from rich.logging import RichHandler
5
+
6
+ from lite_agent.agent import Agent
7
+ from lite_agent.runner import Runner
8
+
9
+ logging.basicConfig(
10
+ level=logging.WARNING,
11
+ format="%(message)s",
12
+ datefmt="[%X]",
13
+ handlers=[RichHandler(rich_tracebacks=True)],
14
+ )
15
+
16
+ logger = logging.getLogger("lite_agent")
17
+ logger.setLevel(logging.DEBUG)
18
+
19
+
20
+ async def get_temperature(city: str) -> str:
21
+ """Get the temperature for a city."""
22
+ return f"The temperature in {city} is 25°C."
23
+
24
+
25
+ agent = Agent(
26
+ model="gpt-4.1-nano",
27
+ name="Weather Assistant",
28
+ instructions="You are a helpful weather assistant. Before using tools, briefly explain what you are going to do. Provide friendly and informative responses.",
29
+ tools=[get_temperature],
30
+ )
31
+
32
+
33
+ async def main():
34
+ runner = Runner(agent, streaming=False, api="completion")
35
+ resp = await runner.run_until_complete(
36
+ "What is the temperature in New York?",
37
+ includes=["usage", "assistant_message", "function_call", "function_call_output", "timing"],
38
+ )
39
+ print(resp[0])
40
+
41
+
42
+ if __name__ == "__main__":
43
+ asyncio.run(main())
@@ -5,6 +5,7 @@ from rich.logging import RichHandler
5
5
 
6
6
  from lite_agent.agent import Agent
7
7
  from lite_agent.chat_display import display_messages
8
+ from lite_agent.client import LiteLLMClient
8
9
  from lite_agent.runner import Runner
9
10
 
10
11
  logging.basicConfig(
@@ -24,7 +25,12 @@ async def get_temperature(city: str) -> str:
24
25
 
25
26
 
26
27
  agent = Agent(
27
- model="gpt-4.1-nano",
28
+ model=LiteLLMClient(
29
+ model="gpt-4o-mini",
30
+ temperature=0.7,
31
+ max_tokens=150,
32
+ top_p=0.9,
33
+ ),
28
34
  name="Weather Assistant",
29
35
  instructions="You are a helpful weather assistant. Before using tools, briefly explain what you are going to do. Provide friendly and informative responses.",
30
36
  tools=[get_temperature],
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demo showing two new features:
4
+ 1. Cancellation of pending tool calls when user provides new input
5
+ 2. function_call_output events for transfer_to_agent and transfer_to_parent
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+
11
+ from funcall.decorators import tool
12
+ from rich.logging import RichHandler
13
+
14
+ from lite_agent.agent import Agent
15
+ from lite_agent.runner import Runner
16
+ from lite_agent.types import AssistantTextContent, AssistantToolCall, NewAssistantMessage
17
+
18
+ logging.basicConfig(
19
+ level=logging.WARNING,
20
+ format="%(message)s",
21
+ datefmt="[%X]",
22
+ handlers=[RichHandler(rich_tracebacks=True)],
23
+ )
24
+
25
+ logger = logging.getLogger("lite_agent")
26
+ logger.setLevel(logging.DEBUG)
27
+
28
+
29
+ @tool
30
+ async def get_weather(city: str) -> str:
31
+ """Get the weather for a city."""
32
+ await asyncio.sleep(2) # Simulate long operation
33
+ return f"The weather in {city} is sunny with a few clouds."
34
+
35
+
36
+ @tool
37
+ async def get_temperature(city: str) -> str:
38
+ """Get the temperature for a city."""
39
+ await asyncio.sleep(2) # Simulate long operation
40
+ return f"The temperature in {city} is 25°C."
41
+
42
+
43
+ @tool
44
+ async def book_flight(destination: str, date: str) -> str:
45
+ """Book a flight to a destination."""
46
+ await asyncio.sleep(3) # Simulate very long operation
47
+ return f"Flight booked to {destination} on {date}"
48
+
49
+
50
+ async def demo_1_cancel_pending_tools():
51
+ """Demo 1: Cancellation of pending tool calls"""
52
+ print("=== Demo 1: Cancellation of Pending Tool Calls ===\n")
53
+
54
+ agent = Agent(
55
+ model="gpt-4.1-nano",
56
+ name="WeatherBot",
57
+ instructions="You are a helpful weather assistant.",
58
+ tools=[get_weather, get_temperature, book_flight],
59
+ )
60
+
61
+ runner = Runner(agent)
62
+
63
+ # Simulate a scenario where the agent made tool calls but user interrupts
64
+ print("1. Simulating assistant making tool calls...")
65
+ assistant_message = NewAssistantMessage(
66
+ content=[
67
+ AssistantTextContent(text="I'll check the weather and temperature for you, and also help you book that flight."),
68
+ AssistantToolCall(
69
+ call_id="weather_call",
70
+ name="get_weather",
71
+ arguments='{"city": "Tokyo"}',
72
+ ),
73
+ AssistantToolCall(
74
+ call_id="temp_call",
75
+ name="get_temperature",
76
+ arguments='{"city": "Tokyo"}',
77
+ ),
78
+ AssistantToolCall(
79
+ call_id="flight_call",
80
+ name="book_flight",
81
+ arguments='{"destination": "Paris", "date": "2024-12-25"}',
82
+ ),
83
+ ],
84
+ )
85
+
86
+ runner.messages.append(assistant_message)
87
+
88
+ # Check pending calls
89
+ pending_calls = runner._find_pending_tool_calls()
90
+ print(f"Pending tool calls: {len(pending_calls)}")
91
+ for call in pending_calls:
92
+ print(f" - {call.name} (call_id: {call.call_id})")
93
+
94
+ print(f"Total messages before cancellation: {len(runner.messages)}")
95
+ print(f"Content items in assistant message: {len(assistant_message.content)}")
96
+
97
+ # User provides new input - this should cancel pending calls and generate events
98
+ print("\n2. User provides new input: 'Actually, never mind about the weather. What's the capital of Japan?'")
99
+
100
+ print("\n2a. Testing via run() method (integrated flow with event yielding):")
101
+ # Test the integrated flow - collect cancellation events from run()
102
+ # This will automatically call _cancel_pending_tool_calls() and yield events
103
+ chunks = []
104
+ try:
105
+ async for chunk in runner.run("What's the capital of Japan?", includes=["function_call_output"]):
106
+ chunks.append(chunk)
107
+ if len(chunks) >= 3: # Stop after getting cancellation events
108
+ break
109
+ except Exception:
110
+ # Expected - no real LLM configured
111
+ pass
112
+
113
+ print(f"Events from run() method: {len(chunks)}")
114
+ for i, chunk in enumerate(chunks):
115
+ if chunk.type == "function_call_output":
116
+ print(f" - Chunk {i + 1}: {chunk.type} for {chunk.tool_call_id}")
117
+ print(f" Tool: {chunk.name}")
118
+ print(f" Content: {chunk.content}")
119
+
120
+ # Now demonstrate the direct method on fresh data
121
+ print("\n2b. Testing direct cancellation method (returns events):")
122
+ # Create a new runner with fresh pending calls
123
+ runner2 = Runner(agent)
124
+ runner2.messages.append(
125
+ NewAssistantMessage(
126
+ content=[
127
+ AssistantTextContent(text="Let me help with a different task."),
128
+ AssistantToolCall(
129
+ call_id="new_call_1",
130
+ name="get_weather",
131
+ arguments='{"city": "London"}',
132
+ ),
133
+ AssistantToolCall(
134
+ call_id="new_call_2",
135
+ name="get_temperature",
136
+ arguments='{"city": "London"}',
137
+ ),
138
+ ],
139
+ ),
140
+ )
141
+
142
+ cancellation_events = runner2._cancel_pending_tool_calls()
143
+ print(f"Cancellation events generated: {len(cancellation_events)}")
144
+ for event in cancellation_events:
145
+ print(f" - Event: {event.type} for {event.tool_call_id} ({event.name})")
146
+ print(f" Content: {event.content}")
147
+ print(f" Execution time: {event.execution_time_ms}ms")
148
+
149
+ print("\n3. After cancellation:")
150
+ pending_calls_after = runner._find_pending_tool_calls()
151
+ print(f"Pending tool calls: {len(pending_calls_after)}")
152
+
153
+ print(f"Total messages after cancellation: {len(runner.messages)}")
154
+ if runner.messages:
155
+ assistant_msg = runner.messages[0]
156
+ print(f"Content items in assistant message: {len(assistant_msg.content)}")
157
+
158
+ # Show the cancellation results
159
+ cancellation_results = [item for item in assistant_msg.content if item.type == "tool_call_result"]
160
+ print(f"Cancellation results added: {len(cancellation_results)}")
161
+ for result in cancellation_results:
162
+ print(f" - {result.call_id}: {result.output}")
163
+
164
+
165
+ async def demo_2_transfer_events():
166
+ """Demo 2: function_call_output events for agent transfers"""
167
+ print("\n\n=== Demo 2: Transfer Events ===\n")
168
+
169
+ # Create agents with handoff relationships
170
+ weather_agent = Agent(
171
+ model="gpt-4",
172
+ name="WeatherAgent",
173
+ instructions="You specialize in weather information.",
174
+ tools=[get_weather, get_temperature],
175
+ )
176
+
177
+ travel_agent = Agent(
178
+ model="gpt-4",
179
+ name="TravelAgent",
180
+ instructions="You specialize in travel bookings.",
181
+ tools=[book_flight],
182
+ )
183
+
184
+ main_agent = Agent(
185
+ model="gpt-4",
186
+ name="MainAgent",
187
+ instructions="You are the main agent that coordinates with specialists.",
188
+ handoffs=[weather_agent, travel_agent],
189
+ )
190
+
191
+ runner = Runner(main_agent)
192
+
193
+ print("1. Testing transfer_to_agent with function_call_output events:")
194
+ print(f"Current agent: {runner.agent.name}")
195
+
196
+ # Simulate transfer_to_agent call
197
+ import json
198
+
199
+ from lite_agent.types import ToolCall, ToolCallFunction
200
+
201
+ transfer_call = ToolCall(
202
+ type="function",
203
+ id="transfer_001",
204
+ function=ToolCallFunction(
205
+ name="transfer_to_agent",
206
+ arguments=json.dumps({"name": "WeatherAgent"}),
207
+ ),
208
+ index=0,
209
+ )
210
+
211
+ # Handle transfer and collect events
212
+ chunks = []
213
+ async for chunk in runner._handle_tool_calls([transfer_call], ["function_call_output"]):
214
+ chunks.append(chunk)
215
+
216
+ print(f"Events generated: {len(chunks)}")
217
+ for i, chunk in enumerate(chunks):
218
+ print(f" Event {i + 1}: {chunk.type} - {chunk.content}")
219
+
220
+ print(f"Agent after transfer: {runner.agent.name}")
221
+
222
+ print("\n2. Testing transfer_to_parent with function_call_output events:")
223
+
224
+ parent_transfer_call = ToolCall(
225
+ type="function",
226
+ id="transfer_002",
227
+ function=ToolCallFunction(
228
+ name="transfer_to_parent",
229
+ arguments="{}",
230
+ ),
231
+ index=0,
232
+ )
233
+
234
+ # Handle parent transfer and collect events
235
+ chunks = []
236
+ async for chunk in runner._handle_tool_calls([parent_transfer_call], ["function_call_output"]):
237
+ chunks.append(chunk)
238
+
239
+ print(f"Events generated: {len(chunks)}")
240
+ for i, chunk in enumerate(chunks):
241
+ print(f" Event {i + 1}: {chunk.type} - {chunk.content}")
242
+
243
+ print(f"Agent after parent transfer: {runner.agent.name}")
244
+
245
+ print("\n3. Testing multiple transfers (only first executes):")
246
+
247
+ # Multiple transfer calls
248
+ transfer_call_1 = ToolCall(
249
+ type="function",
250
+ id="multi_001",
251
+ function=ToolCallFunction(
252
+ name="transfer_to_agent",
253
+ arguments=json.dumps({"name": "TravelAgent"}),
254
+ ),
255
+ index=0,
256
+ )
257
+
258
+ transfer_call_2 = ToolCall(
259
+ type="function",
260
+ id="multi_002",
261
+ function=ToolCallFunction(
262
+ name="transfer_to_agent",
263
+ arguments=json.dumps({"name": "WeatherAgent"}),
264
+ ),
265
+ index=1,
266
+ )
267
+
268
+ chunks = []
269
+ async for chunk in runner._handle_tool_calls([transfer_call_1, transfer_call_2], ["function_call_output"]):
270
+ chunks.append(chunk)
271
+
272
+ print(f"Events generated: {len(chunks)}")
273
+ for i, chunk in enumerate(chunks):
274
+ print(f" Event {i + 1}: {chunk.content}")
275
+
276
+ print(f"Final agent: {runner.agent.name}")
277
+
278
+
279
+ async def main():
280
+ """Run both demos"""
281
+ print("🚀 Demo: New LiteAgent Features\n")
282
+
283
+ # Demo 1: Cancellation of pending tools
284
+ await demo_1_cancel_pending_tools()
285
+
286
+ # Demo 2: Transfer events
287
+ await demo_2_transfer_events()
288
+
289
+ print("\n✅ All demos completed!")
290
+ print("\nKey takeaways:")
291
+ print("1. Pending tool calls are automatically cancelled when user provides new input")
292
+ print("2. Cancellation now generates function_call_output events (not just history records)")
293
+ print("3. Transfer operations generate function_call_output events like regular tools")
294
+ print("4. Multiple transfers in one call: only the first executes, others get 'already executed' message")
295
+ print("5. Both cancellation and transfer events are yielded through the run() method")
296
+
297
+
298
+ if __name__ == "__main__":
299
+ asyncio.run(main())
@@ -48,7 +48,7 @@ async def main():
48
48
  )
49
49
  async for chunk in resp:
50
50
  logger.info(chunk)
51
- resp = runner.run_continue_stream(
51
+ resp = runner.run(
52
52
  includes=["usage", "assistant_message", "function_call", "function_call_output"],
53
53
  record_to="tests/mocks/confirm_and_continue/2.jsonl",
54
54
  )
@@ -0,0 +1,57 @@
1
+ """
2
+ Debug example to investigate non-streaming mode issues.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+
8
+ from lite_agent import Agent, Runner
9
+
10
+ # Enable debug logging
11
+ logging.basicConfig(level=logging.DEBUG)
12
+ logger = logging.getLogger("lite_agent")
13
+ logger.setLevel(logging.DEBUG)
14
+
15
+
16
+ async def main():
17
+ # Create an agent
18
+ agent = Agent(
19
+ name="DebugAgent",
20
+ model="gpt-4o-mini",
21
+ instructions="You are a helpful assistant.",
22
+ )
23
+
24
+ print("=== Debug Non-Streaming Mode ===")
25
+
26
+ # Test with streaming=False
27
+ runner = Runner(agent, streaming=False)
28
+
29
+ print("Running in non-streaming mode...")
30
+ chunks = []
31
+
32
+ async for chunk in runner.run("Hello, please say hi back."):
33
+ print(f"Received chunk: {chunk}")
34
+ print(f"Chunk type: {chunk.type}")
35
+ if hasattr(chunk, "message"):
36
+ print(f"Chunk message: {chunk.message}")
37
+ if hasattr(chunk, "content"):
38
+ print(f"Chunk content: {chunk.content}")
39
+ chunks.append(chunk)
40
+
41
+ print(f"\nTotal chunks received: {len(chunks)}")
42
+
43
+ # Compare with streaming mode
44
+ print("\n=== Compare with Streaming Mode ===")
45
+ runner_streaming = Runner(agent, streaming=True)
46
+
47
+ streaming_chunks = []
48
+ async for chunk in runner_streaming.run("Hello, please say hi back too."):
49
+ streaming_chunks.append(chunk)
50
+ if chunk.type == "content_delta":
51
+ print(chunk.delta, end="", flush=True)
52
+
53
+ print(f"\nStreaming chunks received: {len(streaming_chunks)}")
54
+
55
+
56
+ if __name__ == "__main__":
57
+ asyncio.run(main())
@@ -0,0 +1,39 @@
1
+ """
2
+ Debug with full logging enabled.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+
8
+ from lite_agent import Agent, Runner
9
+
10
+ # Configure logging
11
+ logging.basicConfig(
12
+ level=logging.DEBUG,
13
+ format="%(name)s - %(levelname)s - %(message)s",
14
+ )
15
+
16
+ # Enable specific loggers
17
+ logging.getLogger("lite_agent").setLevel(logging.DEBUG)
18
+
19
+
20
+ async def main():
21
+ agent = Agent(
22
+ name="TestAgent",
23
+ model="gpt-4o-mini",
24
+ instructions="You are helpful.",
25
+ )
26
+
27
+ print("=== Testing Non-Streaming ===")
28
+ runner = Runner(agent, streaming=False)
29
+
30
+ chunks = []
31
+ async for chunk in runner.run("Hello"):
32
+ chunks.append(chunk)
33
+ print(f"Got chunk: {chunk.type}")
34
+
35
+ print(f"Total chunks: {len(chunks)}")
36
+
37
+
38
+ if __name__ == "__main__":
39
+ asyncio.run(main())
@@ -70,7 +70,7 @@ async def main():
70
70
  )
71
71
  async for message in resp:
72
72
  logger.info(message)
73
- print(f"{runner.get_messages_dict()}")
73
+ print(f"{runner.get_messages()}")
74
74
  logger.info(runner.agent.name)
75
75
 
76
76
 
@@ -0,0 +1,90 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ from rich.logging import RichHandler
5
+
6
+ from lite_agent.agent import Agent
7
+ from lite_agent.chat_display import display_messages
8
+ from lite_agent.client import LiteLLMClient, LLMConfig
9
+ from lite_agent.runner import Runner
10
+
11
+ logging.basicConfig(
12
+ level=logging.WARNING,
13
+ format="%(message)s",
14
+ datefmt="[%X]",
15
+ handlers=[RichHandler(rich_tracebacks=True)],
16
+ )
17
+
18
+ logger = logging.getLogger("lite_agent")
19
+ logger.setLevel(logging.DEBUG)
20
+
21
+
22
+ async def get_weather(city: str) -> str:
23
+ """Get the current weather for a city."""
24
+ return f"The weather in {city} is sunny, 25°C."
25
+
26
+
27
+ # Method 1: Using individual parameters
28
+ agent1 = Agent(
29
+ model=LiteLLMClient(
30
+ model="gpt-4o-mini",
31
+ temperature=0.3,
32
+ max_tokens=100,
33
+ top_p=0.8,
34
+ frequency_penalty=0.1,
35
+ presence_penalty=0.1,
36
+ stop=["END"],
37
+ ),
38
+ name="Weather Bot (Individual Params)",
39
+ instructions="You are a weather assistant. Keep responses brief and factual.",
40
+ tools=[get_weather],
41
+ )
42
+
43
+ # Method 2: Using LLMConfig object
44
+ llm_config = LLMConfig(
45
+ temperature=0.8,
46
+ max_tokens=200,
47
+ top_p=0.9,
48
+ frequency_penalty=0.0,
49
+ presence_penalty=0.0,
50
+ )
51
+
52
+ agent2 = Agent(
53
+ model=LiteLLMClient(
54
+ model="gpt-4o-mini",
55
+ llm_config=llm_config,
56
+ ),
57
+ name="Weather Bot (LLMConfig)",
58
+ instructions="You are a creative weather assistant. Add some personality to your responses.",
59
+ tools=[get_weather],
60
+ )
61
+
62
+
63
+ async def main():
64
+ # Test agent with conservative settings (low temperature, short responses)
65
+ print("=== Testing Agent 1 (Conservative Settings) ===")
66
+ runner1 = Runner(agent1)
67
+ resp1 = runner1.run(
68
+ "What's the weather like in Tokyo?",
69
+ includes=["assistant_message"],
70
+ )
71
+ async for chunk in resp1:
72
+ logger.info(chunk)
73
+ display_messages(runner1.messages)
74
+
75
+ print("\n" + "=" * 50 + "\n")
76
+
77
+ # Test agent with creative settings (high temperature, longer responses)
78
+ print("=== Testing Agent 2 (Creative Settings) ===")
79
+ runner2 = Runner(agent2)
80
+ resp2 = runner2.run(
81
+ "What's the weather like in Tokyo?",
82
+ includes=["assistant_message"],
83
+ )
84
+ async for chunk in resp2:
85
+ logger.info(chunk)
86
+ display_messages(runner2.messages)
87
+
88
+
89
+ if __name__ == "__main__":
90
+ asyncio.run(main())