tinyagent-py 0.0.5__py3-none-any.whl → 0.0.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,7 +1,5 @@
1
1
  from .base import Storage
2
- from .json_file_storage import JsonFileStorage
2
+
3
3
  # Import your concrete backends here if you like:
4
4
  # from .postgres_storage import PostgresStorage
5
- # from .json_file_storage import JsonFileStorage
6
-
7
- __all__ = ["Storage", "JsonFileStorage"]
5
+ # from .json_file_storage import JsonFileStorage
@@ -1,6 +1,7 @@
1
1
  import aiosqlite
2
2
  import json
3
3
  import os
4
+ import logging
4
5
  from typing import Optional, Dict, Any
5
6
  from tinyagent.storage import Storage
6
7
 
@@ -13,6 +14,7 @@ class SqliteStorage(Storage):
13
14
  self._db_path = db_path
14
15
  self._table = table_name
15
16
  self._conn: Optional[aiosqlite.Connection] = None
17
+ self.logger = logging.getLogger(__name__)
16
18
 
17
19
  # Ensure the directory exists
18
20
  os.makedirs(os.path.dirname(os.path.abspath(db_path)), exist_ok=True)
@@ -46,7 +48,8 @@ class SqliteStorage(Storage):
46
48
 
47
49
  async def save_session(self, session_id: str, data: Dict[str, Any], user_id: Optional[str] = None):
48
50
  await self._connect()
49
- print(f"Saving session {session_id} for user {user_id} to sqlite {data}")
51
+ self.logger.info(f"[sqlite] Saving session {session_id} for user {user_id}")
52
+ self.logger.debug(f"[sqlite] Save data: {json.dumps(data)[:200]}...")
50
53
  # Extract data following the TinyAgent schema
51
54
  metadata = data.get("metadata", {}) or {}
52
55
  session_state = data.get("session_state", {}) or {}
tinyagent/tiny_agent.py CHANGED
@@ -186,8 +186,7 @@ class TinyAgent:
186
186
  self.temperature = temperature
187
187
  if model in ["o1", "o1-preview","o3","o4-mini"]:
188
188
  self.temperature = 1
189
- if api_key:
190
- litellm.api_key = api_key
189
+
191
190
 
192
191
  self.model_kwargs = model_kwargs
193
192
  self.encoder = tiktoken.get_encoding("o200k_base")
@@ -560,6 +559,7 @@ class TinyAgent:
560
559
  return str(result)
561
560
  except Exception as e:
562
561
  self.logger.error(f"Error executing custom tool {tool_name}: {str(e)}")
562
+ self.logger.error(f"Error: {traceback.format_exc()}")
563
563
  return f"Error executing tool {tool_name}: {str(e)}"
564
564
 
565
565
  async def run(self, user_input: str, max_turns: int = 10) -> str:
@@ -604,6 +604,7 @@ class TinyAgent:
604
604
 
605
605
  response = await litellm.acompletion(
606
606
  model=self.model,
607
+ api_key=self.api_key,
607
608
  messages=self.messages,
608
609
  tools=all_tools,
609
610
  tool_choice="auto",
@@ -746,13 +747,58 @@ class TinyAgent:
746
747
 
747
748
 
748
749
  async def close(self):
749
- """Clean up *all* MCP clients."""
750
+ """
751
+ Clean up all resources used by the agent including MCP clients and storage.
752
+
753
+ This method should be called when the agent is no longer needed to ensure
754
+ proper resource cleanup, especially in web frameworks like FastAPI.
755
+ """
756
+ cleanup_errors = []
757
+
758
+ # 1. First save any pending state if storage is configured
759
+ if self.storage:
760
+ try:
761
+ self.logger.debug(f"Saving final state before closing (session={self.session_id})")
762
+ await self.save_agent()
763
+ except Exception as e:
764
+ error_msg = f"Error saving final state: {str(e)}"
765
+ self.logger.error(error_msg)
766
+ cleanup_errors.append(error_msg)
767
+
768
+ # 2. Close all MCP clients
750
769
  for client in self.mcp_clients:
751
770
  try:
771
+ self.logger.debug(f"Closing MCP client: {client}")
752
772
  await client.close()
753
- except RuntimeError as e:
754
- self.logger.error(f"Error closing MCP client: {str(e)}")
755
- # Continue closing other clients even if one fails
773
+ except Exception as e:
774
+ error_msg = f"Error closing MCP client: {str(e)}"
775
+ self.logger.error(error_msg)
776
+ cleanup_errors.append(error_msg)
777
+
778
+ # 3. Close storage connection if available
779
+ if self.storage:
780
+ try:
781
+ self.logger.debug("Closing storage connection")
782
+ await self.storage.close()
783
+ except Exception as e:
784
+ error_msg = f"Error closing storage: {str(e)}"
785
+ self.logger.error(error_msg)
786
+ cleanup_errors.append(error_msg)
787
+
788
+ # 4. Run any cleanup callbacks
789
+ try:
790
+ await self._run_callbacks("agent_cleanup")
791
+ except Exception as e:
792
+ error_msg = f"Error in cleanup callbacks: {str(e)}"
793
+ self.logger.error(error_msg)
794
+ cleanup_errors.append(error_msg)
795
+
796
+ # Log summary of cleanup
797
+ if cleanup_errors:
798
+ self.logger.warning(f"TinyAgent cleanup completed with {len(cleanup_errors)} errors")
799
+ else:
800
+ self.logger.info(f"TinyAgent cleanup completed successfully (session={self.session_id})")
801
+
756
802
  def clear_conversation(self):
757
803
  """
758
804
  Clear the conversation history, preserving only the initial system prompt.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinyagent-py
3
- Version: 0.0.5
4
- Summary: Tiny Agent with MCP Client
3
+ Version: 0.0.7
4
+ Summary: Tiny Agent with MCP Client and Extendable Hooks, Tiny but powerful
5
5
  Author-email: Mahdi Golchin <golchin@askdev.ai>
6
6
  Project-URL: Homepage, https://github.com/askbudi/tinyagent
7
7
  Project-URL: Bug Tracker, https://github.com/askbudi/tinyagent/issues
@@ -0,0 +1,20 @@
1
+ tinyagent/__init__.py,sha256=GrD21npMQGzl9ZYKYTP8VxHLzCfJCvA0oTKQZTkmnCw,117
2
+ tinyagent/mcp_client.py,sha256=9dmLtJ8CTwKWKTH6K9z8CaCQuaicOH9ifAuNyX7Kdo0,6030
3
+ tinyagent/memory_manager.py,sha256=tAaZZdxBJ235wJIyr04n3f2Damok4s2UXunTtur_p-4,44916
4
+ tinyagent/tiny_agent.py,sha256=_qHQc2yroSI1Ihn5Ex3FH_ml7aaSNC9-yR540C16Cxs,41125
5
+ tinyagent/hooks/__init__.py,sha256=UztCHjoqF5JyDolbWwkBsBZkWguDQg23l2GD_zMHt-s,178
6
+ tinyagent/hooks/gradio_callback.py,sha256=jGsZlObAd6I5lN9cE53dDL_LfiB8I0tBsicuHwwmL-M,44833
7
+ tinyagent/hooks/logging_manager.py,sha256=UpdmpQ7HRPyer-jrmQSXcBwi409tV9LnGvXSHjTcYTI,7935
8
+ tinyagent/hooks/rich_code_ui_callback.py,sha256=PLcu5MOSoP4oZR3BtvcV9DquxcIT_d0WzSlkvaDcGOk,19820
9
+ tinyagent/hooks/rich_ui_callback.py,sha256=5iCNOiJmhc1lOL7ZjaOt5Sk3rompko4zu_pAxfTVgJQ,22897
10
+ tinyagent/storage/__init__.py,sha256=7qwfdD4smCl891xaRuiReSUgfOJFy7jJZsN0ul1iQdY,173
11
+ tinyagent/storage/base.py,sha256=GGAMvOoslmm1INLFG_jtwOkRk2Qg39QXx-1LnN7fxDI,1474
12
+ tinyagent/storage/json_file_storage.py,sha256=SYD8lvTHu2-FEHm1tZmsrcgEOirBrlUsUM186X-UPgI,1114
13
+ tinyagent/storage/postgres_storage.py,sha256=IGwan8UXHNnTZFK1F8x4kvMDex3GAAGWUg9ePx_5IF4,9018
14
+ tinyagent/storage/redis_storage.py,sha256=hu3y7wHi49HkpiR-AW7cWVQuTVOUk1WaB8TEPGUKVJ8,1742
15
+ tinyagent/storage/sqlite_storage.py,sha256=ZyOYe0d_oHO1wOIT8FxKIbc67tP_0e_8FnM2Zq8Pwj8,5915
16
+ tinyagent_py-0.0.7.dist-info/licenses/LICENSE,sha256=YIogcVQnknaaE4K-oaQylFWo8JGRBWnwmGb3fWB_Pww,1064
17
+ tinyagent_py-0.0.7.dist-info/METADATA,sha256=C1qNkWSjwmrsaYVXkEyMrvsg-57nCZsIMmsUaZ4OOpo,9094
18
+ tinyagent_py-0.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ tinyagent_py-0.0.7.dist-info/top_level.txt,sha256=Ny8aJNchZpc2Vvhp3306L5vjceJakvFxBk-UjjVeA_I,10
20
+ tinyagent_py-0.0.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,128 +0,0 @@
1
- try:
2
- import agno
3
- from agno.storage.postgres import PostgresStorage
4
- from agno.storage.sqlite import SqliteStorage
5
- from agno.storage.session.agent import AgentSession
6
- except ImportError as e:
7
- raise ImportError("agno is not installed. Please install it with `pip install agno`.", e)
8
-
9
- import asyncio
10
- from typing import Optional
11
- from agno.storage.postgres import PostgresStorage
12
- from agno.storage.sqlite import SqliteStorage
13
- from agno.storage.session.agent import AgentSession
14
-
15
- class PostgresStorageHook:
16
- def __init__(
17
- self,
18
- table_name: str,
19
- db_url: Optional[str] = None,
20
- db_engine=None,
21
- schema: Optional[str] = "ai",
22
- schema_version: int = 1,
23
- auto_upgrade_schema: bool = True,
24
- mode: str = "agent",
25
- ):
26
- self.storage = PostgresStorage(
27
- table_name=table_name,
28
- db_url=db_url,
29
- db_engine=db_engine,
30
- schema=schema,
31
- schema_version=schema_version,
32
- auto_upgrade_schema=auto_upgrade_schema,
33
- mode=mode,
34
- )
35
-
36
- async def __call__(self, event_name: str, agent, **kwargs):
37
- if event_name == "agent_start":
38
- # Load session from storage
39
- session_id = getattr(agent, "session_id", None)
40
- user_id = getattr(agent, "user_id", None)
41
- if session_id:
42
- session = self.storage.read(session_id=session_id, user_id=user_id)
43
- if session:
44
- # Populate agent state from session
45
- agent.messages = session.session_data.get("messages", [])
46
- agent.memory = session.memory
47
- agent.metadata = session.extra_data
48
- # You may need to adapt this depending on tinyagent's state structure
49
-
50
- elif event_name in ("llm_end", "agent_end"):
51
- # Save session to storage
52
- session_id = getattr(agent, "session_id", None)
53
- user_id = getattr(agent, "user_id", None)
54
- if session_id:
55
- # Create AgentSession from agent state
56
- session_data = {
57
- "messages": getattr(agent, "messages", []),
58
- }
59
- session = AgentSession(
60
- session_id=session_id,
61
- user_id=user_id,
62
- memory=getattr(agent, "memory", {}),
63
- session_data=session_data,
64
- extra_data=getattr(agent, "metadata", {}),
65
- agent_id=getattr(agent, "agent_id", None),
66
- team_session_id=None,
67
- agent_data=None,
68
- )
69
- await asyncio.to_thread(self.storage.upsert, session)
70
-
71
- class SqliteStorageHook:
72
- def __init__(
73
- self,
74
- table_name: str,
75
- db_url: Optional[str] = None,
76
- db_file: Optional[str] = None,
77
- db_engine=None,
78
- schema_version: int = 1,
79
- auto_upgrade_schema: bool = True,
80
- mode: str = "agent",
81
- ):
82
- self.storage = SqliteStorage(
83
- table_name=table_name,
84
- db_url=db_url,
85
- db_file=db_file,
86
- db_engine=db_engine,
87
- schema_version=schema_version,
88
- auto_upgrade_schema=auto_upgrade_schema,
89
- mode=mode,
90
- )
91
-
92
- async def __call__(self, event_name: str, agent, **kwargs):
93
- if event_name == "agent_start":
94
- # Load session from storage
95
- session_id = getattr(agent, "session_id", None)
96
- user_id = getattr(agent, "user_id", None)
97
- print("Session ID",session_id)
98
- print("User ID",user_id)
99
- if session_id:
100
- session = self.storage.read(session_id=session_id, user_id=user_id)
101
- print(f"Session: {session}")
102
- if session:
103
- # Populate agent state from session
104
- agent.messages = session.memory.get("messages", [])
105
- agent.memory = session.memory
106
- agent.metadata = session.extra_data
107
-
108
- elif event_name in ("llm_end", "agent_end"):
109
- # Save session to storage
110
- print("Agent metadata",getattr(agent, "metadata", {}))
111
- session_id = getattr(agent, "session_id", None)
112
- user_id = getattr(agent, "user_id", None)
113
- if session_id:
114
- session_data = {
115
- "messages": getattr(agent, "messages", []),
116
- }
117
- session = AgentSession(
118
- session_id=session_id,
119
- user_id=user_id,
120
- memory=getattr(agent, "memory", {}),
121
- session_data=session_data,
122
- extra_data=getattr(agent, "metadata", {}),
123
- agent_id=getattr(agent, "agent_id", None),
124
- team_session_id=None,
125
- agent_data=None,
126
- )
127
- await asyncio.to_thread(self.storage.upsert, session)
128
-
@@ -1,114 +0,0 @@
1
- import asyncio
2
- from typing import Dict, Any, Optional
3
- try:
4
- from tinyagent.storage.base import Storage
5
- from agno.storage.postgres import PostgresStorage as AgnoPG
6
- from agno.storage.sqlite import SqliteStorage as AgnoSL
7
- from agno.storage.session.agent import AgentSession
8
- except ImportError as e:
9
- raise ImportError("agno is not installed. Please install it with `pip install agno`.", e)
10
-
11
- def _remap_agno_to_tiny(ag: Dict[str, Any]) -> Dict[str, Any]:
12
- """Map a full Agno to_dict() into TinyAgent’s to_dict() shape."""
13
- sess_id = ag.get("session_id", "")
14
-
15
- # meta = everything except the session_state fields
16
- metadata = {
17
- k: v
18
- for k, v in ag.items()
19
- if k not in ("session_id", "session_data", "memory", "runs")
20
- }
21
-
22
- # Safe‐guard: use {} if any of these are None
23
- _session_data = ag.get("session_data") or {}
24
- _memory = ag.get("memory") or {}
25
-
26
-
27
- session_state = {
28
- "messages": _memory.get("messages", []),
29
- "memory": _memory,
30
- **_session_data,
31
- }
32
-
33
- return {
34
- "session_id": sess_id,
35
- "metadata": metadata,
36
- "session_state": session_state,
37
- }
38
-
39
- def _remap_tiny_to_agno(tiny: Dict[str, Any]) -> Dict[str, Any]:
40
- """
41
- Given TinyAgent.to_dict() output:
42
- { "session_id": str,
43
- "metadata": {...},
44
- "session_state": { "messages": [...], "memory": {...}, "runs": [...] }
45
- }
46
- produce a full AgnoSession.to_dict() shape:
47
- { "session_id":..., "user_id":..., "memory":..., "runs":...,
48
- "session_data": {"messages": [...]},
49
- "extra_data":...,
50
- ... (other Agno fields remain None/default) }
51
- """
52
- session_id = tiny["session_id"]
53
- meta = tiny.get("metadata", {}) or {}
54
- state = tiny.get("session_state", {}) or {}
55
-
56
- return {
57
- "session_id": session_id,
58
- "user_id": meta.get("user_id"),
59
- "team_session_id": meta.get("team_session_id"),
60
- "memory": state.get("memory", {}),
61
- "runs": state.get("runs", []),
62
- "session_data": {"messages": state.get("messages", [])},
63
- "extra_data": meta.get("extra_data"),
64
- # created_at/updated_at/agent_id/agent_data will default in AgnoSession
65
- }
66
-
67
-
68
- class AgnoPostgresStorage(Storage):
69
- def __init__(self, table_name: str, db_url: str, schema: str = "ai", mode: str = "agent"):
70
- super().__init__()
71
- self.backend = AgnoPG(table_name=table_name, schema=schema, db_url=db_url, mode=mode)
72
- self.backend.create()
73
-
74
- async def save_session(self, session_id: str, data: Dict[str, Any], user_id: Optional[str] = None) -> None:
75
- # Pack TinyAgent dict into AgnoSession record
76
- agno_dict = _remap_tiny_to_agno(data)
77
- session_obj = AgentSession.from_dict(agno_dict)
78
- loop = asyncio.get_event_loop()
79
- await loop.run_in_executor(None, self.backend.upsert, session_obj)
80
-
81
- async def load_session(self, session_id: str, user_id: Optional[str] = None) -> Dict[str, Any]:
82
- loop = asyncio.get_event_loop()
83
- agno_obj = await loop.run_in_executor(None, self.backend.read, session_id, user_id)
84
- if not agno_obj:
85
- return {}
86
- ag = agno_obj.to_dict()
87
- return _remap_agno_to_tiny(ag)
88
-
89
- async def close(self) -> None:
90
- pass
91
-
92
-
93
- class AgnoSqliteStorage(Storage):
94
- def __init__(self, table_name: str, db_url: str, mode: str = "agent"):
95
- super().__init__()
96
- self.backend = AgnoSL(table_name=table_name, db_url=db_url, mode=mode)
97
- self.backend.create()
98
-
99
- async def save_session(self, session_id: str, data: Dict[str, Any], user_id: Optional[str] = None) -> None:
100
- agno_dict = _remap_tiny_to_agno(data)
101
- session_obj = AgentSession.from_dict(agno_dict)
102
- loop = asyncio.get_event_loop()
103
- await loop.run_in_executor(None, self.backend.upsert, session_obj)
104
-
105
- async def load_session(self, session_id: str, user_id: Optional[str] = None) -> Dict[str, Any]:
106
- loop = asyncio.get_event_loop()
107
- agno_obj = await loop.run_in_executor(None, self.backend.read, session_id, user_id)
108
- if not agno_obj:
109
- return {}
110
- ag = agno_obj.to_dict()
111
- return _remap_agno_to_tiny(ag)
112
-
113
- async def close(self) -> None:
114
- pass
@@ -1,20 +0,0 @@
1
- tinyagent/__init__.py,sha256=GrD21npMQGzl9ZYKYTP8VxHLzCfJCvA0oTKQZTkmnCw,117
2
- tinyagent/mcp_client.py,sha256=ZyhIWFQvOveNVhMKkMkUMYdbqRSuqjK_m-ApP2llFVE,6215
3
- tinyagent/tiny_agent.py,sha256=89OLYj95VUW7uoHpkWXZjmxsQISUMjHffUmDT-3-Nd4,39250
4
- tinyagent/hooks/__init__.py,sha256=UztCHjoqF5JyDolbWwkBsBZkWguDQg23l2GD_zMHt-s,178
5
- tinyagent/hooks/agno_storage_hook.py,sha256=5qvvjmtraanPa-A46Zstrqq3s1e-sC7Ly0o3zifuw_4,5003
6
- tinyagent/hooks/gradio_callback.py,sha256=jGsZlObAd6I5lN9cE53dDL_LfiB8I0tBsicuHwwmL-M,44833
7
- tinyagent/hooks/logging_manager.py,sha256=UpdmpQ7HRPyer-jrmQSXcBwi409tV9LnGvXSHjTcYTI,7935
8
- tinyagent/hooks/rich_ui_callback.py,sha256=5iCNOiJmhc1lOL7ZjaOt5Sk3rompko4zu_pAxfTVgJQ,22897
9
- tinyagent/storage/__init__.py,sha256=NebvYxwEGJtvPnRO9dGa-bgOwA7cPkLjFHnMWDxMg5I,261
10
- tinyagent/storage/agno_storage.py,sha256=ol4qwdH-9jYjBjDvsYkHh7I-vu8uHArPtQylUpoEaCc,4322
11
- tinyagent/storage/base.py,sha256=GGAMvOoslmm1INLFG_jtwOkRk2Qg39QXx-1LnN7fxDI,1474
12
- tinyagent/storage/json_file_storage.py,sha256=SYD8lvTHu2-FEHm1tZmsrcgEOirBrlUsUM186X-UPgI,1114
13
- tinyagent/storage/postgres_storage.py,sha256=IGwan8UXHNnTZFK1F8x4kvMDex3GAAGWUg9ePx_5IF4,9018
14
- tinyagent/storage/redis_storage.py,sha256=hu3y7wHi49HkpiR-AW7cWVQuTVOUk1WaB8TEPGUKVJ8,1742
15
- tinyagent/storage/sqlite_storage.py,sha256=7lk1XZpr2t4s2bjVr9-AqrI74w4hwkuK3taWtyJZhBc,5769
16
- tinyagent_py-0.0.5.dist-info/licenses/LICENSE,sha256=YIogcVQnknaaE4K-oaQylFWo8JGRBWnwmGb3fWB_Pww,1064
17
- tinyagent_py-0.0.5.dist-info/METADATA,sha256=EW9xVFf0vYbe1DQYxQAlNgOXUzuP9g2pqc__yx4V8NQ,9054
18
- tinyagent_py-0.0.5.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
19
- tinyagent_py-0.0.5.dist-info/top_level.txt,sha256=Ny8aJNchZpc2Vvhp3306L5vjceJakvFxBk-UjjVeA_I,10
20
- tinyagent_py-0.0.5.dist-info/RECORD,,