cloudbase-agent-core 0.1.1__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.
- cloudbase_agent/__init__.py +69 -0
- cloudbase_agent/__init__.pyi +12 -0
- cloudbase_agent/base_agent.py +743 -0
- cloudbase_agent/schemas/__init__.py +58 -0
- cloudbase_agent/schemas/core.py +35 -0
- cloudbase_agent/schemas/server.py +113 -0
- cloudbase_agent/schemas/storage.py +66 -0
- cloudbase_agent/schemas/streaming.py +39 -0
- cloudbase_agent/schemas/tools.py +59 -0
- cloudbase_agent/schemas/types.py +14 -0
- cloudbase_agent_core-0.1.1.dist-info/METADATA +28 -0
- cloudbase_agent_core-0.1.1.dist-info/RECORD +13 -0
- cloudbase_agent_core-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Agents Package.
|
|
2
|
+
|
|
3
|
+
This package provides agent creation utilities for the Cloudbase Agent Python SDK.
|
|
4
|
+
It includes base agent classes and framework-specific implementations with
|
|
5
|
+
lazy loading support for optional dependencies.
|
|
6
|
+
|
|
7
|
+
Framework Support:
|
|
8
|
+
- LangGraph: Install with `pip install cloudbase_agent_py[langgraph]`
|
|
9
|
+
- CrewAI: Install with `pip install cloudbase_agent_py[crewai]`
|
|
10
|
+
- All frameworks: Install with `pip install cloudbase_agent_py[all]`
|
|
11
|
+
|
|
12
|
+
Available Classes:
|
|
13
|
+
- BaseAgent: Abstract base class for all Cloudbase Agent agents
|
|
14
|
+
- AgentCallback: Protocol for event callbacks
|
|
15
|
+
- ToolProxy: Protocol for tool call interception
|
|
16
|
+
- ToolCallResult: Result of tool proxy interception
|
|
17
|
+
- LangGraphAgent: LangGraph-based agent implementation (lazy-loaded)
|
|
18
|
+
- CrewAIAgent: CrewAI-based agent implementation (lazy-loaded)
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
Using LangGraph::
|
|
22
|
+
|
|
23
|
+
# Install: pip install cloudbase_agent_py[langgraph]
|
|
24
|
+
from cloudbase_agent.agents import LangGraphAgent
|
|
25
|
+
from langgraph.graph import StateGraph
|
|
26
|
+
|
|
27
|
+
workflow = StateGraph(State)
|
|
28
|
+
compiled_graph = workflow.compile()
|
|
29
|
+
agent = LangGraphAgent("MyAgent", "Description", compiled_graph)
|
|
30
|
+
|
|
31
|
+
Using CrewAI::
|
|
32
|
+
|
|
33
|
+
# Install: pip install cloudbase_agent_py[crewai]
|
|
34
|
+
from cloudbase_agent.agents import CrewAIAgent
|
|
35
|
+
from crewai.flow.flow import Flow
|
|
36
|
+
|
|
37
|
+
class MyFlow(Flow):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
flow = MyFlow()
|
|
41
|
+
agent = CrewAIAgent("MyAgent", "Description", flow)
|
|
42
|
+
|
|
43
|
+
Note:
|
|
44
|
+
Framework-specific agents are lazy-loaded. If you try to import an agent
|
|
45
|
+
without installing its dependencies, you'll get a helpful error message
|
|
46
|
+
indicating which package to install.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
import lazy_loader as lazy
|
|
50
|
+
|
|
51
|
+
# Eager imports (always available, no optional dependencies)
|
|
52
|
+
from .base_agent import AgentCallback, BaseAgent, ToolCallResult, ToolProxy
|
|
53
|
+
|
|
54
|
+
# Lazy imports setup (framework-specific agents loaded on demand)
|
|
55
|
+
__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
|
|
56
|
+
|
|
57
|
+
# Ensure base classes and lazy-loaded agents are in __all__
|
|
58
|
+
__all__ = [
|
|
59
|
+
"BaseAgent",
|
|
60
|
+
"AgentCallback",
|
|
61
|
+
"ToolProxy",
|
|
62
|
+
"ToolCallResult",
|
|
63
|
+
"LangGraphAgent", # Lazy-loaded when accessed
|
|
64
|
+
"CrewAIAgent", # Lazy-loaded when accessed
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Enable namespace package support to allow other cloudbase_agent subpackages
|
|
68
|
+
# (like cloudbase_agent.server, cloudbase_agent.storage) to be imported
|
|
69
|
+
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Type stubs for cloudbase_agent core package.
|
|
2
|
+
|
|
3
|
+
This stub file provides type hints for IDE and static analysis tools.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# Eager imports (always available)
|
|
7
|
+
from .base_agent import AgentCallback as AgentCallback
|
|
8
|
+
from .base_agent import BaseAgent as BaseAgent
|
|
9
|
+
from .base_agent import ToolCallResult as ToolCallResult
|
|
10
|
+
from .base_agent import ToolProxy as ToolProxy
|
|
11
|
+
|
|
12
|
+
__all__: list[str]
|
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Base Agent Class.
|
|
4
|
+
|
|
5
|
+
This module provides the abstract base class for all Cloudbase Agent agents.
|
|
6
|
+
It defines the common interface and functionality that all agent implementations
|
|
7
|
+
must provide, similar to the TypeScript Agent class.
|
|
8
|
+
|
|
9
|
+
Enhanced with optional callback support, context management, and event processing utilities.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from contextlib import contextmanager
|
|
15
|
+
from contextvars import ContextVar
|
|
16
|
+
from typing import Any, AsyncGenerator, Dict, List, Optional, Protocol
|
|
17
|
+
|
|
18
|
+
from ag_ui.core import BaseEvent, EventType, RunAgentInput
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Callback Protocol
|
|
22
|
+
class AgentCallback(Protocol):
|
|
23
|
+
"""Agent callback protocol for event processing.
|
|
24
|
+
|
|
25
|
+
Callbacks can be used to intercept and process events during agent execution,
|
|
26
|
+
enabling features like logging, monitoring, state mutation, and custom event handling.
|
|
27
|
+
|
|
28
|
+
All callback methods are optional - implement only the ones you need.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
async def on_text_message_content(self, event: BaseEvent, buffer: str) -> Optional[Dict[str, Any]]:
|
|
32
|
+
"""Called when text message content is received.
|
|
33
|
+
|
|
34
|
+
:param event: The text message content event
|
|
35
|
+
:type event: BaseEvent
|
|
36
|
+
:param buffer: Accumulated text buffer for this message
|
|
37
|
+
:type buffer: str
|
|
38
|
+
:return: Optional mutation dict to modify agent state
|
|
39
|
+
:rtype: Optional[Dict[str, Any]]
|
|
40
|
+
"""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
async def on_tool_call_args(
|
|
44
|
+
self, event: BaseEvent, buffer: str, partial_args: Dict[str, Any]
|
|
45
|
+
) -> Optional[Dict[str, Any]]:
|
|
46
|
+
"""Called when tool call arguments are received.
|
|
47
|
+
|
|
48
|
+
:param event: The tool call args event
|
|
49
|
+
:type event: BaseEvent
|
|
50
|
+
:param buffer: Raw accumulated argument string
|
|
51
|
+
:type buffer: str
|
|
52
|
+
:param partial_args: Partially parsed arguments (if valid JSON)
|
|
53
|
+
:type partial_args: Dict[str, Any]
|
|
54
|
+
:return: Optional mutation dict to modify agent state
|
|
55
|
+
:rtype: Optional[Dict[str, Any]]
|
|
56
|
+
"""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
async def on_run_started(self, event: BaseEvent) -> None:
|
|
60
|
+
"""Called when agent run starts.
|
|
61
|
+
|
|
62
|
+
:param event: The run started event
|
|
63
|
+
:type event: BaseEvent
|
|
64
|
+
"""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
async def on_run_finished(self, event: BaseEvent) -> None:
|
|
68
|
+
"""Called when agent run finishes.
|
|
69
|
+
|
|
70
|
+
:param event: The run finished event
|
|
71
|
+
:type event: BaseEvent
|
|
72
|
+
"""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
async def on_run_error(self, event: BaseEvent) -> None:
|
|
76
|
+
"""Called when agent run encounters an error.
|
|
77
|
+
|
|
78
|
+
:param event: The run error event
|
|
79
|
+
:type event: BaseEvent
|
|
80
|
+
"""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ============ ToolProxy Protocol ============
|
|
85
|
+
class ToolCallResult:
|
|
86
|
+
"""Result of a tool call interception.
|
|
87
|
+
|
|
88
|
+
This class encapsulates the result of a tool proxy's interception,
|
|
89
|
+
allowing proxies to either allow, block, or modify tool calls.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
allowed: bool = True,
|
|
95
|
+
modified_args: Optional[Dict[str, Any]] = None,
|
|
96
|
+
override_result: Optional[Any] = None,
|
|
97
|
+
error_message: Optional[str] = None,
|
|
98
|
+
):
|
|
99
|
+
"""Initialize tool call result.
|
|
100
|
+
|
|
101
|
+
:param allowed: Whether the tool call is allowed to proceed
|
|
102
|
+
:type allowed: bool
|
|
103
|
+
:param modified_args: Modified arguments (if None, use original)
|
|
104
|
+
:type modified_args: Optional[Dict[str, Any]]
|
|
105
|
+
:param override_result: Override result without calling the tool
|
|
106
|
+
:type override_result: Optional[Any]
|
|
107
|
+
:param error_message: Error message if call is blocked
|
|
108
|
+
:type error_message: Optional[str]
|
|
109
|
+
"""
|
|
110
|
+
self.allowed = allowed
|
|
111
|
+
self.modified_args = modified_args
|
|
112
|
+
self.override_result = override_result
|
|
113
|
+
self.error_message = error_message
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class ToolProxy(Protocol):
|
|
117
|
+
"""Tool proxy protocol for intercepting and modifying tool calls.
|
|
118
|
+
|
|
119
|
+
Tool proxies can be used to:
|
|
120
|
+
- Validate and modify tool arguments before execution
|
|
121
|
+
- Implement permission checks and access control
|
|
122
|
+
- Cache tool results
|
|
123
|
+
- Log tool usage
|
|
124
|
+
- Implement human-in-the-loop approval
|
|
125
|
+
- Transform or filter tool results
|
|
126
|
+
|
|
127
|
+
All proxy methods are optional - implement only the ones you need.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
async def before_tool_call(self, tool_name: str, args: Dict[str, Any], context: Dict[str, Any]) -> ToolCallResult:
|
|
131
|
+
"""Called before a tool is invoked.
|
|
132
|
+
|
|
133
|
+
This method can:
|
|
134
|
+
- Validate arguments
|
|
135
|
+
- Modify arguments
|
|
136
|
+
- Block the tool call
|
|
137
|
+
- Return a cached result
|
|
138
|
+
- Request human approval
|
|
139
|
+
|
|
140
|
+
:param tool_name: Name of the tool being called
|
|
141
|
+
:type tool_name: str
|
|
142
|
+
:param args: Tool arguments
|
|
143
|
+
:type args: Dict[str, Any]
|
|
144
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
145
|
+
:type context: Dict[str, Any]
|
|
146
|
+
:return: Result indicating whether to proceed and any modifications
|
|
147
|
+
:rtype: ToolCallResult
|
|
148
|
+
"""
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
async def after_tool_call(
|
|
152
|
+
self,
|
|
153
|
+
tool_name: str,
|
|
154
|
+
args: Dict[str, Any],
|
|
155
|
+
result: Any,
|
|
156
|
+
context: Dict[str, Any],
|
|
157
|
+
) -> Any:
|
|
158
|
+
"""Called after a tool has been invoked.
|
|
159
|
+
|
|
160
|
+
This method can:
|
|
161
|
+
- Transform the result
|
|
162
|
+
- Cache the result
|
|
163
|
+
- Log the result
|
|
164
|
+
- Trigger side effects
|
|
165
|
+
|
|
166
|
+
:param tool_name: Name of the tool that was called
|
|
167
|
+
:type tool_name: str
|
|
168
|
+
:param args: Tool arguments that were used
|
|
169
|
+
:type args: Dict[str, Any]
|
|
170
|
+
:param result: Result returned by the tool
|
|
171
|
+
:type result: Any
|
|
172
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
173
|
+
:type context: Dict[str, Any]
|
|
174
|
+
:return: Potentially modified result
|
|
175
|
+
:rtype: Any
|
|
176
|
+
"""
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
async def on_tool_error(
|
|
180
|
+
self,
|
|
181
|
+
tool_name: str,
|
|
182
|
+
args: Dict[str, Any],
|
|
183
|
+
error: Exception,
|
|
184
|
+
context: Dict[str, Any],
|
|
185
|
+
) -> Optional[Any]:
|
|
186
|
+
"""Called when a tool call raises an error.
|
|
187
|
+
|
|
188
|
+
This method can:
|
|
189
|
+
- Handle the error gracefully
|
|
190
|
+
- Return a fallback result
|
|
191
|
+
- Re-raise the error
|
|
192
|
+
- Log the error
|
|
193
|
+
|
|
194
|
+
:param tool_name: Name of the tool that failed
|
|
195
|
+
:type tool_name: str
|
|
196
|
+
:param args: Tool arguments that were used
|
|
197
|
+
:type args: Dict[str, Any]
|
|
198
|
+
:param error: The exception that was raised
|
|
199
|
+
:type error: Exception
|
|
200
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
201
|
+
:type context: Dict[str, Any]
|
|
202
|
+
:return: Optional fallback result (None to re-raise)
|
|
203
|
+
:rtype: Optional[Any]
|
|
204
|
+
"""
|
|
205
|
+
...
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# Context Management
|
|
209
|
+
_current_agent: ContextVar[Optional["BaseAgent"]] = ContextVar("current_agent", default=None)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class BaseAgent(ABC):
|
|
213
|
+
"""Abstract base class for all Cloudbase Agent agents.
|
|
214
|
+
|
|
215
|
+
This class defines the common interface that all agent implementations
|
|
216
|
+
must follow. It provides the basic structure for agent configuration,
|
|
217
|
+
execution, and event handling.
|
|
218
|
+
|
|
219
|
+
Enhanced with optional features:
|
|
220
|
+
- Callback system for event processing (monitoring, logging)
|
|
221
|
+
- Tool proxy system for tool call interception (validation, control)
|
|
222
|
+
- Context management for accessing current agent
|
|
223
|
+
- Event ID fixing utilities
|
|
224
|
+
- Buffer management for streaming events
|
|
225
|
+
|
|
226
|
+
:param name: Human-readable name for the agent
|
|
227
|
+
:type name: str
|
|
228
|
+
:param description: Detailed description of the agent's purpose and capabilities
|
|
229
|
+
:type description: str
|
|
230
|
+
:param agent: The underlying agent implementation (e.g., LangGraphAgent)
|
|
231
|
+
:type agent: Any
|
|
232
|
+
:param flow: Optional flow/graph object for the agent
|
|
233
|
+
:type flow: Any
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
Creating a custom agent implementation::
|
|
237
|
+
|
|
238
|
+
class MyCustomAgent(BaseAgent):
|
|
239
|
+
def __init__(self, name: str, description: str, custom_config: dict):
|
|
240
|
+
# Initialize with custom agent implementation
|
|
241
|
+
agent = create_custom_agent(custom_config)
|
|
242
|
+
super().__init__(name, description, agent)
|
|
243
|
+
|
|
244
|
+
async def run(
|
|
245
|
+
self,
|
|
246
|
+
run_input: RunAgentInput
|
|
247
|
+
) -> AsyncGenerator[BaseEvent, None]:
|
|
248
|
+
# Implement custom run logic
|
|
249
|
+
pass
|
|
250
|
+
|
|
251
|
+
Using callbacks::
|
|
252
|
+
|
|
253
|
+
class LoggingCallback:
|
|
254
|
+
async def on_text_message_content(self, event, buffer):
|
|
255
|
+
print(f"Message: {buffer}")
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
agent = MyCustomAgent(...)
|
|
259
|
+
agent.add_callback(LoggingCallback())
|
|
260
|
+
|
|
261
|
+
Note:
|
|
262
|
+
All concrete agent implementations must implement the abstract `run` method.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
def __init__(self, name: str, description: str, agent: Any, flow: Any = None):
|
|
266
|
+
"""Initialize the base agent.
|
|
267
|
+
|
|
268
|
+
:param name: Human-readable name for the agent
|
|
269
|
+
:type name: str
|
|
270
|
+
:param description: Detailed description of the agent's purpose and capabilities
|
|
271
|
+
:type description: str
|
|
272
|
+
:param agent: The underlying agent implementation
|
|
273
|
+
:type agent: Any
|
|
274
|
+
:param flow: Optional flow/graph object
|
|
275
|
+
:type flow: Any
|
|
276
|
+
"""
|
|
277
|
+
self._name = name
|
|
278
|
+
self._description = description
|
|
279
|
+
self._agent = agent
|
|
280
|
+
self._flow = flow
|
|
281
|
+
|
|
282
|
+
# Optional callback support
|
|
283
|
+
self._callbacks: List[AgentCallback] = []
|
|
284
|
+
|
|
285
|
+
# Optional tool proxy support
|
|
286
|
+
self._tool_proxies: List[ToolProxy] = []
|
|
287
|
+
|
|
288
|
+
# Optional buffer management for streaming events
|
|
289
|
+
self._text_buffers: Dict[str, str] = {}
|
|
290
|
+
self._tool_call_buffers: Dict[str, Dict[str, Any]] = {}
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def name(self) -> str:
|
|
294
|
+
"""Get the agent name.
|
|
295
|
+
|
|
296
|
+
:return: Agent name
|
|
297
|
+
:rtype: str
|
|
298
|
+
"""
|
|
299
|
+
return self._name
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def description(self) -> str:
|
|
303
|
+
"""Get the agent description.
|
|
304
|
+
|
|
305
|
+
:return: Agent description
|
|
306
|
+
:rtype: str
|
|
307
|
+
"""
|
|
308
|
+
return self._description
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def agent(self) -> Any:
|
|
312
|
+
"""Get the underlying agent implementation.
|
|
313
|
+
|
|
314
|
+
:return: The underlying agent instance
|
|
315
|
+
:rtype: Any
|
|
316
|
+
"""
|
|
317
|
+
return self._agent
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def flow(self) -> Any:
|
|
321
|
+
"""Get the flow/graph object.
|
|
322
|
+
|
|
323
|
+
:return: The flow/graph instance
|
|
324
|
+
:rtype: Any
|
|
325
|
+
"""
|
|
326
|
+
return self._flow
|
|
327
|
+
|
|
328
|
+
# ============ Callback System ============
|
|
329
|
+
|
|
330
|
+
def add_callback(self, callback: AgentCallback) -> None:
|
|
331
|
+
"""Add a callback for event processing.
|
|
332
|
+
|
|
333
|
+
:param callback: Callback instance implementing AgentCallback protocol
|
|
334
|
+
:type callback: AgentCallback
|
|
335
|
+
"""
|
|
336
|
+
self._callbacks.append(callback)
|
|
337
|
+
|
|
338
|
+
def remove_callback(self, callback: AgentCallback) -> None:
|
|
339
|
+
"""Remove a callback.
|
|
340
|
+
|
|
341
|
+
:param callback: Callback instance to remove
|
|
342
|
+
:type callback: AgentCallback
|
|
343
|
+
"""
|
|
344
|
+
if callback in self._callbacks:
|
|
345
|
+
self._callbacks.remove(callback)
|
|
346
|
+
|
|
347
|
+
def clear_callbacks(self) -> None:
|
|
348
|
+
"""Remove all callbacks."""
|
|
349
|
+
self._callbacks.clear()
|
|
350
|
+
|
|
351
|
+
# Tool Proxy Management
|
|
352
|
+
|
|
353
|
+
def add_tool_proxy(self, proxy: ToolProxy) -> None:
|
|
354
|
+
"""Add a tool proxy to intercept tool calls.
|
|
355
|
+
|
|
356
|
+
Tool proxies are invoked in the order they are added.
|
|
357
|
+
Each proxy can modify arguments, block calls, or transform results.
|
|
358
|
+
|
|
359
|
+
:param proxy: Tool proxy instance to add
|
|
360
|
+
:type proxy: ToolProxy
|
|
361
|
+
|
|
362
|
+
Example::
|
|
363
|
+
|
|
364
|
+
class PermissionProxy:
|
|
365
|
+
async def before_tool_call(self, tool_name, args, context):
|
|
366
|
+
if not self.has_permission(tool_name):
|
|
367
|
+
return ToolCallResult(allowed=False, error_message="Permission denied")
|
|
368
|
+
return ToolCallResult(allowed=True)
|
|
369
|
+
|
|
370
|
+
agent.add_tool_proxy(PermissionProxy())
|
|
371
|
+
"""
|
|
372
|
+
self._tool_proxies.append(proxy)
|
|
373
|
+
|
|
374
|
+
def remove_tool_proxy(self, proxy: ToolProxy) -> None:
|
|
375
|
+
"""Remove a tool proxy.
|
|
376
|
+
|
|
377
|
+
:param proxy: Tool proxy instance to remove
|
|
378
|
+
:type proxy: ToolProxy
|
|
379
|
+
"""
|
|
380
|
+
if proxy in self._tool_proxies:
|
|
381
|
+
self._tool_proxies.remove(proxy)
|
|
382
|
+
|
|
383
|
+
def clear_tool_proxies(self) -> None:
|
|
384
|
+
"""Remove all tool proxies."""
|
|
385
|
+
self._tool_proxies.clear()
|
|
386
|
+
|
|
387
|
+
async def _invoke_tool_proxies_before(
|
|
388
|
+
self, tool_name: str, args: Dict[str, Any], context: Dict[str, Any]
|
|
389
|
+
) -> ToolCallResult:
|
|
390
|
+
"""Invoke all tool proxies before a tool call.
|
|
391
|
+
|
|
392
|
+
Proxies are invoked in order. If any proxy blocks the call,
|
|
393
|
+
the chain stops and the blocking result is returned.
|
|
394
|
+
|
|
395
|
+
:param tool_name: Name of the tool being called
|
|
396
|
+
:type tool_name: str
|
|
397
|
+
:param args: Tool arguments
|
|
398
|
+
:type args: Dict[str, Any]
|
|
399
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
400
|
+
:type context: Dict[str, Any]
|
|
401
|
+
:return: Aggregated result from all proxies
|
|
402
|
+
:rtype: ToolCallResult
|
|
403
|
+
"""
|
|
404
|
+
if not self._tool_proxies:
|
|
405
|
+
return ToolCallResult(allowed=True)
|
|
406
|
+
|
|
407
|
+
current_args = args
|
|
408
|
+
for proxy in self._tool_proxies:
|
|
409
|
+
if hasattr(proxy, "before_tool_call"):
|
|
410
|
+
result = await proxy.before_tool_call(tool_name, current_args, context)
|
|
411
|
+
|
|
412
|
+
# If blocked, stop the chain
|
|
413
|
+
if not result.allowed:
|
|
414
|
+
return result
|
|
415
|
+
|
|
416
|
+
# If result has override, stop the chain
|
|
417
|
+
if result.override_result is not None:
|
|
418
|
+
return result
|
|
419
|
+
|
|
420
|
+
# If args were modified, use them for next proxy
|
|
421
|
+
if result.modified_args is not None:
|
|
422
|
+
current_args = result.modified_args
|
|
423
|
+
|
|
424
|
+
# All proxies passed, return final args
|
|
425
|
+
return ToolCallResult(allowed=True, modified_args=current_args)
|
|
426
|
+
|
|
427
|
+
async def _invoke_tool_proxies_after(
|
|
428
|
+
self,
|
|
429
|
+
tool_name: str,
|
|
430
|
+
args: Dict[str, Any],
|
|
431
|
+
result: Any,
|
|
432
|
+
context: Dict[str, Any],
|
|
433
|
+
) -> Any:
|
|
434
|
+
"""Invoke all tool proxies after a tool call.
|
|
435
|
+
|
|
436
|
+
Proxies are invoked in order. Each proxy can transform the result.
|
|
437
|
+
|
|
438
|
+
:param tool_name: Name of the tool that was called
|
|
439
|
+
:type tool_name: str
|
|
440
|
+
:param args: Tool arguments that were used
|
|
441
|
+
:type args: Dict[str, Any]
|
|
442
|
+
:param result: Result returned by the tool
|
|
443
|
+
:type result: Any
|
|
444
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
445
|
+
:type context: Dict[str, Any]
|
|
446
|
+
:return: Potentially transformed result
|
|
447
|
+
:rtype: Any
|
|
448
|
+
"""
|
|
449
|
+
if not self._tool_proxies:
|
|
450
|
+
return result
|
|
451
|
+
|
|
452
|
+
current_result = result
|
|
453
|
+
for proxy in self._tool_proxies:
|
|
454
|
+
if hasattr(proxy, "after_tool_call"):
|
|
455
|
+
current_result = await proxy.after_tool_call(tool_name, args, current_result, context)
|
|
456
|
+
|
|
457
|
+
return current_result
|
|
458
|
+
|
|
459
|
+
async def _invoke_tool_proxies_error(
|
|
460
|
+
self,
|
|
461
|
+
tool_name: str,
|
|
462
|
+
args: Dict[str, Any],
|
|
463
|
+
error: Exception,
|
|
464
|
+
context: Dict[str, Any],
|
|
465
|
+
) -> Optional[Any]:
|
|
466
|
+
"""Invoke all tool proxies when a tool call fails.
|
|
467
|
+
|
|
468
|
+
Proxies are invoked in order. The first proxy that returns a non-None
|
|
469
|
+
result will provide the fallback value.
|
|
470
|
+
|
|
471
|
+
:param tool_name: Name of the tool that failed
|
|
472
|
+
:type tool_name: str
|
|
473
|
+
:param args: Tool arguments that were used
|
|
474
|
+
:type args: Dict[str, Any]
|
|
475
|
+
:param error: The exception that was raised
|
|
476
|
+
:type error: Exception
|
|
477
|
+
:param context: Additional context (thread_id, run_id, etc.)
|
|
478
|
+
:type context: Dict[str, Any]
|
|
479
|
+
:return: Optional fallback result (None to re-raise)
|
|
480
|
+
:rtype: Optional[Any]
|
|
481
|
+
"""
|
|
482
|
+
if not self._tool_proxies:
|
|
483
|
+
return None
|
|
484
|
+
|
|
485
|
+
for proxy in self._tool_proxies:
|
|
486
|
+
if hasattr(proxy, "on_tool_error"):
|
|
487
|
+
fallback = await proxy.on_tool_error(tool_name, args, error, context)
|
|
488
|
+
if fallback is not None:
|
|
489
|
+
return fallback
|
|
490
|
+
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
# ============ Callback Management ============
|
|
494
|
+
|
|
495
|
+
async def _invoke_callbacks(self, event: BaseEvent) -> Optional[Dict[str, Any]]:
|
|
496
|
+
"""Invoke callbacks for an event.
|
|
497
|
+
|
|
498
|
+
This method processes events through registered callbacks and manages
|
|
499
|
+
internal buffers for streaming events.
|
|
500
|
+
|
|
501
|
+
:param event: Event to process
|
|
502
|
+
:type event: BaseEvent
|
|
503
|
+
:return: Aggregated mutations from all callbacks
|
|
504
|
+
:rtype: Optional[Dict[str, Any]]
|
|
505
|
+
"""
|
|
506
|
+
if not self._callbacks:
|
|
507
|
+
return None
|
|
508
|
+
|
|
509
|
+
mutations = {}
|
|
510
|
+
|
|
511
|
+
# Handle text message events
|
|
512
|
+
if event.type == EventType.TEXT_MESSAGE_CONTENT:
|
|
513
|
+
message_id = getattr(event, "message_id", None)
|
|
514
|
+
if message_id:
|
|
515
|
+
# Update buffer
|
|
516
|
+
delta = getattr(event, "delta", "")
|
|
517
|
+
self._text_buffers[message_id] = self._text_buffers.get(message_id, "") + delta
|
|
518
|
+
|
|
519
|
+
# Invoke callbacks
|
|
520
|
+
for callback in self._callbacks:
|
|
521
|
+
if hasattr(callback, "on_text_message_content"):
|
|
522
|
+
result = await callback.on_text_message_content(event, self._text_buffers[message_id])
|
|
523
|
+
if result:
|
|
524
|
+
mutations.update(result)
|
|
525
|
+
|
|
526
|
+
# Handle tool call events
|
|
527
|
+
elif event.type == EventType.TOOL_CALL_ARGS:
|
|
528
|
+
tool_call_id = getattr(event, "tool_call_id", None)
|
|
529
|
+
if tool_call_id:
|
|
530
|
+
# Update buffer
|
|
531
|
+
delta = getattr(event, "delta", "")
|
|
532
|
+
if tool_call_id not in self._tool_call_buffers:
|
|
533
|
+
self._tool_call_buffers[tool_call_id] = {
|
|
534
|
+
"buffer": "",
|
|
535
|
+
"partial_args": {},
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
self._tool_call_buffers[tool_call_id]["buffer"] += delta
|
|
539
|
+
|
|
540
|
+
# Try to parse partial JSON
|
|
541
|
+
try:
|
|
542
|
+
partial_args = json.loads(self._tool_call_buffers[tool_call_id]["buffer"])
|
|
543
|
+
self._tool_call_buffers[tool_call_id]["partial_args"] = partial_args
|
|
544
|
+
except (json.JSONDecodeError, ValueError):
|
|
545
|
+
# Not valid JSON yet, keep accumulating
|
|
546
|
+
pass
|
|
547
|
+
|
|
548
|
+
# Invoke callbacks
|
|
549
|
+
for callback in self._callbacks:
|
|
550
|
+
if hasattr(callback, "on_tool_call_args"):
|
|
551
|
+
result = await callback.on_tool_call_args(
|
|
552
|
+
event,
|
|
553
|
+
self._tool_call_buffers[tool_call_id]["buffer"],
|
|
554
|
+
self._tool_call_buffers[tool_call_id]["partial_args"],
|
|
555
|
+
)
|
|
556
|
+
if result:
|
|
557
|
+
mutations.update(result)
|
|
558
|
+
|
|
559
|
+
# Handle lifecycle events
|
|
560
|
+
elif event.type == EventType.RUN_STARTED:
|
|
561
|
+
for callback in self._callbacks:
|
|
562
|
+
if hasattr(callback, "on_run_started"):
|
|
563
|
+
await callback.on_run_started(event)
|
|
564
|
+
|
|
565
|
+
elif event.type == EventType.RUN_FINISHED:
|
|
566
|
+
# Clear buffers on run finish
|
|
567
|
+
self._text_buffers.clear()
|
|
568
|
+
self._tool_call_buffers.clear()
|
|
569
|
+
|
|
570
|
+
for callback in self._callbacks:
|
|
571
|
+
if hasattr(callback, "on_run_finished"):
|
|
572
|
+
await callback.on_run_finished(event)
|
|
573
|
+
|
|
574
|
+
elif event.type == EventType.RUN_ERROR:
|
|
575
|
+
for callback in self._callbacks:
|
|
576
|
+
if hasattr(callback, "on_run_error"):
|
|
577
|
+
await callback.on_run_error(event)
|
|
578
|
+
|
|
579
|
+
return mutations if mutations else None
|
|
580
|
+
|
|
581
|
+
async def _process_event(self, event: BaseEvent) -> AsyncGenerator[BaseEvent, None]:
|
|
582
|
+
"""Process an event through callbacks and yield result.
|
|
583
|
+
|
|
584
|
+
This is a helper method that subclasses can use to integrate callbacks
|
|
585
|
+
into their event processing pipeline.
|
|
586
|
+
|
|
587
|
+
:param event: Event to process
|
|
588
|
+
:type event: BaseEvent
|
|
589
|
+
:yield: Processed event (potentially modified by callbacks)
|
|
590
|
+
:rtype: AsyncGenerator[BaseEvent, None]
|
|
591
|
+
"""
|
|
592
|
+
# Invoke callbacks
|
|
593
|
+
mutations = await self._invoke_callbacks(event)
|
|
594
|
+
|
|
595
|
+
# Apply mutations if any
|
|
596
|
+
if mutations:
|
|
597
|
+
await self._apply_mutations(mutations)
|
|
598
|
+
|
|
599
|
+
# Yield the event
|
|
600
|
+
yield event
|
|
601
|
+
|
|
602
|
+
async def _apply_mutations(self, mutations: Dict[str, Any]) -> None:
|
|
603
|
+
"""Apply mutations to agent state.
|
|
604
|
+
|
|
605
|
+
Subclasses should override this to implement state mutation logic.
|
|
606
|
+
By default, this method does nothing.
|
|
607
|
+
|
|
608
|
+
:param mutations: Mutations to apply
|
|
609
|
+
:type mutations: Dict[str, Any]
|
|
610
|
+
"""
|
|
611
|
+
pass
|
|
612
|
+
|
|
613
|
+
# Context Management
|
|
614
|
+
|
|
615
|
+
@contextmanager
|
|
616
|
+
def as_current(self):
|
|
617
|
+
"""Context manager to set this agent as current.
|
|
618
|
+
|
|
619
|
+
This allows accessing the current agent from anywhere in the call stack
|
|
620
|
+
using BaseAgent.get_current().
|
|
621
|
+
|
|
622
|
+
:yields: The current agent instance
|
|
623
|
+
:ytype: BaseAgent
|
|
624
|
+
|
|
625
|
+
Example::
|
|
626
|
+
|
|
627
|
+
with agent.as_current():
|
|
628
|
+
# agent is now accessible via BaseAgent.get_current()
|
|
629
|
+
current = BaseAgent.get_current()
|
|
630
|
+
assert current is agent
|
|
631
|
+
"""
|
|
632
|
+
token = _current_agent.set(self)
|
|
633
|
+
try:
|
|
634
|
+
yield self
|
|
635
|
+
finally:
|
|
636
|
+
_current_agent.reset(token)
|
|
637
|
+
|
|
638
|
+
@staticmethod
|
|
639
|
+
def get_current() -> Optional["BaseAgent"]:
|
|
640
|
+
"""Get the current agent from context.
|
|
641
|
+
|
|
642
|
+
:return: Current agent or None if no agent is set
|
|
643
|
+
:rtype: Optional[BaseAgent]
|
|
644
|
+
"""
|
|
645
|
+
return _current_agent.get()
|
|
646
|
+
|
|
647
|
+
# ============ Event ID Fixing ============
|
|
648
|
+
|
|
649
|
+
def _fix_event_ids(self, event: BaseEvent, thread_id: str, run_id: str) -> BaseEvent:
|
|
650
|
+
"""Fix event IDs to match run context.
|
|
651
|
+
|
|
652
|
+
Useful for frameworks that don't set thread_id/run_id correctly.
|
|
653
|
+
This method modifies the event in-place.
|
|
654
|
+
|
|
655
|
+
:param event: Event to fix
|
|
656
|
+
:type event: BaseEvent
|
|
657
|
+
:param thread_id: Correct thread ID
|
|
658
|
+
:type thread_id: str
|
|
659
|
+
:param run_id: Correct run ID
|
|
660
|
+
:type run_id: str
|
|
661
|
+
:return: Fixed event (same instance)
|
|
662
|
+
:rtype: BaseEvent
|
|
663
|
+
"""
|
|
664
|
+
if hasattr(event, "thread_id") and not event.thread_id:
|
|
665
|
+
event.thread_id = thread_id
|
|
666
|
+
if hasattr(event, "run_id") and not event.run_id:
|
|
667
|
+
event.run_id = run_id
|
|
668
|
+
return event
|
|
669
|
+
|
|
670
|
+
# Core Interface
|
|
671
|
+
|
|
672
|
+
@abstractmethod
|
|
673
|
+
def run(self, run_input: RunAgentInput) -> AsyncGenerator[BaseEvent, None]:
|
|
674
|
+
"""Execute the agent with the given input.
|
|
675
|
+
|
|
676
|
+
This is the main execution method that all agent implementations must provide.
|
|
677
|
+
It should process the input, execute the agent logic, and yield events
|
|
678
|
+
representing the agent's progress and results.
|
|
679
|
+
|
|
680
|
+
:param run_input: Input data for the agent execution containing messages, run_id,
|
|
681
|
+
thread_id, state, context, tools, and forwarded_props
|
|
682
|
+
:type run_input: RunAgentInput
|
|
683
|
+
|
|
684
|
+
:yield: Events representing the agent's execution progress
|
|
685
|
+
:rtype: AsyncGenerator[BaseEvent, None]
|
|
686
|
+
|
|
687
|
+
:raises NotImplementedError: If the method is not implemented by subclass
|
|
688
|
+
|
|
689
|
+
Example:
|
|
690
|
+
Implementing the run method::
|
|
691
|
+
|
|
692
|
+
from ag_ui.core import RunAgentInput
|
|
693
|
+
|
|
694
|
+
async def run(
|
|
695
|
+
self,
|
|
696
|
+
run_input: RunAgentInput
|
|
697
|
+
) -> AsyncGenerator[BaseEvent, None]:
|
|
698
|
+
# Emit run started event
|
|
699
|
+
yield RunStartedEvent(
|
|
700
|
+
type=EventType.RUN_STARTED,
|
|
701
|
+
thread_id=run_input.thread_id,
|
|
702
|
+
run_id=run_input.run_id
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
try:
|
|
706
|
+
# Execute agent logic
|
|
707
|
+
result = await self._agent.execute(run_input)
|
|
708
|
+
|
|
709
|
+
# Emit result events
|
|
710
|
+
yield TextMessageStartEvent(...)
|
|
711
|
+
yield TextMessageContentEvent(...)
|
|
712
|
+
yield TextMessageEndEvent(...)
|
|
713
|
+
|
|
714
|
+
# Emit run finished event
|
|
715
|
+
yield RunFinishedEvent(
|
|
716
|
+
type=EventType.RUN_FINISHED,
|
|
717
|
+
thread_id=run_input.thread_id,
|
|
718
|
+
run_id=run_input.run_id
|
|
719
|
+
)
|
|
720
|
+
except Exception as e:
|
|
721
|
+
# Emit error event
|
|
722
|
+
yield RunErrorEvent(
|
|
723
|
+
type=EventType.RUN_ERROR,
|
|
724
|
+
thread_id=run_input.thread_id,
|
|
725
|
+
run_id=run_input.run_id,
|
|
726
|
+
message=str(e)
|
|
727
|
+
)
|
|
728
|
+
"""
|
|
729
|
+
raise NotImplementedError("Subclasses must implement the run method")
|
|
730
|
+
|
|
731
|
+
def destroy(self) -> None:
|
|
732
|
+
"""Clean up resources used by the agent.
|
|
733
|
+
|
|
734
|
+
This method should be called when the agent is no longer needed
|
|
735
|
+
to properly release any resources (connections, memory, etc.).
|
|
736
|
+
|
|
737
|
+
Subclasses can override this method to provide custom cleanup logic.
|
|
738
|
+
"""
|
|
739
|
+
# Clear callbacks, proxies, and buffers
|
|
740
|
+
self._callbacks.clear()
|
|
741
|
+
self._tool_proxies.clear()
|
|
742
|
+
self._text_buffers.clear()
|
|
743
|
+
self._tool_call_buffers.clear()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Cloudbase Agent type definitions organized by domain.
|
|
4
|
+
|
|
5
|
+
All type definitions are centralized here to avoid circular dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .core import Message, MessageRole, IMemoryEvent
|
|
9
|
+
from .streaming import StreamEvent, EventType, BaseEvent
|
|
10
|
+
from .tools import ToolCall, ToolResult, ToolFunction, ErrorType
|
|
11
|
+
from .server import (
|
|
12
|
+
Tool,
|
|
13
|
+
SystemMessage,
|
|
14
|
+
UserMessage,
|
|
15
|
+
ToolMessage,
|
|
16
|
+
AssistantMessage,
|
|
17
|
+
ClientMessage,
|
|
18
|
+
ResumeMessage,
|
|
19
|
+
SendMessageRequest,
|
|
20
|
+
SendMessageResponse,
|
|
21
|
+
)
|
|
22
|
+
from .storage import Checkpoint, CheckpointMetadata, CheckpointConfig
|
|
23
|
+
from .types import JSON, Headers, Metadata, AgentState
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Core types
|
|
27
|
+
"Message",
|
|
28
|
+
"MessageRole",
|
|
29
|
+
"IMemoryEvent",
|
|
30
|
+
# Streaming types
|
|
31
|
+
"StreamEvent",
|
|
32
|
+
"EventType",
|
|
33
|
+
"BaseEvent",
|
|
34
|
+
# Tool types
|
|
35
|
+
"ToolCall",
|
|
36
|
+
"ToolResult",
|
|
37
|
+
"ToolFunction",
|
|
38
|
+
"ErrorType",
|
|
39
|
+
# Server types
|
|
40
|
+
"Tool",
|
|
41
|
+
"SystemMessage",
|
|
42
|
+
"UserMessage",
|
|
43
|
+
"ToolMessage",
|
|
44
|
+
"AssistantMessage",
|
|
45
|
+
"ClientMessage",
|
|
46
|
+
"ResumeMessage",
|
|
47
|
+
"SendMessageRequest",
|
|
48
|
+
"SendMessageResponse",
|
|
49
|
+
# Storage types
|
|
50
|
+
"Checkpoint",
|
|
51
|
+
"CheckpointMetadata",
|
|
52
|
+
"CheckpointConfig",
|
|
53
|
+
# Common types
|
|
54
|
+
"JSON",
|
|
55
|
+
"Headers",
|
|
56
|
+
"Metadata",
|
|
57
|
+
"AgentState",
|
|
58
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Core message and event types.
|
|
4
|
+
|
|
5
|
+
This module defines the fundamental message and event types used throughout Cloudbase Agent.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Literal, Optional
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MessageRole:
|
|
13
|
+
"""Message role constants."""
|
|
14
|
+
SYSTEM = "system"
|
|
15
|
+
USER = "user"
|
|
16
|
+
ASSISTANT = "assistant"
|
|
17
|
+
TOOL = "tool"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Message(BaseModel):
|
|
21
|
+
"""Base message model."""
|
|
22
|
+
role: Literal["system", "user", "assistant", "tool"]
|
|
23
|
+
content: str
|
|
24
|
+
|
|
25
|
+
class Config:
|
|
26
|
+
"""Pydantic model configuration."""
|
|
27
|
+
validate_by_name = True
|
|
28
|
+
validate_assignment = True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class IMemoryEvent(BaseModel):
|
|
32
|
+
"""Memory event interface."""
|
|
33
|
+
type: str
|
|
34
|
+
timestamp: float
|
|
35
|
+
data: dict
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Server API types.
|
|
4
|
+
|
|
5
|
+
This module defines types for server API requests and responses.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, List, Literal, Optional, Union
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
from .tools import ToolCall
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Tool(BaseModel):
|
|
15
|
+
"""Tool definition.
|
|
16
|
+
|
|
17
|
+
:param name: The name of the tool
|
|
18
|
+
:type name: str
|
|
19
|
+
:param description: Human-readable description of the tool
|
|
20
|
+
:type description: str
|
|
21
|
+
:param parameters: JSON schema defining the tool's input parameters
|
|
22
|
+
:type parameters: Any
|
|
23
|
+
"""
|
|
24
|
+
name: str
|
|
25
|
+
description: str
|
|
26
|
+
parameters: Any
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SystemMessage(BaseModel):
|
|
30
|
+
"""System message."""
|
|
31
|
+
role: Literal["system"] = "system"
|
|
32
|
+
content: str
|
|
33
|
+
|
|
34
|
+
class Config:
|
|
35
|
+
"""Pydantic model configuration."""
|
|
36
|
+
validate_by_name = True
|
|
37
|
+
validate_assignment = True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class UserMessage(BaseModel):
|
|
41
|
+
"""User message."""
|
|
42
|
+
role: Literal["user"] = "user"
|
|
43
|
+
content: str
|
|
44
|
+
|
|
45
|
+
class Config:
|
|
46
|
+
"""Pydantic model configuration."""
|
|
47
|
+
validate_by_name = True
|
|
48
|
+
validate_assignment = True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ToolMessage(BaseModel):
|
|
52
|
+
"""Tool result message."""
|
|
53
|
+
role: Literal["tool"] = "tool"
|
|
54
|
+
content: str
|
|
55
|
+
tool_call_id: str = Field(..., alias="toolCallId")
|
|
56
|
+
|
|
57
|
+
class Config:
|
|
58
|
+
"""Pydantic model configuration."""
|
|
59
|
+
validate_by_name = True
|
|
60
|
+
validate_assignment = True
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AssistantMessage(BaseModel):
|
|
64
|
+
"""AI assistant message."""
|
|
65
|
+
id: str
|
|
66
|
+
role: Literal["assistant"] = "assistant"
|
|
67
|
+
content: Optional[str] = None
|
|
68
|
+
tool_calls: Optional[List[ToolCall]] = Field(None, alias="toolCalls")
|
|
69
|
+
|
|
70
|
+
class Config:
|
|
71
|
+
"""Pydantic model configuration."""
|
|
72
|
+
validate_by_name = True
|
|
73
|
+
validate_assignment = True
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
ClientMessage = Union[SystemMessage, UserMessage, ToolMessage, AssistantMessage]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ResumeMessage(BaseModel):
|
|
80
|
+
"""Resume message for interrupted conversations."""
|
|
81
|
+
interruptId: str
|
|
82
|
+
payload: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class SendMessageRequest(BaseModel):
|
|
86
|
+
"""Send message API request.
|
|
87
|
+
|
|
88
|
+
:param messages: List of conversation messages
|
|
89
|
+
:type messages: Optional[List[ClientMessage]]
|
|
90
|
+
:param tools: Optional list of available tools
|
|
91
|
+
:type tools: Optional[List[Tool]]
|
|
92
|
+
:param resume: Optional resume message
|
|
93
|
+
:type resume: Optional[ResumeMessage]
|
|
94
|
+
:param conversationId: Unique identifier for the conversation
|
|
95
|
+
:type conversationId: str
|
|
96
|
+
"""
|
|
97
|
+
messages: Optional[List[ClientMessage]] = []
|
|
98
|
+
tools: Optional[List[Tool]] = []
|
|
99
|
+
resume: Optional[ResumeMessage] = None
|
|
100
|
+
conversationId: str
|
|
101
|
+
|
|
102
|
+
class Config:
|
|
103
|
+
"""Pydantic model configuration."""
|
|
104
|
+
validate_by_name = True
|
|
105
|
+
validate_assignment = True
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SendMessageResponse(BaseModel):
|
|
109
|
+
"""Send message API response."""
|
|
110
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
111
|
+
|
|
112
|
+
type: str
|
|
113
|
+
data: Any
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Storage and checkpoint types.
|
|
4
|
+
|
|
5
|
+
This module defines types for checkpoint storage and state management.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CheckpointMetadata(BaseModel):
|
|
13
|
+
"""Checkpoint metadata.
|
|
14
|
+
|
|
15
|
+
:param source: Source of the checkpoint (e.g., "update", "input")
|
|
16
|
+
:type source: str
|
|
17
|
+
:param step: Step number in the execution
|
|
18
|
+
:type step: int
|
|
19
|
+
:param writes: Optional writes metadata
|
|
20
|
+
:type writes: Optional[Dict[str, Any]]
|
|
21
|
+
"""
|
|
22
|
+
source: str = "update"
|
|
23
|
+
step: int = -1
|
|
24
|
+
writes: Optional[Dict[str, Any]] = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CheckpointConfig(BaseModel):
|
|
28
|
+
"""Checkpoint configuration.
|
|
29
|
+
|
|
30
|
+
:param thread_id: Thread identifier
|
|
31
|
+
:type thread_id: str
|
|
32
|
+
:param checkpoint_ns: Checkpoint namespace
|
|
33
|
+
:type checkpoint_ns: str
|
|
34
|
+
:param checkpoint_id: Optional checkpoint identifier
|
|
35
|
+
:type checkpoint_id: Optional[str]
|
|
36
|
+
"""
|
|
37
|
+
thread_id: str
|
|
38
|
+
checkpoint_ns: str = ""
|
|
39
|
+
checkpoint_id: Optional[str] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Checkpoint(BaseModel):
|
|
43
|
+
"""Checkpoint data.
|
|
44
|
+
|
|
45
|
+
:param v: Checkpoint version
|
|
46
|
+
:type v: int
|
|
47
|
+
:param id: Checkpoint identifier
|
|
48
|
+
:type id: str
|
|
49
|
+
:param ts: Timestamp
|
|
50
|
+
:type ts: str
|
|
51
|
+
:param channel_values: Channel values
|
|
52
|
+
:type channel_values: Dict[str, Any]
|
|
53
|
+
:param channel_versions: Channel versions
|
|
54
|
+
:type channel_versions: Dict[str, Any]
|
|
55
|
+
:param versions_seen: Versions seen
|
|
56
|
+
:type versions_seen: Dict[str, Any]
|
|
57
|
+
:param pending_sends: Pending sends
|
|
58
|
+
:type pending_sends: list
|
|
59
|
+
"""
|
|
60
|
+
v: int = 1
|
|
61
|
+
id: str
|
|
62
|
+
ts: str
|
|
63
|
+
channel_values: Dict[str, Any]
|
|
64
|
+
channel_versions: Dict[str, Any]
|
|
65
|
+
versions_seen: Dict[str, Any]
|
|
66
|
+
pending_sends: list = []
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Streaming event types.
|
|
4
|
+
|
|
5
|
+
This module defines event types for streaming agent responses.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Literal, Optional
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EventType:
|
|
13
|
+
"""Event type constants."""
|
|
14
|
+
RUN_STARTED = "run-started"
|
|
15
|
+
RUN_FINISHED = "run-finished"
|
|
16
|
+
RUN_ERROR = "run-error"
|
|
17
|
+
TEXT_MESSAGE_START = "text-message-start"
|
|
18
|
+
TEXT_MESSAGE_CONTENT = "text-message-content"
|
|
19
|
+
TEXT_MESSAGE_END = "text-message-end"
|
|
20
|
+
TOOL_CALL_START = "tool-call-start"
|
|
21
|
+
TOOL_CALL_ARGS = "tool-call-args"
|
|
22
|
+
TOOL_CALL_END = "tool-call-end"
|
|
23
|
+
TOOL_RESULT = "tool-result"
|
|
24
|
+
INTERRUPT = "interrupt"
|
|
25
|
+
ERROR = "error"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BaseEvent(BaseModel):
|
|
29
|
+
"""Base event model."""
|
|
30
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
31
|
+
|
|
32
|
+
type: str
|
|
33
|
+
thread_id: Optional[str] = Field(None, alias="threadId")
|
|
34
|
+
run_id: Optional[str] = Field(None, alias="runId")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class StreamEvent(BaseEvent):
|
|
38
|
+
"""Generic streaming event."""
|
|
39
|
+
data: Optional[Any] = None
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tool-related types.
|
|
4
|
+
|
|
5
|
+
This module defines types for tool calls, results, and errors.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Literal, Optional
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ErrorType:
|
|
13
|
+
"""Tool error type constants."""
|
|
14
|
+
VALIDATION_ERROR = "validation_error"
|
|
15
|
+
EXECUTION_ERROR = "execution_error"
|
|
16
|
+
TIMEOUT_ERROR = "timeout_error"
|
|
17
|
+
PERMISSION_ERROR = "permission_error"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ToolFunction(BaseModel):
|
|
21
|
+
"""Tool function definition.
|
|
22
|
+
|
|
23
|
+
:param name: The name of the function to call
|
|
24
|
+
:type name: str
|
|
25
|
+
:param arguments: JSON-serialized function arguments as a string
|
|
26
|
+
:type arguments: str
|
|
27
|
+
"""
|
|
28
|
+
name: str
|
|
29
|
+
arguments: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ToolCall(BaseModel):
|
|
33
|
+
"""Tool function call.
|
|
34
|
+
|
|
35
|
+
:param id: Unique identifier for this tool call
|
|
36
|
+
:type id: str
|
|
37
|
+
:param type: Type of the call, always "function"
|
|
38
|
+
:type type: Literal["function"]
|
|
39
|
+
:param function: The function being called with its arguments
|
|
40
|
+
:type function: ToolFunction
|
|
41
|
+
"""
|
|
42
|
+
id: str
|
|
43
|
+
type: Literal["function"] = "function"
|
|
44
|
+
function: ToolFunction
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ToolResult(BaseModel):
|
|
48
|
+
"""Tool execution result.
|
|
49
|
+
|
|
50
|
+
:param tool_call_id: ID of the tool call this result corresponds to
|
|
51
|
+
:type tool_call_id: str
|
|
52
|
+
:param result: The tool execution result
|
|
53
|
+
:type result: str
|
|
54
|
+
:param error: Optional error message if execution failed
|
|
55
|
+
:type error: Optional[str]
|
|
56
|
+
"""
|
|
57
|
+
tool_call_id: str = Field(..., alias="toolCallId")
|
|
58
|
+
result: Optional[str] = None
|
|
59
|
+
error: Optional[str] = None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Common utility types.
|
|
4
|
+
|
|
5
|
+
This module defines common utility types used throughout Cloudbase Agent.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
# Type aliases
|
|
11
|
+
JSON = Dict[str, Any]
|
|
12
|
+
Headers = Dict[str, str]
|
|
13
|
+
Metadata = Dict[str, Any]
|
|
14
|
+
AgentState = Dict[str, Any]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudbase-agent-core
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Cloudbase Agent Python SDK - Core functionality
|
|
5
|
+
Author-email: Cloudbase Agent Team <ag-kit@example.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: Cloudbase Agent,agent,ai,llm
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: ag-ui-protocol>=0.1.9
|
|
17
|
+
Requires-Dist: lazy-loader>=0.4
|
|
18
|
+
Requires-Dist: pydantic>=2.0.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.12.0; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Cloudbase Agent Python SDK - Core
|
|
27
|
+
|
|
28
|
+
Core functionality for Cloudbase Agent Python SDK, including base agent classes and protocols.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
cloudbase_agent/__init__.py,sha256=b7HT7WjWOzZ1l8eoUXFEVYM9co6GQJwr91KtszoHlHU,2486
|
|
2
|
+
cloudbase_agent/__init__.pyi,sha256=-JkUj3AM5FEX0IoJWAHiOuuDPAdVsRTQIqgWJi4OfYg,385
|
|
3
|
+
cloudbase_agent/base_agent.py,sha256=diliCEZb1hFm64gG_bcKLxxbWVCd38OnLXoaI-Y6IaA,25528
|
|
4
|
+
cloudbase_agent/schemas/__init__.py,sha256=QGu4M3MSXJinbPBH4lSnYN24C3Y-tzkUthUwcYhmMpc,1306
|
|
5
|
+
cloudbase_agent/schemas/core.py,sha256=P9VbHyA-rQgBHj0gPEJofQnkHkwiUklW8z8ha5YjE0I,771
|
|
6
|
+
cloudbase_agent/schemas/server.py,sha256=IlEkjKg9ixiVgm22C50UFYp6qGsZcb7igg2b4S_kJUY,2917
|
|
7
|
+
cloudbase_agent/schemas/storage.py,sha256=wzxZ9XjPVgldpB4-8GCL1A1APSKne-Brxmo-FGdkYew,1761
|
|
8
|
+
cloudbase_agent/schemas/streaming.py,sha256=kpFiZr1GAagl8Utg9LDwd-ZEbVirhzPpJZpHhuJyT3Q,1055
|
|
9
|
+
cloudbase_agent/schemas/tools.py,sha256=UXQEf6rLRlMjYZjhR9mkEulf2oVsId_fwA3mMzIN4i0,1579
|
|
10
|
+
cloudbase_agent/schemas/types.py,sha256=QhO5ZMo2CYwWGshyMqZ3_LUAZahiAh21mUo3NVbRSQc,298
|
|
11
|
+
cloudbase_agent_core-0.1.1.dist-info/METADATA,sha256=Rc5jc7jh5V3aKg7r_vgpwIXnLA85DMQjaZZCgeQYaRI,1091
|
|
12
|
+
cloudbase_agent_core-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
13
|
+
cloudbase_agent_core-0.1.1.dist-info/RECORD,,
|