cloudbase-agent-server 0.1.9__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.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Cloudbase Agent Server Package.
4
+
5
+ This package extends the base Cloudbase Agent package with server functionality.
6
+ """
7
+
8
+ __all__ = []
@@ -0,0 +1,91 @@
1
+ """Cloudbase Agent Server Package.
2
+
3
+ This package provides server components for the Cloudbase Agent Python SDK,
4
+ including FastAPI integration, request handling, streaming responses,
5
+ and resource cleanup support.
6
+
7
+ Core Capabilities (Primary Exports):
8
+ - create_send_message_adapter: Create adapter for Cloudbase Agent native send_message endpoint
9
+ - create_openai_adapter: Create adapter for OpenAI-compatible chat/completions endpoint
10
+
11
+ Convenience Tools (Secondary Exports):
12
+ - AgentServiceApp: FastAPI application wrapper for quick server setup
13
+
14
+ Data Models:
15
+ - RunAgentInput: Request model for send_message endpoint
16
+ - OpenAIChatCompletionRequest: Request model for OpenAI-compatible endpoint
17
+ - AgentCreatorResult: TypedDict for agent creator return type
18
+ - AgentCreator: Type alias for agent creator functions
19
+
20
+ Example:
21
+ Using core capabilities (advanced users)::
22
+
23
+ from fastapi import FastAPI
24
+ from cloudbase_agent.server import create_send_message_adapter, create_openai_adapter
25
+ from cloudbase_agent.server import RunAgentInput, OpenAIChatCompletionRequest
26
+
27
+ def create_agent():
28
+ return {"agent": MyAgent()}
29
+
30
+ app = FastAPI()
31
+
32
+ @app.post("/custom/send-message")
33
+ async def send_message(request: RunAgentInput):
34
+ return await create_send_message_adapter(create_agent, request)
35
+
36
+ @app.post("/custom/chat/completions")
37
+ async def chat_completions(request: OpenAIChatCompletionRequest):
38
+ return await create_openai_adapter(create_agent, request)
39
+
40
+ Using convenience tool (quick start)::
41
+
42
+ from cloudbase_agent.server import AgentServiceApp
43
+
44
+ def create_agent():
45
+ return {"agent": MyAgent()}
46
+
47
+ AgentServiceApp().run(create_agent, port=8000)
48
+
49
+ With resource cleanup::
50
+
51
+ from cloudbase_agent.server import AgentServiceApp, AgentCreatorResult
52
+
53
+ def create_agent() -> AgentCreatorResult:
54
+ db = connect_database()
55
+ agent = MyAgent(db)
56
+
57
+ def cleanup():
58
+ db.close()
59
+ print("Resources cleaned up")
60
+
61
+ return {"agent": agent, "cleanup": cleanup}
62
+
63
+ AgentServiceApp().run(create_agent, port=8000)
64
+ """
65
+
66
+ # Core capabilities (primary exports)
67
+ # Convenience tool (secondary export)
68
+ from .app import AgentServiceApp
69
+ from .healthz.models import HealthzConfig, HealthzResponse
70
+ from .openai.models import OpenAIChatCompletionRequest
71
+ from .openai.server import create_adapter as create_openai_adapter
72
+
73
+ # Data models
74
+ from .send_message.models import RunAgentInput
75
+ from .send_message.server import create_adapter as create_send_message_adapter
76
+ from .utils.types import AgentCreator, AgentCreatorResult
77
+
78
+ __all__ = [
79
+ # Core capabilities (primary)
80
+ "create_send_message_adapter",
81
+ "create_openai_adapter",
82
+ # Convenience tool (secondary)
83
+ "AgentServiceApp",
84
+ # Data models
85
+ "RunAgentInput",
86
+ "OpenAIChatCompletionRequest",
87
+ "HealthzConfig",
88
+ "HealthzResponse",
89
+ "AgentCreatorResult",
90
+ "AgentCreator",
91
+ ]
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Cloudbase Agent API Application.
4
+
5
+ This module provides a convenient FastAPI application wrapper for Cloudbase Agent agents,
6
+ offering both quick one-line startup and advanced customization options.
7
+ """
8
+
9
+ import os
10
+ from typing import Any, Optional
11
+
12
+ from fastapi import FastAPI
13
+ from fastapi.middleware.cors import CORSMiddleware
14
+
15
+ from .healthz.handler import create_healthz_handler
16
+ from .healthz.models import HealthzConfig
17
+ from .openai.models import OpenAIChatCompletionRequest
18
+ from .openai.server import create_adapter as create_openai_adapter
19
+ from .send_message.models import RunAgentInput
20
+ from .send_message.server import create_adapter as create_send_message_adapter
21
+ from .utils.types import AgentCreator
22
+
23
+
24
+ class AgentServiceApp:
25
+ r"""FastAPI Application Wrapper for Cloudbase Agent Agents.
26
+
27
+ This class provides a simple and flexible way to deploy Cloudbase Agent agents
28
+ as web services. It supports both quick one-line startup and advanced
29
+ customization through the build() method.
30
+
31
+ Key Features:
32
+ - One-line server startup with run()
33
+ - Flexible configuration with method chaining
34
+ - Advanced customization with build()
35
+ - Automatic environment detection (e.g., Tencent Cloud SCF)
36
+ - Resource cleanup support
37
+
38
+ Example:
39
+ Quick start (one line)::
40
+
41
+ AgentServiceApp().run(create_agent, port=8000)
42
+
43
+ With configuration (method chaining)::
44
+
45
+ AgentServiceApp() \
46
+ .set_cors_config(allow_origins=["*"]) \
47
+ .run(create_agent, port=8000, reload=True)
48
+
49
+ Advanced usage (build method)::
50
+
51
+ app = AgentServiceApp()
52
+ fastapi_app = app.build(create_agent, base_path="/api")
53
+
54
+ # Add custom routes
55
+ @fastapi_app.get("/custom")
56
+ def custom_route():
57
+ return {"custom": "data"}
58
+
59
+ # Run manually
60
+ import uvicorn
61
+ uvicorn.run(fastapi_app, port=8000)
62
+ """
63
+
64
+ def __init__(self, auto_detect_env: bool = True):
65
+ """Initialize the Cloudbase Agent API application.
66
+
67
+ :param auto_detect_env: Automatically detect runtime environment
68
+ (e.g., Tencent Cloud SCF) and configure accordingly
69
+ :type auto_detect_env: bool
70
+ """
71
+ self._cors_config: Optional[dict[str, Any]] = None
72
+ self._base_path: str = ""
73
+
74
+ # Auto-detect environment if enabled
75
+ if auto_detect_env:
76
+ self._detect_environment()
77
+
78
+ def _detect_environment(self) -> None:
79
+ """Detect runtime environment and apply environment-specific configuration."""
80
+ # Tencent Cloud SCF
81
+ if os.getenv("TENCENTCLOUD_RUNENV") == "SCF":
82
+ self._base_path = "/v1/aibot/bots"
83
+
84
+ def set_cors_config(
85
+ self,
86
+ allow_origins: Optional[list[str]] = None,
87
+ allow_credentials: bool = True,
88
+ allow_methods: Optional[list[str]] = None,
89
+ allow_headers: Optional[list[str]] = None,
90
+ **kwargs: Any,
91
+ ) -> "AgentServiceApp":
92
+ r"""Set CORS configuration for the application.
93
+
94
+ :param allow_origins: List of allowed origins, defaults to ["*"]
95
+ :type allow_origins: Optional[list[str]]
96
+ :param allow_credentials: Whether to allow credentials, defaults to True
97
+ :type allow_credentials: bool
98
+ :param allow_methods: List of allowed HTTP methods, defaults to ["*"]
99
+ :type allow_methods: Optional[list[str]]
100
+ :param allow_headers: List of allowed headers, defaults to ["*"]
101
+ :type allow_headers: Optional[list[str]]
102
+ :param kwargs: Additional CORS configuration options
103
+ :type kwargs: Any
104
+ :return: Self for method chaining
105
+ :rtype: AgentServiceApp
106
+
107
+ Example:
108
+ Method chaining::
109
+
110
+ AgentServiceApp() \
111
+ .set_cors_config(
112
+ allow_origins=["https://example.com"],
113
+ allow_credentials=True
114
+ ) \
115
+ .run(create_agent, port=8000)
116
+ """
117
+ self._cors_config = {
118
+ "allow_origins": allow_origins or ["*"],
119
+ "allow_credentials": allow_credentials,
120
+ "allow_methods": allow_methods or ["*"],
121
+ "allow_headers": allow_headers or ["*"],
122
+ **kwargs,
123
+ }
124
+ return self
125
+
126
+ def build(
127
+ self,
128
+ create_agent: AgentCreator,
129
+ base_path: str = "",
130
+ enable_cors: bool = True,
131
+ enable_openai_endpoint: bool = False,
132
+ enable_healthz: bool = True,
133
+ healthz_config: Optional[HealthzConfig] = None,
134
+ **fastapi_kwargs: Any,
135
+ ) -> FastAPI:
136
+ """Build and return a configured FastAPI application.
137
+
138
+ This method creates a FastAPI instance with all routes and middleware
139
+ configured. Use this when you need to customize the application before
140
+ running it (e.g., adding custom routes, middleware, or event handlers).
141
+
142
+ :param create_agent: Function that creates and returns agent with optional cleanup
143
+ :type create_agent: AgentCreator
144
+ :param base_path: Base path for agent routes (overrides environment-detected path)
145
+ :type base_path: str
146
+ :param enable_cors: Whether to enable CORS middleware
147
+ :type enable_cors: bool
148
+ :param enable_openai_endpoint: Whether to enable OpenAI-compatible /chat/completions endpoint
149
+ :type enable_openai_endpoint: bool
150
+ :param enable_healthz: Whether to enable health check endpoint (default: True)
151
+ :type enable_healthz: bool
152
+ :param healthz_config: Optional health check configuration
153
+ :type healthz_config: Optional[HealthzConfig]
154
+ :param fastapi_kwargs: Additional keyword arguments to pass to FastAPI constructor
155
+ (e.g., title, description, version)
156
+ :type fastapi_kwargs: Any
157
+ :return: Configured FastAPI application
158
+ :rtype: FastAPI
159
+
160
+ Example:
161
+ Build and customize::
162
+
163
+ app = AgentServiceApp()
164
+ fastapi_app = app.build(
165
+ create_agent,
166
+ base_path="/api",
167
+ title="My Agent API",
168
+ version="1.0.0"
169
+ )
170
+
171
+ # Add custom routes
172
+ @fastapi_app.get("/custom")
173
+ def custom_route():
174
+ return {"custom": "data"}
175
+
176
+ # Add custom middleware
177
+ @fastapi_app.middleware("http")
178
+ async def add_custom_header(request, call_next):
179
+ response = await call_next(request)
180
+ response.headers["X-Custom-Header"] = "value"
181
+ return response
182
+
183
+ # Run the application
184
+ import uvicorn
185
+ uvicorn.run(fastapi_app, host="0.0.0.0", port=8000)
186
+
187
+ With custom health check::
188
+
189
+ from cloudbase_agent.server import HealthzConfig
190
+
191
+ def check_database():
192
+ # Perform database health check
193
+ if not db.is_connected():
194
+ raise Exception("Database not connected")
195
+ return {"database": "connected"}
196
+
197
+ config = HealthzConfig(
198
+ service_name="my-agent-service",
199
+ version="2.0.0",
200
+ custom_checks=check_database
201
+ )
202
+
203
+ fastapi_app = AgentServiceApp().build(
204
+ create_agent,
205
+ healthz_config=config
206
+ )
207
+
208
+ Disable health check::
209
+
210
+ fastapi_app = AgentServiceApp().build(
211
+ create_agent,
212
+ enable_healthz=False # No /healthz endpoint
213
+ )
214
+ """
215
+ # Create FastAPI instance
216
+ app = FastAPI(**fastapi_kwargs)
217
+
218
+ # Apply CORS middleware if enabled
219
+ if enable_cors:
220
+ cors_config = self._cors_config or {
221
+ "allow_origins": ["*"],
222
+ "allow_credentials": True,
223
+ "allow_methods": ["*"],
224
+ "allow_headers": ["*"],
225
+ }
226
+ app.add_middleware(CORSMiddleware, **cors_config)
227
+
228
+ # Determine final path (environment base path + user base path)
229
+ final_path = self._base_path + base_path
230
+
231
+ # Register send_message endpoint
232
+ @app.post(f"{final_path}/{{agent_id}}/send-message")
233
+ async def send_message_endpoint(agent_id: str, request: RunAgentInput):
234
+ """Main agent endpoint for processing messages with Cloudbase Agent native format.
235
+
236
+ Args:
237
+ agent_id: Agent identifier (reserved for future use)
238
+ request: Message request payload
239
+ """
240
+ # agent_id parameter is reserved for future use
241
+ return await create_send_message_adapter(create_agent, request)
242
+
243
+ # Register health check endpoint if enabled
244
+ if enable_healthz:
245
+ healthz_handler = create_healthz_handler(
246
+ config=healthz_config,
247
+ base_path=final_path,
248
+ )
249
+
250
+ @app.get(f"{final_path}/healthz")
251
+ def healthz_endpoint() -> dict[str, Any]:
252
+ """Health check endpoint.
253
+
254
+ Returns server status and metadata for monitoring and diagnostics.
255
+ This endpoint is automatically added to all Cloudbase Agent servers and can
256
+ be used by load balancers, monitoring systems, and orchestration
257
+ platforms to verify service health.
258
+
259
+ The response includes:
260
+ - Service status (healthy/unhealthy)
261
+ - Timestamp of the check
262
+ - Service name and version
263
+ - Python runtime version
264
+ - Base path configuration
265
+ - Optional custom check results
266
+
267
+ Returns:
268
+ dict: Health check response with status and metadata
269
+
270
+ Example Response:
271
+ {
272
+ "status": "healthy",
273
+ "timestamp": "2024-01-01T00:00:00.000Z",
274
+ "service": "Cloudbase Agent-python-server",
275
+ "version": "1.0.0",
276
+ "python_version": "3.11.0",
277
+ "base_path": "/"
278
+ }
279
+ """
280
+ return healthz_handler()
281
+
282
+ # Register OpenAI-compatible endpoint if enabled
283
+ if enable_openai_endpoint:
284
+
285
+ @app.post(f"{final_path}/chat/completions")
286
+ async def chat_completions_endpoint(request: OpenAIChatCompletionRequest):
287
+ """OpenAI-compatible chat completions endpoint.
288
+
289
+ This endpoint provides compatibility with OpenAI's chat completion API,
290
+ allowing Cloudbase Agent agents to be used as drop-in replacements for OpenAI models.
291
+ """
292
+ return await create_openai_adapter(create_agent, request)
293
+
294
+ return app
295
+
296
+ def run(
297
+ self,
298
+ create_agent: AgentCreator,
299
+ base_path: str = "",
300
+ host: str = "0.0.0.0",
301
+ port: int = 9000,
302
+ enable_openai_endpoint: bool = False,
303
+ enable_healthz: bool = True,
304
+ healthz_config: Optional[HealthzConfig] = None,
305
+ **uvicorn_kwargs: Any,
306
+ ) -> None:
307
+ r"""Build and run the FastAPI application (one-line startup).
308
+
309
+ This is the simplest way to start an Cloudbase Agent server. It builds the
310
+ FastAPI application and immediately starts the uvicorn server.
311
+
312
+ :param create_agent: Function that creates and returns agent with optional cleanup
313
+ :type create_agent: AgentCreator
314
+ :param base_path: Base path for agent routes
315
+ :type base_path: str
316
+ :param host: Server host address
317
+ :type host: str
318
+ :param port: Server port number
319
+ :type port: int
320
+ :param enable_openai_endpoint: Whether to enable OpenAI-compatible /chat/completions endpoint
321
+ :type enable_openai_endpoint: bool
322
+ :param enable_healthz: Whether to enable health check endpoint (default: True)
323
+ :type enable_healthz: bool
324
+ :param healthz_config: Optional health check configuration
325
+ :type healthz_config: Optional[HealthzConfig]
326
+ :param uvicorn_kwargs: Additional keyword arguments to pass to uvicorn.run
327
+ Examples: reload=True, workers=4, log_level="debug"
328
+ :type uvicorn_kwargs: Any
329
+
330
+ Example:
331
+ Simplest usage::
332
+
333
+ AgentServiceApp().run(create_agent, port=8000)
334
+
335
+ With configuration::
336
+
337
+ AgentServiceApp() \
338
+ .set_cors_config(allow_origins=["https://example.com"]) \
339
+ .run(create_agent, port=8000, reload=True)
340
+
341
+ Development mode::
342
+
343
+ AgentServiceApp().run(
344
+ create_agent,
345
+ port=8000,
346
+ reload=True,
347
+ log_level="debug"
348
+ )
349
+
350
+ Production mode::
351
+
352
+ AgentServiceApp().run(
353
+ create_agent,
354
+ port=8000,
355
+ workers=4,
356
+ access_log=False
357
+ )
358
+ """
359
+ # Build the FastAPI application
360
+ app = self.build(
361
+ create_agent,
362
+ base_path,
363
+ enable_openai_endpoint=enable_openai_endpoint,
364
+ enable_healthz=enable_healthz,
365
+ healthz_config=healthz_config,
366
+ )
367
+
368
+ # Run the server
369
+ import uvicorn
370
+
371
+ uvicorn.run(app, host=host, port=port, **uvicorn_kwargs)
@@ -0,0 +1,14 @@
1
+ """Health Check Module.
2
+
3
+ This module provides health check functionality for Cloudbase Agent servers,
4
+ automatically integrated into AgentServiceApp.
5
+ """
6
+
7
+ from .handler import create_healthz_handler
8
+ from .models import HealthzConfig, HealthzResponse
9
+
10
+ __all__ = [
11
+ "HealthzConfig",
12
+ "HealthzResponse",
13
+ "create_healthz_handler",
14
+ ]
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Health Check Handler.
4
+
5
+ This module provides the handler function for health check endpoints.
6
+ """
7
+
8
+ import platform
9
+ from datetime import datetime
10
+ from typing import Any, Callable, Optional
11
+
12
+ from .models import HealthzConfig
13
+
14
+
15
+ def create_healthz_handler(
16
+ config: Optional[HealthzConfig] = None,
17
+ base_path: str = "",
18
+ agent_name: Optional[str] = None,
19
+ ) -> Callable[[], dict[str, Any]]:
20
+ """Create a health check handler function.
21
+
22
+ This function creates a handler that can be used as a FastAPI route handler
23
+ for health check endpoints. The handler performs basic health checks and
24
+ optionally executes custom health check logic.
25
+
26
+ :param config: Health check configuration
27
+ :type config: Optional[HealthzConfig]
28
+ :param base_path: Base path of the service
29
+ :type base_path: str
30
+ :param agent_name: Optional agent name to include in response
31
+ :type agent_name: Optional[str]
32
+ :return: Handler function that returns health check response
33
+ :rtype: Callable[[], dict[str, Any]]
34
+
35
+ Example:
36
+ Basic usage::
37
+
38
+ handler = create_healthz_handler()
39
+
40
+ @app.get("/healthz")
41
+ def healthz():
42
+ return handler()
43
+
44
+ With configuration::
45
+
46
+ config = HealthzConfig(
47
+ service_name="my-service",
48
+ version="2.0.0"
49
+ )
50
+ handler = create_healthz_handler(config, base_path="/api")
51
+
52
+ @app.get("/api/healthz")
53
+ def healthz():
54
+ return handler()
55
+
56
+ With custom checks::
57
+
58
+ async def check_redis():
59
+ if not redis.ping():
60
+ raise Exception("Redis not available")
61
+ return {"redis": "connected"}
62
+
63
+ config = HealthzConfig(custom_checks=check_redis)
64
+ handler = create_healthz_handler(config)
65
+ """
66
+ # Use default config if not provided
67
+ if config is None:
68
+ config = HealthzConfig()
69
+
70
+ def handler() -> dict[str, Any]:
71
+ """Health check handler function.
72
+
73
+ This function performs health checks and returns a structured response.
74
+ If custom checks are configured and fail, it returns an unhealthy status.
75
+
76
+ :return: Health check response as dictionary
77
+ :rtype: dict[str, Any]
78
+ """
79
+ # Build base response
80
+ response_data = {
81
+ "status": "healthy",
82
+ "timestamp": datetime.utcnow().isoformat() + "Z",
83
+ "service": config.service_name,
84
+ "version": config.version,
85
+ "python_version": platform.python_version(),
86
+ "base_path": base_path or "/",
87
+ }
88
+
89
+ # Add agent info if enabled
90
+ if config.include_agent_info and agent_name:
91
+ response_data["agent_info"] = {
92
+ "name": agent_name,
93
+ "endpoints": [
94
+ f"{base_path}/send-message",
95
+ f"{base_path}/healthz",
96
+ ],
97
+ }
98
+
99
+ # Execute custom checks if provided
100
+ if config.custom_checks:
101
+ try:
102
+ custom_results = config.custom_checks()
103
+ if custom_results:
104
+ response_data["custom"] = custom_results
105
+ except Exception as e:
106
+ # Custom check failed, mark as unhealthy
107
+ response_data["status"] = "unhealthy"
108
+ response_data["error"] = str(e)
109
+
110
+ return response_data
111
+
112
+ return handler
113
+
114
+
115
+ def create_async_healthz_handler(
116
+ config: Optional[HealthzConfig] = None,
117
+ base_path: str = "",
118
+ agent_name: Optional[str] = None,
119
+ ) -> Callable[[], dict[str, Any]]:
120
+ """Create an async health check handler function.
121
+
122
+ This is the async version of create_healthz_handler, supporting
123
+ async custom health check functions.
124
+
125
+ :param config: Health check configuration
126
+ :type config: Optional[HealthzConfig]
127
+ :param base_path: Base path of the service
128
+ :type base_path: str
129
+ :param agent_name: Optional agent name to include in response
130
+ :type agent_name: Optional[str]
131
+ :return: Async handler function that returns health check response
132
+ :rtype: Callable[[], dict[str, Any]]
133
+
134
+ Example:
135
+ With async custom checks::
136
+
137
+ async def check_database():
138
+ if not await db.is_connected():
139
+ raise Exception("Database not connected")
140
+ return {"database": "connected"}
141
+
142
+ config = HealthzConfig(custom_checks=check_database)
143
+ handler = create_async_healthz_handler(config)
144
+
145
+ @app.get("/healthz")
146
+ async def healthz():
147
+ return await handler()
148
+ """
149
+ # Use default config if not provided
150
+ if config is None:
151
+ config = HealthzConfig()
152
+
153
+ async def handler() -> dict[str, Any]:
154
+ """Async health check handler function.
155
+
156
+ This function performs health checks asynchronously and returns
157
+ a structured response. If custom checks are configured and fail,
158
+ it returns an unhealthy status.
159
+
160
+ :return: Health check response as dictionary
161
+ :rtype: dict[str, Any]
162
+ """
163
+ # Build base response
164
+ response_data = {
165
+ "status": "healthy",
166
+ "timestamp": datetime.utcnow().isoformat() + "Z",
167
+ "service": config.service_name,
168
+ "version": config.version,
169
+ "python_version": platform.python_version(),
170
+ "base_path": base_path or "/",
171
+ }
172
+
173
+ # Add agent info if enabled
174
+ if config.include_agent_info and agent_name:
175
+ response_data["agent_info"] = {
176
+ "name": agent_name,
177
+ "endpoints": [
178
+ f"{base_path}/send-message",
179
+ f"{base_path}/healthz",
180
+ ],
181
+ }
182
+
183
+ # Execute custom checks if provided
184
+ if config.custom_checks:
185
+ try:
186
+ # Support both sync and async custom checks
187
+ import inspect
188
+
189
+ if inspect.iscoroutinefunction(config.custom_checks):
190
+ custom_results = await config.custom_checks()
191
+ else:
192
+ custom_results = config.custom_checks()
193
+
194
+ if custom_results:
195
+ response_data["custom"] = custom_results
196
+ except Exception as e:
197
+ # Custom check failed, mark as unhealthy
198
+ response_data["status"] = "unhealthy"
199
+ response_data["error"] = str(e)
200
+
201
+ return response_data
202
+
203
+ return handler