letta-nightly 0.8.0.dev20250606195656__py3-none-any.whl → 0.8.2.dev20250606215616__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.
- letta/__init__.py +1 -1
- letta/agent.py +1 -1
- letta/agents/letta_agent.py +49 -29
- letta/agents/letta_agent_batch.py +1 -2
- letta/agents/voice_agent.py +19 -13
- letta/agents/voice_sleeptime_agent.py +11 -3
- letta/constants.py +18 -0
- letta/data_sources/__init__.py +0 -0
- letta/data_sources/redis_client.py +282 -0
- letta/errors.py +0 -4
- letta/functions/function_sets/files.py +58 -0
- letta/functions/schema_generator.py +18 -1
- letta/groups/sleeptime_multi_agent_v2.py +1 -1
- letta/helpers/datetime_helpers.py +47 -3
- letta/helpers/decorators.py +69 -0
- letta/{services/helpers/noop_helper.py → helpers/singleton.py} +5 -0
- letta/interfaces/anthropic_streaming_interface.py +43 -24
- letta/interfaces/openai_streaming_interface.py +21 -19
- letta/llm_api/anthropic.py +1 -1
- letta/llm_api/anthropic_client.py +22 -14
- letta/llm_api/google_vertex_client.py +1 -1
- letta/llm_api/helpers.py +36 -30
- letta/llm_api/llm_api_tools.py +1 -1
- letta/llm_api/llm_client_base.py +29 -1
- letta/llm_api/openai.py +1 -1
- letta/llm_api/openai_client.py +6 -8
- letta/local_llm/chat_completion_proxy.py +1 -1
- letta/memory.py +1 -1
- letta/orm/enums.py +1 -0
- letta/orm/file.py +80 -3
- letta/orm/files_agents.py +13 -0
- letta/orm/sqlalchemy_base.py +34 -11
- letta/otel/__init__.py +0 -0
- letta/otel/context.py +25 -0
- letta/otel/events.py +0 -0
- letta/otel/metric_registry.py +122 -0
- letta/otel/metrics.py +66 -0
- letta/otel/resource.py +26 -0
- letta/{tracing.py → otel/tracing.py} +55 -78
- letta/plugins/README.md +22 -0
- letta/plugins/__init__.py +0 -0
- letta/plugins/defaults.py +11 -0
- letta/plugins/plugins.py +72 -0
- letta/schemas/enums.py +8 -0
- letta/schemas/file.py +12 -0
- letta/schemas/tool.py +4 -0
- letta/server/db.py +7 -7
- letta/server/rest_api/app.py +8 -6
- letta/server/rest_api/routers/v1/agents.py +37 -36
- letta/server/rest_api/routers/v1/groups.py +3 -3
- letta/server/rest_api/routers/v1/sources.py +26 -3
- letta/server/rest_api/utils.py +9 -6
- letta/server/server.py +18 -12
- letta/services/agent_manager.py +185 -193
- letta/services/block_manager.py +1 -1
- letta/services/context_window_calculator/token_counter.py +3 -2
- letta/services/file_processor/chunker/line_chunker.py +34 -0
- letta/services/file_processor/file_processor.py +40 -11
- letta/services/file_processor/parser/mistral_parser.py +11 -1
- letta/services/files_agents_manager.py +96 -7
- letta/services/group_manager.py +6 -6
- letta/services/helpers/agent_manager_helper.py +373 -3
- letta/services/identity_manager.py +1 -1
- letta/services/job_manager.py +1 -1
- letta/services/llm_batch_manager.py +1 -1
- letta/services/message_manager.py +1 -1
- letta/services/organization_manager.py +1 -1
- letta/services/passage_manager.py +1 -1
- letta/services/per_agent_lock_manager.py +1 -1
- letta/services/provider_manager.py +1 -1
- letta/services/sandbox_config_manager.py +1 -1
- letta/services/source_manager.py +178 -19
- letta/services/step_manager.py +2 -2
- letta/services/summarizer/summarizer.py +1 -1
- letta/services/telemetry_manager.py +1 -1
- letta/services/tool_executor/builtin_tool_executor.py +117 -0
- letta/services/tool_executor/composio_tool_executor.py +53 -0
- letta/services/tool_executor/core_tool_executor.py +474 -0
- letta/services/tool_executor/files_tool_executor.py +131 -0
- letta/services/tool_executor/mcp_tool_executor.py +45 -0
- letta/services/tool_executor/multi_agent_tool_executor.py +123 -0
- letta/services/tool_executor/tool_execution_manager.py +34 -14
- letta/services/tool_executor/tool_execution_sandbox.py +1 -1
- letta/services/tool_executor/tool_executor.py +3 -802
- letta/services/tool_executor/tool_executor_base.py +43 -0
- letta/services/tool_manager.py +55 -59
- letta/services/tool_sandbox/e2b_sandbox.py +1 -1
- letta/services/tool_sandbox/local_sandbox.py +6 -3
- letta/services/user_manager.py +6 -3
- letta/settings.py +21 -1
- letta/utils.py +7 -2
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.2.dev20250606215616.dist-info}/METADATA +4 -2
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.2.dev20250606215616.dist-info}/RECORD +96 -74
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.2.dev20250606215616.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.2.dev20250606215616.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.2.dev20250606215616.dist-info}/entry_points.txt +0 -0
letta/__init__.py
CHANGED
letta/agent.py
CHANGED
@@ -41,6 +41,7 @@ from letta.log import get_logger
|
|
41
41
|
from letta.memory import summarize_messages
|
42
42
|
from letta.orm import User
|
43
43
|
from letta.orm.enums import ToolType
|
44
|
+
from letta.otel.tracing import log_event, trace_method
|
44
45
|
from letta.schemas.agent import AgentState, AgentStepResponse, UpdateAgent, get_prompt_template_for_agent_type
|
45
46
|
from letta.schemas.block import BlockUpdate
|
46
47
|
from letta.schemas.embedding_config import EmbeddingConfig
|
@@ -72,7 +73,6 @@ from letta.services.tool_manager import ToolManager
|
|
72
73
|
from letta.settings import settings, summarizer_settings, model_settings
|
73
74
|
from letta.streaming_interface import StreamingRefreshCLIInterface
|
74
75
|
from letta.system import get_heartbeat, get_token_limit_warning, package_function_response, package_summarize_message, package_user_message
|
75
|
-
from letta.tracing import log_event, trace_method
|
76
76
|
from letta.utils import count_tokens, get_friendly_error_msg, get_tool_call_id, log_telemetry, parse_json, validate_function_response
|
77
77
|
|
78
78
|
logger = get_logger(__name__)
|
letta/agents/letta_agent.py
CHANGED
@@ -14,9 +14,9 @@ from letta.agents.helpers import (
|
|
14
14
|
_prepare_in_context_messages_no_persist_async,
|
15
15
|
generate_step_id,
|
16
16
|
)
|
17
|
-
from letta.errors import
|
17
|
+
from letta.errors import ContextWindowExceededError
|
18
18
|
from letta.helpers import ToolRulesSolver
|
19
|
-
from letta.helpers.datetime_helpers import get_utc_timestamp_ns
|
19
|
+
from letta.helpers.datetime_helpers import AsyncTimer, get_utc_timestamp_ns, ns_to_ms
|
20
20
|
from letta.helpers.tool_execution_helper import enable_strict_mode
|
21
21
|
from letta.interfaces.anthropic_streaming_interface import AnthropicStreamingInterface
|
22
22
|
from letta.interfaces.openai_streaming_interface import OpenAIStreamingInterface
|
@@ -25,6 +25,9 @@ from letta.llm_api.llm_client_base import LLMClientBase
|
|
25
25
|
from letta.local_llm.constants import INNER_THOUGHTS_KWARG
|
26
26
|
from letta.log import get_logger
|
27
27
|
from letta.orm.enums import ToolType
|
28
|
+
from letta.otel.context import get_ctx_attributes
|
29
|
+
from letta.otel.metric_registry import MetricRegistry
|
30
|
+
from letta.otel.tracing import log_event, trace_method, tracer
|
28
31
|
from letta.schemas.agent import AgentState
|
29
32
|
from letta.schemas.enums import MessageRole, MessageStreamStatus
|
30
33
|
from letta.schemas.letta_message_content import OmittedReasoningContent, ReasoningContent, RedactedReasoningContent, TextContent
|
@@ -48,7 +51,7 @@ from letta.services.telemetry_manager import NoopTelemetryManager, TelemetryMana
|
|
48
51
|
from letta.services.tool_executor.tool_execution_manager import ToolExecutionManager
|
49
52
|
from letta.settings import model_settings
|
50
53
|
from letta.system import package_function_response
|
51
|
-
from letta.
|
54
|
+
from letta.types import JsonDict
|
52
55
|
from letta.utils import log_telemetry, validate_function_response
|
53
56
|
|
54
57
|
logger = get_logger(__name__)
|
@@ -178,7 +181,7 @@ class LettaAgent(BaseAgent):
|
|
178
181
|
# log llm request time
|
179
182
|
now = get_utc_timestamp_ns()
|
180
183
|
llm_request_ns = now - step_start
|
181
|
-
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": llm_request_ns
|
184
|
+
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": ns_to_ms(llm_request_ns)})
|
182
185
|
|
183
186
|
response = llm_client.convert_response_to_chat_completion(response_data, in_context_messages, agent_state.llm_config)
|
184
187
|
|
@@ -210,7 +213,7 @@ class LettaAgent(BaseAgent):
|
|
210
213
|
# log LLM request time
|
211
214
|
now = get_utc_timestamp_ns()
|
212
215
|
llm_request_ns = now - step_start
|
213
|
-
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": llm_request_ns
|
216
|
+
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": ns_to_ms(llm_request_ns)})
|
214
217
|
|
215
218
|
persisted_messages, should_continue = await self._handle_ai_response(
|
216
219
|
tool_call,
|
@@ -227,7 +230,7 @@ class LettaAgent(BaseAgent):
|
|
227
230
|
# log step time
|
228
231
|
now = get_utc_timestamp_ns()
|
229
232
|
step_ns = now - step_start
|
230
|
-
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": step_ns
|
233
|
+
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": ns_to_ms(step_ns)})
|
231
234
|
agent_step_span.end()
|
232
235
|
|
233
236
|
# Log LLM Trace
|
@@ -267,7 +270,7 @@ class LettaAgent(BaseAgent):
|
|
267
270
|
if request_start_timestamp_ns:
|
268
271
|
now = get_utc_timestamp_ns()
|
269
272
|
request_ns = now - request_start_timestamp_ns
|
270
|
-
request_span.add_event(name="letta_request_ms", attributes={"duration_ms": request_ns
|
273
|
+
request_span.add_event(name="letta_request_ms", attributes={"duration_ms": ns_to_ms(request_ns)})
|
271
274
|
request_span.end()
|
272
275
|
|
273
276
|
# Return back usage
|
@@ -321,7 +324,7 @@ class LettaAgent(BaseAgent):
|
|
321
324
|
# log LLM request time
|
322
325
|
now = get_utc_timestamp_ns()
|
323
326
|
llm_request_ns = now - step_start
|
324
|
-
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": llm_request_ns
|
327
|
+
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": ns_to_ms(llm_request_ns)})
|
325
328
|
|
326
329
|
# TODO: add run_id
|
327
330
|
usage.step_count += 1
|
@@ -363,7 +366,7 @@ class LettaAgent(BaseAgent):
|
|
363
366
|
# log step time
|
364
367
|
now = get_utc_timestamp_ns()
|
365
368
|
step_ns = now - step_start
|
366
|
-
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": step_ns
|
369
|
+
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": ns_to_ms(step_ns)})
|
367
370
|
agent_step_span.end()
|
368
371
|
|
369
372
|
# Log LLM Trace
|
@@ -384,7 +387,7 @@ class LettaAgent(BaseAgent):
|
|
384
387
|
if request_start_timestamp_ns:
|
385
388
|
now = get_utc_timestamp_ns()
|
386
389
|
request_ns = now - request_start_timestamp_ns
|
387
|
-
request_span.add_event(name="request_ms", attributes={"duration_ms": request_ns
|
390
|
+
request_span.add_event(name="request_ms", attributes={"duration_ms": ns_to_ms(request_ns)})
|
388
391
|
request_span.end()
|
389
392
|
|
390
393
|
# Extend the in context message ids
|
@@ -480,7 +483,7 @@ class LettaAgent(BaseAgent):
|
|
480
483
|
if first_chunk and request_span is not None:
|
481
484
|
now = get_utc_timestamp_ns()
|
482
485
|
ttft_ns = now - request_start_timestamp_ns
|
483
|
-
request_span.add_event(name="time_to_first_token_ms", attributes={"ttft_ms": ttft_ns
|
486
|
+
request_span.add_event(name="time_to_first_token_ms", attributes={"ttft_ms": ns_to_ms(ttft_ns)})
|
484
487
|
first_chunk = False
|
485
488
|
|
486
489
|
yield f"data: {chunk.model_dump_json()}\n\n"
|
@@ -490,6 +493,9 @@ class LettaAgent(BaseAgent):
|
|
490
493
|
usage.completion_tokens += interface.output_tokens
|
491
494
|
usage.prompt_tokens += interface.input_tokens
|
492
495
|
usage.total_tokens += interface.input_tokens + interface.output_tokens
|
496
|
+
MetricRegistry().message_output_tokens.record(
|
497
|
+
interface.output_tokens, dict(get_ctx_attributes(), **{"model.name": agent_state.llm_config.model})
|
498
|
+
)
|
493
499
|
|
494
500
|
# Persist input messages if not already
|
495
501
|
# Special strategy to lower TTFT
|
@@ -500,7 +506,7 @@ class LettaAgent(BaseAgent):
|
|
500
506
|
# log LLM request time
|
501
507
|
now = get_utc_timestamp_ns()
|
502
508
|
llm_request_ns = now - step_start
|
503
|
-
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": llm_request_ns
|
509
|
+
agent_step_span.add_event(name="llm_request_ms", attributes={"duration_ms": ns_to_ms(llm_request_ns)})
|
504
510
|
|
505
511
|
# Process resulting stream content
|
506
512
|
tool_call = interface.get_tool_call_object()
|
@@ -515,8 +521,7 @@ class LettaAgent(BaseAgent):
|
|
515
521
|
total_tokens=interface.input_tokens + interface.output_tokens,
|
516
522
|
),
|
517
523
|
reasoning_content=reasoning_content,
|
518
|
-
pre_computed_assistant_message_id=interface.
|
519
|
-
pre_computed_tool_message_id=interface.letta_tool_message_id,
|
524
|
+
pre_computed_assistant_message_id=interface.letta_message_id,
|
520
525
|
step_id=step_id,
|
521
526
|
agent_step_span=agent_step_span,
|
522
527
|
)
|
@@ -526,7 +531,7 @@ class LettaAgent(BaseAgent):
|
|
526
531
|
# log total step time
|
527
532
|
now = get_utc_timestamp_ns()
|
528
533
|
step_ns = now - step_start
|
529
|
-
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": step_ns
|
534
|
+
agent_step_span.add_event(name="step_ms", attributes={"duration_ms": ns_to_ms(step_ns)})
|
530
535
|
agent_step_span.end()
|
531
536
|
|
532
537
|
# TODO (cliandy): the stream POST request span has ended at this point, we should tie this to the stream
|
@@ -556,8 +561,8 @@ class LettaAgent(BaseAgent):
|
|
556
561
|
),
|
557
562
|
)
|
558
563
|
|
559
|
-
if
|
560
|
-
|
564
|
+
tool_return = [msg for msg in persisted_messages if msg.role == "tool"][-1].to_letta_messages()[0]
|
565
|
+
if not (use_assistant_message and tool_return.name == "send_message"):
|
561
566
|
yield f"data: {tool_return.model_dump_json()}\n\n"
|
562
567
|
|
563
568
|
if not should_continue:
|
@@ -577,7 +582,7 @@ class LettaAgent(BaseAgent):
|
|
577
582
|
if request_start_timestamp_ns:
|
578
583
|
now = get_utc_timestamp_ns()
|
579
584
|
request_ns = now - request_start_timestamp_ns
|
580
|
-
request_span.add_event(name="letta_request_ms", attributes={"duration_ms": request_ns
|
585
|
+
request_span.add_event(name="letta_request_ms", attributes={"duration_ms": ns_to_ms(request_ns)})
|
581
586
|
request_span.end()
|
582
587
|
|
583
588
|
# TODO: Also yield out a letta usage stats SSE
|
@@ -604,10 +609,16 @@ class LettaAgent(BaseAgent):
|
|
604
609
|
)
|
605
610
|
log_event("agent.stream_no_tokens.llm_request.created")
|
606
611
|
|
612
|
+
async with AsyncTimer() as timer:
|
613
|
+
response = await llm_client.request_async(request_data, agent_state.llm_config)
|
614
|
+
MetricRegistry().llm_execution_time_ms_histogram.record(
|
615
|
+
timer.elapsed_ms,
|
616
|
+
dict(get_ctx_attributes(), **{"model.name": agent_state.llm_config.model}),
|
617
|
+
)
|
607
618
|
# Attempt LLM request
|
608
619
|
return (
|
609
620
|
request_data,
|
610
|
-
|
621
|
+
response,
|
611
622
|
current_in_context_messages,
|
612
623
|
new_in_context_messages,
|
613
624
|
)
|
@@ -654,9 +665,7 @@ class LettaAgent(BaseAgent):
|
|
654
665
|
if first_chunk and ttft_span is not None:
|
655
666
|
provider_request_start_timestamp_ns = get_utc_timestamp_ns()
|
656
667
|
provider_req_start_ns = provider_request_start_timestamp_ns - request_start_timestamp_ns
|
657
|
-
ttft_span.add_event(
|
658
|
-
name="provider_req_start_ns", attributes={"provider_req_start_ms": provider_req_start_ns // 1_000_000}
|
659
|
-
)
|
668
|
+
ttft_span.add_event(name="provider_req_start_ns", attributes={"provider_req_start_ms": ns_to_ms(provider_req_start_ns)})
|
660
669
|
|
661
670
|
# Attempt LLM request
|
662
671
|
return (
|
@@ -692,7 +701,7 @@ class LettaAgent(BaseAgent):
|
|
692
701
|
llm_config: LLMConfig,
|
693
702
|
force: bool,
|
694
703
|
) -> List[Message]:
|
695
|
-
if isinstance(e,
|
704
|
+
if isinstance(e, ContextWindowExceededError):
|
696
705
|
return await self._rebuild_context_window(
|
697
706
|
in_context_messages=in_context_messages, new_letta_messages=new_letta_messages, llm_config=llm_config, force=force
|
698
707
|
)
|
@@ -775,6 +784,7 @@ class LettaAgent(BaseAgent):
|
|
775
784
|
ToolType.LETTA_SLEEPTIME_CORE,
|
776
785
|
ToolType.LETTA_VOICE_SLEEPTIME_CORE,
|
777
786
|
ToolType.LETTA_BUILTIN,
|
787
|
+
ToolType.LETTA_FILES_CORE,
|
778
788
|
ToolType.EXTERNAL_COMPOSIO,
|
779
789
|
ToolType.EXTERNAL_MCP,
|
780
790
|
}
|
@@ -810,7 +820,6 @@ class LettaAgent(BaseAgent):
|
|
810
820
|
usage: UsageStatistics,
|
811
821
|
reasoning_content: Optional[List[Union[TextContent, ReasoningContent, RedactedReasoningContent, OmittedReasoningContent]]] = None,
|
812
822
|
pre_computed_assistant_message_id: Optional[str] = None,
|
813
|
-
pre_computed_tool_message_id: Optional[str] = None,
|
814
823
|
step_id: str | None = None,
|
815
824
|
new_in_context_messages: Optional[List[Message]] = None,
|
816
825
|
agent_step_span: Optional["Span"] = None,
|
@@ -822,6 +831,9 @@ class LettaAgent(BaseAgent):
|
|
822
831
|
"""
|
823
832
|
tool_call_name = tool_call.function.name
|
824
833
|
tool_call_args_str = tool_call.function.arguments
|
834
|
+
# Temp hack to gracefully handle parallel tool calling attempt, only take first one
|
835
|
+
if "}{" in tool_call_args_str:
|
836
|
+
tool_call_args_str = tool_call_args_str.split("}{", 1)[0] + "}"
|
825
837
|
|
826
838
|
try:
|
827
839
|
tool_args = json.loads(tool_call_args_str)
|
@@ -859,6 +871,7 @@ class LettaAgent(BaseAgent):
|
|
859
871
|
tool_args=tool_args,
|
860
872
|
agent_state=agent_state,
|
861
873
|
agent_step_span=agent_step_span,
|
874
|
+
step_id=step_id,
|
862
875
|
)
|
863
876
|
log_telemetry(
|
864
877
|
self.logger, "_handle_ai_response execute tool finish", tool_execution_result=tool_execution_result, tool_call_id=tool_call_id
|
@@ -926,7 +939,6 @@ class LettaAgent(BaseAgent):
|
|
926
939
|
add_heartbeat_request_system_message=continue_stepping,
|
927
940
|
reasoning_content=reasoning_content,
|
928
941
|
pre_computed_assistant_message_id=pre_computed_assistant_message_id,
|
929
|
-
pre_computed_tool_message_id=pre_computed_tool_message_id,
|
930
942
|
step_id=logged_step.id if logged_step else None, # TODO (cliandy): eventually move over other agent loops
|
931
943
|
)
|
932
944
|
|
@@ -937,10 +949,15 @@ class LettaAgent(BaseAgent):
|
|
937
949
|
|
938
950
|
@trace_method
|
939
951
|
async def _execute_tool(
|
940
|
-
self,
|
952
|
+
self,
|
953
|
+
tool_name: str,
|
954
|
+
tool_args: JsonDict,
|
955
|
+
agent_state: AgentState,
|
956
|
+
agent_step_span: Optional["Span"] = None,
|
957
|
+
step_id: str | None = None,
|
941
958
|
) -> "ToolExecutionResult":
|
942
959
|
"""
|
943
|
-
Executes a tool and returns
|
960
|
+
Executes a tool and returns the ToolExecutionResult.
|
944
961
|
"""
|
945
962
|
from letta.schemas.tool_execution_result import ToolExecutionResult
|
946
963
|
|
@@ -972,7 +989,10 @@ class LettaAgent(BaseAgent):
|
|
972
989
|
# TODO: Integrate sandbox result
|
973
990
|
log_event(name=f"start_{tool_name}_execution", attributes=tool_args)
|
974
991
|
tool_execution_result = await tool_execution_manager.execute_tool_async(
|
975
|
-
function_name=tool_name,
|
992
|
+
function_name=tool_name,
|
993
|
+
function_args=tool_args,
|
994
|
+
tool=target_tool,
|
995
|
+
step_id=step_id,
|
976
996
|
)
|
977
997
|
if agent_step_span:
|
978
998
|
end_time = get_utc_timestamp_ns()
|
@@ -980,7 +1000,7 @@ class LettaAgent(BaseAgent):
|
|
980
1000
|
name="tool_execution_completed",
|
981
1001
|
attributes={
|
982
1002
|
"tool_name": target_tool.name,
|
983
|
-
"duration_ms": (end_time - start_time)
|
1003
|
+
"duration_ms": ns_to_ms((end_time - start_time)),
|
984
1004
|
"success": tool_execution_result.success_flag,
|
985
1005
|
"tool_type": target_tool.tool_type,
|
986
1006
|
"tool_id": target_tool.id,
|
@@ -16,6 +16,7 @@ from letta.llm_api.llm_client import LLMClient
|
|
16
16
|
from letta.local_llm.constants import INNER_THOUGHTS_KWARG
|
17
17
|
from letta.log import get_logger
|
18
18
|
from letta.orm.enums import ToolType
|
19
|
+
from letta.otel.tracing import log_event, trace_method
|
19
20
|
from letta.schemas.agent import AgentState, AgentStepState
|
20
21
|
from letta.schemas.enums import AgentStepStatus, JobStatus, MessageStreamStatus, ProviderType
|
21
22
|
from letta.schemas.job import JobUpdate
|
@@ -39,7 +40,6 @@ from letta.services.passage_manager import PassageManager
|
|
39
40
|
from letta.services.sandbox_config_manager import SandboxConfigManager
|
40
41
|
from letta.services.tool_executor.tool_execution_manager import ToolExecutionManager
|
41
42
|
from letta.settings import tool_settings
|
42
|
-
from letta.tracing import log_event, trace_method
|
43
43
|
|
44
44
|
logger = get_logger(__name__)
|
45
45
|
|
@@ -551,7 +551,6 @@ class LettaAgentBatch(BaseAgent):
|
|
551
551
|
add_heartbeat_request_system_message=False,
|
552
552
|
reasoning_content=reasoning_content,
|
553
553
|
pre_computed_assistant_message_id=None,
|
554
|
-
pre_computed_tool_message_id=None,
|
555
554
|
llm_batch_item_id=llm_batch_item_id,
|
556
555
|
)
|
557
556
|
|
letta/agents/voice_agent.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
import json
|
2
3
|
import uuid
|
3
4
|
from datetime import datetime, timedelta, timezone
|
@@ -81,8 +82,8 @@ class VoiceAgent(BaseAgent):
|
|
81
82
|
self.summary_block_label = "human"
|
82
83
|
|
83
84
|
# Cached archival memory/message size
|
84
|
-
self.num_messages =
|
85
|
-
self.num_archival_memories =
|
85
|
+
self.num_messages = None
|
86
|
+
self.num_archival_memories = None
|
86
87
|
|
87
88
|
def init_summarizer(self, agent_state: AgentState) -> Summarizer:
|
88
89
|
if not agent_state.multi_agent_group:
|
@@ -118,13 +119,12 @@ class VoiceAgent(BaseAgent):
|
|
118
119
|
Main streaming loop that yields partial tokens.
|
119
120
|
Whenever we detect a tool call, we yield from _handle_ai_response as well.
|
120
121
|
"""
|
121
|
-
print("CALL STREAM")
|
122
122
|
if len(input_messages) != 1 or input_messages[0].role != MessageRole.user:
|
123
123
|
raise ValueError(f"Voice Agent was invoked with multiple input messages or message did not have role `user`: {input_messages}")
|
124
124
|
|
125
125
|
user_query = input_messages[0].content[0].text
|
126
126
|
|
127
|
-
agent_state = self.agent_manager.
|
127
|
+
agent_state = await self.agent_manager.get_agent_by_id_async(self.agent_id, actor=self.actor)
|
128
128
|
|
129
129
|
# TODO: Refactor this so it uses our in-house clients
|
130
130
|
# TODO: For now, piggyback off of OpenAI client for ease
|
@@ -140,7 +140,7 @@ class VoiceAgent(BaseAgent):
|
|
140
140
|
|
141
141
|
summarizer = self.init_summarizer(agent_state=agent_state)
|
142
142
|
|
143
|
-
in_context_messages = self.message_manager.
|
143
|
+
in_context_messages = await self.message_manager.get_messages_by_ids_async(message_ids=agent_state.message_ids, actor=self.actor)
|
144
144
|
memory_edit_timestamp = get_utc_time()
|
145
145
|
in_context_messages[0].content[0].text = compile_system_message(
|
146
146
|
system_prompt=agent_state.system,
|
@@ -183,10 +183,6 @@ class VoiceAgent(BaseAgent):
|
|
183
183
|
# Rebuild context window if desired
|
184
184
|
await self._rebuild_context_window(summarizer, in_context_messages, letta_message_db_queue)
|
185
185
|
|
186
|
-
# TODO: This may be out of sync, if in between steps users add files
|
187
|
-
self.num_messages = self.message_manager.size(actor=self.actor, agent_id=agent_state.id)
|
188
|
-
self.num_archival_memories = self.passage_manager.size(actor=self.actor, agent_id=agent_state.id)
|
189
|
-
|
190
186
|
yield "data: [DONE]\n\n"
|
191
187
|
|
192
188
|
async def _handle_ai_response(
|
@@ -286,14 +282,14 @@ class VoiceAgent(BaseAgent):
|
|
286
282
|
async def _rebuild_context_window(
|
287
283
|
self, summarizer: Summarizer, in_context_messages: List[Message], letta_message_db_queue: List[Message]
|
288
284
|
) -> None:
|
289
|
-
new_letta_messages = self.message_manager.
|
285
|
+
new_letta_messages = await self.message_manager.create_many_messages_async(letta_message_db_queue, actor=self.actor)
|
290
286
|
|
291
287
|
# TODO: Make this more general and configurable, less brittle
|
292
288
|
new_in_context_messages, updated = summarizer.summarize(
|
293
289
|
in_context_messages=in_context_messages, new_letta_messages=new_letta_messages
|
294
290
|
)
|
295
291
|
|
296
|
-
self.agent_manager.
|
292
|
+
await self.agent_manager.set_in_context_messages_async(
|
297
293
|
agent_id=self.agent_id, message_ids=[m.id for m in new_in_context_messages], actor=self.actor
|
298
294
|
)
|
299
295
|
|
@@ -301,9 +297,19 @@ class VoiceAgent(BaseAgent):
|
|
301
297
|
self,
|
302
298
|
in_context_messages: List[Message],
|
303
299
|
agent_state: AgentState,
|
304
|
-
num_messages: int | None = None,
|
305
|
-
num_archival_memories: int | None = None,
|
306
300
|
) -> List[Message]:
|
301
|
+
self.num_messages, self.num_archival_memories = await asyncio.gather(
|
302
|
+
(
|
303
|
+
self.message_manager.size_async(actor=self.actor, agent_id=agent_state.id)
|
304
|
+
if self.num_messages is None
|
305
|
+
else asyncio.sleep(0, result=self.num_messages)
|
306
|
+
),
|
307
|
+
(
|
308
|
+
self.passage_manager.size_async(actor=self.actor, agent_id=agent_state.id)
|
309
|
+
if self.num_archival_memories is None
|
310
|
+
else asyncio.sleep(0, result=self.num_archival_memories)
|
311
|
+
),
|
312
|
+
)
|
307
313
|
return await super()._rebuild_memory_async(
|
308
314
|
in_context_messages, agent_state, num_messages=self.num_messages, num_archival_memories=self.num_archival_memories
|
309
315
|
)
|
@@ -3,6 +3,7 @@ from typing import AsyncGenerator, List, Optional, Tuple, Union
|
|
3
3
|
from letta.agents.helpers import _create_letta_response, serialize_message_history
|
4
4
|
from letta.agents.letta_agent import LettaAgent
|
5
5
|
from letta.orm.enums import ToolType
|
6
|
+
from letta.otel.tracing import trace_method
|
6
7
|
from letta.schemas.agent import AgentState
|
7
8
|
from letta.schemas.block import BlockUpdate
|
8
9
|
from letta.schemas.enums import MessageStreamStatus
|
@@ -17,7 +18,7 @@ from letta.services.message_manager import MessageManager
|
|
17
18
|
from letta.services.passage_manager import PassageManager
|
18
19
|
from letta.services.summarizer.enums import SummarizationMode
|
19
20
|
from letta.services.summarizer.summarizer import Summarizer
|
20
|
-
from letta.
|
21
|
+
from letta.types import JsonDict
|
21
22
|
|
22
23
|
|
23
24
|
class VoiceSleeptimeAgent(LettaAgent):
|
@@ -89,9 +90,16 @@ class VoiceSleeptimeAgent(LettaAgent):
|
|
89
90
|
)
|
90
91
|
|
91
92
|
@trace_method
|
92
|
-
async def _execute_tool(
|
93
|
+
async def _execute_tool(
|
94
|
+
self,
|
95
|
+
tool_name: str,
|
96
|
+
tool_args: JsonDict,
|
97
|
+
agent_state: AgentState,
|
98
|
+
agent_step_span: Optional["Span"] = None,
|
99
|
+
step_id: str | None = None,
|
100
|
+
) -> "ToolExecutionResult":
|
93
101
|
"""
|
94
|
-
Executes a tool and returns
|
102
|
+
Executes a tool and returns the ToolExecutionResult
|
95
103
|
"""
|
96
104
|
from letta.schemas.tool_execution_result import ToolExecutionResult
|
97
105
|
|
letta/constants.py
CHANGED
@@ -21,6 +21,15 @@ LETTA_CORE_TOOL_MODULE_NAME = "letta.functions.function_sets.base"
|
|
21
21
|
LETTA_MULTI_AGENT_TOOL_MODULE_NAME = "letta.functions.function_sets.multi_agent"
|
22
22
|
LETTA_VOICE_TOOL_MODULE_NAME = "letta.functions.function_sets.voice"
|
23
23
|
LETTA_BUILTIN_TOOL_MODULE_NAME = "letta.functions.function_sets.builtin"
|
24
|
+
LETTA_FILES_TOOL_MODULE_NAME = "letta.functions.function_sets.files"
|
25
|
+
|
26
|
+
LETTA_TOOL_MODULE_NAMES = [
|
27
|
+
LETTA_CORE_TOOL_MODULE_NAME,
|
28
|
+
LETTA_MULTI_AGENT_TOOL_MODULE_NAME,
|
29
|
+
LETTA_VOICE_TOOL_MODULE_NAME,
|
30
|
+
LETTA_BUILTIN_TOOL_MODULE_NAME,
|
31
|
+
LETTA_FILES_TOOL_MODULE_NAME,
|
32
|
+
]
|
24
33
|
|
25
34
|
|
26
35
|
# String in the error message for when the context window is too large
|
@@ -112,6 +121,9 @@ MEMORY_TOOLS_LINE_NUMBER_PREFIX_REGEX = re.compile(
|
|
112
121
|
# Built in tools
|
113
122
|
BUILTIN_TOOLS = ["run_code", "web_search"]
|
114
123
|
|
124
|
+
# Built in tools
|
125
|
+
FILES_TOOLS = ["open_file", "close_file", "grep", "search_files"]
|
126
|
+
|
115
127
|
# Set of all built-in Letta tools
|
116
128
|
LETTA_TOOL_SET = set(
|
117
129
|
BASE_TOOLS
|
@@ -121,6 +133,7 @@ LETTA_TOOL_SET = set(
|
|
121
133
|
+ BASE_VOICE_SLEEPTIME_TOOLS
|
122
134
|
+ BASE_VOICE_SLEEPTIME_CHAT_TOOLS
|
123
135
|
+ BUILTIN_TOOLS
|
136
|
+
+ FILES_TOOLS
|
124
137
|
)
|
125
138
|
|
126
139
|
|
@@ -294,6 +307,7 @@ CORE_MEMORY_SOURCE_CHAR_LIMIT: int = 5000
|
|
294
307
|
# Function return limits
|
295
308
|
FUNCTION_RETURN_CHAR_LIMIT = 6000 # ~300 words
|
296
309
|
BASE_FUNCTION_RETURN_CHAR_LIMIT = 1000000 # very high (we rely on implementation)
|
310
|
+
FILE_IS_TRUNCATED_WARNING = "# NOTE: This block is truncated, use functions to view the full content."
|
297
311
|
|
298
312
|
MAX_PAUSE_HEARTBEATS = 360 # in min
|
299
313
|
|
@@ -316,3 +330,7 @@ RESERVED_FILENAMES = {"CON", "PRN", "AUX", "NUL", "COM1", "COM2", "LPT1", "LPT2"
|
|
316
330
|
WEB_SEARCH_CLIP_CONTENT = False
|
317
331
|
WEB_SEARCH_INCLUDE_SCORE = False
|
318
332
|
WEB_SEARCH_SEPARATOR = "\n" + "-" * 40 + "\n"
|
333
|
+
|
334
|
+
REDIS_INCLUDE = "INCLUDE"
|
335
|
+
REDIS_EXCLUDE = "EXCLUDE"
|
336
|
+
REDIS_SET_DEFAULT_VAL = "None"
|
File without changes
|