agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.1.6__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.
Files changed (90) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent.py +447 -0
  2. agentscope_runtime/engine/agents/agno_agent.py +19 -18
  3. agentscope_runtime/engine/agents/autogen_agent.py +13 -8
  4. agentscope_runtime/engine/agents/utils.py +53 -0
  5. agentscope_runtime/engine/deployers/__init__.py +0 -13
  6. agentscope_runtime/engine/deployers/local_deployer.py +501 -356
  7. agentscope_runtime/engine/helpers/helper.py +60 -41
  8. agentscope_runtime/engine/runner.py +11 -36
  9. agentscope_runtime/engine/schemas/agent_schemas.py +2 -70
  10. agentscope_runtime/engine/services/sandbox_service.py +62 -70
  11. agentscope_runtime/engine/services/tablestore_memory_service.py +304 -0
  12. agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
  13. agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
  14. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
  15. agentscope_runtime/sandbox/__init__.py +2 -0
  16. agentscope_runtime/sandbox/box/base/__init__.py +4 -0
  17. agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -3
  18. agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
  19. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +8 -13
  20. agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
  21. agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
  22. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +8 -6
  23. agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
  24. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +80 -0
  25. agentscope_runtime/sandbox/box/sandbox.py +5 -2
  26. agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
  27. agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
  28. agentscope_runtime/sandbox/box/training_box/training_box.py +10 -15
  29. agentscope_runtime/sandbox/build.py +143 -58
  30. agentscope_runtime/sandbox/client/http_client.py +43 -49
  31. agentscope_runtime/sandbox/client/training_client.py +0 -1
  32. agentscope_runtime/sandbox/constant.py +24 -1
  33. agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
  34. agentscope_runtime/sandbox/custom/example.py +2 -2
  35. agentscope_runtime/sandbox/enums.py +1 -0
  36. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +11 -6
  37. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
  38. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  39. agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1098 -0
  40. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +33 -205
  41. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +8 -555
  42. agentscope_runtime/sandbox/manager/sandbox_manager.py +187 -88
  43. agentscope_runtime/sandbox/manager/server/app.py +82 -14
  44. agentscope_runtime/sandbox/manager/server/config.py +50 -3
  45. agentscope_runtime/sandbox/model/container.py +6 -23
  46. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  47. agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
  48. agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
  49. agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
  50. agentscope_runtime/sandbox/utils.py +124 -0
  51. agentscope_runtime/version.py +1 -1
  52. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/METADATA +168 -77
  53. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/RECORD +59 -78
  54. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/entry_points.txt +0 -1
  55. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
  56. agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
  57. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
  58. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  59. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +0 -2886
  60. agentscope_runtime/engine/deployers/adapter/responses/response_api_agent_adapter.py +0 -51
  61. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +0 -314
  62. agentscope_runtime/engine/deployers/cli_fc_deploy.py +0 -184
  63. agentscope_runtime/engine/deployers/kubernetes_deployer.py +0 -265
  64. agentscope_runtime/engine/deployers/modelstudio_deployer.py +0 -677
  65. agentscope_runtime/engine/deployers/utils/deployment_modes.py +0 -14
  66. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +0 -8
  67. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +0 -429
  68. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +0 -240
  69. agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +0 -297
  70. agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -932
  71. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -9
  72. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +0 -504
  73. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +0 -157
  74. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +0 -268
  75. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
  76. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
  77. agentscope_runtime/engine/deployers/utils/wheel_packager.py +0 -389
  78. agentscope_runtime/engine/helpers/agent_api_builder.py +0 -651
  79. agentscope_runtime/engine/llms/__init__.py +0 -3
  80. agentscope_runtime/engine/llms/base_llm.py +0 -60
  81. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  82. agentscope_runtime/engine/schemas/embedding.py +0 -37
  83. agentscope_runtime/engine/schemas/modelstudio_llm.py +0 -310
  84. agentscope_runtime/engine/schemas/oai_llm.py +0 -538
  85. agentscope_runtime/engine/schemas/realtime.py +0 -254
  86. /agentscope_runtime/engine/{deployers/adapter/responses → services/utils}/__init__.py +0 -0
  87. /agentscope_runtime/{engine/deployers/utils → sandbox/box/gui/box}/__init__.py +0 -0
  88. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/WHEEL +0 -0
  89. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/licenses/LICENSE +0 -0
  90. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.1.6.dist-info}/top_level.txt +0 -0
@@ -1,9 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from .fastapi_factory import FastAPIAppFactory
3
- from .service_config import (
4
- ServicesConfig,
5
- ServiceConfig,
6
- ServiceProvider,
7
- )
8
- from .fastapi_templates import FastAPITemplateManager
9
- from .process_manager import ProcessManager
@@ -1,504 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # pylint:disable=too-many-branches, unused-argument
3
-
4
-
5
- import asyncio
6
- import json
7
- from contextlib import asynccontextmanager
8
- from typing import Optional, Callable, Type, Any
9
-
10
- from fastapi import FastAPI, Request
11
- from fastapi.middleware.cors import CORSMiddleware
12
- from fastapi.responses import StreamingResponse, JSONResponse
13
-
14
- from .service_config import ServicesConfig, DEFAULT_SERVICES_CONFIG
15
- from .service_factory import ServiceFactory
16
- from ..deployment_modes import DeploymentMode
17
- from ...adapter.protocol_adapter import ProtocolAdapter
18
-
19
-
20
- class FastAPIAppFactory:
21
- """Factory for creating FastAPI applications with unified architecture."""
22
-
23
- @staticmethod
24
- def create_app(
25
- func: Optional[Callable] = None,
26
- runner: Optional[Any] = None,
27
- endpoint_path: str = "/process",
28
- request_model: Optional[Type] = None,
29
- response_type: str = "sse",
30
- stream: bool = True,
31
- before_start: Optional[Callable] = None,
32
- after_finish: Optional[Callable] = None,
33
- mode: DeploymentMode = DeploymentMode.DAEMON_THREAD,
34
- services_config: Optional[ServicesConfig] = None,
35
- protocol_adapters: Optional[list[ProtocolAdapter]] = None,
36
- **kwargs: Any,
37
- ) -> FastAPI:
38
- """Create a FastAPI application with unified architecture.
39
-
40
- Args:
41
- func: Custom processing function
42
- runner: Runner instance (for DAEMON_THREAD mode)
43
- endpoint_path: API endpoint path for the processing function
44
- request_model: Pydantic model for request validation
45
- response_type: Response type - "json", "sse", or "text"
46
- stream: Enable streaming responses
47
- before_start: Callback function called before server starts
48
- after_finish: Callback function called after server finishes
49
- mode: Deployment mode
50
- services_config: Services configuration
51
- protocol_adapters: Protocol adapters
52
- **kwargs: Additional keyword arguments
53
-
54
- Returns:
55
- FastAPI application instance
56
- """
57
- # Use default services config if not provided
58
- if services_config is None:
59
- services_config = DEFAULT_SERVICES_CONFIG
60
-
61
- # Create lifespan manager
62
- @asynccontextmanager
63
- async def lifespan(app: FastAPI):
64
- """Application lifespan manager."""
65
- # Startup
66
- try:
67
- await FastAPIAppFactory._handle_startup(
68
- app,
69
- mode,
70
- services_config,
71
- runner,
72
- before_start,
73
- **kwargs,
74
- )
75
- yield
76
- finally:
77
- # Shutdown
78
- await FastAPIAppFactory._handle_shutdown(
79
- app,
80
- after_finish,
81
- **kwargs,
82
- )
83
-
84
- # Create FastAPI app
85
- app = FastAPI(lifespan=lifespan)
86
-
87
- # Store configuration in app state
88
- app.state.deployment_mode = mode
89
- app.state.services_config = services_config
90
- app.state.stream_enabled = stream
91
- app.state.response_type = response_type
92
- app.state.custom_func = func
93
- app.state.external_runner = runner
94
- app.state.endpoint_path = endpoint_path
95
- app.state.protocol_adapters = protocol_adapters # Store for later use
96
-
97
- # Add middleware
98
- FastAPIAppFactory._add_middleware(app, mode)
99
-
100
- # Add routes
101
- FastAPIAppFactory._add_routes(
102
- app,
103
- endpoint_path,
104
- request_model,
105
- stream,
106
- mode,
107
- )
108
-
109
- # Note: protocol_adapters will be added in _handle_startup
110
- # after runner is available
111
-
112
- return app
113
-
114
- @staticmethod
115
- async def _handle_startup(
116
- app: FastAPI,
117
- mode: DeploymentMode,
118
- services_config: ServicesConfig,
119
- external_runner: Optional[Any],
120
- before_start: Optional[Callable],
121
- **kwargs,
122
- ):
123
- """Handle application startup."""
124
- # Mode-specific initialization
125
- if mode == DeploymentMode.DAEMON_THREAD:
126
- # Use external runner
127
- app.state.runner = external_runner
128
- app.state.runner_managed_externally = True
129
-
130
- elif mode in [
131
- DeploymentMode.DETACHED_PROCESS,
132
- DeploymentMode.STANDALONE,
133
- ]:
134
- # Create internal runner
135
- app.state.runner = await FastAPIAppFactory._create_internal_runner(
136
- services_config,
137
- )
138
- app.state.runner_managed_externally = False
139
-
140
- # Call custom startup callback
141
- if before_start:
142
- if asyncio.iscoroutinefunction(before_start):
143
- await before_start(app, **kwargs)
144
- else:
145
- before_start(app, **kwargs)
146
-
147
- # Add protocol adapter endpoints after runner is available
148
- if (
149
- hasattr(app.state, "protocol_adapters")
150
- and app.state.protocol_adapters
151
- ):
152
- # Determine the effective function to use
153
- if hasattr(app.state, "custom_func") and app.state.custom_func:
154
- effective_func = app.state.custom_func
155
- elif hasattr(app.state, "runner") and app.state.runner:
156
- # Use stream_query if streaming is enabled, otherwise query
157
- if (
158
- hasattr(app.state, "stream_enabled")
159
- and app.state.stream_enabled
160
- ):
161
- effective_func = app.state.runner.stream_query
162
- else:
163
- effective_func = app.state.runner.query
164
- else:
165
- effective_func = None
166
-
167
- if effective_func:
168
- for protocol_adapter in app.state.protocol_adapters:
169
- protocol_adapter.add_endpoint(app=app, func=effective_func)
170
-
171
- @staticmethod
172
- async def _handle_shutdown(
173
- app: FastAPI,
174
- after_finish: Optional[Callable],
175
- **kwargs,
176
- ):
177
- """Handle application shutdown."""
178
- # Call custom shutdown callback
179
- if after_finish:
180
- if asyncio.iscoroutinefunction(after_finish):
181
- await after_finish(app, **kwargs)
182
- else:
183
- after_finish(app, **kwargs)
184
-
185
- # Cleanup internal runner
186
- if (
187
- hasattr(app.state, "runner")
188
- and not app.state.runner_managed_externally
189
- ):
190
- runner = app.state.runner
191
- if runner and hasattr(runner, "context_manager"):
192
- try:
193
- await runner.context_manager.__aexit__(None, None, None)
194
- except Exception as e:
195
- print(f"Warning: Error during runner cleanup: {e}")
196
-
197
- @staticmethod
198
- async def _create_internal_runner(services_config: ServicesConfig):
199
- """Create internal runner with configured services."""
200
- from agentscope_runtime.engine import Runner
201
- from agentscope_runtime.engine.services.context_manager import (
202
- ContextManager,
203
- )
204
-
205
- # Create services
206
- services = ServiceFactory.create_services_from_config(services_config)
207
-
208
- # Create context manager
209
- context_manager = ContextManager(
210
- session_history_service=services["session_history"],
211
- memory_service=services["memory"],
212
- )
213
-
214
- # Initialize context manager
215
- await context_manager.__aenter__()
216
-
217
- # Create runner (agent will be set later)
218
- runner = Runner(
219
- agent=None, # Will be set by the specific deployment
220
- context_manager=context_manager,
221
- )
222
-
223
- return runner
224
-
225
- @staticmethod
226
- def _add_middleware(app: FastAPI, mode: DeploymentMode):
227
- """Add middleware based on deployment mode."""
228
- # Common middleware
229
- app.add_middleware(
230
- CORSMiddleware,
231
- allow_origins=["*"],
232
- allow_credentials=True,
233
- allow_methods=["*"],
234
- allow_headers=["*"],
235
- )
236
-
237
- # Mode-specific middleware
238
- if mode == DeploymentMode.DETACHED_PROCESS:
239
- # Add process management middleware
240
- @app.middleware("http")
241
- async def process_middleware(request: Request, call_next):
242
- # Add process-specific headers
243
- response = await call_next(request)
244
- response.headers["X-Process-Mode"] = "detached"
245
- return response
246
-
247
- elif mode == DeploymentMode.STANDALONE:
248
- # Add configuration middleware
249
- @app.middleware("http")
250
- async def config_middleware(request: Request, call_next):
251
- # Add configuration headers
252
- response = await call_next(request)
253
- response.headers["X-Deployment-Mode"] = "standalone"
254
- return response
255
-
256
- @staticmethod
257
- def _add_routes(
258
- app: FastAPI,
259
- endpoint_path: str,
260
- request_model: Optional[Type],
261
- stream_enabled: bool,
262
- mode: DeploymentMode,
263
- ):
264
- """Add routes to the FastAPI application."""
265
-
266
- # Health check endpoint
267
- @app.get("/health")
268
- async def health_check():
269
- """Health check endpoint."""
270
- status = {"status": "healthy", "mode": mode.value}
271
-
272
- # Add service health checks
273
- if hasattr(app.state, "runner") and app.state.runner:
274
- status["runner"] = "ready"
275
- else:
276
- status["runner"] = "not_ready"
277
-
278
- return status
279
-
280
- # Main processing endpoint
281
- # if stream_enabled:
282
- # Streaming endpoint
283
- @app.post(endpoint_path)
284
- async def stream_endpoint(request: dict):
285
- """Streaming endpoint."""
286
- return StreamingResponse(
287
- FastAPIAppFactory._create_stream_generator(app, request),
288
- media_type="text/event-stream",
289
- headers={
290
- "Cache-Control": "no-cache",
291
- "Connection": "keep-alive",
292
- },
293
- )
294
-
295
- # # Standard endpoint
296
- # @app.post(endpoint_path)
297
- # async def process_endpoint(request: dict):
298
- # """Main processing endpoint."""
299
- # return await FastAPIAppFactory._handle_request(
300
- # app,
301
- # request,
302
- # stream_enabled,
303
- # )
304
-
305
- # Root endpoint
306
- @app.get("/")
307
- async def root():
308
- """Root endpoint."""
309
- return {
310
- "service": "AgentScope Runtime",
311
- "mode": mode.value,
312
- "endpoints": {
313
- "process": endpoint_path,
314
- "stream": f"{endpoint_path}/stream"
315
- if stream_enabled
316
- else None,
317
- "health": "/health",
318
- },
319
- }
320
-
321
- # Mode-specific endpoints
322
- if mode == DeploymentMode.DETACHED_PROCESS:
323
- FastAPIAppFactory._add_process_control_endpoints(app)
324
- elif mode == DeploymentMode.STANDALONE:
325
- FastAPIAppFactory._add_configuration_endpoints(app)
326
-
327
- @staticmethod
328
- def _add_process_control_endpoints(app: FastAPI):
329
- """Add process control endpoints for detached mode."""
330
-
331
- @app.post("/admin/shutdown")
332
- async def shutdown_process():
333
- """Gracefully shutdown the process."""
334
- # Import here to avoid circular imports
335
- import os
336
- import signal
337
-
338
- # Schedule shutdown after response
339
- async def delayed_shutdown():
340
- await asyncio.sleep(1)
341
- os.kill(os.getpid(), signal.SIGTERM)
342
-
343
- asyncio.create_task(delayed_shutdown())
344
- return {"message": "Shutdown initiated"}
345
-
346
- @app.get("/admin/status")
347
- async def get_process_status():
348
- """Get process status information."""
349
- import os
350
- import psutil
351
-
352
- process = psutil.Process(os.getpid())
353
- return {
354
- "pid": os.getpid(),
355
- "status": process.status(),
356
- "memory_usage": process.memory_info().rss,
357
- "cpu_percent": process.cpu_percent(),
358
- "uptime": process.create_time(),
359
- }
360
-
361
- @staticmethod
362
- def _add_configuration_endpoints(app: FastAPI):
363
- """Add configuration endpoints for standalone mode."""
364
-
365
- @app.get("/config")
366
- async def get_configuration():
367
- """Get current service configuration."""
368
- return {
369
- "services_config": app.state.services_config.model_dump(),
370
- "deployment_mode": app.state.deployment_mode.value,
371
- "stream_enabled": app.state.stream_enabled,
372
- }
373
-
374
- @app.get("/config/services")
375
- async def get_services_status():
376
- """Get services status."""
377
- status = {}
378
- if hasattr(app.state, "runner") and app.state.runner:
379
- runner = app.state.runner
380
- if hasattr(runner, "context_manager"):
381
- cm = runner.context_manager
382
- status["memory_service"] = (
383
- "connected" if cm.memory_service else "disconnected"
384
- )
385
- status["session_history_service"] = (
386
- "connected"
387
- if cm.session_history_service
388
- else "disconnected"
389
- )
390
-
391
- return {"services": status}
392
-
393
- @staticmethod
394
- async def _handle_request(
395
- app: FastAPI,
396
- request: dict,
397
- stream_enabled: bool,
398
- ):
399
- """Handle a standard request."""
400
- try:
401
- # Get runner instance
402
- runner = FastAPIAppFactory._get_runner_instance(app)
403
- if not runner:
404
- return JSONResponse(
405
- status_code=503,
406
- content={
407
- "error": "Service not ready",
408
- "message": "Runner not initialized",
409
- },
410
- )
411
-
412
- # Handle custom function vs runner
413
- if app.state.custom_func:
414
- # Use custom function
415
- result = await FastAPIAppFactory._call_custom_function(
416
- app.state.custom_func,
417
- request,
418
- )
419
- return {"response": result}
420
- else:
421
- # Use runner
422
- if stream_enabled:
423
- # Collect streaming response
424
- result = await FastAPIAppFactory._collect_stream_response(
425
- runner,
426
- request,
427
- )
428
- return {"response": result}
429
- else:
430
- # Direct query
431
- result = await runner.query(request)
432
- return {"response": result}
433
-
434
- except Exception as e:
435
- return JSONResponse(
436
- status_code=500,
437
- content={"error": "Internal server error", "message": str(e)},
438
- )
439
-
440
- @staticmethod
441
- async def _create_stream_generator(app: FastAPI, request: dict):
442
- """Create streaming response generator."""
443
- try:
444
- runner = FastAPIAppFactory._get_runner_instance(app)
445
- if not runner:
446
- yield (
447
- f"data: {json.dumps({'error': 'Runner not initialized'})}"
448
- f"\n\n"
449
- )
450
- return
451
-
452
- if app.state.custom_func:
453
- # Handle custom function (convert to stream)
454
- result = await FastAPIAppFactory._call_custom_function(
455
- app.state.custom_func,
456
- request,
457
- )
458
- yield f"data: {json.dumps({'text': str(result)})}\n\n"
459
- else:
460
- # Use runner streaming
461
- async for chunk in runner.stream_query(request):
462
- if hasattr(chunk, "model_dump_json"):
463
- yield f"data: {chunk.model_dump_json()}\n\n"
464
- elif hasattr(chunk, "json"):
465
- yield f"data: {chunk.json()}\n\n"
466
- else:
467
- yield f"data: {json.dumps({'text': str(chunk)})}\n\n"
468
-
469
- except Exception as e:
470
- yield f"data: {json.dumps({'error': str(e)})}\n\n"
471
-
472
- @staticmethod
473
- async def _collect_stream_response(runner, request: dict) -> str:
474
- """Collect streaming response into a single string."""
475
- response_parts = []
476
- async for chunk in runner.stream_query(request):
477
- if hasattr(chunk, "text"):
478
- response_parts.append(chunk.text)
479
- else:
480
- response_parts.append(str(chunk))
481
- return "".join(response_parts)
482
-
483
- @staticmethod
484
- async def _call_custom_function(func: Callable, request: dict):
485
- """Call custom function with proper parameters."""
486
- if asyncio.iscoroutinefunction(func):
487
- return await func(
488
- user_id="default",
489
- request=request,
490
- request_id="generated",
491
- )
492
- else:
493
- return func(
494
- user_id="default",
495
- request=request,
496
- request_id="generated",
497
- )
498
-
499
- @staticmethod
500
- def _get_runner_instance(app: FastAPI):
501
- """Get runner instance from app state."""
502
- if hasattr(app.state, "runner"):
503
- return app.state.runner
504
- return None
@@ -1,157 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """FastAPI templates management and rendering."""
3
-
4
- import os
5
- from typing import Dict, Any, Optional
6
- from jinja2 import Template, Environment, FileSystemLoader
7
- from ..deployment_modes import DeploymentMode
8
-
9
-
10
- class FastAPITemplateManager:
11
- """Manager for FastAPI deployment templates."""
12
-
13
- def __init__(self):
14
- """Initialize template manager."""
15
- self.template_dir = os.path.join(
16
- os.path.dirname(__file__),
17
- )
18
- self.env = Environment(
19
- loader=FileSystemLoader(self.template_dir),
20
- trim_blocks=True,
21
- lstrip_blocks=True,
22
- )
23
-
24
- def render_standalone_template(
25
- self,
26
- agent_name: str,
27
- endpoint_path: str = "/process",
28
- deployment_mode: str = DeploymentMode.STANDALONE,
29
- protocol_adapters: Optional[str] = None,
30
- **kwargs,
31
- ) -> str:
32
- """Render the standalone deployment template.
33
-
34
- Args:
35
- agent_name: Name of the agent variable
36
- endpoint_path: API endpoint path
37
- deployment_mode: Deployment mode (standalone or detached_process)
38
- protocol_adapters: Protocol adapters code string
39
- **kwargs: Additional template variables
40
-
41
- Returns:
42
- Rendered template content
43
- """
44
- template = self.env.get_template("standalone_main.py.j2")
45
- return template.render(
46
- agent_name=agent_name,
47
- endpoint_path=endpoint_path,
48
- deployment_mode=deployment_mode,
49
- protocol_adapters=protocol_adapters,
50
- **kwargs,
51
- )
52
-
53
- def render_detached_script_template(
54
- self,
55
- endpoint_path: str = "/process",
56
- host: str = "127.0.0.1",
57
- port: int = 8000,
58
- stream_enabled: bool = True,
59
- response_type: str = "sse",
60
- runner_code: str = "",
61
- func_code: str = "",
62
- services_config: str = "",
63
- protocol_adapters: Optional[str] = None,
64
- **kwargs,
65
- ) -> str:
66
- """Render the detached process script template.
67
-
68
- Args:
69
- endpoint_path: API endpoint path
70
- host: Host to bind to
71
- port: Port to bind to
72
- stream_enabled: Enable streaming responses
73
- response_type: Response type
74
- runner_code: Code to setup runner
75
- func_code: Code to setup custom function
76
- services_config: Services configuration code
77
- protocol_adapters: Protocol adapters code string
78
- **kwargs: Additional template variables
79
-
80
- Returns:
81
- Rendered template content
82
- """
83
- template = self.env.get_template("detached_script.py.j2")
84
- return template.render(
85
- endpoint_path=endpoint_path,
86
- host=host,
87
- port=port,
88
- stream_enabled=str(stream_enabled).lower(),
89
- response_type=response_type,
90
- runner_code=runner_code,
91
- func_code=func_code,
92
- services_config=services_config,
93
- protocol_adapters=protocol_adapters,
94
- **kwargs,
95
- )
96
-
97
- def render_template_from_string(
98
- self,
99
- template_string: str,
100
- **variables,
101
- ) -> str:
102
- """Render a template from string.
103
-
104
- Args:
105
- template_string: Template content as string
106
- **variables: Template variables
107
-
108
- Returns:
109
- Rendered template content
110
- """
111
- template = Template(template_string)
112
- return template.render(**variables)
113
-
114
- def get_template_list(self) -> list:
115
- """Get list of available templates.
116
-
117
- Returns:
118
- List of template filenames
119
- """
120
- if not os.path.exists(self.template_dir):
121
- return []
122
-
123
- templates = []
124
- for filename in os.listdir(self.template_dir):
125
- if filename.endswith(".j2"):
126
- templates.append(filename)
127
-
128
- return templates
129
-
130
- def validate_template_variables(
131
- self,
132
- template_name: str,
133
- variables: Dict[str, Any],
134
- ) -> Dict[str, list]:
135
- """Validate template variables.
136
-
137
- Args:
138
- template_name: Name of the template
139
- variables: Variables to validate
140
-
141
- Returns:
142
- Dictionary with 'missing' and 'extra' keys containing lists
143
- """
144
- # This is a basic implementation
145
- # In practice, you might want to parse the template to find
146
- # required variables
147
- required_vars = {
148
- "standalone_main.py.j2": ["agent_name", "endpoint_path"],
149
- }
150
-
151
- required = required_vars.get(template_name, [])
152
- provided = list(variables.keys())
153
-
154
- missing = [var for var in required if var not in provided]
155
- extra = [var for var in provided if var not in required]
156
-
157
- return {"missing": missing, "extra": extra}