bedrock-agentcore-starter-toolkit 0.1.2__py3-none-any.whl → 0.1.4__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.
Potentially problematic release.
This version of bedrock-agentcore-starter-toolkit might be problematic. Click here for more details.
- bedrock_agentcore_starter_toolkit/cli/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/cli.py +3 -1
- bedrock_agentcore_starter_toolkit/cli/common.py +1 -1
- bedrock_agentcore_starter_toolkit/cli/import_agent/README.md +35 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/agent_info.py +230 -0
- bedrock_agentcore_starter_toolkit/cli/import_agent/commands.py +518 -0
- bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +132 -42
- bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +120 -22
- bedrock_agentcore_starter_toolkit/operations/gateway/client.py +2 -2
- bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +5 -2
- bedrock_agentcore_starter_toolkit/operations/runtime/invoke.py +1 -1
- bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +108 -30
- bedrock_agentcore_starter_toolkit/operations/runtime/models.py +1 -1
- bedrock_agentcore_starter_toolkit/services/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/memory_manager_template.py +207 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_langchain.j2 +9 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/requirements_strands.j2 +5 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/assets/template_fixtures_merged.json +1102 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/__init__.py +1 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/base_bedrock_translate.py +1668 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_langchain.py +382 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/scripts/bedrock_to_strands.py +374 -0
- bedrock_agentcore_starter_toolkit/services/import_agent/utils.py +417 -0
- bedrock_agentcore_starter_toolkit/services/runtime.py +35 -12
- bedrock_agentcore_starter_toolkit/utils/runtime/container.py +54 -3
- bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +11 -5
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +2 -1
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/METADATA +22 -2
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/RECORD +35 -19
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/WHEEL +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/entry_points.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/licenses/LICENSE.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.2.dist-info → bedrock_agentcore_starter_toolkit-0.1.4.dist-info}/licenses/NOTICE.txt +0 -0
|
@@ -15,6 +15,7 @@ from ...services.ecr import deploy_to_ecr, get_or_create_ecr_repository
|
|
|
15
15
|
from ...services.runtime import BedrockAgentCoreClient
|
|
16
16
|
from ...utils.runtime.config import load_config, save_config
|
|
17
17
|
from ...utils.runtime.container import ContainerRuntime
|
|
18
|
+
from ...utils.runtime.schema import BedrockAgentCoreAgentSchema, BedrockAgentCoreConfigSchema
|
|
18
19
|
from .create_role import get_or_create_runtime_execution_role
|
|
19
20
|
from .models import LaunchResult
|
|
20
21
|
|
|
@@ -137,7 +138,14 @@ def _ensure_execution_role(agent_config, project_config, config_path, agent_name
|
|
|
137
138
|
|
|
138
139
|
|
|
139
140
|
def _deploy_to_bedrock_agentcore(
|
|
140
|
-
agent_config
|
|
141
|
+
agent_config: BedrockAgentCoreAgentSchema,
|
|
142
|
+
project_config: BedrockAgentCoreConfigSchema,
|
|
143
|
+
config_path: Path,
|
|
144
|
+
agent_name: str,
|
|
145
|
+
ecr_uri: str,
|
|
146
|
+
region: str,
|
|
147
|
+
env_vars: Optional[dict] = None,
|
|
148
|
+
auto_update_on_conflict: bool = False,
|
|
141
149
|
):
|
|
142
150
|
"""Deploy agent to Bedrock AgentCore with retry logic for role validation."""
|
|
143
151
|
log.info("Deploying to Bedrock AgentCore...")
|
|
@@ -215,6 +223,16 @@ def _deploy_to_bedrock_agentcore(
|
|
|
215
223
|
agent_config.bedrock_agentcore.agent_id = agent_id
|
|
216
224
|
agent_config.bedrock_agentcore.agent_arn = agent_arn
|
|
217
225
|
|
|
226
|
+
# Reset session id if present
|
|
227
|
+
existing_session_id = agent_config.bedrock_agentcore.agent_session_id
|
|
228
|
+
if existing_session_id is not None:
|
|
229
|
+
log.warning(
|
|
230
|
+
"⚠️ Session ID will be reset to connect to the updated agent. "
|
|
231
|
+
"The previous agent remains accessible via the original session ID: %s",
|
|
232
|
+
existing_session_id,
|
|
233
|
+
)
|
|
234
|
+
agent_config.bedrock_agentcore.agent_session_id = None
|
|
235
|
+
|
|
218
236
|
# Update the project config and save
|
|
219
237
|
project_config.agents[agent_config.name] = agent_config
|
|
220
238
|
save_config(project_config, config_path)
|
|
@@ -233,8 +251,7 @@ def launch_bedrock_agentcore(
|
|
|
233
251
|
config_path: Path,
|
|
234
252
|
agent_name: Optional[str] = None,
|
|
235
253
|
local: bool = False,
|
|
236
|
-
|
|
237
|
-
use_codebuild: bool = False,
|
|
254
|
+
use_codebuild: bool = True,
|
|
238
255
|
env_vars: Optional[dict] = None,
|
|
239
256
|
auto_update_on_conflict: bool = False,
|
|
240
257
|
) -> LaunchResult:
|
|
@@ -244,7 +261,6 @@ def launch_bedrock_agentcore(
|
|
|
244
261
|
config_path: Path to BedrockAgentCore configuration file
|
|
245
262
|
agent_name: Name of agent to launch (for project configurations)
|
|
246
263
|
local: Whether to run locally
|
|
247
|
-
push_ecr_only: Whether to only build and push to ECR without deploying
|
|
248
264
|
use_codebuild: Whether to use CodeBuild for ARM64 builds
|
|
249
265
|
env_vars: Environment variables to pass to local container (dict of key-value pairs)
|
|
250
266
|
auto_update_on_conflict: Whether to automatically update when agent already exists (default: False)
|
|
@@ -256,18 +272,19 @@ def launch_bedrock_agentcore(
|
|
|
256
272
|
project_config = load_config(config_path)
|
|
257
273
|
agent_config = project_config.get_agent_config(agent_name)
|
|
258
274
|
|
|
259
|
-
# Handle CodeBuild deployment
|
|
260
|
-
if use_codebuild:
|
|
275
|
+
# Handle CodeBuild deployment (but not for local mode)
|
|
276
|
+
if use_codebuild and not local:
|
|
261
277
|
return _launch_with_codebuild(
|
|
262
278
|
config_path=config_path,
|
|
263
279
|
agent_name=agent_config.name,
|
|
264
280
|
agent_config=agent_config,
|
|
265
281
|
project_config=project_config,
|
|
266
282
|
auto_update_on_conflict=auto_update_on_conflict,
|
|
283
|
+
env_vars=env_vars,
|
|
267
284
|
)
|
|
268
285
|
|
|
269
286
|
# Log which agent is being launched
|
|
270
|
-
mode = "locally" if local else "to
|
|
287
|
+
mode = "locally" if local else "to cloud"
|
|
271
288
|
log.info("Launching Bedrock AgentCore agent '%s' %s", agent_config.name, mode)
|
|
272
289
|
|
|
273
290
|
# Validate configuration
|
|
@@ -278,17 +295,45 @@ def launch_bedrock_agentcore(
|
|
|
278
295
|
# Initialize container runtime
|
|
279
296
|
runtime = ContainerRuntime(agent_config.container_runtime)
|
|
280
297
|
|
|
298
|
+
# Check if we need local runtime for this operation
|
|
299
|
+
if local and not runtime.has_local_runtime:
|
|
300
|
+
raise RuntimeError(
|
|
301
|
+
"Cannot run locally - no container runtime available\n"
|
|
302
|
+
"💡 Recommendation: Use CodeBuild for cloud deployment\n"
|
|
303
|
+
"💡 Run 'agentcore launch' (without --local) for CodeBuild deployment\n"
|
|
304
|
+
"💡 For local runs, please install Docker, Finch, or Podman"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Check if we need local runtime for local-build mode (cloud deployment with local build)
|
|
308
|
+
if not local and not use_codebuild and not runtime.has_local_runtime:
|
|
309
|
+
raise RuntimeError(
|
|
310
|
+
"Cannot build locally - no container runtime available\n"
|
|
311
|
+
"💡 Recommendation: Use CodeBuild for cloud deployment (no Docker needed)\n"
|
|
312
|
+
"💡 Run 'agentcore launch' (without --local-build) for CodeBuild deployment\n"
|
|
313
|
+
"💡 For local builds, please install Docker, Finch, or Podman"
|
|
314
|
+
)
|
|
315
|
+
|
|
281
316
|
# Get build context - always use project root (where config and Dockerfile are)
|
|
282
317
|
build_dir = config_path.parent
|
|
283
318
|
|
|
284
319
|
bedrock_agentcore_name = agent_config.name
|
|
285
320
|
tag = f"bedrock_agentcore-{bedrock_agentcore_name}:latest"
|
|
286
321
|
|
|
287
|
-
# Step 1: Build Docker image
|
|
322
|
+
# Step 1: Build Docker image (only if we need it)
|
|
288
323
|
success, output = runtime.build(build_dir, tag)
|
|
289
324
|
if not success:
|
|
290
325
|
error_lines = output[-10:] if len(output) > 10 else output
|
|
291
|
-
|
|
326
|
+
error_message = " ".join(error_lines)
|
|
327
|
+
|
|
328
|
+
# Check if this is a container runtime issue and suggest CodeBuild
|
|
329
|
+
if "No container runtime available" in error_message:
|
|
330
|
+
raise RuntimeError(
|
|
331
|
+
f"Build failed: {error_message}\n"
|
|
332
|
+
"💡 Recommendation: Use CodeBuild for building containers in the cloud\n"
|
|
333
|
+
"💡 Run 'agentcore launch' (default) for CodeBuild deployment"
|
|
334
|
+
)
|
|
335
|
+
else:
|
|
336
|
+
raise RuntimeError(f"Build failed: {error_message}")
|
|
292
337
|
|
|
293
338
|
log.info("Docker image built: %s", tag)
|
|
294
339
|
|
|
@@ -323,15 +368,6 @@ def launch_bedrock_agentcore(
|
|
|
323
368
|
|
|
324
369
|
log.info("Image uploaded to ECR: %s", ecr_uri)
|
|
325
370
|
|
|
326
|
-
# If push_ecr_only, return early
|
|
327
|
-
if push_ecr_only:
|
|
328
|
-
return LaunchResult(
|
|
329
|
-
mode="push-ecr",
|
|
330
|
-
tag=tag,
|
|
331
|
-
ecr_uri=ecr_uri,
|
|
332
|
-
build_output=output,
|
|
333
|
-
)
|
|
334
|
-
|
|
335
371
|
# Step 4: Deploy agent (with retry logic for role readiness)
|
|
336
372
|
agent_id, agent_arn = _deploy_to_bedrock_agentcore(
|
|
337
373
|
agent_config,
|
|
@@ -354,12 +390,14 @@ def launch_bedrock_agentcore(
|
|
|
354
390
|
)
|
|
355
391
|
|
|
356
392
|
|
|
357
|
-
def
|
|
393
|
+
def _execute_codebuild_workflow(
|
|
358
394
|
config_path: Path,
|
|
359
395
|
agent_name: str,
|
|
360
396
|
agent_config,
|
|
361
397
|
project_config,
|
|
398
|
+
ecr_only: bool = False,
|
|
362
399
|
auto_update_on_conflict: bool = False,
|
|
400
|
+
env_vars: Optional[dict] = None,
|
|
363
401
|
) -> LaunchResult:
|
|
364
402
|
"""Launch using CodeBuild for ARM64 builds."""
|
|
365
403
|
log.info(
|
|
@@ -368,7 +406,6 @@ def _launch_with_codebuild(
|
|
|
368
406
|
agent_config.aws.account,
|
|
369
407
|
agent_config.aws.region,
|
|
370
408
|
)
|
|
371
|
-
|
|
372
409
|
# Validate configuration
|
|
373
410
|
errors = agent_config.validate(for_local=False)
|
|
374
411
|
if errors:
|
|
@@ -381,13 +418,16 @@ def _launch_with_codebuild(
|
|
|
381
418
|
session = boto3.Session(region_name=region)
|
|
382
419
|
account_id = agent_config.aws.account # Use existing account from config
|
|
383
420
|
|
|
384
|
-
#
|
|
385
|
-
log.info("Setting up AWS resources (ECR repository, execution roles
|
|
421
|
+
# Setup AWS resources
|
|
422
|
+
log.info("Setting up AWS resources (ECR repository%s)...", "" if ecr_only else ", execution roles")
|
|
386
423
|
ecr_uri = _ensure_ecr_repository(agent_config, project_config, config_path, agent_name, region)
|
|
387
424
|
ecr_repository_arn = f"arn:aws:ecr:{region}:{account_id}:repository/{ecr_uri.split('/')[-1]}"
|
|
388
|
-
_ensure_execution_role(agent_config, project_config, config_path, agent_name, region, account_id)
|
|
389
425
|
|
|
390
|
-
#
|
|
426
|
+
# Setup execution role only if not ECR-only mode
|
|
427
|
+
if not ecr_only:
|
|
428
|
+
_ensure_execution_role(agent_config, project_config, config_path, agent_name, region, account_id)
|
|
429
|
+
|
|
430
|
+
# Prepare CodeBuild
|
|
391
431
|
log.info("Preparing CodeBuild project and uploading source...")
|
|
392
432
|
codebuild_service = CodeBuildService(session)
|
|
393
433
|
|
|
@@ -404,16 +444,54 @@ def _launch_with_codebuild(
|
|
|
404
444
|
source_location=source_location,
|
|
405
445
|
)
|
|
406
446
|
|
|
407
|
-
#
|
|
447
|
+
# Execute CodeBuild
|
|
408
448
|
log.info("Starting CodeBuild build (this may take several minutes)...")
|
|
409
449
|
build_id = codebuild_service.start_build(project_name, source_location)
|
|
410
450
|
codebuild_service.wait_for_completion(build_id)
|
|
411
451
|
log.info("CodeBuild completed successfully")
|
|
412
452
|
|
|
413
|
-
# Update CodeBuild config
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
453
|
+
# Update CodeBuild config only for full deployments, not ECR-only
|
|
454
|
+
if not ecr_only:
|
|
455
|
+
agent_config.codebuild.project_name = project_name
|
|
456
|
+
agent_config.codebuild.execution_role = codebuild_execution_role
|
|
457
|
+
agent_config.codebuild.source_bucket = codebuild_service.source_bucket
|
|
458
|
+
|
|
459
|
+
# Save config changes
|
|
460
|
+
project_config.agents[agent_config.name] = agent_config
|
|
461
|
+
save_config(project_config, config_path)
|
|
462
|
+
log.info("✅ CodeBuild project configuration saved")
|
|
463
|
+
else:
|
|
464
|
+
log.info("✅ ECR-only build completed (project configuration not saved)")
|
|
465
|
+
|
|
466
|
+
return build_id, ecr_uri, region, account_id
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def _launch_with_codebuild(
|
|
470
|
+
config_path: Path,
|
|
471
|
+
agent_name: str,
|
|
472
|
+
agent_config,
|
|
473
|
+
project_config,
|
|
474
|
+
auto_update_on_conflict: bool = False,
|
|
475
|
+
env_vars: Optional[dict] = None,
|
|
476
|
+
) -> LaunchResult:
|
|
477
|
+
"""Launch using CodeBuild for ARM64 builds."""
|
|
478
|
+
log.info(
|
|
479
|
+
"Starting CodeBuild ARM64 deployment for agent '%s' to account %s (%s)",
|
|
480
|
+
agent_name,
|
|
481
|
+
agent_config.aws.account,
|
|
482
|
+
agent_config.aws.region,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Execute shared CodeBuild workflow with full deployment mode
|
|
486
|
+
build_id, ecr_uri, region, account_id = _execute_codebuild_workflow(
|
|
487
|
+
config_path=config_path,
|
|
488
|
+
agent_name=agent_name,
|
|
489
|
+
agent_config=agent_config,
|
|
490
|
+
project_config=project_config,
|
|
491
|
+
ecr_only=False,
|
|
492
|
+
auto_update_on_conflict=auto_update_on_conflict,
|
|
493
|
+
env_vars=env_vars,
|
|
494
|
+
)
|
|
417
495
|
|
|
418
496
|
# Deploy to Bedrock AgentCore
|
|
419
497
|
agent_id, agent_arn = _deploy_to_bedrock_agentcore(
|
|
@@ -423,7 +501,7 @@ def _launch_with_codebuild(
|
|
|
423
501
|
agent_name,
|
|
424
502
|
ecr_uri,
|
|
425
503
|
region,
|
|
426
|
-
env_vars=
|
|
504
|
+
env_vars=env_vars,
|
|
427
505
|
auto_update_on_conflict=auto_update_on_conflict,
|
|
428
506
|
)
|
|
429
507
|
|
|
@@ -27,7 +27,7 @@ class ConfigureResult(BaseModel):
|
|
|
27
27
|
class LaunchResult(BaseModel):
|
|
28
28
|
"""Result of launch operation."""
|
|
29
29
|
|
|
30
|
-
mode: str = Field(..., description="Launch mode: local,
|
|
30
|
+
mode: str = Field(..., description="Launch mode: local, cloud, or codebuild")
|
|
31
31
|
tag: str = Field(..., description="Docker image tag")
|
|
32
32
|
env_vars: Optional[Dict[str, str]] = Field(default=None, description="Environment variables for local deployment")
|
|
33
33
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Services module for the Bedrock Agent Core Starter Toolkit."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Import Agent Utility for Bedrock Agents -> Bedrock AgentCore."""
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# pylint: disable=line-too-long
|
|
2
|
+
"""Long Term Memory Manager for generated agents.
|
|
3
|
+
|
|
4
|
+
This module provides a custom memory manager that mimics the functionality of Bedrock Agents
|
|
5
|
+
Long Term Memory and Sessions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import weakref
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import Any, Dict, List, Set
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LongTermMemoryManager:
|
|
17
|
+
"""Custom Memory Manager to have equivalent functionality with Bedrock Agents Long Term Memory and Sessions."""
|
|
18
|
+
|
|
19
|
+
# Class variable to keep track of all instances
|
|
20
|
+
_instances: Set[weakref.ref] = set()
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
llm_summarizer,
|
|
25
|
+
storage_path: str = "output",
|
|
26
|
+
max_sessions: int = 10,
|
|
27
|
+
summarization_prompt: str = None,
|
|
28
|
+
max_days: int = 30,
|
|
29
|
+
platform: str = "langchain",
|
|
30
|
+
):
|
|
31
|
+
"""Initialize the LongTermMemoryManager."""
|
|
32
|
+
self.llm_summarizer = llm_summarizer
|
|
33
|
+
self.storage_path = storage_path
|
|
34
|
+
self.max_sessions = max_sessions
|
|
35
|
+
self.max_days = max_days
|
|
36
|
+
self.current_session_messages = []
|
|
37
|
+
self.summarization_prompt = summarization_prompt
|
|
38
|
+
self._last_memory_update_time = 0
|
|
39
|
+
self.platform = platform
|
|
40
|
+
self._session_ended = False # Track if this instance has ended its session
|
|
41
|
+
|
|
42
|
+
self.session_summaries = self._load_session_summaries()
|
|
43
|
+
|
|
44
|
+
# Register this instance in the class-level instances set
|
|
45
|
+
self._instances.add(weakref.ref(self, self._cleanup_reference))
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _cleanup_reference(ref):
|
|
49
|
+
"""Callback for when a weak reference is removed."""
|
|
50
|
+
LongTermMemoryManager._instances.discard(ref)
|
|
51
|
+
|
|
52
|
+
def _load_session_summaries(self) -> List[Dict[str, Any]]:
|
|
53
|
+
"""Load all stored session summaries."""
|
|
54
|
+
summary_file = self.storage_path
|
|
55
|
+
if os.path.exists(summary_file):
|
|
56
|
+
with open(summary_file, "r") as f:
|
|
57
|
+
return json.load(f)
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
def _save_session_summaries(self):
|
|
61
|
+
summary_file = self.storage_path
|
|
62
|
+
with open(summary_file, "a+", encoding="utf-8") as f:
|
|
63
|
+
f.truncate(0)
|
|
64
|
+
json.dump(self.session_summaries, f)
|
|
65
|
+
self._last_memory_update_time = datetime.now().timestamp()
|
|
66
|
+
|
|
67
|
+
def add_message(self, message: Dict[str, str]):
|
|
68
|
+
"""Add a message to the current session."""
|
|
69
|
+
self.current_session_messages.append(message)
|
|
70
|
+
|
|
71
|
+
def _generate_session_summary(self) -> str:
|
|
72
|
+
try:
|
|
73
|
+
conversation_str = "\n\n".join(
|
|
74
|
+
[f"{msg['role'].capitalize()}: {msg['content']}" for msg in self.current_session_messages]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
past_summaries = "\n".join([summary["summary"] for summary in self.session_summaries])
|
|
78
|
+
|
|
79
|
+
summarization_prompt = self.summarization_prompt.replace(
|
|
80
|
+
"$past_conversation_summary$", past_summaries
|
|
81
|
+
).replace("$conversation$", conversation_str)
|
|
82
|
+
|
|
83
|
+
if self.platform == "langchain":
|
|
84
|
+
summary_response = self.llm_summarizer.invoke(summarization_prompt).content
|
|
85
|
+
else:
|
|
86
|
+
|
|
87
|
+
def inference(model, messages, system_prompt=""):
|
|
88
|
+
async def run_inference():
|
|
89
|
+
results = []
|
|
90
|
+
async for event in model.stream(messages=messages, system_prompt=system_prompt):
|
|
91
|
+
results.append(event)
|
|
92
|
+
return results
|
|
93
|
+
|
|
94
|
+
response = asyncio.run(run_inference())
|
|
95
|
+
|
|
96
|
+
text = ""
|
|
97
|
+
for chunk in response:
|
|
98
|
+
if "contentBlockDelta" not in chunk:
|
|
99
|
+
continue
|
|
100
|
+
text += chunk["contentBlockDelta"].get("delta", {}).get("text", "")
|
|
101
|
+
|
|
102
|
+
return text
|
|
103
|
+
|
|
104
|
+
summary_response = inference(
|
|
105
|
+
self.llm_summarizer, messages=[{"role": "user", "content": [{"text": summarization_prompt}]}]
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return summary_response
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"Error generating summary: {str(e)}")
|
|
111
|
+
message = self.current_session_messages[-1]["content"] if self.current_session_messages else "No messages"
|
|
112
|
+
return f"Session summary generation failed. Last message: {message}"
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def _cleanup_instance(cls):
|
|
116
|
+
"""Remove dead references from the instances set."""
|
|
117
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def get_active_instances_count(cls):
|
|
121
|
+
"""Return the number of active memory manager instances."""
|
|
122
|
+
# Clean up any dead references first
|
|
123
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
124
|
+
return len(cls._instances)
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def get_active_instances(cls):
|
|
128
|
+
"""Return a list of all active memory manager instances."""
|
|
129
|
+
# Clean up any dead references first
|
|
130
|
+
cls._instances = {ref for ref in cls._instances if ref() is not None}
|
|
131
|
+
return [ref() for ref in cls._instances if ref() is not None]
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def end_all_sessions(cls):
|
|
135
|
+
"""End sessions for all active memory manager instances.
|
|
136
|
+
|
|
137
|
+
This is a convenience method that can be called from anywhere to end all sessions.
|
|
138
|
+
"""
|
|
139
|
+
instances = cls.get_active_instances()
|
|
140
|
+
if instances:
|
|
141
|
+
instances[0].end_session()
|
|
142
|
+
|
|
143
|
+
def end_session(self):
|
|
144
|
+
"""End the current session and trigger end_session for all other instances.
|
|
145
|
+
|
|
146
|
+
This ensures that when one agent ends its session, all other agents do the same.
|
|
147
|
+
"""
|
|
148
|
+
# Prevent recursive calls
|
|
149
|
+
if self._session_ended:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
self._session_ended = True
|
|
153
|
+
|
|
154
|
+
# Process this instance's session
|
|
155
|
+
if self.current_session_messages:
|
|
156
|
+
summary = self._generate_session_summary()
|
|
157
|
+
session_summary = {"timestamp": datetime.now().isoformat(), "summary": summary}
|
|
158
|
+
self.session_summaries.append(session_summary)
|
|
159
|
+
|
|
160
|
+
self.session_summaries = [
|
|
161
|
+
summary
|
|
162
|
+
for summary in self.session_summaries
|
|
163
|
+
if (
|
|
164
|
+
datetime.fromisoformat(session_summary["timestamp"]) - datetime.fromisoformat(summary["timestamp"])
|
|
165
|
+
).days
|
|
166
|
+
<= self.max_days
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
if len(self.session_summaries) > self.max_sessions:
|
|
170
|
+
self.session_summaries = self.session_summaries[-self.max_sessions :]
|
|
171
|
+
|
|
172
|
+
self._save_session_summaries()
|
|
173
|
+
|
|
174
|
+
self.current_session_messages = []
|
|
175
|
+
|
|
176
|
+
# End sessions for all other instances
|
|
177
|
+
for instance_ref in list(self._instances):
|
|
178
|
+
instance = instance_ref()
|
|
179
|
+
if instance is not None and instance is not self and not instance._session_ended:
|
|
180
|
+
try:
|
|
181
|
+
instance.end_session()
|
|
182
|
+
except Exception as e:
|
|
183
|
+
print(f"Error ending session for another instance: {str(e)}")
|
|
184
|
+
|
|
185
|
+
# Reset the flag so this instance can be used again if needed
|
|
186
|
+
self._session_ended = False
|
|
187
|
+
|
|
188
|
+
def get_memory_synopsis(self) -> str:
|
|
189
|
+
"""Get a synopsis of the memory, including all session summaries."""
|
|
190
|
+
return "\n".join([summary["summary"] for summary in self.session_summaries])
|
|
191
|
+
|
|
192
|
+
def has_memory_changed(self) -> bool:
|
|
193
|
+
"""Check if the memory has changed since the last update."""
|
|
194
|
+
summary_file = self.storage_path
|
|
195
|
+
|
|
196
|
+
if not os.path.exists(summary_file):
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
current_mtime = os.path.getmtime(summary_file)
|
|
200
|
+
if current_mtime != self._last_memory_update_time:
|
|
201
|
+
self._last_memory_update_time = current_mtime
|
|
202
|
+
return True
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def clear_current_session(self):
|
|
206
|
+
"""Clear the current session messages."""
|
|
207
|
+
self.current_session_messages = []
|