letta-nightly 0.6.1.dev20241206104246__py3-none-any.whl → 0.6.1.dev20241207104149__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 +54 -37
- letta/agent_store/db.py +1 -77
- letta/agent_store/storage.py +0 -5
- letta/cli/cli.py +0 -1
- letta/client/client.py +3 -7
- letta/constants.py +1 -0
- letta/functions/function_sets/base.py +33 -5
- letta/main.py +2 -2
- letta/memory.py +4 -82
- letta/metadata.py +0 -35
- letta/o1_agent.py +7 -2
- letta/offline_memory_agent.py +6 -0
- letta/orm/__init__.py +2 -0
- letta/orm/file.py +1 -1
- letta/orm/message.py +66 -0
- letta/orm/mixins.py +16 -0
- letta/orm/organization.py +1 -0
- letta/orm/sqlalchemy_base.py +118 -26
- letta/schemas/letta_base.py +7 -6
- letta/schemas/message.py +1 -7
- letta/server/rest_api/routers/v1/agents.py +2 -2
- letta/server/rest_api/routers/v1/blocks.py +2 -2
- letta/server/server.py +52 -50
- letta/server/static_files/assets/index-43ab4d62.css +1 -0
- letta/server/static_files/assets/index-4848e3d7.js +40 -0
- letta/server/static_files/index.html +2 -2
- letta/services/block_manager.py +1 -1
- letta/services/message_manager.py +182 -0
- letta/services/organization_manager.py +6 -9
- letta/services/source_manager.py +1 -1
- letta/services/tool_manager.py +1 -1
- letta/services/user_manager.py +1 -1
- {letta_nightly-0.6.1.dev20241206104246.dist-info → letta_nightly-0.6.1.dev20241207104149.dist-info}/METADATA +1 -1
- {letta_nightly-0.6.1.dev20241206104246.dist-info → letta_nightly-0.6.1.dev20241207104149.dist-info}/RECORD +37 -37
- letta/agent_store/lancedb.py +0 -177
- letta/persistence_manager.py +0 -149
- letta/server/static_files/assets/index-1b5d1a41.js +0 -271
- letta/server/static_files/assets/index-56a3f8c6.css +0 -1
- {letta_nightly-0.6.1.dev20241206104246.dist-info → letta_nightly-0.6.1.dev20241207104149.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.1.dev20241206104246.dist-info → letta_nightly-0.6.1.dev20241207104149.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.1.dev20241206104246.dist-info → letta_nightly-0.6.1.dev20241207104149.dist-info}/entry_points.txt +0 -0
letta/server/server.py
CHANGED
|
@@ -77,14 +77,15 @@ from letta.schemas.user import User
|
|
|
77
77
|
from letta.services.agents_tags_manager import AgentsTagsManager
|
|
78
78
|
from letta.services.block_manager import BlockManager
|
|
79
79
|
from letta.services.blocks_agents_manager import BlocksAgentsManager
|
|
80
|
-
from letta.services.tools_agents_manager import ToolsAgentsManager
|
|
81
80
|
from letta.services.job_manager import JobManager
|
|
81
|
+
from letta.services.message_manager import MessageManager
|
|
82
82
|
from letta.services.organization_manager import OrganizationManager
|
|
83
83
|
from letta.services.per_agent_lock_manager import PerAgentLockManager
|
|
84
84
|
from letta.services.sandbox_config_manager import SandboxConfigManager
|
|
85
85
|
from letta.services.source_manager import SourceManager
|
|
86
86
|
from letta.services.tool_execution_sandbox import ToolExecutionSandbox
|
|
87
87
|
from letta.services.tool_manager import ToolManager
|
|
88
|
+
from letta.services.tools_agents_manager import ToolsAgentsManager
|
|
88
89
|
from letta.services.user_manager import UserManager
|
|
89
90
|
from letta.utils import create_random_username, get_utc_time, json_dumps, json_loads
|
|
90
91
|
|
|
@@ -260,6 +261,7 @@ class SyncServer(Server):
|
|
|
260
261
|
self.agents_tags_manager = AgentsTagsManager()
|
|
261
262
|
self.sandbox_config_manager = SandboxConfigManager(tool_settings)
|
|
262
263
|
self.blocks_agents_manager = BlocksAgentsManager()
|
|
264
|
+
self.message_manager = MessageManager()
|
|
263
265
|
self.tools_agents_manager = ToolsAgentsManager()
|
|
264
266
|
self.job_manager = JobManager()
|
|
265
267
|
|
|
@@ -414,7 +416,7 @@ class SyncServer(Server):
|
|
|
414
416
|
agent = OfflineMemoryAgent(agent_state=agent_state, interface=interface, user=actor)
|
|
415
417
|
elif agent_state.agent_type == AgentType.chat_only_agent:
|
|
416
418
|
agent = ChatOnlyAgent(agent_state=agent_state, interface=interface, user=actor)
|
|
417
|
-
else:
|
|
419
|
+
else:
|
|
418
420
|
raise ValueError(f"Invalid agent type {agent_state.agent_type}")
|
|
419
421
|
|
|
420
422
|
# Rebuild the system prompt - may be linked to new blocks now
|
|
@@ -422,7 +424,7 @@ class SyncServer(Server):
|
|
|
422
424
|
|
|
423
425
|
# Persist to agent
|
|
424
426
|
save_agent(agent, self.ms)
|
|
425
|
-
return agent
|
|
427
|
+
return agent
|
|
426
428
|
|
|
427
429
|
def _step(
|
|
428
430
|
self,
|
|
@@ -518,12 +520,7 @@ class SyncServer(Server):
|
|
|
518
520
|
letta_agent.interface.print_messages_raw(letta_agent.messages)
|
|
519
521
|
|
|
520
522
|
elif command.lower() == "memory":
|
|
521
|
-
ret_str = (
|
|
522
|
-
f"\nDumping memory contents:\n"
|
|
523
|
-
+ f"\n{str(letta_agent.agent_state.memory)}"
|
|
524
|
-
+ f"\n{str(letta_agent.persistence_manager.archival_memory)}"
|
|
525
|
-
+ f"\n{str(letta_agent.persistence_manager.recall_memory)}"
|
|
526
|
-
)
|
|
523
|
+
ret_str = f"\nDumping memory contents:\n" + f"\n{str(letta_agent.agent_state.memory)}" + f"\n{str(letta_agent.archival_memory)}"
|
|
527
524
|
return ret_str
|
|
528
525
|
|
|
529
526
|
elif command.lower() == "pop" or command.lower().startswith("pop "):
|
|
@@ -625,7 +622,6 @@ class SyncServer(Server):
|
|
|
625
622
|
# Convert to a Message object
|
|
626
623
|
if timestamp:
|
|
627
624
|
message = Message(
|
|
628
|
-
user_id=user_id,
|
|
629
625
|
agent_id=agent_id,
|
|
630
626
|
role="user",
|
|
631
627
|
text=packaged_user_message,
|
|
@@ -633,7 +629,6 @@ class SyncServer(Server):
|
|
|
633
629
|
)
|
|
634
630
|
else:
|
|
635
631
|
message = Message(
|
|
636
|
-
user_id=user_id,
|
|
637
632
|
agent_id=agent_id,
|
|
638
633
|
role="user",
|
|
639
634
|
text=packaged_user_message,
|
|
@@ -672,7 +667,6 @@ class SyncServer(Server):
|
|
|
672
667
|
|
|
673
668
|
if timestamp:
|
|
674
669
|
message = Message(
|
|
675
|
-
user_id=user_id,
|
|
676
670
|
agent_id=agent_id,
|
|
677
671
|
role="system",
|
|
678
672
|
text=packaged_system_message,
|
|
@@ -680,7 +674,6 @@ class SyncServer(Server):
|
|
|
680
674
|
)
|
|
681
675
|
else:
|
|
682
676
|
message = Message(
|
|
683
|
-
user_id=user_id,
|
|
684
677
|
agent_id=agent_id,
|
|
685
678
|
role="system",
|
|
686
679
|
text=packaged_system_message,
|
|
@@ -743,7 +736,6 @@ class SyncServer(Server):
|
|
|
743
736
|
# Create the Message object
|
|
744
737
|
message_objects.append(
|
|
745
738
|
Message(
|
|
746
|
-
user_id=user_id,
|
|
747
739
|
agent_id=agent_id,
|
|
748
740
|
role=message.role,
|
|
749
741
|
text=message.text,
|
|
@@ -876,7 +868,7 @@ class SyncServer(Server):
|
|
|
876
868
|
else:
|
|
877
869
|
raise ValueError(f"Invalid message role: {message.role}")
|
|
878
870
|
|
|
879
|
-
init_messages.append(Message(role=message.role, text=packed_message,
|
|
871
|
+
init_messages.append(Message(role=message.role, text=packed_message, agent_id=agent_state.id))
|
|
880
872
|
# init_messages = [Message.dict_to_message(user_id=user_id, agent_id=agent_state.id, openai_message_dict=message.model_dump()) for message in request.initial_message_sequence]
|
|
881
873
|
else:
|
|
882
874
|
init_messages = None
|
|
@@ -1160,11 +1152,11 @@ class SyncServer(Server):
|
|
|
1160
1152
|
|
|
1161
1153
|
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
|
1162
1154
|
agent = self.load_agent(agent_id=agent_id)
|
|
1163
|
-
return ArchivalMemorySummary(size=len(agent.
|
|
1155
|
+
return ArchivalMemorySummary(size=len(agent.archival_memory))
|
|
1164
1156
|
|
|
1165
1157
|
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
|
1166
1158
|
agent = self.load_agent(agent_id=agent_id)
|
|
1167
|
-
return RecallMemorySummary(size=len(agent.
|
|
1159
|
+
return RecallMemorySummary(size=len(agent.message_manager))
|
|
1168
1160
|
|
|
1169
1161
|
def get_in_context_message_ids(self, agent_id: str) -> List[str]:
|
|
1170
1162
|
"""Get the message ids of the in-context messages in the agent's memory"""
|
|
@@ -1182,7 +1174,7 @@ class SyncServer(Server):
|
|
|
1182
1174
|
"""Get a single message from the agent's memory"""
|
|
1183
1175
|
# Get the agent object (loaded in memory)
|
|
1184
1176
|
agent = self.load_agent(agent_id=agent_id)
|
|
1185
|
-
message = agent.
|
|
1177
|
+
message = agent.message_manager.get_message_by_id(id=message_id, actor=self.default_user)
|
|
1186
1178
|
return message
|
|
1187
1179
|
|
|
1188
1180
|
def get_agent_messages(
|
|
@@ -1213,14 +1205,16 @@ class SyncServer(Server):
|
|
|
1213
1205
|
|
|
1214
1206
|
else:
|
|
1215
1207
|
# need to access persistence manager for additional messages
|
|
1216
|
-
db_iterator = letta_agent.persistence_manager.recall_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
1217
1208
|
|
|
1218
|
-
# get
|
|
1219
|
-
|
|
1220
|
-
|
|
1209
|
+
# get messages using message manager
|
|
1210
|
+
page = letta_agent.message_manager.list_user_messages_for_agent(
|
|
1211
|
+
agent_id=agent_id,
|
|
1212
|
+
actor=self.default_user,
|
|
1213
|
+
cursor=start,
|
|
1214
|
+
limit=count,
|
|
1215
|
+
)
|
|
1221
1216
|
|
|
1222
|
-
|
|
1223
|
-
messages = sorted(page, key=lambda x: x.created_at, reverse=True)
|
|
1217
|
+
messages = page
|
|
1224
1218
|
assert all(isinstance(m, Message) for m in messages)
|
|
1225
1219
|
|
|
1226
1220
|
## Convert to json
|
|
@@ -1243,7 +1237,7 @@ class SyncServer(Server):
|
|
|
1243
1237
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1244
1238
|
|
|
1245
1239
|
# iterate over records
|
|
1246
|
-
db_iterator = letta_agent.
|
|
1240
|
+
db_iterator = letta_agent.archival_memory.storage.get_all_paginated(page_size=count, offset=start)
|
|
1247
1241
|
|
|
1248
1242
|
# get a single page of messages
|
|
1249
1243
|
page = next(db_iterator, [])
|
|
@@ -1268,7 +1262,7 @@ class SyncServer(Server):
|
|
|
1268
1262
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1269
1263
|
|
|
1270
1264
|
# iterate over recorde
|
|
1271
|
-
cursor, records = letta_agent.
|
|
1265
|
+
cursor, records = letta_agent.archival_memory.storage.get_all_cursor(
|
|
1272
1266
|
after=after, before=before, limit=limit, order_by=order_by, reverse=reverse
|
|
1273
1267
|
)
|
|
1274
1268
|
return records
|
|
@@ -1283,14 +1277,14 @@ class SyncServer(Server):
|
|
|
1283
1277
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1284
1278
|
|
|
1285
1279
|
# Insert into archival memory
|
|
1286
|
-
passage_ids = letta_agent.
|
|
1280
|
+
passage_ids = letta_agent.archival_memory.insert(memory_string=memory_contents, return_ids=True)
|
|
1287
1281
|
|
|
1288
1282
|
# Update the agent
|
|
1289
1283
|
# TODO: should this update the system prompt?
|
|
1290
1284
|
save_agent(letta_agent, self.ms)
|
|
1291
1285
|
|
|
1292
1286
|
# TODO: this is gross, fix
|
|
1293
|
-
return [letta_agent.
|
|
1287
|
+
return [letta_agent.archival_memory.storage.get(id=passage_id) for passage_id in passage_ids]
|
|
1294
1288
|
|
|
1295
1289
|
def delete_archival_memory(self, user_id: str, agent_id: str, memory_id: str):
|
|
1296
1290
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
@@ -1305,7 +1299,7 @@ class SyncServer(Server):
|
|
|
1305
1299
|
|
|
1306
1300
|
# Delete by ID
|
|
1307
1301
|
# TODO check if it exists first, and throw error if not
|
|
1308
|
-
letta_agent.
|
|
1302
|
+
letta_agent.archival_memory.storage.delete({"id": memory_id})
|
|
1309
1303
|
|
|
1310
1304
|
# TODO: return archival memory
|
|
1311
1305
|
|
|
@@ -1313,17 +1307,15 @@ class SyncServer(Server):
|
|
|
1313
1307
|
self,
|
|
1314
1308
|
user_id: str,
|
|
1315
1309
|
agent_id: str,
|
|
1316
|
-
|
|
1317
|
-
before: Optional[str] = None,
|
|
1310
|
+
cursor: Optional[str] = None,
|
|
1318
1311
|
limit: Optional[int] = 100,
|
|
1319
|
-
order_by: Optional[str] = "created_at",
|
|
1320
|
-
order: Optional[str] = "asc",
|
|
1321
1312
|
reverse: Optional[bool] = False,
|
|
1322
1313
|
return_message_object: bool = True,
|
|
1323
1314
|
assistant_message_tool_name: str = constants.DEFAULT_MESSAGE_TOOL,
|
|
1324
1315
|
assistant_message_tool_kwarg: str = constants.DEFAULT_MESSAGE_TOOL_KWARG,
|
|
1325
1316
|
) -> Union[List[Message], List[LettaMessage]]:
|
|
1326
|
-
|
|
1317
|
+
actor = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1318
|
+
if actor is None:
|
|
1327
1319
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
1328
1320
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1329
1321
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
@@ -1332,8 +1324,12 @@ class SyncServer(Server):
|
|
|
1332
1324
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1333
1325
|
|
|
1334
1326
|
# iterate over records
|
|
1335
|
-
|
|
1336
|
-
|
|
1327
|
+
# TODO: Check "order_by", "order"
|
|
1328
|
+
records = letta_agent.message_manager.list_messages_for_agent(
|
|
1329
|
+
agent_id=agent_id,
|
|
1330
|
+
actor=actor,
|
|
1331
|
+
cursor=cursor,
|
|
1332
|
+
limit=limit,
|
|
1337
1333
|
)
|
|
1338
1334
|
|
|
1339
1335
|
assert all(isinstance(m, Message) for m in records)
|
|
@@ -1353,7 +1349,7 @@ class SyncServer(Server):
|
|
|
1353
1349
|
records = records[::-1]
|
|
1354
1350
|
|
|
1355
1351
|
return records
|
|
1356
|
-
|
|
1352
|
+
|
|
1357
1353
|
def get_server_config(self, include_defaults: bool = False) -> dict:
|
|
1358
1354
|
"""Return the base config"""
|
|
1359
1355
|
|
|
@@ -1425,19 +1421,25 @@ class SyncServer(Server):
|
|
|
1425
1421
|
self.agents_tags_manager.delete_all_tags_from_agent(agent_id=agent_id, actor=actor)
|
|
1426
1422
|
self.blocks_agents_manager.remove_all_agent_blocks(agent_id=agent_id)
|
|
1427
1423
|
|
|
1428
|
-
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1429
|
-
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
1430
|
-
|
|
1431
1424
|
# Verify that the agent exists and belongs to the org of the user
|
|
1432
1425
|
agent_state = self.ms.get_agent(agent_id=agent_id, user_id=user_id)
|
|
1433
|
-
if
|
|
1426
|
+
if agent_state is None:
|
|
1434
1427
|
raise ValueError(f"Could not find agent_id={agent_id} under user_id={user_id}")
|
|
1435
1428
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1429
|
+
# TODO: REMOVE THIS ONCE WE MIGRATE AGENTMODEL TO ORM MODEL
|
|
1430
|
+
messages = self.message_manager.list_messages_for_agent(agent_id=agent_state.id)
|
|
1431
|
+
for message in messages:
|
|
1432
|
+
self.message_manager.delete_message_by_id(message.id, actor=actor)
|
|
1433
|
+
|
|
1434
|
+
# TODO: REMOVE THIS ONCE WE MIGRATE AGENTMODEL TO ORM
|
|
1435
|
+
try:
|
|
1436
|
+
agent_state_user = self.user_manager.get_user_by_id(user_id=agent_state.user_id)
|
|
1437
|
+
if agent_state_user.organization_id != actor.organization_id:
|
|
1438
|
+
raise ValueError(
|
|
1439
|
+
f"Could not authorize agent_id={agent_id} with user_id={user_id} because of differing organizations; agent_id was created in {agent_state_user.organization_id} while user belongs to {actor.organization_id}. How did they get the agent id?"
|
|
1440
|
+
)
|
|
1441
|
+
except NoResultFound:
|
|
1442
|
+
logger.error(f"Agent with id {agent_state.id} has nonexistent user {agent_state.user_id}")
|
|
1441
1443
|
|
|
1442
1444
|
# First, if the agent is in the in-memory cache we should remove it
|
|
1443
1445
|
# List of {'user_id': user_id, 'agent_id': agent_id, 'agent': agent_obj} dicts
|
|
@@ -1582,7 +1584,7 @@ class SyncServer(Server):
|
|
|
1582
1584
|
|
|
1583
1585
|
# delete all Passage objects with source_id==source_id from agent's archival memory
|
|
1584
1586
|
agent = self.load_agent(agent_id=agent_id)
|
|
1585
|
-
archival_memory = agent.
|
|
1587
|
+
archival_memory = agent.archival_memory
|
|
1586
1588
|
archival_memory.storage.delete({"source_id": source_id})
|
|
1587
1589
|
|
|
1588
1590
|
# delete agent-source mapping
|
|
@@ -1661,7 +1663,7 @@ class SyncServer(Server):
|
|
|
1661
1663
|
"""Get a single message from the agent's memory"""
|
|
1662
1664
|
# Get the agent object (loaded in memory)
|
|
1663
1665
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1664
|
-
message = letta_agent.
|
|
1666
|
+
message = letta_agent.message_manager.get_message_by_id(id=message_id)
|
|
1665
1667
|
save_agent(letta_agent, self.ms)
|
|
1666
1668
|
return message
|
|
1667
1669
|
|
|
@@ -1705,7 +1707,7 @@ class SyncServer(Server):
|
|
|
1705
1707
|
|
|
1706
1708
|
try:
|
|
1707
1709
|
return self.user_manager.get_user_by_id(user_id=user_id)
|
|
1708
|
-
except
|
|
1710
|
+
except NoResultFound:
|
|
1709
1711
|
raise HTTPException(status_code=404, detail=f"User with id {user_id} not found")
|
|
1710
1712
|
|
|
1711
1713
|
def get_organization_or_default(self, org_id: Optional[str]) -> Organization:
|
|
@@ -1715,7 +1717,7 @@ class SyncServer(Server):
|
|
|
1715
1717
|
|
|
1716
1718
|
try:
|
|
1717
1719
|
return self.organization_manager.get_organization_by_id(org_id=org_id)
|
|
1718
|
-
except
|
|
1720
|
+
except NoResultFound:
|
|
1719
1721
|
raise HTTPException(status_code=404, detail=f"Organization with id {org_id} not found")
|
|
1720
1722
|
|
|
1721
1723
|
def list_llm_models(self) -> List[LLMConfig]:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}:root{--background: 210, 10%, 92%;--background-lighter: 0, 0%, 100%;--background-darker: 210, 6%, 86%;--foreground: 224 71.4% 4.1%;--card: 0 0% 100%;--card-foreground: 224 71.4% 4.1%;--popover: 0 0% 100%;--popover-foreground: 224 71.4% 4.1%;--primary: 220.9 39.3% 11%;--primary-foreground: 210 20% 98%;--secondary: 240, 92%, 35%;--secondary-foreground: 0, 0%, 100%;--muted: 220 14.3% 95.9%;--muted-foreground: 220 8.9% 46.1%;--accent: 220 14.3% 95.9%;--accent-foreground: 220.9 39.3% 11%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 210 20% 98%;--border: 210, 6%, 86%;--input: 210, 6%, 86%;--ring: 224 71.4% 4.1%;--radius: .5rem}.dark{--background: 224 71.4% 4.1%;--background-lighter: 224 71.4% 4.1%;--background-darker: 224 71.4% 4.1%;--foreground: 210 20% 98%;--card: 224 71.4% 4.1%;--card-foreground: 210 20% 98%;--popover: 224 71.4% 4.1%;--popover-foreground: 210 20% 98%;--primary: 210 20% 98%;--primary-foreground: 220.9 39.3% 11%;--secondary: 10, 100%, 60%;--secondary-foreground: 210 20% 98%;--muted: 215 27.9% 16.9%;--muted-foreground: 217.9 10.6% 64.9%;--accent: 215 27.9% 16.9%;--accent-foreground: 210 20% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 210 20% 98%;--border: 215 27.9% 16.9%;--input: 215 27.9% 16.9%;--ring: 216 12.2% 83.9%}*{border-color:hsl(var(--border))}html{height:100%}body{height:100%;width:100%;background-color:hsl(var(--background));color:hsl(var(--foreground));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}input::file-selector-button{color:hsl(var(--foreground))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.mt-10{margin-top:2.5rem}.mt-3{margin-top:.75rem}.flex{display:flex}.h-\[100dvh\]{height:100dvh}.h-full{height:100%}.w-\[100dvh\]{width:100dvh}.w-full{width:100%}.max-w-\[600px\]{max-width:600px}.max-w-\[893px\]{max-width:893px}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-5{gap:1.25rem}.border{border-width:1px}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.p-0{padding:0}.p-10{padding:2.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.PopoverContent{width:var(--radix-popover-trigger-width);max-height:var(--radix-popover-content-available-height)}
|