Phase 1 cleanup: onboarding flow, per-user S3 MEMORY.md, seed script
This commit is contained in:
@@ -149,7 +149,7 @@ def main(payload: dict, context) -> dict:
|
||||
user_context = f'Name: {name}'
|
||||
if username:
|
||||
user_context += f'\nTelegram username: @{username}'
|
||||
system_prompt = build_system_prompt(user_context=user_context)
|
||||
system_prompt = build_system_prompt(user_context=user_context, actor_id=actor_id)
|
||||
|
||||
# Model: claude-sonnet-4-6 via cross-region inference
|
||||
model = BedrockModel(
|
||||
|
||||
@@ -1,34 +1,54 @@
|
||||
import os
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
# Cache: built once per warm session (shared base only)
|
||||
_base_prompt: str | None = None
|
||||
# Cache keyed by actor_id ('' = global/no user)
|
||||
_prompt_cache: dict[str, str] = {}
|
||||
|
||||
|
||||
def build_system_prompt(user_context: str = '') -> str:
|
||||
def build_system_prompt(user_context: str = '', actor_id: str = '') -> str:
|
||||
"""Build system prompt from S3 workspace files + optional per-user context."""
|
||||
base = _get_base_prompt()
|
||||
base = _get_base_prompt(actor_id)
|
||||
if user_context:
|
||||
return base + f'\n\n---\n\n## User\n{user_context}'
|
||||
return base
|
||||
|
||||
|
||||
def _get_base_prompt() -> str:
|
||||
global _base_prompt
|
||||
if _base_prompt is not None:
|
||||
return _base_prompt
|
||||
def _get_base_prompt(actor_id: str = '') -> str:
|
||||
if actor_id in _prompt_cache:
|
||||
return _prompt_cache[actor_id]
|
||||
|
||||
bucket = os.environ.get('WORKSPACE_BUCKET_NAME', '') or 'agent-claw-workspace-495395224548'
|
||||
print(f'[prompt_builder] Loading from bucket: {bucket!r}')
|
||||
print(f'[prompt_builder] Loading from bucket: {bucket!r} actor_id={actor_id!r}')
|
||||
|
||||
if not bucket:
|
||||
print('[prompt_builder] WARNING: WORKSPACE_BUCKET_NAME not set!')
|
||||
_base_prompt = 'You are a helpful personal assistant.'
|
||||
return _base_prompt
|
||||
_prompt_cache[actor_id] = 'You are a helpful personal assistant.'
|
||||
return _prompt_cache[actor_id]
|
||||
|
||||
s3 = boto3.client('s3')
|
||||
parts = []
|
||||
|
||||
# Per-user MEMORY.md (falls back to global)
|
||||
memory_key = f'users/{actor_id}/MEMORY.md' if actor_id else 'MEMORY.md'
|
||||
try:
|
||||
obj = s3.get_object(Bucket=bucket, Key=memory_key)
|
||||
content = obj['Body'].read().decode('utf-8')
|
||||
parts.append(f'## MEMORY.md\n{content}')
|
||||
print(f'[prompt_builder] Loaded {memory_key} ({len(content)} bytes)')
|
||||
except ClientError as e:
|
||||
if e.response['Error']['Code'] in ('NoSuchKey', 'AccessDenied') and actor_id:
|
||||
# Fall back to global MEMORY.md
|
||||
try:
|
||||
obj = s3.get_object(Bucket=bucket, Key='MEMORY.md')
|
||||
content = obj['Body'].read().decode('utf-8')
|
||||
parts.append(f'## MEMORY.md\n{content}')
|
||||
print(f'[prompt_builder] Loaded MEMORY.md (fallback, {len(content)} bytes)')
|
||||
except Exception as e2:
|
||||
print(f'[prompt_builder] Failed to load MEMORY.md: {e2}')
|
||||
else:
|
||||
print(f'[prompt_builder] Failed to load {memory_key}: {e}')
|
||||
|
||||
for fname in ['SOUL.md', 'AGENTS.md', 'IDENTITY.md', 'TOOLS.md']:
|
||||
try:
|
||||
obj = s3.get_object(Bucket=bucket, Key=fname)
|
||||
@@ -40,12 +60,15 @@ def _get_base_prompt() -> str:
|
||||
|
||||
parts.append('## Runtime\nRuntime: agent-claw | host=AgentCore | model=bedrock-claude-sonnet | channel=telegram\nCurrent date/time is provided by the system. Timezone: America/Chicago.')
|
||||
|
||||
_base_prompt = '\n\n---\n\n'.join(parts)
|
||||
print(f'[prompt_builder] Base prompt built: {len(_base_prompt)} chars')
|
||||
return _base_prompt
|
||||
result = '\n\n---\n\n'.join(parts)
|
||||
_prompt_cache[actor_id] = result
|
||||
print(f'[prompt_builder] Prompt built for actor_id={actor_id!r}: {len(result)} chars')
|
||||
return result
|
||||
|
||||
|
||||
def invalidate_prompt() -> None:
|
||||
"""Force rebuild of system prompt on next invocation (call after workspace write)."""
|
||||
global _base_prompt
|
||||
_base_prompt = None
|
||||
def invalidate_prompt(actor_id: str = '') -> None:
|
||||
"""Invalidate cached prompt for a specific actor_id, or all if not specified."""
|
||||
if actor_id:
|
||||
_prompt_cache.pop(actor_id, None)
|
||||
else:
|
||||
_prompt_cache.clear()
|
||||
|
||||
Reference in New Issue
Block a user