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.

Files changed (189) hide show
  1. letta/__init__.py +24 -0
  2. letta/__main__.py +3 -0
  3. letta/agent.py +1427 -0
  4. letta/agent_store/chroma.py +295 -0
  5. letta/agent_store/db.py +546 -0
  6. letta/agent_store/lancedb.py +177 -0
  7. letta/agent_store/milvus.py +198 -0
  8. letta/agent_store/qdrant.py +201 -0
  9. letta/agent_store/storage.py +188 -0
  10. letta/benchmark/benchmark.py +96 -0
  11. letta/benchmark/constants.py +14 -0
  12. letta/cli/cli.py +689 -0
  13. letta/cli/cli_config.py +1282 -0
  14. letta/cli/cli_load.py +166 -0
  15. letta/client/__init__.py +0 -0
  16. letta/client/admin.py +171 -0
  17. letta/client/client.py +2360 -0
  18. letta/client/streaming.py +90 -0
  19. letta/client/utils.py +61 -0
  20. letta/config.py +484 -0
  21. letta/configs/anthropic.json +13 -0
  22. letta/configs/letta_hosted.json +11 -0
  23. letta/configs/openai.json +12 -0
  24. letta/constants.py +134 -0
  25. letta/credentials.py +140 -0
  26. letta/data_sources/connectors.py +247 -0
  27. letta/embeddings.py +218 -0
  28. letta/errors.py +26 -0
  29. letta/functions/__init__.py +0 -0
  30. letta/functions/function_sets/base.py +174 -0
  31. letta/functions/function_sets/extras.py +132 -0
  32. letta/functions/functions.py +105 -0
  33. letta/functions/schema_generator.py +205 -0
  34. letta/humans/__init__.py +0 -0
  35. letta/humans/examples/basic.txt +1 -0
  36. letta/humans/examples/cs_phd.txt +9 -0
  37. letta/interface.py +314 -0
  38. letta/llm_api/__init__.py +0 -0
  39. letta/llm_api/anthropic.py +383 -0
  40. letta/llm_api/azure_openai.py +155 -0
  41. letta/llm_api/cohere.py +396 -0
  42. letta/llm_api/google_ai.py +468 -0
  43. letta/llm_api/llm_api_tools.py +485 -0
  44. letta/llm_api/openai.py +470 -0
  45. letta/local_llm/README.md +3 -0
  46. letta/local_llm/__init__.py +0 -0
  47. letta/local_llm/chat_completion_proxy.py +279 -0
  48. letta/local_llm/constants.py +31 -0
  49. letta/local_llm/function_parser.py +68 -0
  50. letta/local_llm/grammars/__init__.py +0 -0
  51. letta/local_llm/grammars/gbnf_grammar_generator.py +1324 -0
  52. letta/local_llm/grammars/json.gbnf +26 -0
  53. letta/local_llm/grammars/json_func_calls_with_inner_thoughts.gbnf +32 -0
  54. letta/local_llm/groq/api.py +97 -0
  55. letta/local_llm/json_parser.py +202 -0
  56. letta/local_llm/koboldcpp/api.py +62 -0
  57. letta/local_llm/koboldcpp/settings.py +23 -0
  58. letta/local_llm/llamacpp/api.py +58 -0
  59. letta/local_llm/llamacpp/settings.py +22 -0
  60. letta/local_llm/llm_chat_completion_wrappers/__init__.py +0 -0
  61. letta/local_llm/llm_chat_completion_wrappers/airoboros.py +452 -0
  62. letta/local_llm/llm_chat_completion_wrappers/chatml.py +470 -0
  63. letta/local_llm/llm_chat_completion_wrappers/configurable_wrapper.py +387 -0
  64. letta/local_llm/llm_chat_completion_wrappers/dolphin.py +246 -0
  65. letta/local_llm/llm_chat_completion_wrappers/llama3.py +345 -0
  66. letta/local_llm/llm_chat_completion_wrappers/simple_summary_wrapper.py +156 -0
  67. letta/local_llm/llm_chat_completion_wrappers/wrapper_base.py +11 -0
  68. letta/local_llm/llm_chat_completion_wrappers/zephyr.py +345 -0
  69. letta/local_llm/lmstudio/api.py +100 -0
  70. letta/local_llm/lmstudio/settings.py +29 -0
  71. letta/local_llm/ollama/api.py +88 -0
  72. letta/local_llm/ollama/settings.py +32 -0
  73. letta/local_llm/settings/__init__.py +0 -0
  74. letta/local_llm/settings/deterministic_mirostat.py +45 -0
  75. letta/local_llm/settings/settings.py +72 -0
  76. letta/local_llm/settings/simple.py +28 -0
  77. letta/local_llm/utils.py +265 -0
  78. letta/local_llm/vllm/api.py +63 -0
  79. letta/local_llm/webui/api.py +60 -0
  80. letta/local_llm/webui/legacy_api.py +58 -0
  81. letta/local_llm/webui/legacy_settings.py +23 -0
  82. letta/local_llm/webui/settings.py +24 -0
  83. letta/log.py +76 -0
  84. letta/main.py +437 -0
  85. letta/memory.py +440 -0
  86. letta/metadata.py +884 -0
  87. letta/openai_backcompat/__init__.py +0 -0
  88. letta/openai_backcompat/openai_object.py +437 -0
  89. letta/persistence_manager.py +148 -0
  90. letta/personas/__init__.py +0 -0
  91. letta/personas/examples/anna_pa.txt +13 -0
  92. letta/personas/examples/google_search_persona.txt +15 -0
  93. letta/personas/examples/memgpt_doc.txt +6 -0
  94. letta/personas/examples/memgpt_starter.txt +4 -0
  95. letta/personas/examples/sam.txt +14 -0
  96. letta/personas/examples/sam_pov.txt +14 -0
  97. letta/personas/examples/sam_simple_pov_gpt35.txt +13 -0
  98. letta/personas/examples/sqldb/test.db +0 -0
  99. letta/prompts/__init__.py +0 -0
  100. letta/prompts/gpt_summarize.py +14 -0
  101. letta/prompts/gpt_system.py +26 -0
  102. letta/prompts/system/memgpt_base.txt +49 -0
  103. letta/prompts/system/memgpt_chat.txt +58 -0
  104. letta/prompts/system/memgpt_chat_compressed.txt +13 -0
  105. letta/prompts/system/memgpt_chat_fstring.txt +51 -0
  106. letta/prompts/system/memgpt_doc.txt +50 -0
  107. letta/prompts/system/memgpt_gpt35_extralong.txt +53 -0
  108. letta/prompts/system/memgpt_intuitive_knowledge.txt +31 -0
  109. letta/prompts/system/memgpt_modified_chat.txt +23 -0
  110. letta/pytest.ini +0 -0
  111. letta/schemas/agent.py +117 -0
  112. letta/schemas/api_key.py +21 -0
  113. letta/schemas/block.py +135 -0
  114. letta/schemas/document.py +21 -0
  115. letta/schemas/embedding_config.py +54 -0
  116. letta/schemas/enums.py +35 -0
  117. letta/schemas/job.py +38 -0
  118. letta/schemas/letta_base.py +80 -0
  119. letta/schemas/letta_message.py +175 -0
  120. letta/schemas/letta_request.py +23 -0
  121. letta/schemas/letta_response.py +28 -0
  122. letta/schemas/llm_config.py +54 -0
  123. letta/schemas/memory.py +224 -0
  124. letta/schemas/message.py +727 -0
  125. letta/schemas/openai/chat_completion_request.py +123 -0
  126. letta/schemas/openai/chat_completion_response.py +136 -0
  127. letta/schemas/openai/chat_completions.py +123 -0
  128. letta/schemas/openai/embedding_response.py +11 -0
  129. letta/schemas/openai/openai.py +157 -0
  130. letta/schemas/organization.py +20 -0
  131. letta/schemas/passage.py +80 -0
  132. letta/schemas/source.py +62 -0
  133. letta/schemas/tool.py +143 -0
  134. letta/schemas/usage.py +18 -0
  135. letta/schemas/user.py +33 -0
  136. letta/server/__init__.py +0 -0
  137. letta/server/constants.py +6 -0
  138. letta/server/rest_api/__init__.py +0 -0
  139. letta/server/rest_api/admin/__init__.py +0 -0
  140. letta/server/rest_api/admin/agents.py +21 -0
  141. letta/server/rest_api/admin/tools.py +83 -0
  142. letta/server/rest_api/admin/users.py +98 -0
  143. letta/server/rest_api/app.py +193 -0
  144. letta/server/rest_api/auth/__init__.py +0 -0
  145. letta/server/rest_api/auth/index.py +43 -0
  146. letta/server/rest_api/auth_token.py +22 -0
  147. letta/server/rest_api/interface.py +726 -0
  148. letta/server/rest_api/routers/__init__.py +0 -0
  149. letta/server/rest_api/routers/openai/__init__.py +0 -0
  150. letta/server/rest_api/routers/openai/assistants/__init__.py +0 -0
  151. letta/server/rest_api/routers/openai/assistants/assistants.py +115 -0
  152. letta/server/rest_api/routers/openai/assistants/schemas.py +121 -0
  153. letta/server/rest_api/routers/openai/assistants/threads.py +336 -0
  154. letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
  155. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +131 -0
  156. letta/server/rest_api/routers/v1/__init__.py +15 -0
  157. letta/server/rest_api/routers/v1/agents.py +543 -0
  158. letta/server/rest_api/routers/v1/blocks.py +73 -0
  159. letta/server/rest_api/routers/v1/jobs.py +46 -0
  160. letta/server/rest_api/routers/v1/llms.py +28 -0
  161. letta/server/rest_api/routers/v1/organizations.py +61 -0
  162. letta/server/rest_api/routers/v1/sources.py +199 -0
  163. letta/server/rest_api/routers/v1/tools.py +103 -0
  164. letta/server/rest_api/routers/v1/users.py +109 -0
  165. letta/server/rest_api/static_files.py +74 -0
  166. letta/server/rest_api/utils.py +69 -0
  167. letta/server/server.py +1995 -0
  168. letta/server/startup.sh +8 -0
  169. letta/server/static_files/assets/index-0cbf7ad5.js +274 -0
  170. letta/server/static_files/assets/index-156816da.css +1 -0
  171. letta/server/static_files/assets/index-486e3228.js +274 -0
  172. letta/server/static_files/favicon.ico +0 -0
  173. letta/server/static_files/index.html +39 -0
  174. letta/server/static_files/memgpt_logo_transparent.png +0 -0
  175. letta/server/utils.py +46 -0
  176. letta/server/ws_api/__init__.py +0 -0
  177. letta/server/ws_api/example_client.py +104 -0
  178. letta/server/ws_api/interface.py +108 -0
  179. letta/server/ws_api/protocol.py +100 -0
  180. letta/server/ws_api/server.py +145 -0
  181. letta/settings.py +165 -0
  182. letta/streaming_interface.py +396 -0
  183. letta/system.py +207 -0
  184. letta/utils.py +1065 -0
  185. letta_nightly-0.1.7.dev20240924104148.dist-info/LICENSE +190 -0
  186. letta_nightly-0.1.7.dev20240924104148.dist-info/METADATA +98 -0
  187. letta_nightly-0.1.7.dev20240924104148.dist-info/RECORD +189 -0
  188. letta_nightly-0.1.7.dev20240924104148.dist-info/WHEEL +4 -0
  189. 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"))