letta-nightly 0.1.7.dev20240924104148__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/__init__.py +24 -0
- letta/__main__.py +3 -0
- letta/agent.py +1427 -0
- letta/agent_store/chroma.py +295 -0
- letta/agent_store/db.py +546 -0
- letta/agent_store/lancedb.py +177 -0
- letta/agent_store/milvus.py +198 -0
- letta/agent_store/qdrant.py +201 -0
- letta/agent_store/storage.py +188 -0
- letta/benchmark/benchmark.py +96 -0
- letta/benchmark/constants.py +14 -0
- letta/cli/cli.py +689 -0
- letta/cli/cli_config.py +1282 -0
- letta/cli/cli_load.py +166 -0
- letta/client/__init__.py +0 -0
- letta/client/admin.py +171 -0
- letta/client/client.py +2360 -0
- letta/client/streaming.py +90 -0
- letta/client/utils.py +61 -0
- letta/config.py +484 -0
- letta/configs/anthropic.json +13 -0
- letta/configs/letta_hosted.json +11 -0
- letta/configs/openai.json +12 -0
- letta/constants.py +134 -0
- letta/credentials.py +140 -0
- letta/data_sources/connectors.py +247 -0
- letta/embeddings.py +218 -0
- letta/errors.py +26 -0
- letta/functions/__init__.py +0 -0
- letta/functions/function_sets/base.py +174 -0
- letta/functions/function_sets/extras.py +132 -0
- letta/functions/functions.py +105 -0
- letta/functions/schema_generator.py +205 -0
- letta/humans/__init__.py +0 -0
- letta/humans/examples/basic.txt +1 -0
- letta/humans/examples/cs_phd.txt +9 -0
- letta/interface.py +314 -0
- letta/llm_api/__init__.py +0 -0
- letta/llm_api/anthropic.py +383 -0
- letta/llm_api/azure_openai.py +155 -0
- letta/llm_api/cohere.py +396 -0
- letta/llm_api/google_ai.py +468 -0
- letta/llm_api/llm_api_tools.py +485 -0
- letta/llm_api/openai.py +470 -0
- letta/local_llm/README.md +3 -0
- letta/local_llm/__init__.py +0 -0
- letta/local_llm/chat_completion_proxy.py +279 -0
- letta/local_llm/constants.py +31 -0
- letta/local_llm/function_parser.py +68 -0
- letta/local_llm/grammars/__init__.py +0 -0
- letta/local_llm/grammars/gbnf_grammar_generator.py +1324 -0
- letta/local_llm/grammars/json.gbnf +26 -0
- letta/local_llm/grammars/json_func_calls_with_inner_thoughts.gbnf +32 -0
- letta/local_llm/groq/api.py +97 -0
- letta/local_llm/json_parser.py +202 -0
- letta/local_llm/koboldcpp/api.py +62 -0
- letta/local_llm/koboldcpp/settings.py +23 -0
- letta/local_llm/llamacpp/api.py +58 -0
- letta/local_llm/llamacpp/settings.py +22 -0
- letta/local_llm/llm_chat_completion_wrappers/__init__.py +0 -0
- letta/local_llm/llm_chat_completion_wrappers/airoboros.py +452 -0
- letta/local_llm/llm_chat_completion_wrappers/chatml.py +470 -0
- letta/local_llm/llm_chat_completion_wrappers/configurable_wrapper.py +387 -0
- letta/local_llm/llm_chat_completion_wrappers/dolphin.py +246 -0
- letta/local_llm/llm_chat_completion_wrappers/llama3.py +345 -0
- letta/local_llm/llm_chat_completion_wrappers/simple_summary_wrapper.py +156 -0
- letta/local_llm/llm_chat_completion_wrappers/wrapper_base.py +11 -0
- letta/local_llm/llm_chat_completion_wrappers/zephyr.py +345 -0
- letta/local_llm/lmstudio/api.py +100 -0
- letta/local_llm/lmstudio/settings.py +29 -0
- letta/local_llm/ollama/api.py +88 -0
- letta/local_llm/ollama/settings.py +32 -0
- letta/local_llm/settings/__init__.py +0 -0
- letta/local_llm/settings/deterministic_mirostat.py +45 -0
- letta/local_llm/settings/settings.py +72 -0
- letta/local_llm/settings/simple.py +28 -0
- letta/local_llm/utils.py +265 -0
- letta/local_llm/vllm/api.py +63 -0
- letta/local_llm/webui/api.py +60 -0
- letta/local_llm/webui/legacy_api.py +58 -0
- letta/local_llm/webui/legacy_settings.py +23 -0
- letta/local_llm/webui/settings.py +24 -0
- letta/log.py +76 -0
- letta/main.py +437 -0
- letta/memory.py +440 -0
- letta/metadata.py +884 -0
- letta/openai_backcompat/__init__.py +0 -0
- letta/openai_backcompat/openai_object.py +437 -0
- letta/persistence_manager.py +148 -0
- letta/personas/__init__.py +0 -0
- letta/personas/examples/anna_pa.txt +13 -0
- letta/personas/examples/google_search_persona.txt +15 -0
- letta/personas/examples/memgpt_doc.txt +6 -0
- letta/personas/examples/memgpt_starter.txt +4 -0
- letta/personas/examples/sam.txt +14 -0
- letta/personas/examples/sam_pov.txt +14 -0
- letta/personas/examples/sam_simple_pov_gpt35.txt +13 -0
- letta/personas/examples/sqldb/test.db +0 -0
- letta/prompts/__init__.py +0 -0
- letta/prompts/gpt_summarize.py +14 -0
- letta/prompts/gpt_system.py +26 -0
- letta/prompts/system/memgpt_base.txt +49 -0
- letta/prompts/system/memgpt_chat.txt +58 -0
- letta/prompts/system/memgpt_chat_compressed.txt +13 -0
- letta/prompts/system/memgpt_chat_fstring.txt +51 -0
- letta/prompts/system/memgpt_doc.txt +50 -0
- letta/prompts/system/memgpt_gpt35_extralong.txt +53 -0
- letta/prompts/system/memgpt_intuitive_knowledge.txt +31 -0
- letta/prompts/system/memgpt_modified_chat.txt +23 -0
- letta/pytest.ini +0 -0
- letta/schemas/agent.py +117 -0
- letta/schemas/api_key.py +21 -0
- letta/schemas/block.py +135 -0
- letta/schemas/document.py +21 -0
- letta/schemas/embedding_config.py +54 -0
- letta/schemas/enums.py +35 -0
- letta/schemas/job.py +38 -0
- letta/schemas/letta_base.py +80 -0
- letta/schemas/letta_message.py +175 -0
- letta/schemas/letta_request.py +23 -0
- letta/schemas/letta_response.py +28 -0
- letta/schemas/llm_config.py +54 -0
- letta/schemas/memory.py +224 -0
- letta/schemas/message.py +727 -0
- letta/schemas/openai/chat_completion_request.py +123 -0
- letta/schemas/openai/chat_completion_response.py +136 -0
- letta/schemas/openai/chat_completions.py +123 -0
- letta/schemas/openai/embedding_response.py +11 -0
- letta/schemas/openai/openai.py +157 -0
- letta/schemas/organization.py +20 -0
- letta/schemas/passage.py +80 -0
- letta/schemas/source.py +62 -0
- letta/schemas/tool.py +143 -0
- letta/schemas/usage.py +18 -0
- letta/schemas/user.py +33 -0
- letta/server/__init__.py +0 -0
- letta/server/constants.py +6 -0
- letta/server/rest_api/__init__.py +0 -0
- letta/server/rest_api/admin/__init__.py +0 -0
- letta/server/rest_api/admin/agents.py +21 -0
- letta/server/rest_api/admin/tools.py +83 -0
- letta/server/rest_api/admin/users.py +98 -0
- letta/server/rest_api/app.py +193 -0
- letta/server/rest_api/auth/__init__.py +0 -0
- letta/server/rest_api/auth/index.py +43 -0
- letta/server/rest_api/auth_token.py +22 -0
- letta/server/rest_api/interface.py +726 -0
- letta/server/rest_api/routers/__init__.py +0 -0
- letta/server/rest_api/routers/openai/__init__.py +0 -0
- letta/server/rest_api/routers/openai/assistants/__init__.py +0 -0
- letta/server/rest_api/routers/openai/assistants/assistants.py +115 -0
- letta/server/rest_api/routers/openai/assistants/schemas.py +121 -0
- letta/server/rest_api/routers/openai/assistants/threads.py +336 -0
- letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +131 -0
- letta/server/rest_api/routers/v1/__init__.py +15 -0
- letta/server/rest_api/routers/v1/agents.py +543 -0
- letta/server/rest_api/routers/v1/blocks.py +73 -0
- letta/server/rest_api/routers/v1/jobs.py +46 -0
- letta/server/rest_api/routers/v1/llms.py +28 -0
- letta/server/rest_api/routers/v1/organizations.py +61 -0
- letta/server/rest_api/routers/v1/sources.py +199 -0
- letta/server/rest_api/routers/v1/tools.py +103 -0
- letta/server/rest_api/routers/v1/users.py +109 -0
- letta/server/rest_api/static_files.py +74 -0
- letta/server/rest_api/utils.py +69 -0
- letta/server/server.py +1995 -0
- letta/server/startup.sh +8 -0
- letta/server/static_files/assets/index-0cbf7ad5.js +274 -0
- letta/server/static_files/assets/index-156816da.css +1 -0
- letta/server/static_files/assets/index-486e3228.js +274 -0
- letta/server/static_files/favicon.ico +0 -0
- letta/server/static_files/index.html +39 -0
- letta/server/static_files/memgpt_logo_transparent.png +0 -0
- letta/server/utils.py +46 -0
- letta/server/ws_api/__init__.py +0 -0
- letta/server/ws_api/example_client.py +104 -0
- letta/server/ws_api/interface.py +108 -0
- letta/server/ws_api/protocol.py +100 -0
- letta/server/ws_api/server.py +145 -0
- letta/settings.py +165 -0
- letta/streaming_interface.py +396 -0
- letta/system.py +207 -0
- letta/utils.py +1065 -0
- letta_nightly-0.1.7.dev20240924104148.dist-info/LICENSE +190 -0
- letta_nightly-0.1.7.dev20240924104148.dist-info/METADATA +98 -0
- letta_nightly-0.1.7.dev20240924104148.dist-info/RECORD +189 -0
- letta_nightly-0.1.7.dev20240924104148.dist-info/WHEEL +4 -0
- letta_nightly-0.1.7.dev20240924104148.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
4
|
+
|
|
5
|
+
from letta.schemas.block import Block, CreateBlock, UpdateBlock
|
|
6
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
7
|
+
from letta.server.server import SyncServer
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
router = APIRouter(prefix="/blocks", tags=["blocks"])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/", response_model=List[Block], operation_id="list_memory_blocks")
|
|
16
|
+
def list_blocks(
|
|
17
|
+
# query parameters
|
|
18
|
+
label: Optional[str] = Query(None, description="Labels to include (e.g. human, persona)"),
|
|
19
|
+
templates_only: bool = Query(True, description="Whether to include only templates"),
|
|
20
|
+
name: Optional[str] = Query(None, description="Name of the block"),
|
|
21
|
+
server: SyncServer = Depends(get_letta_server),
|
|
22
|
+
):
|
|
23
|
+
actor = server.get_current_user()
|
|
24
|
+
|
|
25
|
+
blocks = server.get_blocks(user_id=actor.id, label=label, template=templates_only, name=name)
|
|
26
|
+
if blocks is None:
|
|
27
|
+
return []
|
|
28
|
+
return blocks
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@router.post("/", response_model=Block, operation_id="create_memory_block")
|
|
32
|
+
def create_block(
|
|
33
|
+
create_block: CreateBlock = Body(...),
|
|
34
|
+
server: SyncServer = Depends(get_letta_server),
|
|
35
|
+
):
|
|
36
|
+
actor = server.get_current_user()
|
|
37
|
+
|
|
38
|
+
create_block.user_id = actor.id
|
|
39
|
+
return server.create_block(user_id=actor.id, request=create_block)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@router.patch("/{block_id}", response_model=Block, operation_id="update_memory_block")
|
|
43
|
+
def update_block(
|
|
44
|
+
block_id: str,
|
|
45
|
+
updated_block: UpdateBlock = Body(...),
|
|
46
|
+
server: SyncServer = Depends(get_letta_server),
|
|
47
|
+
):
|
|
48
|
+
# actor = server.get_current_user()
|
|
49
|
+
|
|
50
|
+
updated_block.id = block_id
|
|
51
|
+
return server.update_block(request=updated_block)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# TODO: delete should not return anything
|
|
55
|
+
@router.delete("/{block_id}", response_model=Block, operation_id="delete_memory_block")
|
|
56
|
+
def delete_block(
|
|
57
|
+
block_id: str,
|
|
58
|
+
server: SyncServer = Depends(get_letta_server),
|
|
59
|
+
):
|
|
60
|
+
|
|
61
|
+
return server.delete_block(block_id=block_id)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@router.get("/{block_id}", response_model=Block, operation_id="get_memory_block")
|
|
65
|
+
def get_block(
|
|
66
|
+
block_id: str,
|
|
67
|
+
server: SyncServer = Depends(get_letta_server),
|
|
68
|
+
):
|
|
69
|
+
|
|
70
|
+
block = server.get_block(block_id=block_id)
|
|
71
|
+
if block is None:
|
|
72
|
+
raise HTTPException(status_code=404, detail="Block not found")
|
|
73
|
+
return block
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends
|
|
4
|
+
|
|
5
|
+
from letta.schemas.job import Job
|
|
6
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
7
|
+
from letta.server.server import SyncServer
|
|
8
|
+
|
|
9
|
+
router = APIRouter(prefix="/jobs", tags=["jobs"])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@router.get("/", response_model=List[Job], operation_id="list_jobs")
|
|
13
|
+
def list_jobs(
|
|
14
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
15
|
+
):
|
|
16
|
+
"""
|
|
17
|
+
List all jobs.
|
|
18
|
+
"""
|
|
19
|
+
actor = server.get_current_user()
|
|
20
|
+
|
|
21
|
+
# TODO: add filtering by status
|
|
22
|
+
return server.list_jobs(user_id=actor.id)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@router.get("/active", response_model=List[Job], operation_id="list_active_jobs")
|
|
26
|
+
def list_active_jobs(
|
|
27
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
List all active jobs.
|
|
31
|
+
"""
|
|
32
|
+
actor = server.get_current_user()
|
|
33
|
+
|
|
34
|
+
return server.list_active_jobs(user_id=actor.id)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@router.get("/{job_id}", response_model=Job, operation_id="get_job")
|
|
38
|
+
def get_job(
|
|
39
|
+
job_id: str,
|
|
40
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Get the status of a job.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
return server.get_job(job_id=job_id)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends
|
|
4
|
+
|
|
5
|
+
from letta.schemas.embedding_config import EmbeddingConfig
|
|
6
|
+
from letta.schemas.llm_config import LLMConfig
|
|
7
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from letta.server.server import SyncServer
|
|
11
|
+
|
|
12
|
+
router = APIRouter(prefix="/models", tags=["models", "llms"])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/", response_model=List[LLMConfig], operation_id="list_models")
|
|
16
|
+
def list_llm_backends(
|
|
17
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
18
|
+
):
|
|
19
|
+
|
|
20
|
+
return server.list_models()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@router.get("/embedding", response_model=List[EmbeddingConfig], operation_id="list_embedding_models")
|
|
24
|
+
def list_embedding_backends(
|
|
25
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
26
|
+
):
|
|
27
|
+
|
|
28
|
+
return server.list_embedding_models()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
4
|
+
|
|
5
|
+
from letta.schemas.organization import Organization, OrganizationCreate
|
|
6
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from letta.server.server import SyncServer
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
router = APIRouter(prefix="/orgs", tags=["organization", "admin"])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/", tags=["admin"], response_model=List[Organization], operation_id="list_orgs")
|
|
16
|
+
def get_all_orgs(
|
|
17
|
+
cursor: Optional[str] = Query(None),
|
|
18
|
+
limit: Optional[int] = Query(50),
|
|
19
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Get a list of all orgs in the database
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
next_cursor, orgs = server.ms.list_organizations(cursor=cursor, limit=limit)
|
|
26
|
+
except HTTPException:
|
|
27
|
+
raise
|
|
28
|
+
except Exception as e:
|
|
29
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
|
30
|
+
return orgs
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.post("/", tags=["admin"], response_model=Organization, operation_id="create_organization")
|
|
34
|
+
def create_org(
|
|
35
|
+
request: OrganizationCreate = Body(...),
|
|
36
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Create a new org in the database
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
org = server.create_organization(request)
|
|
43
|
+
return org
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@router.delete("/", tags=["admin"], response_model=Organization, operation_id="delete_organization")
|
|
47
|
+
def delete_org(
|
|
48
|
+
org_id: str = Query(..., description="The org_id key to be deleted."),
|
|
49
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
50
|
+
):
|
|
51
|
+
# TODO make a soft deletion, instead of a hard deletion
|
|
52
|
+
try:
|
|
53
|
+
org = server.ms.get_organization(org_id=org_id)
|
|
54
|
+
if org is None:
|
|
55
|
+
raise HTTPException(status_code=404, detail=f"Organization does not exist")
|
|
56
|
+
server.ms.delete_organization(org_id=org_id)
|
|
57
|
+
except HTTPException:
|
|
58
|
+
raise
|
|
59
|
+
except Exception as e:
|
|
60
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
|
61
|
+
return org
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from fastapi import APIRouter, BackgroundTasks, Depends, Query, UploadFile
|
|
6
|
+
|
|
7
|
+
from letta.schemas.document import Document
|
|
8
|
+
from letta.schemas.job import Job
|
|
9
|
+
from letta.schemas.passage import Passage
|
|
10
|
+
from letta.schemas.source import Source, SourceCreate, SourceUpdate
|
|
11
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
12
|
+
from letta.server.server import SyncServer
|
|
13
|
+
|
|
14
|
+
# These can be forward refs, but because Fastapi needs them at runtime the must be imported normally
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
router = APIRouter(prefix="/sources", tags=["sources"])
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@router.get("/{source_id}", response_model=Source, operation_id="get_source")
|
|
21
|
+
def get_source(
|
|
22
|
+
source_id: str,
|
|
23
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Get all sources
|
|
27
|
+
"""
|
|
28
|
+
actor = server.get_current_user()
|
|
29
|
+
|
|
30
|
+
return server.get_source(source_id=source_id, user_id=actor.id)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.get("/name/{source_name}", response_model=str, operation_id="get_source_id_by_name")
|
|
34
|
+
def get_source_id_by_name(
|
|
35
|
+
source_name: str,
|
|
36
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Get a source by name
|
|
40
|
+
"""
|
|
41
|
+
actor = server.get_current_user()
|
|
42
|
+
|
|
43
|
+
source_id = server.get_source_id(source_name=source_name, user_id=actor.id)
|
|
44
|
+
return source_id
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.get("/", response_model=List[Source], operation_id="list_sources")
|
|
48
|
+
def list_sources(
|
|
49
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
List all data sources created by a user.
|
|
53
|
+
"""
|
|
54
|
+
actor = server.get_current_user()
|
|
55
|
+
|
|
56
|
+
return server.list_all_sources(user_id=actor.id)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@router.post("/", response_model=Source, operation_id="create_source")
|
|
60
|
+
def create_source(
|
|
61
|
+
source: SourceCreate,
|
|
62
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
63
|
+
):
|
|
64
|
+
"""
|
|
65
|
+
Create a new data source.
|
|
66
|
+
"""
|
|
67
|
+
actor = server.get_current_user()
|
|
68
|
+
|
|
69
|
+
return server.create_source(request=source, user_id=actor.id)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@router.patch("/{source_id}", response_model=Source, operation_id="update_source")
|
|
73
|
+
def update_source(
|
|
74
|
+
source_id: str,
|
|
75
|
+
source: SourceUpdate,
|
|
76
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
77
|
+
):
|
|
78
|
+
"""
|
|
79
|
+
Update the name or documentation of an existing data source.
|
|
80
|
+
"""
|
|
81
|
+
actor = server.get_current_user()
|
|
82
|
+
assert source.id == source_id, "Source ID in path must match ID in request body"
|
|
83
|
+
|
|
84
|
+
return server.update_source(request=source, user_id=actor.id)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@router.delete("/{source_id}", response_model=None, operation_id="delete_source")
|
|
88
|
+
def delete_source(
|
|
89
|
+
source_id: str,
|
|
90
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Delete a data source.
|
|
94
|
+
"""
|
|
95
|
+
actor = server.get_current_user()
|
|
96
|
+
|
|
97
|
+
server.delete_source(source_id=source_id, user_id=actor.id)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@router.post("/{source_id}/attach", response_model=Source, operation_id="attach_agent_to_source")
|
|
101
|
+
def attach_source_to_agent(
|
|
102
|
+
source_id: str,
|
|
103
|
+
agent_id: str = Query(..., description="The unique identifier of the agent to attach the source to."),
|
|
104
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
105
|
+
):
|
|
106
|
+
"""
|
|
107
|
+
Attach a data source to an existing agent.
|
|
108
|
+
"""
|
|
109
|
+
actor = server.get_current_user()
|
|
110
|
+
|
|
111
|
+
source = server.ms.get_source(source_id=source_id, user_id=actor.id)
|
|
112
|
+
assert source is not None, f"Source with id={source_id} not found."
|
|
113
|
+
source = server.attach_source_to_agent(source_id=source.id, agent_id=agent_id, user_id=actor.id)
|
|
114
|
+
return source
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@router.post("/{source_id}/detach", response_model=None, operation_id="detach_agent_from_source")
|
|
118
|
+
def detach_source_from_agent(
|
|
119
|
+
source_id: str,
|
|
120
|
+
agent_id: str = Query(..., description="The unique identifier of the agent to detach the source from."),
|
|
121
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
122
|
+
) -> None:
|
|
123
|
+
"""
|
|
124
|
+
Detach a data source from an existing agent.
|
|
125
|
+
"""
|
|
126
|
+
actor = server.get_current_user()
|
|
127
|
+
|
|
128
|
+
server.detach_source_from_agent(source_id=source_id, agent_id=agent_id, user_id=actor.id)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@router.post("/{source_id}/upload", response_model=Job, operation_id="upload_file_to_source")
|
|
132
|
+
def upload_file_to_source(
|
|
133
|
+
file: UploadFile,
|
|
134
|
+
source_id: str,
|
|
135
|
+
background_tasks: BackgroundTasks,
|
|
136
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
137
|
+
):
|
|
138
|
+
"""
|
|
139
|
+
Upload a file to a data source.
|
|
140
|
+
"""
|
|
141
|
+
actor = server.get_current_user()
|
|
142
|
+
|
|
143
|
+
source = server.ms.get_source(source_id=source_id, user_id=actor.id)
|
|
144
|
+
assert source is not None, f"Source with id={source_id} not found."
|
|
145
|
+
bytes = file.file.read()
|
|
146
|
+
|
|
147
|
+
# create job
|
|
148
|
+
job = Job(
|
|
149
|
+
user_id=actor.id,
|
|
150
|
+
metadata_={"type": "embedding", "filename": file.filename, "source_id": source_id},
|
|
151
|
+
completed_at=None,
|
|
152
|
+
)
|
|
153
|
+
job_id = job.id
|
|
154
|
+
server.ms.create_job(job)
|
|
155
|
+
|
|
156
|
+
# create background task
|
|
157
|
+
background_tasks.add_task(load_file_to_source_async, server, source_id=source.id, job_id=job.id, file=file, bytes=bytes)
|
|
158
|
+
|
|
159
|
+
# return job information
|
|
160
|
+
job = server.ms.get_job(job_id=job_id)
|
|
161
|
+
assert job is not None, "Job not found"
|
|
162
|
+
return job
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@router.get("/{source_id}/passages", response_model=List[Passage], operation_id="list_source_passages")
|
|
166
|
+
def list_passages(
|
|
167
|
+
source_id: str,
|
|
168
|
+
server: SyncServer = Depends(get_letta_server),
|
|
169
|
+
):
|
|
170
|
+
"""
|
|
171
|
+
List all passages associated with a data source.
|
|
172
|
+
"""
|
|
173
|
+
actor = server.get_current_user()
|
|
174
|
+
passages = server.list_data_source_passages(user_id=actor.id, source_id=source_id)
|
|
175
|
+
return passages
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@router.get("/{source_id}/documents", response_model=List[Document], operation_id="list_source_documents")
|
|
179
|
+
def list_documents(
|
|
180
|
+
source_id: str,
|
|
181
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
182
|
+
):
|
|
183
|
+
"""
|
|
184
|
+
List all documents associated with a data source.
|
|
185
|
+
"""
|
|
186
|
+
actor = server.get_current_user()
|
|
187
|
+
|
|
188
|
+
documents = server.list_data_source_documents(user_id=actor.id, source_id=source_id)
|
|
189
|
+
return documents
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def load_file_to_source_async(server: SyncServer, source_id: str, job_id: str, file: UploadFile, bytes: bytes):
|
|
193
|
+
# write the file to a temporary directory (deleted after the context manager exits)
|
|
194
|
+
with tempfile.TemporaryDirectory() as tmpdirname:
|
|
195
|
+
file_path = os.path.join(str(tmpdirname), str(file.filename))
|
|
196
|
+
with open(file_path, "wb") as buffer:
|
|
197
|
+
buffer.write(bytes)
|
|
198
|
+
|
|
199
|
+
server.load_file_to_source(source_id, file_path, job_id)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException
|
|
4
|
+
|
|
5
|
+
from letta.schemas.tool import Tool, ToolCreate, ToolUpdate
|
|
6
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
7
|
+
from letta.server.server import SyncServer
|
|
8
|
+
|
|
9
|
+
router = APIRouter(prefix="/tools", tags=["tools"])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@router.delete("/{tool_id}", operation_id="delete_tool")
|
|
13
|
+
def delete_tool(
|
|
14
|
+
tool_id: str,
|
|
15
|
+
server: SyncServer = Depends(get_letta_server),
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Delete a tool by name
|
|
19
|
+
"""
|
|
20
|
+
# actor = server.get_current_user()
|
|
21
|
+
server.delete_tool(tool_id=tool_id)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@router.get("/{tool_id}", response_model=Tool, operation_id="get_tool")
|
|
25
|
+
def get_tool(
|
|
26
|
+
tool_id: str,
|
|
27
|
+
server: SyncServer = Depends(get_letta_server),
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Get a tool by ID
|
|
31
|
+
"""
|
|
32
|
+
# actor = server.get_current_user()
|
|
33
|
+
|
|
34
|
+
tool = server.get_tool(tool_id=tool_id)
|
|
35
|
+
if tool is None:
|
|
36
|
+
# return 404 error
|
|
37
|
+
raise HTTPException(status_code=404, detail=f"Tool with id {tool_id} not found.")
|
|
38
|
+
return tool
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@router.get("/name/{tool_name}", response_model=str, operation_id="get_tool_id_by_name")
|
|
42
|
+
def get_tool_id(
|
|
43
|
+
tool_name: str,
|
|
44
|
+
server: SyncServer = Depends(get_letta_server),
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Get a tool ID by name
|
|
48
|
+
"""
|
|
49
|
+
actor = server.get_current_user()
|
|
50
|
+
|
|
51
|
+
tool_id = server.get_tool_id(tool_name, user_id=actor.id)
|
|
52
|
+
if tool_id is None:
|
|
53
|
+
# return 404 error
|
|
54
|
+
raise HTTPException(status_code=404, detail=f"Tool with name {tool_name} not found.")
|
|
55
|
+
return tool_id
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@router.get("/", response_model=List[Tool], operation_id="list_tools")
|
|
59
|
+
def list_all_tools(
|
|
60
|
+
server: SyncServer = Depends(get_letta_server),
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Get a list of all tools available to agents created by a user
|
|
64
|
+
"""
|
|
65
|
+
actor = server.get_current_user()
|
|
66
|
+
actor.id
|
|
67
|
+
|
|
68
|
+
# TODO: add back when user-specific
|
|
69
|
+
return server.list_tools(user_id=actor.id)
|
|
70
|
+
# return server.ms.list_tools(user_id=None)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@router.post("/", response_model=Tool, operation_id="create_tool")
|
|
74
|
+
def create_tool(
|
|
75
|
+
tool: ToolCreate = Body(...),
|
|
76
|
+
update: bool = False,
|
|
77
|
+
server: SyncServer = Depends(get_letta_server),
|
|
78
|
+
):
|
|
79
|
+
"""
|
|
80
|
+
Create a new tool
|
|
81
|
+
"""
|
|
82
|
+
actor = server.get_current_user()
|
|
83
|
+
|
|
84
|
+
return server.create_tool(
|
|
85
|
+
request=tool,
|
|
86
|
+
# update=update,
|
|
87
|
+
update=True,
|
|
88
|
+
user_id=actor.id,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.patch("/{tool_id}", response_model=Tool, operation_id="update_tool")
|
|
93
|
+
def update_tool(
|
|
94
|
+
tool_id: str,
|
|
95
|
+
request: ToolUpdate = Body(...),
|
|
96
|
+
server: SyncServer = Depends(get_letta_server),
|
|
97
|
+
):
|
|
98
|
+
"""
|
|
99
|
+
Update an existing tool
|
|
100
|
+
"""
|
|
101
|
+
assert tool_id == request.id, "Tool ID in path must match tool ID in request body"
|
|
102
|
+
server.get_current_user()
|
|
103
|
+
return server.update_tool(request)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
|
4
|
+
|
|
5
|
+
from letta.schemas.api_key import APIKey, APIKeyCreate
|
|
6
|
+
from letta.schemas.user import User, UserCreate
|
|
7
|
+
from letta.server.rest_api.utils import get_letta_server
|
|
8
|
+
|
|
9
|
+
# from letta.server.schemas.users import (
|
|
10
|
+
# CreateAPIKeyRequest,
|
|
11
|
+
# CreateAPIKeyResponse,
|
|
12
|
+
# CreateUserRequest,
|
|
13
|
+
# CreateUserResponse,
|
|
14
|
+
# DeleteAPIKeyResponse,
|
|
15
|
+
# DeleteUserResponse,
|
|
16
|
+
# GetAllUsersResponse,
|
|
17
|
+
# GetAPIKeysResponse,
|
|
18
|
+
# )
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from letta.schemas.user import User
|
|
22
|
+
from letta.server.server import SyncServer
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
router = APIRouter(prefix="/users", tags=["users", "admin"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/", tags=["admin"], response_model=List[User], operation_id="list_users")
|
|
29
|
+
def get_all_users(
|
|
30
|
+
cursor: Optional[str] = Query(None),
|
|
31
|
+
limit: Optional[int] = Query(50),
|
|
32
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Get a list of all users in the database
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
next_cursor, users = server.ms.get_all_users(cursor=cursor, limit=limit)
|
|
39
|
+
# processed_users = [{"user_id": user.id} for user in users]
|
|
40
|
+
except HTTPException:
|
|
41
|
+
raise
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
|
44
|
+
return users
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@router.post("/", tags=["admin"], response_model=User, operation_id="create_user")
|
|
48
|
+
def create_user(
|
|
49
|
+
request: UserCreate = Body(...),
|
|
50
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Create a new user in the database
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
user = server.create_user(request)
|
|
57
|
+
return user
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@router.delete("/", tags=["admin"], response_model=User, operation_id="delete_user")
|
|
61
|
+
def delete_user(
|
|
62
|
+
user_id: str = Query(..., description="The user_id key to be deleted."),
|
|
63
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
64
|
+
):
|
|
65
|
+
# TODO make a soft deletion, instead of a hard deletion
|
|
66
|
+
try:
|
|
67
|
+
user = server.ms.get_user(user_id=user_id)
|
|
68
|
+
if user is None:
|
|
69
|
+
raise HTTPException(status_code=404, detail=f"User does not exist")
|
|
70
|
+
server.ms.delete_user(user_id=user_id)
|
|
71
|
+
except HTTPException:
|
|
72
|
+
raise
|
|
73
|
+
except Exception as e:
|
|
74
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
|
75
|
+
return user
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@router.post("/keys", response_model=APIKey, operation_id="create_api_key")
|
|
79
|
+
def create_new_api_key(
|
|
80
|
+
create_key: APIKeyCreate = Body(...),
|
|
81
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Create a new API key for a user
|
|
85
|
+
"""
|
|
86
|
+
api_key = server.create_api_key(create_key)
|
|
87
|
+
return api_key
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.get("/keys", response_model=List[APIKey], operation_id="list_api_keys")
|
|
91
|
+
def get_api_keys(
|
|
92
|
+
user_id: str = Query(..., description="The unique identifier of the user."),
|
|
93
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
94
|
+
):
|
|
95
|
+
"""
|
|
96
|
+
Get a list of all API keys for a user
|
|
97
|
+
"""
|
|
98
|
+
if server.ms.get_user(user_id=user_id) is None:
|
|
99
|
+
raise HTTPException(status_code=404, detail=f"User does not exist")
|
|
100
|
+
api_keys = server.ms.get_all_api_keys_for_user(user_id=user_id)
|
|
101
|
+
return api_keys
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@router.delete("/keys", response_model=APIKey, operation_id="delete_api_key")
|
|
105
|
+
def delete_api_key(
|
|
106
|
+
api_key: str = Query(..., description="The API key to be deleted."),
|
|
107
|
+
server: "SyncServer" = Depends(get_letta_server),
|
|
108
|
+
):
|
|
109
|
+
return server.delete_api_key(api_key)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from fastapi import FastAPI, HTTPException
|
|
5
|
+
from fastapi.responses import FileResponse
|
|
6
|
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
7
|
+
from starlette.staticfiles import StaticFiles
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SPAStaticFiles(StaticFiles):
|
|
11
|
+
async def get_response(self, path: str, scope):
|
|
12
|
+
try:
|
|
13
|
+
return await super().get_response(path, scope)
|
|
14
|
+
except (HTTPException, StarletteHTTPException) as ex:
|
|
15
|
+
if ex.status_code == 404:
|
|
16
|
+
return await super().get_response("index.html", scope)
|
|
17
|
+
else:
|
|
18
|
+
raise ex
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def mount_static_files(app: FastAPI):
|
|
22
|
+
static_files_path = os.path.join(os.path.dirname(importlib.util.find_spec("letta").origin), "server", "static_files")
|
|
23
|
+
if os.path.exists(static_files_path):
|
|
24
|
+
app.mount("/assets", StaticFiles(directory=os.path.join(static_files_path, "assets")), name="assets")
|
|
25
|
+
|
|
26
|
+
@app.get("/letta_logo_transparent.png", include_in_schema=False)
|
|
27
|
+
async def serve_spa():
|
|
28
|
+
return FileResponse(os.path.join(static_files_path, "letta_logo_transparent.png"))
|
|
29
|
+
|
|
30
|
+
@app.get("/", include_in_schema=False)
|
|
31
|
+
async def serve_spa():
|
|
32
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
33
|
+
|
|
34
|
+
@app.get("/agents", include_in_schema=False)
|
|
35
|
+
async def serve_spa():
|
|
36
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
37
|
+
|
|
38
|
+
@app.get("/data-sources", include_in_schema=False)
|
|
39
|
+
async def serve_spa():
|
|
40
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
41
|
+
|
|
42
|
+
@app.get("/tools", include_in_schema=False)
|
|
43
|
+
async def serve_spa():
|
|
44
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
45
|
+
|
|
46
|
+
@app.get("/agent-templates", include_in_schema=False)
|
|
47
|
+
async def serve_spa():
|
|
48
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
49
|
+
|
|
50
|
+
@app.get("/human-templates", include_in_schema=False)
|
|
51
|
+
async def serve_spa():
|
|
52
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
53
|
+
|
|
54
|
+
@app.get("/settings/profile", include_in_schema=False)
|
|
55
|
+
async def serve_spa():
|
|
56
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
57
|
+
|
|
58
|
+
@app.get("/agents/{agent-id}/chat", include_in_schema=False)
|
|
59
|
+
async def serve_spa():
|
|
60
|
+
return FileResponse(os.path.join(static_files_path, "index.html"))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# def mount_static_files(app: FastAPI):
|
|
64
|
+
# static_files_path = os.path.join(os.path.dirname(importlib.util.find_spec("letta").origin), "server", "static_files")
|
|
65
|
+
# if os.path.exists(static_files_path):
|
|
66
|
+
|
|
67
|
+
# @app.get("/{full_path:path}")
|
|
68
|
+
# async def serve_spa(full_path: str):
|
|
69
|
+
# if full_path.startswith("v1"):
|
|
70
|
+
# raise HTTPException(status_code=404, detail="Not found")
|
|
71
|
+
# file_path = os.path.join(static_files_path, full_path)
|
|
72
|
+
# if os.path.isfile(file_path):
|
|
73
|
+
# return FileResponse(file_path)
|
|
74
|
+
# return FileResponse(os.path.join(static_files_path, "index.html"))
|