From dceed06f84ba1c0555cd2067e14194a25ccc1df4 Mon Sep 17 00:00:00 2001 From: jnjpng Date: Thu, 8 Jan 2026 14:41:07 -0800 Subject: [PATCH] fix: set value on agent environment variable as pydantic obj (#8452) base --- letta/schemas/environment_variables.py | 16 ++++++++-------- letta/services/agent_manager.py | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/letta/schemas/environment_variables.py b/letta/schemas/environment_variables.py index 62d59460..5db1c28a 100644 --- a/letta/schemas/environment_variables.py +++ b/letta/schemas/environment_variables.py @@ -34,10 +34,10 @@ class EnvironmentVariableBase(OrmMetadataBase): if self.value_enc and not self.value: # ERROR: This should not happen - all code paths should populate value via async decryption # Log error with stack trace to identify the caller that bypassed async decryption - # logger.warning( - # f"Sync decryption fallback triggered for env var key={self.key}. " - # f"This indicates a code path that bypassed async decryption. Stack trace:\n{''.join(traceback.format_stack())}" - # ) + logger.warning( + f"Sync decryption fallback triggered for env var key={self.key}. " + f"This indicates a code path that bypassed async decryption. Stack trace:\n{''.join(traceback.format_stack())}" + ) # Decrypt value_enc -> value (for API responses) plaintext = self.value_enc.get_plaintext() if plaintext: @@ -45,10 +45,10 @@ class EnvironmentVariableBase(OrmMetadataBase): elif self.value and not self.value_enc: # WARNING: This triggers sync encryption - should use async encryption where possible # Log warning with stack trace to identify the caller - # logger.warning( - # f"Sync encryption fallback triggered for env var key={self.key}. " - # f"This indicates a code path that bypassed async encryption. Stack trace:\n{''.join(traceback.format_stack())}" - # ) + logger.warning( + f"Sync encryption fallback triggered for env var key={self.key}. " + f"This indicates a code path that bypassed async encryption. Stack trace:\n{''.join(traceback.format_stack())}" + ) # Encrypt value -> value_enc (for backward compat when value is provided directly) self.value_enc = Secret.from_plaintext(self.value) return self diff --git a/letta/services/agent_manager.py b/letta/services/agent_manager.py index 76a0b705..9f17a4f8 100644 --- a/letta/services/agent_manager.py +++ b/letta/services/agent_manager.py @@ -60,6 +60,7 @@ from letta.schemas.agent import ( from letta.schemas.block import DEFAULT_BLOCKS, Block as PydanticBlock, BlockUpdate from letta.schemas.embedding_config import EmbeddingConfig from letta.schemas.enums import AgentType, PrimitiveType, ProviderType, TagMatchMode, ToolType, VectorDBProvider +from letta.schemas.environment_variables import AgentEnvironmentVariable as PydanticAgentEnvVar from letta.schemas.file import FileMetadata as PydanticFileMetadata from letta.schemas.group import Group as PydanticGroup, ManagerType from letta.schemas.letta_stop_reason import StopReasonType @@ -593,8 +594,8 @@ class AgentManager: result = await new_agent.to_pydantic_async(include_relationships=include_relationships) if agent_secrets and env_rows: - # Populate value from original plaintext (agent_secrets) to avoid sync decryption in model validator - env_vars = [AgentEnvironmentVariable(**{**row, "value": agent_secrets[row["key"]]}) for row in env_rows] + # Use Pydantic schema (not ORM model) with plaintext to avoid sync decryption in model validator + env_vars = [PydanticAgentEnvVar(**{**row, "value": agent_secrets[row["key"]]}) for row in env_rows] result.tool_exec_environment_variables = env_vars result.secrets = env_vars