letta-nightly 0.8.0.dev20250606195656__py3-none-any.whl → 0.8.3.dev20250607000559__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 +16 -12
- letta/agents/base_agent.py +1 -1
- letta/agents/helpers.py +13 -2
- letta/agents/letta_agent.py +72 -34
- letta/agents/letta_agent_batch.py +1 -2
- letta/agents/voice_agent.py +19 -13
- letta/agents/voice_sleeptime_agent.py +23 -6
- 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 +13 -3
- 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 +30 -16
- 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/passage.py +2 -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/letta_request.py +6 -0
- letta/schemas/passage.py +1 -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 +46 -37
- letta/server/rest_api/routers/v1/groups.py +3 -3
- letta/server/rest_api/routers/v1/sources.py +26 -3
- letta/server/rest_api/routers/v1/tools.py +7 -2
- letta/server/rest_api/utils.py +9 -6
- letta/server/server.py +25 -13
- letta/services/agent_manager.py +186 -194
- letta/services/block_manager.py +1 -1
- letta/services/context_window_calculator/context_window_calculator.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 +43 -12
- 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 +404 -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/mcp/stdio_client.py +5 -1
- letta/services/mcp_manager.py +4 -4
- letta/services/message_manager.py +1 -1
- letta/services/organization_manager.py +1 -1
- letta/services/passage_manager.py +604 -19
- 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 +138 -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 +23 -2
- letta/utils.py +7 -2
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/METADATA +4 -2
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/RECORD +105 -83
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.0.dev20250606195656.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/entry_points.txt +0 -0
@@ -1,27 +1,33 @@
|
|
1
1
|
import datetime
|
2
2
|
from typing import List, Literal, Optional
|
3
3
|
|
4
|
-
|
4
|
+
import numpy as np
|
5
|
+
from sqlalchemy import Select, and_, asc, desc, func, literal, or_, select, union_all
|
5
6
|
from sqlalchemy.sql.expression import exists
|
6
7
|
|
7
8
|
from letta import system
|
8
|
-
from letta.constants import IN_CONTEXT_MEMORY_KEYWORD, STRUCTURED_OUTPUT_MODELS
|
9
|
+
from letta.constants import IN_CONTEXT_MEMORY_KEYWORD, MAX_EMBEDDING_DIM, STRUCTURED_OUTPUT_MODELS
|
10
|
+
from letta.embeddings import embedding_model
|
9
11
|
from letta.helpers import ToolRulesSolver
|
10
12
|
from letta.helpers.datetime_helpers import get_local_time, get_local_time_fast
|
13
|
+
from letta.orm import AgentPassage, SourcePassage, SourcesAgents
|
11
14
|
from letta.orm.agent import Agent as AgentModel
|
12
15
|
from letta.orm.agents_tags import AgentsTags
|
13
16
|
from letta.orm.errors import NoResultFound
|
14
17
|
from letta.orm.identity import Identity
|
18
|
+
from letta.orm.sqlite_functions import adapt_array
|
19
|
+
from letta.otel.tracing import trace_method
|
15
20
|
from letta.prompts import gpt_system
|
16
21
|
from letta.schemas.agent import AgentState, AgentType
|
22
|
+
from letta.schemas.embedding_config import EmbeddingConfig
|
17
23
|
from letta.schemas.enums import MessageRole
|
18
24
|
from letta.schemas.letta_message_content import TextContent
|
19
25
|
from letta.schemas.memory import Memory
|
20
26
|
from letta.schemas.message import Message, MessageCreate
|
21
27
|
from letta.schemas.tool_rule import ToolRule
|
22
28
|
from letta.schemas.user import User
|
29
|
+
from letta.settings import settings
|
23
30
|
from letta.system import get_initial_boot_messages, get_login_event, package_function_response
|
24
|
-
from letta.tracing import trace_method
|
25
31
|
|
26
32
|
|
27
33
|
# Static methods
|
@@ -566,3 +572,398 @@ def _apply_filters(
|
|
566
572
|
if base_template_id:
|
567
573
|
query = query.where(AgentModel.base_template_id == base_template_id)
|
568
574
|
return query
|
575
|
+
|
576
|
+
|
577
|
+
def build_passage_query(
|
578
|
+
actor: User,
|
579
|
+
agent_id: Optional[str] = None,
|
580
|
+
file_id: Optional[str] = None,
|
581
|
+
query_text: Optional[str] = None,
|
582
|
+
start_date: Optional[datetime] = None,
|
583
|
+
end_date: Optional[datetime] = None,
|
584
|
+
before: Optional[str] = None,
|
585
|
+
after: Optional[str] = None,
|
586
|
+
source_id: Optional[str] = None,
|
587
|
+
embed_query: bool = False,
|
588
|
+
ascending: bool = True,
|
589
|
+
embedding_config: Optional[EmbeddingConfig] = None,
|
590
|
+
agent_only: bool = False,
|
591
|
+
) -> Select:
|
592
|
+
"""Helper function to build the base passage query with all filters applied.
|
593
|
+
Supports both before and after pagination across merged source and agent passages.
|
594
|
+
|
595
|
+
Returns the query before any limit or count operations are applied.
|
596
|
+
"""
|
597
|
+
embedded_text = None
|
598
|
+
if embed_query:
|
599
|
+
assert embedding_config is not None, "embedding_config must be specified for vector search"
|
600
|
+
assert query_text is not None, "query_text must be specified for vector search"
|
601
|
+
embedded_text = embedding_model(embedding_config).get_text_embedding(query_text)
|
602
|
+
embedded_text = np.array(embedded_text)
|
603
|
+
embedded_text = np.pad(embedded_text, (0, MAX_EMBEDDING_DIM - embedded_text.shape[0]), mode="constant").tolist()
|
604
|
+
|
605
|
+
# Start with base query for source passages
|
606
|
+
source_passages = None
|
607
|
+
if not agent_only: # Include source passages
|
608
|
+
if agent_id is not None:
|
609
|
+
source_passages = (
|
610
|
+
select(
|
611
|
+
SourcePassage.file_name,
|
612
|
+
SourcePassage.id,
|
613
|
+
SourcePassage.text,
|
614
|
+
SourcePassage.embedding_config,
|
615
|
+
SourcePassage.metadata_,
|
616
|
+
SourcePassage.embedding,
|
617
|
+
SourcePassage.created_at,
|
618
|
+
SourcePassage.updated_at,
|
619
|
+
SourcePassage.is_deleted,
|
620
|
+
SourcePassage._created_by_id,
|
621
|
+
SourcePassage._last_updated_by_id,
|
622
|
+
SourcePassage.organization_id,
|
623
|
+
SourcePassage.file_id,
|
624
|
+
SourcePassage.source_id,
|
625
|
+
literal(None).label("agent_id"),
|
626
|
+
)
|
627
|
+
.join(SourcesAgents, SourcesAgents.source_id == SourcePassage.source_id)
|
628
|
+
.where(SourcesAgents.agent_id == agent_id)
|
629
|
+
.where(SourcePassage.organization_id == actor.organization_id)
|
630
|
+
)
|
631
|
+
else:
|
632
|
+
source_passages = select(
|
633
|
+
SourcePassage.file_name,
|
634
|
+
SourcePassage.id,
|
635
|
+
SourcePassage.text,
|
636
|
+
SourcePassage.embedding_config,
|
637
|
+
SourcePassage.metadata_,
|
638
|
+
SourcePassage.embedding,
|
639
|
+
SourcePassage.created_at,
|
640
|
+
SourcePassage.updated_at,
|
641
|
+
SourcePassage.is_deleted,
|
642
|
+
SourcePassage._created_by_id,
|
643
|
+
SourcePassage._last_updated_by_id,
|
644
|
+
SourcePassage.organization_id,
|
645
|
+
SourcePassage.file_id,
|
646
|
+
SourcePassage.source_id,
|
647
|
+
literal(None).label("agent_id"),
|
648
|
+
).where(SourcePassage.organization_id == actor.organization_id)
|
649
|
+
|
650
|
+
if source_id:
|
651
|
+
source_passages = source_passages.where(SourcePassage.source_id == source_id)
|
652
|
+
if file_id:
|
653
|
+
source_passages = source_passages.where(SourcePassage.file_id == file_id)
|
654
|
+
|
655
|
+
# Add agent passages query
|
656
|
+
agent_passages = None
|
657
|
+
if agent_id is not None:
|
658
|
+
agent_passages = (
|
659
|
+
select(
|
660
|
+
literal(None).label("file_name"),
|
661
|
+
AgentPassage.id,
|
662
|
+
AgentPassage.text,
|
663
|
+
AgentPassage.embedding_config,
|
664
|
+
AgentPassage.metadata_,
|
665
|
+
AgentPassage.embedding,
|
666
|
+
AgentPassage.created_at,
|
667
|
+
AgentPassage.updated_at,
|
668
|
+
AgentPassage.is_deleted,
|
669
|
+
AgentPassage._created_by_id,
|
670
|
+
AgentPassage._last_updated_by_id,
|
671
|
+
AgentPassage.organization_id,
|
672
|
+
literal(None).label("file_id"),
|
673
|
+
literal(None).label("source_id"),
|
674
|
+
AgentPassage.agent_id,
|
675
|
+
)
|
676
|
+
.where(AgentPassage.agent_id == agent_id)
|
677
|
+
.where(AgentPassage.organization_id == actor.organization_id)
|
678
|
+
)
|
679
|
+
|
680
|
+
# Combine queries
|
681
|
+
if source_passages is not None and agent_passages is not None:
|
682
|
+
combined_query = union_all(source_passages, agent_passages).cte("combined_passages")
|
683
|
+
elif agent_passages is not None:
|
684
|
+
combined_query = agent_passages.cte("combined_passages")
|
685
|
+
elif source_passages is not None:
|
686
|
+
combined_query = source_passages.cte("combined_passages")
|
687
|
+
else:
|
688
|
+
raise ValueError("No passages found")
|
689
|
+
|
690
|
+
# Build main query from combined CTE
|
691
|
+
main_query = select(combined_query)
|
692
|
+
|
693
|
+
# Apply filters
|
694
|
+
if start_date:
|
695
|
+
main_query = main_query.where(combined_query.c.created_at >= start_date)
|
696
|
+
if end_date:
|
697
|
+
main_query = main_query.where(combined_query.c.created_at <= end_date)
|
698
|
+
if source_id:
|
699
|
+
main_query = main_query.where(combined_query.c.source_id == source_id)
|
700
|
+
if file_id:
|
701
|
+
main_query = main_query.where(combined_query.c.file_id == file_id)
|
702
|
+
|
703
|
+
# Vector search
|
704
|
+
if embedded_text:
|
705
|
+
if settings.letta_pg_uri_no_default:
|
706
|
+
# PostgreSQL with pgvector
|
707
|
+
main_query = main_query.order_by(combined_query.c.embedding.cosine_distance(embedded_text).asc())
|
708
|
+
else:
|
709
|
+
# SQLite with custom vector type
|
710
|
+
query_embedding_binary = adapt_array(embedded_text)
|
711
|
+
main_query = main_query.order_by(
|
712
|
+
func.cosine_distance(combined_query.c.embedding, query_embedding_binary).asc(),
|
713
|
+
combined_query.c.created_at.asc() if ascending else combined_query.c.created_at.desc(),
|
714
|
+
combined_query.c.id.asc(),
|
715
|
+
)
|
716
|
+
else:
|
717
|
+
if query_text:
|
718
|
+
main_query = main_query.where(func.lower(combined_query.c.text).contains(func.lower(query_text)))
|
719
|
+
|
720
|
+
# Handle pagination
|
721
|
+
if before or after:
|
722
|
+
# Create reference CTEs
|
723
|
+
if before:
|
724
|
+
before_ref = select(combined_query.c.created_at, combined_query.c.id).where(combined_query.c.id == before).cte("before_ref")
|
725
|
+
if after:
|
726
|
+
after_ref = select(combined_query.c.created_at, combined_query.c.id).where(combined_query.c.id == after).cte("after_ref")
|
727
|
+
|
728
|
+
if before and after:
|
729
|
+
# Window-based query (get records between before and after)
|
730
|
+
main_query = main_query.where(
|
731
|
+
or_(
|
732
|
+
combined_query.c.created_at < select(before_ref.c.created_at).scalar_subquery(),
|
733
|
+
and_(
|
734
|
+
combined_query.c.created_at == select(before_ref.c.created_at).scalar_subquery(),
|
735
|
+
combined_query.c.id < select(before_ref.c.id).scalar_subquery(),
|
736
|
+
),
|
737
|
+
)
|
738
|
+
)
|
739
|
+
main_query = main_query.where(
|
740
|
+
or_(
|
741
|
+
combined_query.c.created_at > select(after_ref.c.created_at).scalar_subquery(),
|
742
|
+
and_(
|
743
|
+
combined_query.c.created_at == select(after_ref.c.created_at).scalar_subquery(),
|
744
|
+
combined_query.c.id > select(after_ref.c.id).scalar_subquery(),
|
745
|
+
),
|
746
|
+
)
|
747
|
+
)
|
748
|
+
else:
|
749
|
+
# Pure pagination (only before or only after)
|
750
|
+
if before:
|
751
|
+
main_query = main_query.where(
|
752
|
+
or_(
|
753
|
+
combined_query.c.created_at < select(before_ref.c.created_at).scalar_subquery(),
|
754
|
+
and_(
|
755
|
+
combined_query.c.created_at == select(before_ref.c.created_at).scalar_subquery(),
|
756
|
+
combined_query.c.id < select(before_ref.c.id).scalar_subquery(),
|
757
|
+
),
|
758
|
+
)
|
759
|
+
)
|
760
|
+
if after:
|
761
|
+
main_query = main_query.where(
|
762
|
+
or_(
|
763
|
+
combined_query.c.created_at > select(after_ref.c.created_at).scalar_subquery(),
|
764
|
+
and_(
|
765
|
+
combined_query.c.created_at == select(after_ref.c.created_at).scalar_subquery(),
|
766
|
+
combined_query.c.id > select(after_ref.c.id).scalar_subquery(),
|
767
|
+
),
|
768
|
+
)
|
769
|
+
)
|
770
|
+
|
771
|
+
# Add ordering if not already ordered by similarity
|
772
|
+
if not embed_query:
|
773
|
+
if ascending:
|
774
|
+
main_query = main_query.order_by(
|
775
|
+
combined_query.c.created_at.asc(),
|
776
|
+
combined_query.c.id.asc(),
|
777
|
+
)
|
778
|
+
else:
|
779
|
+
main_query = main_query.order_by(
|
780
|
+
combined_query.c.created_at.desc(),
|
781
|
+
combined_query.c.id.asc(),
|
782
|
+
)
|
783
|
+
|
784
|
+
return main_query
|
785
|
+
|
786
|
+
|
787
|
+
def build_source_passage_query(
|
788
|
+
actor: User,
|
789
|
+
agent_id: Optional[str] = None,
|
790
|
+
file_id: Optional[str] = None,
|
791
|
+
query_text: Optional[str] = None,
|
792
|
+
start_date: Optional[datetime] = None,
|
793
|
+
end_date: Optional[datetime] = None,
|
794
|
+
before: Optional[str] = None,
|
795
|
+
after: Optional[str] = None,
|
796
|
+
source_id: Optional[str] = None,
|
797
|
+
embed_query: bool = False,
|
798
|
+
ascending: bool = True,
|
799
|
+
embedding_config: Optional[EmbeddingConfig] = None,
|
800
|
+
) -> Select:
|
801
|
+
"""Build query for source passages with all filters applied."""
|
802
|
+
|
803
|
+
# Handle embedding for vector search
|
804
|
+
embedded_text = None
|
805
|
+
if embed_query:
|
806
|
+
assert embedding_config is not None, "embedding_config must be specified for vector search"
|
807
|
+
assert query_text is not None, "query_text must be specified for vector search"
|
808
|
+
embedded_text = embedding_model(embedding_config).get_text_embedding(query_text)
|
809
|
+
embedded_text = np.array(embedded_text)
|
810
|
+
embedded_text = np.pad(embedded_text, (0, MAX_EMBEDDING_DIM - embedded_text.shape[0]), mode="constant").tolist()
|
811
|
+
|
812
|
+
# Base query for source passages
|
813
|
+
query = select(SourcePassage).where(SourcePassage.organization_id == actor.organization_id)
|
814
|
+
|
815
|
+
# If agent_id is specified, join with SourcesAgents to get only passages linked to that agent
|
816
|
+
if agent_id is not None:
|
817
|
+
query = query.join(SourcesAgents, SourcesAgents.source_id == SourcePassage.source_id)
|
818
|
+
query = query.where(SourcesAgents.agent_id == agent_id)
|
819
|
+
|
820
|
+
# Apply filters
|
821
|
+
if source_id:
|
822
|
+
query = query.where(SourcePassage.source_id == source_id)
|
823
|
+
if file_id:
|
824
|
+
query = query.where(SourcePassage.file_id == file_id)
|
825
|
+
if start_date:
|
826
|
+
query = query.where(SourcePassage.created_at >= start_date)
|
827
|
+
if end_date:
|
828
|
+
query = query.where(SourcePassage.created_at <= end_date)
|
829
|
+
|
830
|
+
# Handle text search or vector search
|
831
|
+
if embedded_text:
|
832
|
+
if settings.letta_pg_uri_no_default:
|
833
|
+
# PostgreSQL with pgvector
|
834
|
+
query = query.order_by(SourcePassage.embedding.cosine_distance(embedded_text).asc())
|
835
|
+
else:
|
836
|
+
# SQLite with custom vector type
|
837
|
+
query_embedding_binary = adapt_array(embedded_text)
|
838
|
+
query = query.order_by(
|
839
|
+
func.cosine_distance(SourcePassage.embedding, query_embedding_binary).asc(),
|
840
|
+
SourcePassage.created_at.asc() if ascending else SourcePassage.created_at.desc(),
|
841
|
+
SourcePassage.id.asc(),
|
842
|
+
)
|
843
|
+
else:
|
844
|
+
if query_text:
|
845
|
+
query = query.where(func.lower(SourcePassage.text).contains(func.lower(query_text)))
|
846
|
+
|
847
|
+
# Handle pagination
|
848
|
+
if before or after:
|
849
|
+
if before:
|
850
|
+
# Get the reference record
|
851
|
+
before_subq = select(SourcePassage.created_at, SourcePassage.id).where(SourcePassage.id == before).subquery()
|
852
|
+
query = query.where(
|
853
|
+
or_(
|
854
|
+
SourcePassage.created_at < before_subq.c.created_at,
|
855
|
+
and_(
|
856
|
+
SourcePassage.created_at == before_subq.c.created_at,
|
857
|
+
SourcePassage.id < before_subq.c.id,
|
858
|
+
),
|
859
|
+
)
|
860
|
+
)
|
861
|
+
|
862
|
+
if after:
|
863
|
+
# Get the reference record
|
864
|
+
after_subq = select(SourcePassage.created_at, SourcePassage.id).where(SourcePassage.id == after).subquery()
|
865
|
+
query = query.where(
|
866
|
+
or_(
|
867
|
+
SourcePassage.created_at > after_subq.c.created_at,
|
868
|
+
and_(
|
869
|
+
SourcePassage.created_at == after_subq.c.created_at,
|
870
|
+
SourcePassage.id > after_subq.c.id,
|
871
|
+
),
|
872
|
+
)
|
873
|
+
)
|
874
|
+
|
875
|
+
# Apply ordering if not already ordered by similarity
|
876
|
+
if not embed_query:
|
877
|
+
if ascending:
|
878
|
+
query = query.order_by(SourcePassage.created_at.asc(), SourcePassage.id.asc())
|
879
|
+
else:
|
880
|
+
query = query.order_by(SourcePassage.created_at.desc(), SourcePassage.id.asc())
|
881
|
+
|
882
|
+
return query
|
883
|
+
|
884
|
+
|
885
|
+
def build_agent_passage_query(
|
886
|
+
actor: User,
|
887
|
+
agent_id: str, # Required for agent passages
|
888
|
+
query_text: Optional[str] = None,
|
889
|
+
start_date: Optional[datetime] = None,
|
890
|
+
end_date: Optional[datetime] = None,
|
891
|
+
before: Optional[str] = None,
|
892
|
+
after: Optional[str] = None,
|
893
|
+
embed_query: bool = False,
|
894
|
+
ascending: bool = True,
|
895
|
+
embedding_config: Optional[EmbeddingConfig] = None,
|
896
|
+
) -> Select:
|
897
|
+
"""Build query for agent passages with all filters applied."""
|
898
|
+
|
899
|
+
# Handle embedding for vector search
|
900
|
+
embedded_text = None
|
901
|
+
if embed_query:
|
902
|
+
assert embedding_config is not None, "embedding_config must be specified for vector search"
|
903
|
+
assert query_text is not None, "query_text must be specified for vector search"
|
904
|
+
embedded_text = embedding_model(embedding_config).get_text_embedding(query_text)
|
905
|
+
embedded_text = np.array(embedded_text)
|
906
|
+
embedded_text = np.pad(embedded_text, (0, MAX_EMBEDDING_DIM - embedded_text.shape[0]), mode="constant").tolist()
|
907
|
+
|
908
|
+
# Base query for agent passages
|
909
|
+
query = select(AgentPassage).where(AgentPassage.agent_id == agent_id, AgentPassage.organization_id == actor.organization_id)
|
910
|
+
|
911
|
+
# Apply filters
|
912
|
+
if start_date:
|
913
|
+
query = query.where(AgentPassage.created_at >= start_date)
|
914
|
+
if end_date:
|
915
|
+
query = query.where(AgentPassage.created_at <= end_date)
|
916
|
+
|
917
|
+
# Handle text search or vector search
|
918
|
+
if embedded_text:
|
919
|
+
if settings.letta_pg_uri_no_default:
|
920
|
+
# PostgreSQL with pgvector
|
921
|
+
query = query.order_by(AgentPassage.embedding.cosine_distance(embedded_text).asc())
|
922
|
+
else:
|
923
|
+
# SQLite with custom vector type
|
924
|
+
query_embedding_binary = adapt_array(embedded_text)
|
925
|
+
query = query.order_by(
|
926
|
+
func.cosine_distance(AgentPassage.embedding, query_embedding_binary).asc(),
|
927
|
+
AgentPassage.created_at.asc() if ascending else AgentPassage.created_at.desc(),
|
928
|
+
AgentPassage.id.asc(),
|
929
|
+
)
|
930
|
+
else:
|
931
|
+
if query_text:
|
932
|
+
query = query.where(func.lower(AgentPassage.text).contains(func.lower(query_text)))
|
933
|
+
|
934
|
+
# Handle pagination
|
935
|
+
if before or after:
|
936
|
+
if before:
|
937
|
+
# Get the reference record
|
938
|
+
before_subq = select(AgentPassage.created_at, AgentPassage.id).where(AgentPassage.id == before).subquery()
|
939
|
+
query = query.where(
|
940
|
+
or_(
|
941
|
+
AgentPassage.created_at < before_subq.c.created_at,
|
942
|
+
and_(
|
943
|
+
AgentPassage.created_at == before_subq.c.created_at,
|
944
|
+
AgentPassage.id < before_subq.c.id,
|
945
|
+
),
|
946
|
+
)
|
947
|
+
)
|
948
|
+
|
949
|
+
if after:
|
950
|
+
# Get the reference record
|
951
|
+
after_subq = select(AgentPassage.created_at, AgentPassage.id).where(AgentPassage.id == after).subquery()
|
952
|
+
query = query.where(
|
953
|
+
or_(
|
954
|
+
AgentPassage.created_at > after_subq.c.created_at,
|
955
|
+
and_(
|
956
|
+
AgentPassage.created_at == after_subq.c.created_at,
|
957
|
+
AgentPassage.id > after_subq.c.id,
|
958
|
+
),
|
959
|
+
)
|
960
|
+
)
|
961
|
+
|
962
|
+
# Apply ordering if not already ordered by similarity
|
963
|
+
if not embed_query:
|
964
|
+
if ascending:
|
965
|
+
query = query.order_by(AgentPassage.created_at.asc(), AgentPassage.id.asc())
|
966
|
+
else:
|
967
|
+
query = query.order_by(AgentPassage.created_at.desc(), AgentPassage.id.asc())
|
968
|
+
|
969
|
+
return query
|
@@ -7,11 +7,11 @@ from sqlalchemy.exc import NoResultFound
|
|
7
7
|
from letta.orm.agent import Agent as AgentModel
|
8
8
|
from letta.orm.block import Block as BlockModel
|
9
9
|
from letta.orm.identity import Identity as IdentityModel
|
10
|
+
from letta.otel.tracing import trace_method
|
10
11
|
from letta.schemas.identity import Identity as PydanticIdentity
|
11
12
|
from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityType, IdentityUpdate, IdentityUpsert
|
12
13
|
from letta.schemas.user import User as PydanticUser
|
13
14
|
from letta.server.db import db_registry
|
14
|
-
from letta.tracing import trace_method
|
15
15
|
from letta.utils import enforce_types
|
16
16
|
|
17
17
|
|
letta/services/job_manager.py
CHANGED
@@ -14,6 +14,7 @@ from letta.orm.message import Message as MessageModel
|
|
14
14
|
from letta.orm.sqlalchemy_base import AccessType
|
15
15
|
from letta.orm.step import Step
|
16
16
|
from letta.orm.step import Step as StepModel
|
17
|
+
from letta.otel.tracing import trace_method
|
17
18
|
from letta.schemas.enums import JobStatus, MessageRole
|
18
19
|
from letta.schemas.job import BatchJob as PydanticBatchJob
|
19
20
|
from letta.schemas.job import Job as PydanticJob
|
@@ -25,7 +26,6 @@ from letta.schemas.step import Step as PydanticStep
|
|
25
26
|
from letta.schemas.usage import LettaUsageStatistics
|
26
27
|
from letta.schemas.user import User as PydanticUser
|
27
28
|
from letta.server.db import db_registry
|
28
|
-
from letta.tracing import trace_method
|
29
29
|
from letta.utils import enforce_types
|
30
30
|
|
31
31
|
|
@@ -9,6 +9,7 @@ from letta.log import get_logger
|
|
9
9
|
from letta.orm import Message as MessageModel
|
10
10
|
from letta.orm.llm_batch_items import LLMBatchItem
|
11
11
|
from letta.orm.llm_batch_job import LLMBatchJob
|
12
|
+
from letta.otel.tracing import trace_method
|
12
13
|
from letta.schemas.agent import AgentStepState
|
13
14
|
from letta.schemas.enums import AgentStepStatus, JobStatus, ProviderType
|
14
15
|
from letta.schemas.llm_batch_job import LLMBatchItem as PydanticLLMBatchItem
|
@@ -17,7 +18,6 @@ from letta.schemas.llm_config import LLMConfig
|
|
17
18
|
from letta.schemas.message import Message as PydanticMessage
|
18
19
|
from letta.schemas.user import User as PydanticUser
|
19
20
|
from letta.server.db import db_registry
|
20
|
-
from letta.tracing import trace_method
|
21
21
|
from letta.utils import enforce_types
|
22
22
|
|
23
23
|
logger = get_logger(__name__)
|
@@ -11,7 +11,11 @@ logger = get_logger(__name__)
|
|
11
11
|
# TODO: Get rid of Async prefix on this class name once we deprecate old sync code
|
12
12
|
class AsyncStdioMCPClient(AsyncBaseMCPClient):
|
13
13
|
async def _initialize_connection(self, server_config: StdioServerConfig) -> None:
|
14
|
-
|
14
|
+
|
15
|
+
args = [arg.split() for arg in server_config.args]
|
16
|
+
# flatten
|
17
|
+
args = [arg for sublist in args for arg in sublist]
|
18
|
+
server_params = StdioServerParameters(command=server_config.command, args=args)
|
15
19
|
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
|
16
20
|
self.stdio, self.write = stdio_transport
|
17
21
|
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
|
letta/services/mcp_manager.py
CHANGED
@@ -220,12 +220,12 @@ class MCPManager:
|
|
220
220
|
# mcp_server.delete(session, actor=actor) # Re-raise other database-related errors
|
221
221
|
|
222
222
|
@enforce_types
|
223
|
-
def delete_mcp_server_by_id(self, mcp_server_id: str, actor: PydanticUser) -> None:
|
223
|
+
async def delete_mcp_server_by_id(self, mcp_server_id: str, actor: PydanticUser) -> None:
|
224
224
|
"""Delete a tool by its ID."""
|
225
|
-
with db_registry.
|
225
|
+
async with db_registry.async_session() as session:
|
226
226
|
try:
|
227
|
-
mcp_server = MCPServerModel.
|
228
|
-
mcp_server.
|
227
|
+
mcp_server = await MCPServerModel.read_async(db_session=session, identifier=mcp_server_id, actor=actor)
|
228
|
+
await mcp_server.hard_delete_async(db_session=session, actor=actor)
|
229
229
|
except NoResultFound:
|
230
230
|
raise ValueError(f"MCP server with id {mcp_server_id} not found.")
|
231
231
|
|
@@ -7,13 +7,13 @@ from letta.log import get_logger
|
|
7
7
|
from letta.orm.agent import Agent as AgentModel
|
8
8
|
from letta.orm.errors import NoResultFound
|
9
9
|
from letta.orm.message import Message as MessageModel
|
10
|
+
from letta.otel.tracing import trace_method
|
10
11
|
from letta.schemas.enums import MessageRole
|
11
12
|
from letta.schemas.letta_message import LettaMessageUpdateUnion
|
12
13
|
from letta.schemas.message import Message as PydanticMessage
|
13
14
|
from letta.schemas.message import MessageUpdate
|
14
15
|
from letta.schemas.user import User as PydanticUser
|
15
16
|
from letta.server.db import db_registry
|
16
|
-
from letta.tracing import trace_method
|
17
17
|
from letta.utils import enforce_types
|
18
18
|
|
19
19
|
logger = get_logger(__name__)
|
@@ -2,10 +2,10 @@ from typing import List, Optional
|
|
2
2
|
|
3
3
|
from letta.orm.errors import NoResultFound
|
4
4
|
from letta.orm.organization import Organization as OrganizationModel
|
5
|
+
from letta.otel.tracing import trace_method
|
5
6
|
from letta.schemas.organization import Organization as PydanticOrganization
|
6
7
|
from letta.schemas.organization import OrganizationUpdate
|
7
8
|
from letta.server.db import db_registry
|
8
|
-
from letta.tracing import trace_method
|
9
9
|
from letta.utils import enforce_types
|
10
10
|
|
11
11
|
|