Skip to content

Commit 9312dcc

Browse files
authored
chore: bump v0.11.5 (#2777)
2 parents a22e55c + 0356035 commit 9312dcc

File tree

92 files changed

+11584
-2696
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+11584
-2696
lines changed

.github/workflows/send-message-integration-tests.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ jobs:
4141
--health-interval 10s
4242
--health-timeout 5s
4343
--health-retries 5
44+
redis:
45+
image: redis:7
46+
ports:
47+
- 6379:6379
48+
options: >-
49+
--health-cmd "redis-cli ping"
50+
--health-interval 5s
51+
--health-timeout 5s
52+
--health-retries 10
4453
4554
steps:
4655
# Ensure secrets don't leak
@@ -138,6 +147,8 @@ jobs:
138147
LETTA_PG_PASSWORD: postgres
139148
LETTA_PG_DB: postgres
140149
LETTA_PG_HOST: localhost
150+
LETTA_REDIS_HOST: localhost
151+
LETTA_REDIS_PORT: 6379
141152
LETTA_SERVER_PASS: test_server_token
142153
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
143154
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

letta/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
__version__ = version("letta")
66
except PackageNotFoundError:
77
# Fallback for development installations
8-
__version__ = "0.11.4"
8+
__version__ = "0.11.5"
99

1010
if os.environ.get("LETTA_VERSION"):
1111
__version__ = os.environ["LETTA_VERSION"]

letta/agent.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from letta.memory import summarize_messages
4343
from letta.orm import User
4444
from letta.otel.tracing import log_event, trace_method
45+
from letta.prompts.prompt_generator import PromptGenerator
4546
from letta.schemas.agent import AgentState, AgentStepResponse, UpdateAgent, get_prompt_template_for_agent_type
4647
from letta.schemas.block import BlockUpdate
4748
from letta.schemas.embedding_config import EmbeddingConfig
@@ -59,7 +60,7 @@
5960
from letta.schemas.usage import LettaUsageStatistics
6061
from letta.services.agent_manager import AgentManager
6162
from letta.services.block_manager import BlockManager
62-
from letta.services.helpers.agent_manager_helper import check_supports_structured_output, compile_memory_metadata_block
63+
from letta.services.helpers.agent_manager_helper import check_supports_structured_output
6364
from letta.services.helpers.tool_parser_helper import runtime_override_tool_json_schema
6465
from letta.services.job_manager import JobManager
6566
from letta.services.mcp.base_client import AsyncBaseMCPClient
@@ -330,8 +331,13 @@ def _get_ai_reply(
330331
return None
331332

332333
allowed_functions = [func for func in agent_state_tool_jsons if func["name"] in allowed_tool_names]
334+
# Extract terminal tool names from tool rules
335+
terminal_tool_names = {rule.tool_name for rule in self.tool_rules_solver.terminal_tool_rules}
333336
allowed_functions = runtime_override_tool_json_schema(
334-
tool_list=allowed_functions, response_format=self.agent_state.response_format, request_heartbeat=True
337+
tool_list=allowed_functions,
338+
response_format=self.agent_state.response_format,
339+
request_heartbeat=True,
340+
terminal_tools=terminal_tool_names,
335341
)
336342

337343
# For the first message, force the initial tool if one is specified
@@ -1246,7 +1252,7 @@ def get_context_window(self) -> ContextWindowOverview:
12461252

12471253
agent_manager_passage_size = self.agent_manager.passage_size(actor=self.user, agent_id=self.agent_state.id)
12481254
message_manager_size = self.message_manager.size(actor=self.user, agent_id=self.agent_state.id)
1249-
external_memory_summary = compile_memory_metadata_block(
1255+
external_memory_summary = PromptGenerator.compile_memory_metadata_block(
12501256
memory_edit_timestamp=get_utc_time(),
12511257
timezone=self.agent_state.timezone,
12521258
previous_message_count=self.message_manager.size(actor=self.user, agent_id=self.agent_state.id),

letta/agents/base_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from letta.helpers import ToolRulesSolver
88
from letta.helpers.datetime_helpers import get_utc_time
99
from letta.log import get_logger
10+
from letta.prompts.prompt_generator import PromptGenerator
1011
from letta.schemas.agent import AgentState
1112
from letta.schemas.enums import MessageStreamStatus
1213
from letta.schemas.letta_message import LegacyLettaMessage, LettaMessage
@@ -17,7 +18,6 @@
1718
from letta.schemas.usage import LettaUsageStatistics
1819
from letta.schemas.user import User
1920
from letta.services.agent_manager import AgentManager
20-
from letta.services.helpers.agent_manager_helper import get_system_message_from_compiled_memory
2121
from letta.services.message_manager import MessageManager
2222
from letta.services.passage_manager import PassageManager
2323
from letta.utils import united_diff
@@ -142,7 +142,7 @@ def extract_dynamic_section(text):
142142
if num_archival_memories is None:
143143
num_archival_memories = await self.passage_manager.agent_passage_size_async(actor=self.actor, agent_id=agent_state.id)
144144

145-
new_system_message_str = get_system_message_from_compiled_memory(
145+
new_system_message_str = PromptGenerator.get_system_message_from_compiled_memory(
146146
system_prompt=agent_state.system,
147147
memory_with_sources=curr_memory_str,
148148
in_context_memory_last_edit=memory_edit_timestamp,

letta/agents/letta_agent.py

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ def __init__(
137137
message_buffer_limit=message_buffer_limit,
138138
message_buffer_min=message_buffer_min,
139139
partial_evict_summarizer_percentage=partial_evict_summarizer_percentage,
140+
agent_manager=self.agent_manager,
141+
message_manager=self.message_manager,
142+
actor=self.actor,
143+
agent_id=self.agent_id,
140144
)
141145

142146
async def _check_run_cancellation(self) -> bool:
@@ -345,16 +349,17 @@ async def step_stream_no_tokens(
345349
agent_step_span.end()
346350

347351
# Log LLM Trace
348-
await self.telemetry_manager.create_provider_trace_async(
349-
actor=self.actor,
350-
provider_trace_create=ProviderTraceCreate(
351-
request_json=request_data,
352-
response_json=response_data,
353-
step_id=step_id, # Use original step_id for telemetry
354-
organization_id=self.actor.organization_id,
355-
),
356-
)
357-
step_progression = StepProgression.LOGGED_TRACE
352+
if settings.track_provider_trace:
353+
await self.telemetry_manager.create_provider_trace_async(
354+
actor=self.actor,
355+
provider_trace_create=ProviderTraceCreate(
356+
request_json=request_data,
357+
response_json=response_data,
358+
step_id=step_id, # Use original step_id for telemetry
359+
organization_id=self.actor.organization_id,
360+
),
361+
)
362+
step_progression = StepProgression.LOGGED_TRACE
358363

359364
# stream step
360365
# TODO: improve TTFT
@@ -642,17 +647,18 @@ async def _step(
642647
agent_step_span.end()
643648

644649
# Log LLM Trace
645-
await self.telemetry_manager.create_provider_trace_async(
646-
actor=self.actor,
647-
provider_trace_create=ProviderTraceCreate(
648-
request_json=request_data,
649-
response_json=response_data,
650-
step_id=step_id, # Use original step_id for telemetry
651-
organization_id=self.actor.organization_id,
652-
),
653-
)
650+
if settings.track_provider_trace:
651+
await self.telemetry_manager.create_provider_trace_async(
652+
actor=self.actor,
653+
provider_trace_create=ProviderTraceCreate(
654+
request_json=request_data,
655+
response_json=response_data,
656+
step_id=step_id, # Use original step_id for telemetry
657+
organization_id=self.actor.organization_id,
658+
),
659+
)
660+
step_progression = StepProgression.LOGGED_TRACE
654661

655-
step_progression = StepProgression.LOGGED_TRACE
656662
MetricRegistry().step_execution_time_ms_histogram.record(get_utc_timestamp_ns() - step_start, get_ctx_attributes())
657663
step_progression = StepProgression.FINISHED
658664

@@ -1003,31 +1009,32 @@ async def step_stream(
10031009
# Log LLM Trace
10041010
# We are piecing together the streamed response here.
10051011
# Content here does not match the actual response schema as streams come in chunks.
1006-
await self.telemetry_manager.create_provider_trace_async(
1007-
actor=self.actor,
1008-
provider_trace_create=ProviderTraceCreate(
1009-
request_json=request_data,
1010-
response_json={
1011-
"content": {
1012-
"tool_call": tool_call.model_dump_json(),
1013-
"reasoning": [content.model_dump_json() for content in reasoning_content],
1012+
if settings.track_provider_trace:
1013+
await self.telemetry_manager.create_provider_trace_async(
1014+
actor=self.actor,
1015+
provider_trace_create=ProviderTraceCreate(
1016+
request_json=request_data,
1017+
response_json={
1018+
"content": {
1019+
"tool_call": tool_call.model_dump_json(),
1020+
"reasoning": [content.model_dump_json() for content in reasoning_content],
1021+
},
1022+
"id": interface.message_id,
1023+
"model": interface.model,
1024+
"role": "assistant",
1025+
# "stop_reason": "",
1026+
# "stop_sequence": None,
1027+
"type": "message",
1028+
"usage": {
1029+
"input_tokens": usage.prompt_tokens,
1030+
"output_tokens": usage.completion_tokens,
1031+
},
10141032
},
1015-
"id": interface.message_id,
1016-
"model": interface.model,
1017-
"role": "assistant",
1018-
# "stop_reason": "",
1019-
# "stop_sequence": None,
1020-
"type": "message",
1021-
"usage": {
1022-
"input_tokens": usage.prompt_tokens,
1023-
"output_tokens": usage.completion_tokens,
1024-
},
1025-
},
1026-
step_id=step_id, # Use original step_id for telemetry
1027-
organization_id=self.actor.organization_id,
1028-
),
1029-
)
1030-
step_progression = StepProgression.LOGGED_TRACE
1033+
step_id=step_id, # Use original step_id for telemetry
1034+
organization_id=self.actor.organization_id,
1035+
),
1036+
)
1037+
step_progression = StepProgression.LOGGED_TRACE
10311038

10321039
# yields tool response as this is handled from Letta and not the response from the LLM provider
10331040
tool_return = [msg for msg in persisted_messages if msg.role == "tool"][-1].to_letta_messages()[0]
@@ -1352,6 +1359,7 @@ async def _rebuild_context_window(
13521359
) -> list[Message]:
13531360
# If total tokens is reached, we truncate down
13541361
# TODO: This can be broken by bad configs, e.g. lower bound too high, initial messages too fat, etc.
1362+
# TODO: `force` and `clear` seem to no longer be used, we should remove
13551363
if force or (total_tokens and total_tokens > llm_config.context_window):
13561364
self.logger.warning(
13571365
f"Total tokens {total_tokens} exceeds configured max tokens {llm_config.context_window}, forcefully clearing message history."
@@ -1363,6 +1371,7 @@ async def _rebuild_context_window(
13631371
clear=True,
13641372
)
13651373
else:
1374+
# NOTE (Sarah): Seems like this is doing nothing?
13661375
self.logger.info(
13671376
f"Total tokens {total_tokens} does not exceed configured max tokens {llm_config.context_window}, passing summarizing w/o force."
13681377
)
@@ -1453,8 +1462,10 @@ async def _create_llm_request_data_async(
14531462
force_tool_call = valid_tool_names[0]
14541463

14551464
allowed_tools = [enable_strict_mode(t.json_schema) for t in tools if t.name in set(valid_tool_names)]
1465+
# Extract terminal tool names from tool rules
1466+
terminal_tool_names = {rule.tool_name for rule in tool_rules_solver.terminal_tool_rules}
14561467
allowed_tools = runtime_override_tool_json_schema(
1457-
tool_list=allowed_tools, response_format=agent_state.response_format, request_heartbeat=True
1468+
tool_list=allowed_tools, response_format=agent_state.response_format, request_heartbeat=True, terminal_tools=terminal_tool_names
14581469
)
14591470

14601471
return (

letta/agents/voice_agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from letta.helpers.tool_execution_helper import add_pre_execution_message, enable_strict_mode, remove_request_heartbeat
1414
from letta.interfaces.openai_chat_completions_streaming_interface import OpenAIChatCompletionsStreamingInterface
1515
from letta.log import get_logger
16+
from letta.prompts.prompt_generator import PromptGenerator
1617
from letta.schemas.agent import AgentState, AgentType
1718
from letta.schemas.enums import MessageRole, ToolType
1819
from letta.schemas.letta_response import LettaResponse
@@ -35,7 +36,6 @@
3536
)
3637
from letta.services.agent_manager import AgentManager
3738
from letta.services.block_manager import BlockManager
38-
from letta.services.helpers.agent_manager_helper import compile_system_message_async
3939
from letta.services.job_manager import JobManager
4040
from letta.services.message_manager import MessageManager
4141
from letta.services.passage_manager import PassageManager
@@ -144,7 +144,7 @@ async def step_stream(self, input_messages: List[MessageCreate], max_steps: int
144144

145145
in_context_messages = await self.message_manager.get_messages_by_ids_async(message_ids=agent_state.message_ids, actor=self.actor)
146146
memory_edit_timestamp = get_utc_time()
147-
in_context_messages[0].content[0].text = await compile_system_message_async(
147+
in_context_messages[0].content[0].text = await PromptGenerator.compile_system_message_async(
148148
system_prompt=agent_state.system,
149149
in_context_memory=agent_state.memory,
150150
in_context_memory_last_edit=memory_edit_timestamp,

0 commit comments

Comments
 (0)