letta-nightly 0.5.2.dev20241111104107__py3-none-any.whl → 0.5.2.dev20241113104112__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.
Potentially problematic release.
This version of letta-nightly might be problematic. Click here for more details.
- letta/agent.py +4 -2
- letta/cli/cli.py +1 -0
- letta/client/client.py +16 -13
- letta/data_sources/connectors.py +3 -3
- letta/llm_api/anthropic.py +2 -2
- letta/llm_api/google_ai.py +0 -2
- letta/llm_api/openai.py +1 -0
- letta/memory.py +10 -6
- letta/metadata.py +3 -92
- letta/orm/__init__.py +1 -0
- letta/orm/organization.py +2 -2
- letta/orm/source.py +50 -0
- letta/providers.py +0 -1
- letta/schemas/source.py +30 -24
- letta/server/rest_api/app.py +27 -0
- letta/server/rest_api/routers/v1/sources.py +11 -13
- letta/server/server.py +31 -52
- letta/services/organization_manager.py +12 -13
- letta/services/source_manager.py +100 -0
- {letta_nightly-0.5.2.dev20241111104107.dist-info → letta_nightly-0.5.2.dev20241113104112.dist-info}/METADATA +1 -1
- {letta_nightly-0.5.2.dev20241111104107.dist-info → letta_nightly-0.5.2.dev20241113104112.dist-info}/RECORD +24 -22
- {letta_nightly-0.5.2.dev20241111104107.dist-info → letta_nightly-0.5.2.dev20241113104112.dist-info}/LICENSE +0 -0
- {letta_nightly-0.5.2.dev20241111104107.dist-info → letta_nightly-0.5.2.dev20241113104112.dist-info}/WHEEL +0 -0
- {letta_nightly-0.5.2.dev20241111104107.dist-info → letta_nightly-0.5.2.dev20241113104112.dist-info}/entry_points.txt +0 -0
letta/agent.py
CHANGED
|
@@ -46,6 +46,8 @@ from letta.schemas.passage import Passage
|
|
|
46
46
|
from letta.schemas.tool import Tool
|
|
47
47
|
from letta.schemas.tool_rule import TerminalToolRule
|
|
48
48
|
from letta.schemas.usage import LettaUsageStatistics
|
|
49
|
+
from letta.services.source_manager import SourceManager
|
|
50
|
+
from letta.services.user_manager import UserManager
|
|
49
51
|
from letta.system import (
|
|
50
52
|
get_heartbeat,
|
|
51
53
|
get_initial_boot_messages,
|
|
@@ -1311,7 +1313,7 @@ class Agent(BaseAgent):
|
|
|
1311
1313
|
def attach_source(self, source_id: str, source_connector: StorageConnector, ms: MetadataStore):
|
|
1312
1314
|
"""Attach data with name `source_name` to the agent from source_connector."""
|
|
1313
1315
|
# TODO: eventually, adding a data source should just give access to the retriever the source table, rather than modifying archival memory
|
|
1314
|
-
|
|
1316
|
+
user = UserManager().get_user_by_id(self.agent_state.user_id)
|
|
1315
1317
|
filters = {"user_id": self.agent_state.user_id, "source_id": source_id}
|
|
1316
1318
|
size = source_connector.size(filters)
|
|
1317
1319
|
page_size = 100
|
|
@@ -1339,7 +1341,7 @@ class Agent(BaseAgent):
|
|
|
1339
1341
|
self.persistence_manager.archival_memory.storage.save()
|
|
1340
1342
|
|
|
1341
1343
|
# attach to agent
|
|
1342
|
-
source =
|
|
1344
|
+
source = SourceManager().get_source_by_id(source_id=source_id, actor=user)
|
|
1343
1345
|
assert source is not None, f"Source {source_id} not found in metadata store"
|
|
1344
1346
|
ms.attach_source(agent_id=self.agent_state.id, source_id=source_id, user_id=self.agent_state.user_id)
|
|
1345
1347
|
|
letta/cli/cli.py
CHANGED
|
@@ -47,6 +47,7 @@ def server(
|
|
|
47
47
|
host: Annotated[Optional[str], typer.Option(help="Host to run the server on (default to localhost)")] = None,
|
|
48
48
|
debug: Annotated[bool, typer.Option(help="Turn debugging output on")] = False,
|
|
49
49
|
ade: Annotated[bool, typer.Option(help="Allows remote access")] = False,
|
|
50
|
+
secure: Annotated[bool, typer.Option(help="Adds simple security access")] = False,
|
|
50
51
|
):
|
|
51
52
|
"""Launch a Letta server process"""
|
|
52
53
|
if type == ServerChoice.rest_api:
|
letta/client/client.py
CHANGED
|
@@ -238,7 +238,7 @@ class AbstractClient(object):
|
|
|
238
238
|
def delete_file_from_source(self, source_id: str, file_id: str) -> None:
|
|
239
239
|
raise NotImplementedError
|
|
240
240
|
|
|
241
|
-
def create_source(self, name: str) -> Source:
|
|
241
|
+
def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
|
|
242
242
|
raise NotImplementedError
|
|
243
243
|
|
|
244
244
|
def delete_source(self, source_id: str):
|
|
@@ -1188,7 +1188,7 @@ class RESTClient(AbstractClient):
|
|
|
1188
1188
|
if response.status_code not in [200, 204]:
|
|
1189
1189
|
raise ValueError(f"Failed to delete tool: {response.text}")
|
|
1190
1190
|
|
|
1191
|
-
def create_source(self, name: str) -> Source:
|
|
1191
|
+
def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
|
|
1192
1192
|
"""
|
|
1193
1193
|
Create a source
|
|
1194
1194
|
|
|
@@ -1198,7 +1198,8 @@ class RESTClient(AbstractClient):
|
|
|
1198
1198
|
Returns:
|
|
1199
1199
|
source (Source): Created source
|
|
1200
1200
|
"""
|
|
1201
|
-
|
|
1201
|
+
source_create = SourceCreate(name=name, embedding_config=embedding_config or self._default_embedding_config)
|
|
1202
|
+
payload = source_create.model_dump()
|
|
1202
1203
|
response = requests.post(f"{self.base_url}/{self.api_prefix}/sources", json=payload, headers=self.headers)
|
|
1203
1204
|
response_json = response.json()
|
|
1204
1205
|
return Source(**response_json)
|
|
@@ -1253,7 +1254,7 @@ class RESTClient(AbstractClient):
|
|
|
1253
1254
|
Returns:
|
|
1254
1255
|
source (Source): Updated source
|
|
1255
1256
|
"""
|
|
1256
|
-
request = SourceUpdate(
|
|
1257
|
+
request = SourceUpdate(name=name)
|
|
1257
1258
|
response = requests.patch(f"{self.base_url}/{self.api_prefix}/sources/{source_id}", json=request.model_dump(), headers=self.headers)
|
|
1258
1259
|
if response.status_code != 200:
|
|
1259
1260
|
raise ValueError(f"Failed to update source: {response.text}")
|
|
@@ -2453,7 +2454,7 @@ class LocalClient(AbstractClient):
|
|
|
2453
2454
|
def list_active_jobs(self):
|
|
2454
2455
|
return self.server.list_active_jobs(user_id=self.user_id)
|
|
2455
2456
|
|
|
2456
|
-
def create_source(self, name: str) -> Source:
|
|
2457
|
+
def create_source(self, name: str, embedding_config: Optional[EmbeddingConfig] = None) -> Source:
|
|
2457
2458
|
"""
|
|
2458
2459
|
Create a source
|
|
2459
2460
|
|
|
@@ -2463,8 +2464,10 @@ class LocalClient(AbstractClient):
|
|
|
2463
2464
|
Returns:
|
|
2464
2465
|
source (Source): Created source
|
|
2465
2466
|
"""
|
|
2466
|
-
|
|
2467
|
-
|
|
2467
|
+
source = Source(
|
|
2468
|
+
name=name, embedding_config=embedding_config or self._default_embedding_config, organization_id=self.user.organization_id
|
|
2469
|
+
)
|
|
2470
|
+
return self.server.source_manager.create_source(source=source, actor=self.user)
|
|
2468
2471
|
|
|
2469
2472
|
def delete_source(self, source_id: str):
|
|
2470
2473
|
"""
|
|
@@ -2475,7 +2478,7 @@ class LocalClient(AbstractClient):
|
|
|
2475
2478
|
"""
|
|
2476
2479
|
|
|
2477
2480
|
# TODO: delete source data
|
|
2478
|
-
self.server.delete_source(source_id=source_id,
|
|
2481
|
+
self.server.delete_source(source_id=source_id, actor=self.user)
|
|
2479
2482
|
|
|
2480
2483
|
def get_source(self, source_id: str) -> Source:
|
|
2481
2484
|
"""
|
|
@@ -2487,7 +2490,7 @@ class LocalClient(AbstractClient):
|
|
|
2487
2490
|
Returns:
|
|
2488
2491
|
source (Source): Source
|
|
2489
2492
|
"""
|
|
2490
|
-
return self.server.
|
|
2493
|
+
return self.server.source_manager.get_source_by_id(source_id=source_id, actor=self.user)
|
|
2491
2494
|
|
|
2492
2495
|
def get_source_id(self, source_name: str) -> str:
|
|
2493
2496
|
"""
|
|
@@ -2499,7 +2502,7 @@ class LocalClient(AbstractClient):
|
|
|
2499
2502
|
Returns:
|
|
2500
2503
|
source_id (str): ID of the source
|
|
2501
2504
|
"""
|
|
2502
|
-
return self.server.
|
|
2505
|
+
return self.server.source_manager.get_source_by_name(source_name=source_name, actor=self.user).id
|
|
2503
2506
|
|
|
2504
2507
|
def attach_source_to_agent(self, agent_id: str, source_id: Optional[str] = None, source_name: Optional[str] = None):
|
|
2505
2508
|
"""
|
|
@@ -2532,7 +2535,7 @@ class LocalClient(AbstractClient):
|
|
|
2532
2535
|
sources (List[Source]): List of sources
|
|
2533
2536
|
"""
|
|
2534
2537
|
|
|
2535
|
-
return self.server.list_all_sources(
|
|
2538
|
+
return self.server.list_all_sources(actor=self.user)
|
|
2536
2539
|
|
|
2537
2540
|
def list_attached_sources(self, agent_id: str) -> List[Source]:
|
|
2538
2541
|
"""
|
|
@@ -2572,8 +2575,8 @@ class LocalClient(AbstractClient):
|
|
|
2572
2575
|
source (Source): Updated source
|
|
2573
2576
|
"""
|
|
2574
2577
|
# TODO should the arg here just be "source_update: Source"?
|
|
2575
|
-
request = SourceUpdate(
|
|
2576
|
-
return self.server.update_source(
|
|
2578
|
+
request = SourceUpdate(name=name)
|
|
2579
|
+
return self.server.source_manager.update_source(source_id=source_id, source_update=request, actor=self.user)
|
|
2577
2580
|
|
|
2578
2581
|
# archival memory
|
|
2579
2582
|
|
letta/data_sources/connectors.py
CHANGED
|
@@ -47,7 +47,7 @@ def load_data(
|
|
|
47
47
|
passage_store: StorageConnector,
|
|
48
48
|
file_metadata_store: StorageConnector,
|
|
49
49
|
):
|
|
50
|
-
"""Load data from a connector (generates file and passages) into a specified source_id,
|
|
50
|
+
"""Load data from a connector (generates file and passages) into a specified source_id, associated with a user_id."""
|
|
51
51
|
embedding_config = source.embedding_config
|
|
52
52
|
|
|
53
53
|
# embedding model
|
|
@@ -88,7 +88,7 @@ def load_data(
|
|
|
88
88
|
file_id=file_metadata.id,
|
|
89
89
|
source_id=source.id,
|
|
90
90
|
metadata_=passage_metadata,
|
|
91
|
-
user_id=source.
|
|
91
|
+
user_id=source.created_by_id,
|
|
92
92
|
embedding_config=source.embedding_config,
|
|
93
93
|
embedding=embedding,
|
|
94
94
|
)
|
|
@@ -155,7 +155,7 @@ class DirectoryConnector(DataConnector):
|
|
|
155
155
|
|
|
156
156
|
for metadata in extract_metadata_from_files(files):
|
|
157
157
|
yield FileMetadata(
|
|
158
|
-
user_id=source.
|
|
158
|
+
user_id=source.created_by_id,
|
|
159
159
|
source_id=source.id,
|
|
160
160
|
file_name=metadata.get("file_name"),
|
|
161
161
|
file_path=metadata.get("file_path"),
|
letta/llm_api/anthropic.py
CHANGED
|
@@ -27,11 +27,11 @@ MODEL_LIST = [
|
|
|
27
27
|
"context_window": 200000,
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
"name": "claude-3-sonnet-
|
|
30
|
+
"name": "claude-3-5-sonnet-20241022",
|
|
31
31
|
"context_window": 200000,
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
|
-
"name": "claude-3-haiku-
|
|
34
|
+
"name": "claude-3-5-haiku-20241022",
|
|
35
35
|
"context_window": 200000,
|
|
36
36
|
},
|
|
37
37
|
]
|
letta/llm_api/google_ai.py
CHANGED
|
@@ -95,10 +95,8 @@ def google_ai_get_model_list(base_url: str, api_key: str, key_in_header: bool =
|
|
|
95
95
|
|
|
96
96
|
try:
|
|
97
97
|
response = requests.get(url, headers=headers)
|
|
98
|
-
printd(f"response = {response}")
|
|
99
98
|
response.raise_for_status() # Raises HTTPError for 4XX/5XX status
|
|
100
99
|
response = response.json() # convert to dict from string
|
|
101
|
-
printd(f"response.json = {response}")
|
|
102
100
|
|
|
103
101
|
# Grab the models out
|
|
104
102
|
model_list = response["models"]
|
letta/llm_api/openai.py
CHANGED
|
@@ -126,6 +126,7 @@ def build_openai_chat_completions_request(
|
|
|
126
126
|
openai_message_list = [
|
|
127
127
|
cast_message_to_subtype(m.to_openai_dict(put_inner_thoughts_in_kwargs=llm_config.put_inner_thoughts_in_kwargs)) for m in messages
|
|
128
128
|
]
|
|
129
|
+
|
|
129
130
|
if llm_config.model:
|
|
130
131
|
model = llm_config.model
|
|
131
132
|
else:
|
letta/memory.py
CHANGED
|
@@ -7,6 +7,7 @@ from letta.embeddings import embedding_model, parse_and_chunk_text, query_embedd
|
|
|
7
7
|
from letta.llm_api.llm_api_tools import create
|
|
8
8
|
from letta.prompts.gpt_summarize import SYSTEM as SUMMARY_PROMPT_SYSTEM
|
|
9
9
|
from letta.schemas.agent import AgentState
|
|
10
|
+
from letta.schemas.enums import MessageRole
|
|
10
11
|
from letta.schemas.memory import Memory
|
|
11
12
|
from letta.schemas.message import Message
|
|
12
13
|
from letta.schemas.passage import Passage
|
|
@@ -50,7 +51,6 @@ def _format_summary_history(message_history: List[Message]):
|
|
|
50
51
|
def summarize_messages(
|
|
51
52
|
agent_state: AgentState,
|
|
52
53
|
message_sequence_to_summarize: List[Message],
|
|
53
|
-
insert_acknowledgement_assistant_message: bool = True,
|
|
54
54
|
):
|
|
55
55
|
"""Summarize a message sequence using GPT"""
|
|
56
56
|
# we need the context_window
|
|
@@ -70,13 +70,17 @@ def summarize_messages(
|
|
|
70
70
|
dummy_user_id = agent_state.user_id
|
|
71
71
|
dummy_agent_id = agent_state.id
|
|
72
72
|
message_sequence = []
|
|
73
|
-
message_sequence.append(Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role=
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
message_sequence.append(Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role=MessageRole.system, text=summary_prompt))
|
|
74
|
+
message_sequence.append(
|
|
75
|
+
Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role=MessageRole.assistant, text=MESSAGE_SUMMARY_REQUEST_ACK)
|
|
76
|
+
)
|
|
77
|
+
message_sequence.append(Message(user_id=dummy_user_id, agent_id=dummy_agent_id, role=MessageRole.user, text=summary_input))
|
|
77
78
|
|
|
79
|
+
# TODO: We need to eventually have a separate LLM config for the summarizer LLM
|
|
80
|
+
llm_config_no_inner_thoughts = agent_state.llm_config.model_copy(deep=True)
|
|
81
|
+
llm_config_no_inner_thoughts.put_inner_thoughts_in_kwargs = False
|
|
78
82
|
response = create(
|
|
79
|
-
llm_config=
|
|
83
|
+
llm_config=llm_config_no_inner_thoughts,
|
|
80
84
|
user_id=agent_state.user_id,
|
|
81
85
|
messages=message_sequence,
|
|
82
86
|
stream=False,
|
letta/metadata.py
CHANGED
|
@@ -29,7 +29,6 @@ from letta.schemas.job import Job
|
|
|
29
29
|
from letta.schemas.llm_config import LLMConfig
|
|
30
30
|
from letta.schemas.memory import Memory
|
|
31
31
|
from letta.schemas.openai.chat_completions import ToolCall, ToolCallFunction
|
|
32
|
-
from letta.schemas.source import Source
|
|
33
32
|
from letta.schemas.tool_rule import (
|
|
34
33
|
BaseToolRule,
|
|
35
34
|
InitToolRule,
|
|
@@ -292,40 +291,6 @@ class AgentModel(Base):
|
|
|
292
291
|
return agent_state
|
|
293
292
|
|
|
294
293
|
|
|
295
|
-
class SourceModel(Base):
|
|
296
|
-
"""Defines data model for storing Passages (consisting of text, embedding)"""
|
|
297
|
-
|
|
298
|
-
__tablename__ = "sources"
|
|
299
|
-
__table_args__ = {"extend_existing": True}
|
|
300
|
-
|
|
301
|
-
# Assuming passage_id is the primary key
|
|
302
|
-
# id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
303
|
-
id = Column(String, primary_key=True)
|
|
304
|
-
user_id = Column(String, nullable=False)
|
|
305
|
-
name = Column(String, nullable=False)
|
|
306
|
-
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
307
|
-
embedding_config = Column(EmbeddingConfigColumn)
|
|
308
|
-
description = Column(String)
|
|
309
|
-
metadata_ = Column(JSON)
|
|
310
|
-
Index(__tablename__ + "_idx_user", user_id),
|
|
311
|
-
|
|
312
|
-
# TODO: add num passages
|
|
313
|
-
|
|
314
|
-
def __repr__(self) -> str:
|
|
315
|
-
return f"<Source(passage_id='{self.id}', name='{self.name}')>"
|
|
316
|
-
|
|
317
|
-
def to_record(self) -> Source:
|
|
318
|
-
return Source(
|
|
319
|
-
id=self.id,
|
|
320
|
-
user_id=self.user_id,
|
|
321
|
-
name=self.name,
|
|
322
|
-
created_at=self.created_at,
|
|
323
|
-
embedding_config=self.embedding_config,
|
|
324
|
-
description=self.description,
|
|
325
|
-
metadata_=self.metadata_,
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
|
|
329
294
|
class AgentSourceMappingModel(Base):
|
|
330
295
|
"""Stores mapping between agent -> source"""
|
|
331
296
|
|
|
@@ -497,14 +462,6 @@ class MetadataStore:
|
|
|
497
462
|
session.add(AgentModel(**fields))
|
|
498
463
|
session.commit()
|
|
499
464
|
|
|
500
|
-
@enforce_types
|
|
501
|
-
def create_source(self, source: Source):
|
|
502
|
-
with self.session_maker() as session:
|
|
503
|
-
if session.query(SourceModel).filter(SourceModel.name == source.name).filter(SourceModel.user_id == source.user_id).count() > 0:
|
|
504
|
-
raise ValueError(f"Source with name {source.name} already exists for user {source.user_id}")
|
|
505
|
-
session.add(SourceModel(**vars(source)))
|
|
506
|
-
session.commit()
|
|
507
|
-
|
|
508
465
|
@enforce_types
|
|
509
466
|
def create_block(self, block: Block):
|
|
510
467
|
with self.session_maker() as session:
|
|
@@ -522,6 +479,7 @@ class MetadataStore:
|
|
|
522
479
|
):
|
|
523
480
|
|
|
524
481
|
raise ValueError(f"Block with name {block.template_name} already exists")
|
|
482
|
+
|
|
525
483
|
session.add(BlockModel(**vars(block)))
|
|
526
484
|
session.commit()
|
|
527
485
|
|
|
@@ -536,12 +494,6 @@ class MetadataStore:
|
|
|
536
494
|
session.query(AgentModel).filter(AgentModel.id == agent.id).update(fields)
|
|
537
495
|
session.commit()
|
|
538
496
|
|
|
539
|
-
@enforce_types
|
|
540
|
-
def update_source(self, source: Source):
|
|
541
|
-
with self.session_maker() as session:
|
|
542
|
-
session.query(SourceModel).filter(SourceModel.id == source.id).update(vars(source))
|
|
543
|
-
session.commit()
|
|
544
|
-
|
|
545
497
|
@enforce_types
|
|
546
498
|
def update_block(self, block: Block):
|
|
547
499
|
with self.session_maker() as session:
|
|
@@ -591,29 +543,12 @@ class MetadataStore:
|
|
|
591
543
|
|
|
592
544
|
session.commit()
|
|
593
545
|
|
|
594
|
-
@enforce_types
|
|
595
|
-
def delete_source(self, source_id: str):
|
|
596
|
-
with self.session_maker() as session:
|
|
597
|
-
# delete from sources table
|
|
598
|
-
session.query(SourceModel).filter(SourceModel.id == source_id).delete()
|
|
599
|
-
|
|
600
|
-
# delete any mappings
|
|
601
|
-
session.query(AgentSourceMappingModel).filter(AgentSourceMappingModel.source_id == source_id).delete()
|
|
602
|
-
|
|
603
|
-
session.commit()
|
|
604
|
-
|
|
605
546
|
@enforce_types
|
|
606
547
|
def list_agents(self, user_id: str) -> List[AgentState]:
|
|
607
548
|
with self.session_maker() as session:
|
|
608
549
|
results = session.query(AgentModel).filter(AgentModel.user_id == user_id).all()
|
|
609
550
|
return [r.to_record() for r in results]
|
|
610
551
|
|
|
611
|
-
@enforce_types
|
|
612
|
-
def list_sources(self, user_id: str) -> List[Source]:
|
|
613
|
-
with self.session_maker() as session:
|
|
614
|
-
results = session.query(SourceModel).filter(SourceModel.user_id == user_id).all()
|
|
615
|
-
return [r.to_record() for r in results]
|
|
616
|
-
|
|
617
552
|
@enforce_types
|
|
618
553
|
def get_agent(
|
|
619
554
|
self, agent_id: Optional[str] = None, agent_name: Optional[str] = None, user_id: Optional[str] = None
|
|
@@ -630,21 +565,6 @@ class MetadataStore:
|
|
|
630
565
|
assert len(results) == 1, f"Expected 1 result, got {len(results)}" # should only be one result
|
|
631
566
|
return results[0].to_record()
|
|
632
567
|
|
|
633
|
-
@enforce_types
|
|
634
|
-
def get_source(
|
|
635
|
-
self, source_id: Optional[str] = None, user_id: Optional[str] = None, source_name: Optional[str] = None
|
|
636
|
-
) -> Optional[Source]:
|
|
637
|
-
with self.session_maker() as session:
|
|
638
|
-
if source_id:
|
|
639
|
-
results = session.query(SourceModel).filter(SourceModel.id == source_id).all()
|
|
640
|
-
else:
|
|
641
|
-
assert user_id is not None and source_name is not None
|
|
642
|
-
results = session.query(SourceModel).filter(SourceModel.name == source_name).filter(SourceModel.user_id == user_id).all()
|
|
643
|
-
if len(results) == 0:
|
|
644
|
-
return None
|
|
645
|
-
assert len(results) == 1, f"Expected 1 result, got {len(results)}"
|
|
646
|
-
return results[0].to_record()
|
|
647
|
-
|
|
648
568
|
@enforce_types
|
|
649
569
|
def get_block(self, block_id: str) -> Optional[Block]:
|
|
650
570
|
with self.session_maker() as session:
|
|
@@ -699,19 +619,10 @@ class MetadataStore:
|
|
|
699
619
|
session.commit()
|
|
700
620
|
|
|
701
621
|
@enforce_types
|
|
702
|
-
def
|
|
622
|
+
def list_attached_source_ids(self, agent_id: str) -> List[str]:
|
|
703
623
|
with self.session_maker() as session:
|
|
704
624
|
results = session.query(AgentSourceMappingModel).filter(AgentSourceMappingModel.agent_id == agent_id).all()
|
|
705
|
-
|
|
706
|
-
sources = []
|
|
707
|
-
# make sure source exists
|
|
708
|
-
for r in results:
|
|
709
|
-
source = self.get_source(source_id=r.source_id)
|
|
710
|
-
if source:
|
|
711
|
-
sources.append(source)
|
|
712
|
-
else:
|
|
713
|
-
printd(f"Warning: source {r.source_id} does not exist but exists in mapping database. This should never happen.")
|
|
714
|
-
return sources
|
|
625
|
+
return [r.source_id for r in results]
|
|
715
626
|
|
|
716
627
|
@enforce_types
|
|
717
628
|
def list_attached_agents(self, source_id: str) -> List[str]:
|
letta/orm/__init__.py
CHANGED
letta/orm/organization.py
CHANGED
|
@@ -21,13 +21,13 @@ class Organization(SqlalchemyBase):
|
|
|
21
21
|
id: Mapped[str] = mapped_column(String, primary_key=True)
|
|
22
22
|
name: Mapped[str] = mapped_column(doc="The display name of the organization.")
|
|
23
23
|
|
|
24
|
+
# relationships
|
|
24
25
|
users: Mapped[List["User"]] = relationship("User", back_populates="organization", cascade="all, delete-orphan")
|
|
25
26
|
tools: Mapped[List["Tool"]] = relationship("Tool", back_populates="organization", cascade="all, delete-orphan")
|
|
27
|
+
sources: Mapped[List["Source"]] = relationship("Source", back_populates="organization", cascade="all, delete-orphan")
|
|
26
28
|
agents_tags: Mapped[List["AgentsTags"]] = relationship("AgentsTags", back_populates="organization", cascade="all, delete-orphan")
|
|
27
|
-
|
|
28
29
|
# TODO: Map these relationships later when we actually make these models
|
|
29
30
|
# below is just a suggestion
|
|
30
31
|
# agents: Mapped[List["Agent"]] = relationship("Agent", back_populates="organization", cascade="all, delete-orphan")
|
|
31
|
-
# sources: Mapped[List["Source"]] = relationship("Source", back_populates="organization", cascade="all, delete-orphan")
|
|
32
32
|
# tools: Mapped[List["Tool"]] = relationship("Tool", back_populates="organization", cascade="all, delete-orphan")
|
|
33
33
|
# documents: Mapped[List["Document"]] = relationship("Document", back_populates="organization", cascade="all, delete-orphan")
|
letta/orm/source.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Optional
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import JSON, TypeDecorator
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
5
|
+
|
|
6
|
+
from letta.orm.mixins import OrganizationMixin
|
|
7
|
+
from letta.orm.sqlalchemy_base import SqlalchemyBase
|
|
8
|
+
from letta.schemas.embedding_config import EmbeddingConfig
|
|
9
|
+
from letta.schemas.source import Source as PydanticSource
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from letta.orm.organization import Organization
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EmbeddingConfigColumn(TypeDecorator):
|
|
16
|
+
"""Custom type for storing EmbeddingConfig as JSON"""
|
|
17
|
+
|
|
18
|
+
impl = JSON
|
|
19
|
+
cache_ok = True
|
|
20
|
+
|
|
21
|
+
def load_dialect_impl(self, dialect):
|
|
22
|
+
return dialect.type_descriptor(JSON())
|
|
23
|
+
|
|
24
|
+
def process_bind_param(self, value, dialect):
|
|
25
|
+
if value:
|
|
26
|
+
# return vars(value)
|
|
27
|
+
if isinstance(value, EmbeddingConfig):
|
|
28
|
+
return value.model_dump()
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
def process_result_value(self, value, dialect):
|
|
32
|
+
if value:
|
|
33
|
+
return EmbeddingConfig(**value)
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Source(SqlalchemyBase, OrganizationMixin):
|
|
38
|
+
"""A source represents an embedded text passage"""
|
|
39
|
+
|
|
40
|
+
__tablename__ = "sources"
|
|
41
|
+
__pydantic_model__ = PydanticSource
|
|
42
|
+
|
|
43
|
+
name: Mapped[str] = mapped_column(doc="the name of the source, must be unique within the org", nullable=False)
|
|
44
|
+
description: Mapped[str] = mapped_column(nullable=True, doc="a human-readable description of the source")
|
|
45
|
+
embedding_config: Mapped[EmbeddingConfig] = mapped_column(EmbeddingConfigColumn, doc="Configuration settings for embedding.")
|
|
46
|
+
metadata_: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, doc="metadata for the source.")
|
|
47
|
+
|
|
48
|
+
# relationships
|
|
49
|
+
organization: Mapped["Organization"] = relationship("Organization", back_populates="sources")
|
|
50
|
+
# agents: Mapped[List["Agent"]] = relationship("Agent", secondary="sources_agents", back_populates="sources")
|
letta/providers.py
CHANGED
letta/schemas/source.py
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
4
|
+
from pydantic import Field
|
|
6
5
|
|
|
7
6
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
8
7
|
from letta.schemas.letta_base import LettaBase
|
|
9
|
-
from letta.utils import get_utc_time
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class BaseSource(LettaBase):
|
|
@@ -15,15 +13,6 @@ class BaseSource(LettaBase):
|
|
|
15
13
|
"""
|
|
16
14
|
|
|
17
15
|
__id_prefix__ = "source"
|
|
18
|
-
description: Optional[str] = Field(None, description="The description of the source.")
|
|
19
|
-
embedding_config: Optional[EmbeddingConfig] = Field(None, description="The embedding configuration used by the passage.")
|
|
20
|
-
# NOTE: .metadata is a reserved attribute on SQLModel
|
|
21
|
-
metadata_: Optional[dict] = Field(None, description="Metadata associated with the source.")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class SourceCreate(BaseSource):
|
|
25
|
-
name: str = Field(..., description="The name of the source.")
|
|
26
|
-
description: Optional[str] = Field(None, description="The description of the source.")
|
|
27
16
|
|
|
28
17
|
|
|
29
18
|
class Source(BaseSource):
|
|
@@ -34,7 +23,6 @@ class Source(BaseSource):
|
|
|
34
23
|
id (str): The ID of the source
|
|
35
24
|
name (str): The name of the source.
|
|
36
25
|
embedding_config (EmbeddingConfig): The embedding configuration used by the source.
|
|
37
|
-
created_at (datetime): The creation date of the source.
|
|
38
26
|
user_id (str): The ID of the user that created the source.
|
|
39
27
|
metadata_ (dict): Metadata associated with the source.
|
|
40
28
|
description (str): The description of the source.
|
|
@@ -42,21 +30,39 @@ class Source(BaseSource):
|
|
|
42
30
|
|
|
43
31
|
id: str = BaseSource.generate_id_field()
|
|
44
32
|
name: str = Field(..., description="The name of the source.")
|
|
33
|
+
description: Optional[str] = Field(None, description="The description of the source.")
|
|
45
34
|
embedding_config: EmbeddingConfig = Field(..., description="The embedding configuration used by the source.")
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
organization_id: Optional[str] = Field(None, description="The ID of the organization that created the source.")
|
|
36
|
+
metadata_: Optional[dict] = Field(None, description="Metadata associated with the source.")
|
|
48
37
|
|
|
38
|
+
# metadata fields
|
|
39
|
+
created_by_id: Optional[str] = Field(None, description="The id of the user that made this Tool.")
|
|
40
|
+
last_updated_by_id: Optional[str] = Field(None, description="The id of the user that made this Tool.")
|
|
41
|
+
created_at: Optional[datetime] = Field(None, description="The timestamp when the source was created.")
|
|
42
|
+
updated_at: Optional[datetime] = Field(None, description="The timestamp when the source was last updated.")
|
|
49
43
|
|
|
50
|
-
class SourceUpdate(BaseSource):
|
|
51
|
-
id: str = Field(..., description="The ID of the source.")
|
|
52
|
-
name: Optional[str] = Field(None, description="The name of the source.")
|
|
53
44
|
|
|
45
|
+
class SourceCreate(BaseSource):
|
|
46
|
+
"""
|
|
47
|
+
Schema for creating a new Source.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# required
|
|
51
|
+
name: str = Field(..., description="The name of the source.")
|
|
52
|
+
# TODO: @matt, make this required after shub makes the FE changes
|
|
53
|
+
embedding_config: Optional[EmbeddingConfig] = Field(None, description="The embedding configuration used by the source.")
|
|
54
|
+
|
|
55
|
+
# optional
|
|
56
|
+
description: Optional[str] = Field(None, description="The description of the source.")
|
|
57
|
+
metadata_: Optional[dict] = Field(None, description="Metadata associated with the source.")
|
|
54
58
|
|
|
55
|
-
class UploadFileToSourceRequest(BaseModel):
|
|
56
|
-
file: UploadFile = Field(..., description="The file to upload.")
|
|
57
59
|
|
|
60
|
+
class SourceUpdate(BaseSource):
|
|
61
|
+
"""
|
|
62
|
+
Schema for updating an existing Source.
|
|
63
|
+
"""
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
name: Optional[str] = Field(None, description="The name of the source.")
|
|
66
|
+
description: Optional[str] = Field(None, description="The description of the source.")
|
|
67
|
+
metadata_: Optional[dict] = Field(None, description="Metadata associated with the source.")
|
|
68
|
+
embedding_config: Optional[EmbeddingConfig] = Field(None, description="The embedding configuration used by the source.")
|
letta/server/rest_api/app.py
CHANGED
|
@@ -6,6 +6,8 @@ from typing import Optional
|
|
|
6
6
|
|
|
7
7
|
import uvicorn
|
|
8
8
|
from fastapi import FastAPI
|
|
9
|
+
from fastapi.responses import JSONResponse
|
|
10
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
9
11
|
from starlette.middleware.cors import CORSMiddleware
|
|
10
12
|
|
|
11
13
|
from letta.__init__ import __version__
|
|
@@ -94,6 +96,27 @@ def generate_openapi_schema(app: FastAPI):
|
|
|
94
96
|
Path(f"openapi_{name}.json").write_text(json.dumps(docs, indent=2))
|
|
95
97
|
|
|
96
98
|
|
|
99
|
+
# middleware that only allows requests to pass through if user provides a password thats randomly generated and stored in memory
|
|
100
|
+
def generate_password():
|
|
101
|
+
import secrets
|
|
102
|
+
|
|
103
|
+
return secrets.token_urlsafe(16)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
random_password = generate_password()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CheckPasswordMiddleware(BaseHTTPMiddleware):
|
|
110
|
+
async def dispatch(self, request, call_next):
|
|
111
|
+
if request.headers.get("X-BARE-PASSWORD") == f"password {random_password}":
|
|
112
|
+
return await call_next(request)
|
|
113
|
+
|
|
114
|
+
return JSONResponse(
|
|
115
|
+
content={"detail": "Unauthorized"},
|
|
116
|
+
status_code=401,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
97
120
|
def create_application() -> "FastAPI":
|
|
98
121
|
"""the application start routine"""
|
|
99
122
|
# global server
|
|
@@ -113,6 +136,10 @@ def create_application() -> "FastAPI":
|
|
|
113
136
|
settings.cors_origins.append("https://app.letta.com")
|
|
114
137
|
print(f"▶ View using ADE at: https://app.letta.com/local-project/agents")
|
|
115
138
|
|
|
139
|
+
if "--secure" in sys.argv:
|
|
140
|
+
print(f"▶ Using secure mode with password: {random_password}")
|
|
141
|
+
app.add_middleware(CheckPasswordMiddleware)
|
|
142
|
+
|
|
116
143
|
app.add_middleware(
|
|
117
144
|
CORSMiddleware,
|
|
118
145
|
allow_origins=settings.cors_origins,
|
|
@@ -36,7 +36,7 @@ def get_source(
|
|
|
36
36
|
"""
|
|
37
37
|
actor = server.get_user_or_default(user_id=user_id)
|
|
38
38
|
|
|
39
|
-
return server.
|
|
39
|
+
return server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@router.get("/name/{source_name}", response_model=str, operation_id="get_source_id_by_name")
|
|
@@ -50,8 +50,8 @@ def get_source_id_by_name(
|
|
|
50
50
|
"""
|
|
51
51
|
actor = server.get_user_or_default(user_id=user_id)
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
return
|
|
53
|
+
source = server.source_manager.get_source_by_name(source_name=source_name, actor=actor)
|
|
54
|
+
return source.id
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
@router.get("/", response_model=List[Source], operation_id="list_sources")
|
|
@@ -64,12 +64,12 @@ def list_sources(
|
|
|
64
64
|
"""
|
|
65
65
|
actor = server.get_user_or_default(user_id=user_id)
|
|
66
66
|
|
|
67
|
-
return server.list_all_sources(
|
|
67
|
+
return server.list_all_sources(actor=actor)
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
@router.post("/", response_model=Source, operation_id="create_source")
|
|
71
71
|
def create_source(
|
|
72
|
-
|
|
72
|
+
source_create: SourceCreate,
|
|
73
73
|
server: "SyncServer" = Depends(get_letta_server),
|
|
74
74
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
75
75
|
):
|
|
@@ -77,8 +77,9 @@ def create_source(
|
|
|
77
77
|
Create a new data source.
|
|
78
78
|
"""
|
|
79
79
|
actor = server.get_user_or_default(user_id=user_id)
|
|
80
|
+
source = Source(**source_create.model_dump())
|
|
80
81
|
|
|
81
|
-
return server.create_source(
|
|
82
|
+
return server.source_manager.create_source(source=source, actor=actor)
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
@router.patch("/{source_id}", response_model=Source, operation_id="update_source")
|
|
@@ -92,10 +93,7 @@ def update_source(
|
|
|
92
93
|
Update the name or documentation of an existing data source.
|
|
93
94
|
"""
|
|
94
95
|
actor = server.get_user_or_default(user_id=user_id)
|
|
95
|
-
|
|
96
|
-
assert source.id == source_id, "Source ID in path must match ID in request body"
|
|
97
|
-
|
|
98
|
-
return server.update_source(request=source, user_id=actor.id)
|
|
96
|
+
return server.source_manager.update_source(source_id=source_id, source_update=source, actor=actor)
|
|
99
97
|
|
|
100
98
|
|
|
101
99
|
@router.delete("/{source_id}", response_model=None, operation_id="delete_source")
|
|
@@ -109,7 +107,7 @@ def delete_source(
|
|
|
109
107
|
"""
|
|
110
108
|
actor = server.get_user_or_default(user_id=user_id)
|
|
111
109
|
|
|
112
|
-
server.delete_source(source_id=source_id,
|
|
110
|
+
server.delete_source(source_id=source_id, actor=actor)
|
|
113
111
|
|
|
114
112
|
|
|
115
113
|
@router.post("/{source_id}/attach", response_model=Source, operation_id="attach_agent_to_source")
|
|
@@ -124,7 +122,7 @@ def attach_source_to_agent(
|
|
|
124
122
|
"""
|
|
125
123
|
actor = server.get_user_or_default(user_id=user_id)
|
|
126
124
|
|
|
127
|
-
source = server.
|
|
125
|
+
source = server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
|
|
128
126
|
assert source is not None, f"Source with id={source_id} not found."
|
|
129
127
|
source = server.attach_source_to_agent(source_id=source.id, agent_id=agent_id, user_id=actor.id)
|
|
130
128
|
return source
|
|
@@ -158,7 +156,7 @@ def upload_file_to_source(
|
|
|
158
156
|
"""
|
|
159
157
|
actor = server.get_user_or_default(user_id=user_id)
|
|
160
158
|
|
|
161
|
-
source = server.
|
|
159
|
+
source = server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
|
|
162
160
|
assert source is not None, f"Source with id={source_id} not found."
|
|
163
161
|
bytes = file.file.read()
|
|
164
162
|
|
letta/server/server.py
CHANGED
|
@@ -78,12 +78,13 @@ from letta.schemas.memory import (
|
|
|
78
78
|
from letta.schemas.message import Message, MessageCreate, MessageRole, UpdateMessage
|
|
79
79
|
from letta.schemas.organization import Organization
|
|
80
80
|
from letta.schemas.passage import Passage
|
|
81
|
-
from letta.schemas.source import Source
|
|
81
|
+
from letta.schemas.source import Source
|
|
82
82
|
from letta.schemas.tool import Tool, ToolCreate
|
|
83
83
|
from letta.schemas.usage import LettaUsageStatistics
|
|
84
84
|
from letta.schemas.user import User
|
|
85
85
|
from letta.services.agents_tags_manager import AgentsTagsManager
|
|
86
86
|
from letta.services.organization_manager import OrganizationManager
|
|
87
|
+
from letta.services.source_manager import SourceManager
|
|
87
88
|
from letta.services.tool_manager import ToolManager
|
|
88
89
|
from letta.services.user_manager import UserManager
|
|
89
90
|
from letta.utils import create_random_username, json_dumps, json_loads
|
|
@@ -249,6 +250,7 @@ class SyncServer(Server):
|
|
|
249
250
|
self.organization_manager = OrganizationManager()
|
|
250
251
|
self.user_manager = UserManager()
|
|
251
252
|
self.tool_manager = ToolManager()
|
|
253
|
+
self.source_manager = SourceManager()
|
|
252
254
|
self.agents_tags_manager = AgentsTagsManager()
|
|
253
255
|
|
|
254
256
|
# Make default user and org
|
|
@@ -1560,44 +1562,12 @@ class SyncServer(Server):
|
|
|
1560
1562
|
self.ms.delete_api_key(api_key=api_key)
|
|
1561
1563
|
return api_key_obj
|
|
1562
1564
|
|
|
1563
|
-
def
|
|
1564
|
-
"""Create a new data source"""
|
|
1565
|
-
source = Source(
|
|
1566
|
-
name=request.name,
|
|
1567
|
-
user_id=user_id,
|
|
1568
|
-
embedding_config=self.list_embedding_models()[0], # TODO: require providing this
|
|
1569
|
-
)
|
|
1570
|
-
self.ms.create_source(source)
|
|
1571
|
-
assert self.ms.get_source(source_name=request.name, user_id=user_id) is not None, f"Failed to create source {request.name}"
|
|
1572
|
-
return source
|
|
1573
|
-
|
|
1574
|
-
def update_source(self, request: SourceUpdate, user_id: str) -> Source:
|
|
1575
|
-
"""Update an existing data source"""
|
|
1576
|
-
if not request.id:
|
|
1577
|
-
existing_source = self.ms.get_source(source_name=request.name, user_id=user_id)
|
|
1578
|
-
else:
|
|
1579
|
-
existing_source = self.ms.get_source(source_id=request.id)
|
|
1580
|
-
if not existing_source:
|
|
1581
|
-
raise ValueError("Source does not exist")
|
|
1582
|
-
|
|
1583
|
-
# override updated fields
|
|
1584
|
-
if request.name:
|
|
1585
|
-
existing_source.name = request.name
|
|
1586
|
-
if request.metadata_:
|
|
1587
|
-
existing_source.metadata_ = request.metadata_
|
|
1588
|
-
if request.description:
|
|
1589
|
-
existing_source.description = request.description
|
|
1590
|
-
|
|
1591
|
-
self.ms.update_source(existing_source)
|
|
1592
|
-
return existing_source
|
|
1593
|
-
|
|
1594
|
-
def delete_source(self, source_id: str, user_id: str):
|
|
1565
|
+
def delete_source(self, source_id: str, actor: User):
|
|
1595
1566
|
"""Delete a data source"""
|
|
1596
|
-
|
|
1597
|
-
self.ms.delete_source(source_id)
|
|
1567
|
+
self.source_manager.delete_source(source_id=source_id, actor=actor)
|
|
1598
1568
|
|
|
1599
1569
|
# delete data from passage store
|
|
1600
|
-
passage_store = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=
|
|
1570
|
+
passage_store = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=actor.id)
|
|
1601
1571
|
passage_store.delete({"source_id": source_id})
|
|
1602
1572
|
|
|
1603
1573
|
# TODO: delete data from agent passage stores (?)
|
|
@@ -1639,9 +1609,9 @@ class SyncServer(Server):
|
|
|
1639
1609
|
# try:
|
|
1640
1610
|
from letta.data_sources.connectors import DirectoryConnector
|
|
1641
1611
|
|
|
1642
|
-
source = self.
|
|
1612
|
+
source = self.source_manager.get_source_by_id(source_id=source_id)
|
|
1643
1613
|
connector = DirectoryConnector(input_files=[file_path])
|
|
1644
|
-
num_passages, num_documents = self.load_data(user_id=source.
|
|
1614
|
+
num_passages, num_documents = self.load_data(user_id=source.created_by_id, source_name=source.name, connector=connector)
|
|
1645
1615
|
# except Exception as e:
|
|
1646
1616
|
# # job failed with error
|
|
1647
1617
|
# error = str(e)
|
|
@@ -1675,7 +1645,8 @@ class SyncServer(Server):
|
|
|
1675
1645
|
# TODO: this should be implemented as a batch job or at least async, since it may take a long time
|
|
1676
1646
|
|
|
1677
1647
|
# load data from a data source into the document store
|
|
1678
|
-
|
|
1648
|
+
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1649
|
+
source = self.source_manager.get_source_by_name(source_name=source_name, actor=user)
|
|
1679
1650
|
if source is None:
|
|
1680
1651
|
raise ValueError(f"Data source {source_name} does not exist for user {user_id}")
|
|
1681
1652
|
|
|
@@ -1696,9 +1667,13 @@ class SyncServer(Server):
|
|
|
1696
1667
|
source_name: Optional[str] = None,
|
|
1697
1668
|
) -> Source:
|
|
1698
1669
|
# attach a data source to an agent
|
|
1699
|
-
|
|
1700
|
-
if
|
|
1701
|
-
|
|
1670
|
+
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1671
|
+
if source_id:
|
|
1672
|
+
data_source = self.source_manager.get_source_by_id(source_id=source_id, actor=user)
|
|
1673
|
+
elif source_name:
|
|
1674
|
+
data_source = self.source_manager.get_source_by_name(source_name=source_name, actor=user)
|
|
1675
|
+
else:
|
|
1676
|
+
raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
|
|
1702
1677
|
|
|
1703
1678
|
# get connection to data source storage
|
|
1704
1679
|
source_connector = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
|
|
@@ -1719,12 +1694,14 @@ class SyncServer(Server):
|
|
|
1719
1694
|
source_id: Optional[str] = None,
|
|
1720
1695
|
source_name: Optional[str] = None,
|
|
1721
1696
|
) -> Source:
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
source = self.
|
|
1725
|
-
|
|
1697
|
+
user = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1698
|
+
if source_id:
|
|
1699
|
+
source = self.source_manager.get_source_by_id(source_id=source_id, actor=user)
|
|
1700
|
+
elif source_name:
|
|
1701
|
+
source = self.source_manager.get_source_by_name(source_name=source_name, actor=user)
|
|
1726
1702
|
else:
|
|
1727
|
-
|
|
1703
|
+
raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
|
|
1704
|
+
source_id = source.id
|
|
1728
1705
|
|
|
1729
1706
|
# delete all Passage objects with source_id==source_id from agent's archival memory
|
|
1730
1707
|
agent = self._get_or_load_agent(agent_id=agent_id)
|
|
@@ -1739,7 +1716,9 @@ class SyncServer(Server):
|
|
|
1739
1716
|
|
|
1740
1717
|
def list_attached_sources(self, agent_id: str) -> List[Source]:
|
|
1741
1718
|
# list all attached sources to an agent
|
|
1742
|
-
|
|
1719
|
+
source_ids = self.ms.list_attached_source_ids(agent_id)
|
|
1720
|
+
|
|
1721
|
+
return [self.source_manager.get_source_by_id(source_id=id) for id in source_ids]
|
|
1743
1722
|
|
|
1744
1723
|
def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
|
|
1745
1724
|
# list all attached sources to an agent
|
|
@@ -1749,17 +1728,17 @@ class SyncServer(Server):
|
|
|
1749
1728
|
warnings.warn("list_data_source_passages is not yet implemented, returning empty list.", category=UserWarning)
|
|
1750
1729
|
return []
|
|
1751
1730
|
|
|
1752
|
-
def list_all_sources(self,
|
|
1731
|
+
def list_all_sources(self, actor: User) -> List[Source]:
|
|
1753
1732
|
"""List all sources (w/ extra metadata) belonging to a user"""
|
|
1754
1733
|
|
|
1755
|
-
sources = self.
|
|
1734
|
+
sources = self.source_manager.list_sources(actor=actor)
|
|
1756
1735
|
|
|
1757
1736
|
# Add extra metadata to the sources
|
|
1758
1737
|
sources_with_metadata = []
|
|
1759
1738
|
for source in sources:
|
|
1760
1739
|
|
|
1761
1740
|
# count number of passages
|
|
1762
|
-
passage_conn = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=
|
|
1741
|
+
passage_conn = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=actor.id)
|
|
1763
1742
|
num_passages = passage_conn.size({"source_id": source.id})
|
|
1764
1743
|
|
|
1765
1744
|
# TODO: add when files table implemented
|
|
@@ -1773,7 +1752,7 @@ class SyncServer(Server):
|
|
|
1773
1752
|
attached_agents = [
|
|
1774
1753
|
{
|
|
1775
1754
|
"id": str(a_id),
|
|
1776
|
-
"name": self.ms.get_agent(user_id=
|
|
1755
|
+
"name": self.ms.get_agent(user_id=actor.id, agent_id=a_id).name,
|
|
1777
1756
|
}
|
|
1778
1757
|
for a_id in agent_ids
|
|
1779
1758
|
]
|
|
@@ -27,18 +27,26 @@ class OrganizationManager:
|
|
|
27
27
|
return self.get_organization_by_id(self.DEFAULT_ORG_ID)
|
|
28
28
|
|
|
29
29
|
@enforce_types
|
|
30
|
-
def get_organization_by_id(self, org_id: str) -> PydanticOrganization:
|
|
30
|
+
def get_organization_by_id(self, org_id: str) -> Optional[PydanticOrganization]:
|
|
31
31
|
"""Fetch an organization by ID."""
|
|
32
32
|
with self.session_maker() as session:
|
|
33
33
|
try:
|
|
34
34
|
organization = OrganizationModel.read(db_session=session, identifier=org_id)
|
|
35
35
|
return organization.to_pydantic()
|
|
36
36
|
except NoResultFound:
|
|
37
|
-
|
|
37
|
+
return None
|
|
38
38
|
|
|
39
39
|
@enforce_types
|
|
40
40
|
def create_organization(self, pydantic_org: PydanticOrganization) -> PydanticOrganization:
|
|
41
41
|
"""Create a new organization. If a name is provided, it is used, otherwise, a random one is generated."""
|
|
42
|
+
org = self.get_organization_by_id(pydantic_org.id)
|
|
43
|
+
if org:
|
|
44
|
+
return org
|
|
45
|
+
else:
|
|
46
|
+
return self._create_organization(pydantic_org=pydantic_org)
|
|
47
|
+
|
|
48
|
+
@enforce_types
|
|
49
|
+
def _create_organization(self, pydantic_org: PydanticOrganization) -> PydanticOrganization:
|
|
42
50
|
with self.session_maker() as session:
|
|
43
51
|
org = OrganizationModel(**pydantic_org.model_dump())
|
|
44
52
|
org.create(session)
|
|
@@ -47,16 +55,7 @@ class OrganizationManager:
|
|
|
47
55
|
@enforce_types
|
|
48
56
|
def create_default_organization(self) -> PydanticOrganization:
|
|
49
57
|
"""Create the default organization."""
|
|
50
|
-
|
|
51
|
-
# Try to get it first
|
|
52
|
-
try:
|
|
53
|
-
org = OrganizationModel.read(db_session=session, identifier=self.DEFAULT_ORG_ID)
|
|
54
|
-
# If it doesn't exist, make it
|
|
55
|
-
except NoResultFound:
|
|
56
|
-
org = OrganizationModel(name=self.DEFAULT_ORG_NAME, id=self.DEFAULT_ORG_ID)
|
|
57
|
-
org.create(session)
|
|
58
|
-
|
|
59
|
-
return org.to_pydantic()
|
|
58
|
+
return self.create_organization(PydanticOrganization(name=self.DEFAULT_ORG_NAME, id=self.DEFAULT_ORG_ID))
|
|
60
59
|
|
|
61
60
|
@enforce_types
|
|
62
61
|
def update_organization_name_using_id(self, org_id: str, name: Optional[str] = None) -> PydanticOrganization:
|
|
@@ -73,7 +72,7 @@ class OrganizationManager:
|
|
|
73
72
|
"""Delete an organization by marking it as deleted."""
|
|
74
73
|
with self.session_maker() as session:
|
|
75
74
|
organization = OrganizationModel.read(db_session=session, identifier=org_id)
|
|
76
|
-
organization.
|
|
75
|
+
organization.hard_delete(session)
|
|
77
76
|
|
|
78
77
|
@enforce_types
|
|
79
78
|
def list_organizations(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticOrganization]:
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from letta.orm.errors import NoResultFound
|
|
4
|
+
from letta.orm.source import Source as SourceModel
|
|
5
|
+
from letta.schemas.source import Source as PydanticSource
|
|
6
|
+
from letta.schemas.source import SourceUpdate
|
|
7
|
+
from letta.schemas.user import User as PydanticUser
|
|
8
|
+
from letta.utils import enforce_types, printd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SourceManager:
|
|
12
|
+
"""Manager class to handle business logic related to Sources."""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
from letta.server.server import db_context
|
|
16
|
+
|
|
17
|
+
self.session_maker = db_context
|
|
18
|
+
|
|
19
|
+
@enforce_types
|
|
20
|
+
def create_source(self, source: PydanticSource, actor: PydanticUser) -> PydanticSource:
|
|
21
|
+
"""Create a new source based on the PydanticSource schema."""
|
|
22
|
+
# Try getting the source first by id
|
|
23
|
+
db_source = self.get_source_by_id(source.id, actor=actor)
|
|
24
|
+
if db_source:
|
|
25
|
+
return db_source
|
|
26
|
+
else:
|
|
27
|
+
with self.session_maker() as session:
|
|
28
|
+
# Provide default embedding config if not given
|
|
29
|
+
source.organization_id = actor.organization_id
|
|
30
|
+
source = SourceModel(**source.model_dump(exclude_none=True))
|
|
31
|
+
source.create(session, actor=actor)
|
|
32
|
+
return source.to_pydantic()
|
|
33
|
+
|
|
34
|
+
@enforce_types
|
|
35
|
+
def update_source(self, source_id: str, source_update: SourceUpdate, actor: PydanticUser) -> PydanticSource:
|
|
36
|
+
"""Update a source by its ID with the given SourceUpdate object."""
|
|
37
|
+
with self.session_maker() as session:
|
|
38
|
+
source = SourceModel.read(db_session=session, identifier=source_id, actor=actor)
|
|
39
|
+
|
|
40
|
+
# get update dictionary
|
|
41
|
+
update_data = source_update.model_dump(exclude_unset=True, exclude_none=True)
|
|
42
|
+
# Remove redundant update fields
|
|
43
|
+
update_data = {key: value for key, value in update_data.items() if getattr(source, key) != value}
|
|
44
|
+
|
|
45
|
+
if update_data:
|
|
46
|
+
for key, value in update_data.items():
|
|
47
|
+
setattr(source, key, value)
|
|
48
|
+
source.update(db_session=session, actor=actor)
|
|
49
|
+
else:
|
|
50
|
+
printd(
|
|
51
|
+
f"`update_source` was called with user_id={actor.id}, organization_id={actor.organization_id}, name={source.name}, but found existing source with nothing to update."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return source.to_pydantic()
|
|
55
|
+
|
|
56
|
+
@enforce_types
|
|
57
|
+
def delete_source(self, source_id: str, actor: PydanticUser) -> PydanticSource:
|
|
58
|
+
"""Delete a source by its ID."""
|
|
59
|
+
with self.session_maker() as session:
|
|
60
|
+
source = SourceModel.read(db_session=session, identifier=source_id)
|
|
61
|
+
source.delete(db_session=session, actor=actor)
|
|
62
|
+
return source.to_pydantic()
|
|
63
|
+
|
|
64
|
+
@enforce_types
|
|
65
|
+
def list_sources(self, actor: PydanticUser, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticSource]:
|
|
66
|
+
"""List all sources with optional pagination."""
|
|
67
|
+
with self.session_maker() as session:
|
|
68
|
+
sources = SourceModel.list(
|
|
69
|
+
db_session=session,
|
|
70
|
+
cursor=cursor,
|
|
71
|
+
limit=limit,
|
|
72
|
+
organization_id=actor.organization_id,
|
|
73
|
+
)
|
|
74
|
+
return [source.to_pydantic() for source in sources]
|
|
75
|
+
|
|
76
|
+
# TODO: We make actor optional for now, but should most likely be enforced due to security reasons
|
|
77
|
+
@enforce_types
|
|
78
|
+
def get_source_by_id(self, source_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticSource]:
|
|
79
|
+
"""Retrieve a source by its ID."""
|
|
80
|
+
with self.session_maker() as session:
|
|
81
|
+
try:
|
|
82
|
+
source = SourceModel.read(db_session=session, identifier=source_id, actor=actor)
|
|
83
|
+
return source.to_pydantic()
|
|
84
|
+
except NoResultFound:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
@enforce_types
|
|
88
|
+
def get_source_by_name(self, source_name: str, actor: PydanticUser) -> Optional[PydanticSource]:
|
|
89
|
+
"""Retrieve a source by its name."""
|
|
90
|
+
with self.session_maker() as session:
|
|
91
|
+
sources = SourceModel.list(
|
|
92
|
+
db_session=session,
|
|
93
|
+
name=source_name,
|
|
94
|
+
organization_id=actor.organization_id,
|
|
95
|
+
limit=1,
|
|
96
|
+
)
|
|
97
|
+
if not sources:
|
|
98
|
+
return None
|
|
99
|
+
else:
|
|
100
|
+
return sources[0].to_pydantic()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
letta/__init__.py,sha256=IMLtpH5HlbVUa1mmPpSyBpTZqVz1rsS7lbuqT7viBQ0,1014
|
|
2
2
|
letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
|
3
|
-
letta/agent.py,sha256=
|
|
3
|
+
letta/agent.py,sha256=mQxkYsG80P_KCzXN6EDmwi6tiXebabHpcYsgs7ugWP0,76952
|
|
4
4
|
letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
|
|
5
5
|
letta/agent_store/db.py,sha256=iBdP9IxvuenKRF7SVRhFlC8yvRwNn2xxOfBFvadBcNA,23383
|
|
6
6
|
letta/agent_store/lancedb.py,sha256=i63d4VZwj9UIOTNs5f0JZ_r5yZD-jKWz4FAH4RMpXOE,5104
|
|
@@ -9,17 +9,17 @@ letta/agent_store/qdrant.py,sha256=6_33V-FEDpT9LG5zmr6-3y9slw1YFLswxpahiyMkvHA,7
|
|
|
9
9
|
letta/agent_store/storage.py,sha256=4gKvMRYBGm9cwyaDOzljxDKgqr4MxGXcC4yGhAdKcAA,6693
|
|
10
10
|
letta/benchmark/benchmark.py,sha256=ebvnwfp3yezaXOQyGXkYCDYpsmre-b9hvNtnyx4xkG0,3701
|
|
11
11
|
letta/benchmark/constants.py,sha256=aXc5gdpMGJT327VuxsT5FngbCK2J41PQYeICBO7g_RE,536
|
|
12
|
-
letta/cli/cli.py,sha256=
|
|
12
|
+
letta/cli/cli.py,sha256=oREx2gmpFvw7CdmXjhzoj_4iypM27WAK5vePESIgcHo,16898
|
|
13
13
|
letta/cli/cli_config.py,sha256=D-CpSZI1cDvdSQr3-zhGuDrJnZo1Ko7bi_wuxcBxxqo,8555
|
|
14
14
|
letta/cli/cli_load.py,sha256=x4L8s15GwIW13xrhKYFWHo_y-IVGtoPDHWWKcHDRP10,4587
|
|
15
15
|
letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
letta/client/client.py,sha256=
|
|
16
|
+
letta/client/client.py,sha256=nnd0n4NWKDpBnt41LgcQYxfNuOGYQ2CuQ4SpHbKArZo,97883
|
|
17
17
|
letta/client/streaming.py,sha256=Hh5pjlyrdCuO2V75ZCxSSOCPd3BmHdKFGaIUJC6fBp0,4775
|
|
18
18
|
letta/client/utils.py,sha256=OJlAKWrldc4I6M1WpcTWNtPJ4wfxlzlZqWLfCozkFtI,2872
|
|
19
19
|
letta/config.py,sha256=eK-ip06ELHNYriInkgfidDvJxQ2tD1u49I-VLXB87nE,18929
|
|
20
20
|
letta/constants.py,sha256=c8pEfIhtpqFGunyzGObnfEeRJNkunfmq9Pfiau8YYfA,6544
|
|
21
21
|
letta/credentials.py,sha256=D9mlcPsdDWlIIXQQD8wSPE9M_QvsRrb0p3LB5i9OF5Q,5806
|
|
22
|
-
letta/data_sources/connectors.py,sha256=
|
|
22
|
+
letta/data_sources/connectors.py,sha256=gEZCpNwGPR5MCnAofmFmQMuLbiJ9I44HJ2rSNRGMqP4,9918
|
|
23
23
|
letta/data_sources/connectors_helper.py,sha256=2TQjCt74fCgT5sw1AP8PalDEk06jPBbhrPG4HVr-WLs,3371
|
|
24
24
|
letta/embeddings.py,sha256=qPt8kB-wmuRIg1py7DHnQGJpw3DmQHJ505FJvc0K6Yk,8873
|
|
25
25
|
letta/errors.py,sha256=cDOo4cSYL-LA0w0b0GdsxXd5k2I1LLOY8nhtXk9YqYs,2875
|
|
@@ -36,15 +36,15 @@ letta/humans/examples/basic.txt,sha256=Lcp8YESTWvOJgO4Yf_yyQmgo5bKakeB1nIVrwEGG6
|
|
|
36
36
|
letta/humans/examples/cs_phd.txt,sha256=9C9ZAV_VuG7GB31ksy3-_NAyk8rjE6YtVOkhp08k1xw,297
|
|
37
37
|
letta/interface.py,sha256=QI4hFP0WrNsgM5qX6TbnhH1ZZxsLYr5DaccuxpEQ8S4,12768
|
|
38
38
|
letta/llm_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
letta/llm_api/anthropic.py,sha256=
|
|
39
|
+
letta/llm_api/anthropic.py,sha256=GZFMAGgertU7GrGK_ahz051Ce0MHXqPzh8f7AlsPslg,12887
|
|
40
40
|
letta/llm_api/azure_openai.py,sha256=Y1HKPog1XzM_f7ujUK_Gv2zQkoy5pU-1bKiUnvSxSrs,6297
|
|
41
41
|
letta/llm_api/azure_openai_constants.py,sha256=oXtKrgBFHf744gyt5l1thILXgyi8NDNUrKEa2GGGpjw,278
|
|
42
42
|
letta/llm_api/cohere.py,sha256=vDRd-SUGp1t_JUIdwC3RkIhwMl0OY7n-tAU9uPORYkY,14826
|
|
43
|
-
letta/llm_api/google_ai.py,sha256=
|
|
43
|
+
letta/llm_api/google_ai.py,sha256=xKz9JDZs3m6yzSfcgCAAUD_rjI20BBIINoiSvlcnOw0,17621
|
|
44
44
|
letta/llm_api/helpers.py,sha256=KqkdjZWYghx4OPwLcHEC6ruc_z9DScbysw3VH4x9A0Q,9887
|
|
45
45
|
letta/llm_api/llm_api_tools.py,sha256=KFG2miI7KrDOIcOSgm2jwBIb3qvzYt2O_5UNjTbTsm8,14786
|
|
46
46
|
letta/llm_api/mistral.py,sha256=fHdfD9ug-rQIk2qn8tRKay1U6w9maF11ryhKi91FfXM,1593
|
|
47
|
-
letta/llm_api/openai.py,sha256=
|
|
47
|
+
letta/llm_api/openai.py,sha256=p1tKbfCfhWOLnx2u-Vt67rq12uVUbo_zyslHHlmuTyU,23845
|
|
48
48
|
letta/local_llm/README.md,sha256=hFJyw5B0TU2jrh9nb0zGZMgdH-Ei1dSRfhvPQG_NSoU,168
|
|
49
49
|
letta/local_llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
letta/local_llm/chat_completion_proxy.py,sha256=SiohxsjGTku4vOryOZx7I0t0xoO_sUuhXgoe62fKq3c,12995
|
|
@@ -84,19 +84,20 @@ letta/local_llm/webui/legacy_settings.py,sha256=BLmd3TSx5StnY3ibjwaxYATPt_Lvq-o1
|
|
|
84
84
|
letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ftVPh8,552
|
|
85
85
|
letta/log.py,sha256=Oy5b71AXfrnQShxI_4ULo5U3kmZJG01bXbP_64Nr4Fk,2105
|
|
86
86
|
letta/main.py,sha256=h-qPQn_Ok5wf2cf54RFPfe8yp6sCmE-Kp9mBk_HZf7o,18797
|
|
87
|
-
letta/memory.py,sha256=
|
|
88
|
-
letta/metadata.py,sha256=
|
|
87
|
+
letta/memory.py,sha256=YupXOvzVJXH59RW4XWBrd7qMNEYaMbtWXCheKeWZwpU,17873
|
|
88
|
+
letta/metadata.py,sha256=2bfM0cJk78Aw3j1aAbxld1mHwwhd8vrnFHQJj1Ts3LI,25750
|
|
89
89
|
letta/o1_agent.py,sha256=LqATgTpkc02-nCH_F87EOvgxLjdjT9F07kdzj3zSdQg,3118
|
|
90
90
|
letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
91
91
|
letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
|
|
92
92
|
letta/orm/__all__.py,sha256=2gh2MZTkA3Hw67VWVKK3JIStJOqTeLdpCvYSVYNeEDA,692
|
|
93
|
-
letta/orm/__init__.py,sha256=
|
|
93
|
+
letta/orm/__init__.py,sha256=mR6vCTKJk4QzmqIFQozBoGo95RPEy3og5a-jQNKOpzk,180
|
|
94
94
|
letta/orm/agents_tags.py,sha256=Qa7Yt9imL8xbGP57fflccAMy7Z32CQiU_7eZKSSPngc,1119
|
|
95
95
|
letta/orm/base.py,sha256=K_LpNUURbsj44ycHbzvNXG_n8pBOjf1YvDaikIPDpQA,2716
|
|
96
96
|
letta/orm/enums.py,sha256=KfHcFt_fR6GUmSlmfsa-TetvmuRxGESNve8MStRYW64,145
|
|
97
97
|
letta/orm/errors.py,sha256=somsGtotFlb3SDM6tKdZ5TDGwEEP3ppx47ICAvNMnkg,225
|
|
98
98
|
letta/orm/mixins.py,sha256=fW4oa1cUFbgVE46KSQlW_hwzsZSqEBSSV-U3xJC6fyw,749
|
|
99
|
-
letta/orm/organization.py,sha256=
|
|
99
|
+
letta/orm/organization.py,sha256=_2HRrc1jCRNcZj-G5V70Xw_eEy21bBW7_f4c_6g_668,1712
|
|
100
|
+
letta/orm/source.py,sha256=Vr6WkD26BbVeZMHmcJFY6h2upChDW5OFNZEnlGvl9Q4,1895
|
|
100
101
|
letta/orm/sqlalchemy_base.py,sha256=QZ_b2jxNjXSvK-bJGxEHQiZqRn8tKNSuJRDorYCDCvE,7369
|
|
101
102
|
letta/orm/tool.py,sha256=7FIeldPJTEOLA5ygasTOVXqUcm2aouYYARUOJXdaC4Y,2151
|
|
102
103
|
letta/orm/user.py,sha256=-KNdpfnKRVJtSw0NrGRrVoSBBv7ASDchVempKtUuk-A,1229
|
|
@@ -123,7 +124,7 @@ letta/prompts/system/memgpt_gpt35_extralong.txt,sha256=FheNhYoIzNz6qnJKhVquZVSMj
|
|
|
123
124
|
letta/prompts/system/memgpt_intuitive_knowledge.txt,sha256=sA7c3urYqREVnSBI81nTGImXAekqC0Fxc7RojFqud1g,2966
|
|
124
125
|
letta/prompts/system/memgpt_modified_chat.txt,sha256=HOaPVurEftD8KsuwsclDgE2afIfklMjxhuSO96q1-6I,4656
|
|
125
126
|
letta/prompts/system/memgpt_modified_o1.txt,sha256=AxxYVjYLZwpZ6yfifh1SuPtwlJGWTcTVzw53QbkN-Ao,5492
|
|
126
|
-
letta/providers.py,sha256=
|
|
127
|
+
letta/providers.py,sha256=miNxSXDMKDZqYjrYtE2N9ecw9ltccDXSJBlJqxOhEws,19611
|
|
127
128
|
letta/pytest.ini,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
129
|
letta/schemas/agent.py,sha256=f0khTBWIRGZva4_C15Nm_tkmn1cwaVQlWa7_7laRbEE,7866
|
|
129
130
|
letta/schemas/agents_tags.py,sha256=9DGr8fN2DHYdWvZ_qcXmrKI0w7YKCGz2lfEcrX2KAkI,1130
|
|
@@ -148,7 +149,7 @@ letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNo
|
|
|
148
149
|
letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
|
|
149
150
|
letta/schemas/organization.py,sha256=d2oN3IK2HeruEHKXwIzCbJ3Fxdi_BEe9JZ8J9aDbHwQ,698
|
|
150
151
|
letta/schemas/passage.py,sha256=eYQMxD_XjHAi72jmqcGBU4wM4VZtSU0XK8uhQxxN3Ug,3563
|
|
151
|
-
letta/schemas/source.py,sha256=
|
|
152
|
+
letta/schemas/source.py,sha256=B1VbaDJV-EGPv1nQXwCx_RAzeAJd50UqP_1m1cIRT8c,2854
|
|
152
153
|
letta/schemas/tool.py,sha256=HCUW4RXUS9RtvfpTPeNhauFN3s23BojSOceaIxv9bKI,9596
|
|
153
154
|
letta/schemas/tool_rule.py,sha256=zv4jE0b8LW78idP4UbJARnrZcnmaqjGNUk_YV99Y0c0,884
|
|
154
155
|
letta/schemas/usage.py,sha256=lvn1ooHwLEdv6gwQpw5PBUbcwn_gwdT6HA-fCiix6sY,817
|
|
@@ -157,7 +158,7 @@ letta/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
157
158
|
letta/server/constants.py,sha256=yAdGbLkzlOU_dLTx0lKDmAnj0ZgRXCEaIcPJWO69eaE,92
|
|
158
159
|
letta/server/generate_openapi_schema.sh,sha256=0OtBhkC1g6CobVmNEd_m2B6sTdppjbJLXaM95icejvE,371
|
|
159
160
|
letta/server/rest_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
160
|
-
letta/server/rest_api/app.py,sha256=
|
|
161
|
+
letta/server/rest_api/app.py,sha256=QbCgYeyzA86iefwomROBAaxwvSWYQT10fhHcNZsyjH4,7555
|
|
161
162
|
letta/server/rest_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
162
163
|
letta/server/rest_api/auth/index.py,sha256=fQBGyVylGSRfEMLQ17cZzrHd5Y1xiVylvPqH5Rl-lXQ,1378
|
|
163
164
|
letta/server/rest_api/auth_token.py,sha256=725EFEIiNj4dh70hrSd94UysmFD8vcJLrTRfNHkzxDo,774
|
|
@@ -177,12 +178,12 @@ letta/server/rest_api/routers/v1/health.py,sha256=pKCuVESlVOhGIb4VC4K-H82eZqfghm
|
|
|
177
178
|
letta/server/rest_api/routers/v1/jobs.py,sha256=a-j0v-5A0un0pVCOHpfeWnzpOWkVDQO6ti42k_qAlZY,2272
|
|
178
179
|
letta/server/rest_api/routers/v1/llms.py,sha256=TcyvSx6MEM3je5F4DysL7ligmssL_pFlJaaO4uL95VY,877
|
|
179
180
|
letta/server/rest_api/routers/v1/organizations.py,sha256=tyqVzXTpMtk3sKxI3Iz4aS6RhbGEbXDzFBB_CpW18v4,2080
|
|
180
|
-
letta/server/rest_api/routers/v1/sources.py,sha256=
|
|
181
|
+
letta/server/rest_api/routers/v1/sources.py,sha256=u6THgcSbSx2r0Sszr0v_6NV1U9AeMUDzZj9n3_kIXr8,8809
|
|
181
182
|
letta/server/rest_api/routers/v1/tools.py,sha256=Bkb9oKswOycj5S3fBeim7LpDrZf37SybGwV6fyi3BFs,4296
|
|
182
183
|
letta/server/rest_api/routers/v1/users.py,sha256=M1wEr2IyHzuRwINYxLXTkrbAH3osLe_cWjzrWrzR1aw,3729
|
|
183
184
|
letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
|
|
184
185
|
letta/server/rest_api/utils.py,sha256=GdHYCzXtbM5VCAYDPR0z5gnNZpRhwPld2BGZV7xT6cU,2924
|
|
185
|
-
letta/server/server.py,sha256=
|
|
186
|
+
letta/server/server.py,sha256=Bk-FPS0aODYFckEAgr5aPWVVizbf2gfcESnRK0aWdfQ,80038
|
|
186
187
|
letta/server/startup.sh,sha256=wTOQOJJZw_Iec57WIu0UW0AVflk0ZMWYZWg8D3T_gSQ,698
|
|
187
188
|
letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
|
|
188
189
|
letta/server/static_files/assets/index-9fa459a2.js,sha256=j2oMcDJO9dWJaH5e-tsflbVpWK20gLWpZKJk4-Kuy6A,1815592
|
|
@@ -197,7 +198,8 @@ letta/server/ws_api/protocol.py,sha256=M_-gM5iuDBwa1cuN2IGNCG5GxMJwU2d3XW93XALv9
|
|
|
197
198
|
letta/server/ws_api/server.py,sha256=C2Kv48PCwl46DQFb0ZP30s86KJLQ6dZk2AhWQEZn9pY,6004
|
|
198
199
|
letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
199
200
|
letta/services/agents_tags_manager.py,sha256=zNqeXDpaf4dQ77jrRHiQfITdk4FawBzcND-9tWrj8gw,3127
|
|
200
|
-
letta/services/organization_manager.py,sha256=
|
|
201
|
+
letta/services/organization_manager.py,sha256=OfE2_NMmhqXURX4sg7hCOiFQVQpV5ZiPu7J3sboCSYc,3555
|
|
202
|
+
letta/services/source_manager.py,sha256=-02XYBsWa3Dt64Bq-anZ-US_b0XFlqrRyy3nNzlgoBs,4332
|
|
201
203
|
letta/services/tool_manager.py,sha256=z3nnUDQWuqB5RYk_y78EvIH6SMx-KJy7qeHqclZHonw,7897
|
|
202
204
|
letta/services/user_manager.py,sha256=UJa0hqCjz0yXtvrCR8OVBqlSR5lC_Ejn-uG__58zLds,4398
|
|
203
205
|
letta/settings.py,sha256=yiYNmnYKj_BdTm0cBEIvQKYGU-lCmFntqsyVfRUy3_k,3411
|
|
@@ -205,8 +207,8 @@ letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,
|
|
|
205
207
|
letta/streaming_utils.py,sha256=329fsvj1ZN0r0LpQtmMPZ2vSxkDBIUUwvGHZFkjm2I8,11745
|
|
206
208
|
letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
|
|
207
209
|
letta/utils.py,sha256=SXLEYhyp3gHyIjrxNIKNZZ5ittKo3KOj6zxgC_Trex0,31012
|
|
208
|
-
letta_nightly-0.5.2.
|
|
209
|
-
letta_nightly-0.5.2.
|
|
210
|
-
letta_nightly-0.5.2.
|
|
211
|
-
letta_nightly-0.5.2.
|
|
212
|
-
letta_nightly-0.5.2.
|
|
210
|
+
letta_nightly-0.5.2.dev20241113104112.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
|
|
211
|
+
letta_nightly-0.5.2.dev20241113104112.dist-info/METADATA,sha256=R-hEIbQCiq8wTmE3Qayy_unZscQ1_6jmhoIzwIGZx_g,11024
|
|
212
|
+
letta_nightly-0.5.2.dev20241113104112.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
213
|
+
letta_nightly-0.5.2.dev20241113104112.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
|
|
214
|
+
letta_nightly-0.5.2.dev20241113104112.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|