From 946aa9d9e9b17712bff93c3a4ae16dccc79a0cb4 Mon Sep 17 00:00:00 2001
From: Slavi Pantaleev
Date: Thu, 12 Sep 2024 13:44:06 +0300
Subject: [PATCH] Initial commit
---
.dockerignore | 2 +
.editorconfig | 32 +
.github/workflows/workflow.yml | 51 +
.gitignore | 2 +
CHANGELOG.md | 1 +
Cargo.lock | 4380 +++++++++++++++++
Cargo.toml | 42 +
Dockerfile | 43 +
LICENSE | 661 +++
README.md | 77 +
docs/README.md | 9 +
docs/access.md | 46 +
docs/agents.md | 61 +
docs/configuration/README.md | 48 +
docs/configuration/handlers.md | 32 +
docs/configuration/image-generation.md | 9 +
docs/configuration/speech-to-text.md | 39 +
docs/configuration/text-generation.md | 75 +
docs/configuration/text-to-speech.md | 63 +
docs/development.md | 134 +
docs/features.md | 150 +
docs/installation.md | 93 +
docs/providers.md | 170 +
docs/sample-provider-configs/anthropic.yml | 8 +
docs/sample-provider-configs/groq.yml | 10 +
docs/sample-provider-configs/localai.yml | 20 +
docs/sample-provider-configs/mistral.yml | 8 +
docs/sample-provider-configs/ollama.yml | 8 +
.../openai-compatible.yml | 10 +
docs/sample-provider-configs/openai.yml | 20 +
docs/sample-provider-configs/openrouter.yml | 8 +
docs/sample-provider-configs/together-ai.yml | 8 +
docs/screenshots/agent-creation.webp | Bin 0 -> 91060 bytes
docs/screenshots/config-status-handlers.webp | Bin 0 -> 66930 bytes
docs/screenshots/image-generation.webp | Bin 0 -> 699966 bytes
.../introduction-and-general-usage.webp | Bin 0 -> 53506 bytes
.../speech-to-text-default-flow.webp | Bin 0 -> 27816 bytes
docs/screenshots/speech-to-text-language.webp | Bin 0 -> 38120 bytes
.../speech-to-text-transcribe-only-mode.webp | Bin 0 -> 13290 bytes
docs/screenshots/sticker-generation.webp | Bin 0 -> 133288 bytes
.../text-generation-prefix-requirement.webp | Bin 0 -> 161658 bytes
docs/screenshots/text-generation.webp | Bin 0 -> 11496 bytes
.../screenshots/text-to-speech-only-mode.webp | Bin 0 -> 16820 bytes
...-to-speech-seamless-voice-interaction.webp | Bin 0 -> 23928 bytes
docs/usage.md | 99 +
etc/app/config.yml.dist | 153 +
etc/assets/baibot-torso-768.png | Bin 0 -> 78468 bytes
etc/assets/baibot-torso.svg | 351 ++
etc/assets/baibot.png | Bin 0 -> 88901 bytes
etc/assets/baibot.svg | 568 +++
etc/assets/baibot.xcf | Bin 0 -> 668911 bytes
etc/services/core/compose.yml | 41 +
etc/services/core/element-web/config.json | 13 +
etc/services/core/element-web/nginx.conf | 60 +
.../core/synapse/config/homeserver.yaml | 1457 ++++++
.../synapse.127.0.0.1.nip.io.log.config | 36 +
.../synapse.127.0.0.1.nip.io.signing.key | 1 +
etc/services/env.dist | 12 +
etc/services/localai/compose.yml | 20 +
etc/services/ollama/compose.yml | 13 +
justfile | 239 +
src/agent/definition.rs | 55 +
src/agent/identifier.rs | 101 +
src/agent/instantiation.rs | 154 +
src/agent/manager.rs | 68 +
src/agent/mod.rs | 21 +
src/agent/provider/anthropic/config.rs | 71 +
src/agent/provider/anthropic/controller.rs | 251 +
src/agent/provider/anthropic/mod.rs | 43 +
src/agent/provider/anthropic/utils.rs | 32 +
src/agent/provider/config.rs | 3 +
src/agent/provider/controller.rs | 172 +
src/agent/provider/entity/agent_provider.rs | 198 +
src/agent/provider/entity/image_generation.rs | 31 +
src/agent/provider/entity/mod.rs | 13 +
src/agent/provider/entity/ping.rs | 4 +
src/agent/provider/entity/speech_to_text.rs | 8 +
src/agent/provider/entity/text_generation.rs | 10 +
src/agent/provider/entity/text_to_speech.rs | 10 +
src/agent/provider/groq/mod.rs | 26 +
src/agent/provider/localai/mod.rs | 33 +
src/agent/provider/mistral/mod.rs | 22 +
src/agent/provider/mod.rs | 25 +
src/agent/provider/ollama/mod.rs | 24 +
src/agent/provider/openai/config.rs | 190 +
src/agent/provider/openai/controller.rs | 465 ++
src/agent/provider/openai/mod.rs | 46 +
src/agent/provider/openai/utils.rs | 55 +
src/agent/provider/openai_compat/config.rs | 261 +
.../provider/openai_compat/controller.rs | 445 ++
src/agent/provider/openai_compat/mod.rs | 70 +
src/agent/provider/openai_compat/utils.rs | 78 +
src/agent/provider/openrouter/mod.rs | 21 +
src/agent/provider/togetherai/mod.rs | 21 +
src/agent/purpose.rs | 67 +
src/agent/utils.rs | 146 +
src/bot/implementation.rs | 419 ++
src/bot/load_config.rs | 110 +
src/bot/messaging.rs | 334 ++
src/bot/mod.rs | 8 +
src/bot/reacting.rs | 262 +
src/bot/rooms.rs | 145 +
src/controller/access/determination/mod.rs | 63 +
src/controller/access/determination/tests.rs | 58 +
src/controller/access/dispatching.rs | 45 +
src/controller/access/help.rs | 183 +
src/controller/access/mod.rs | 8 +
.../access/room_local_agent_managers.rs | 67 +
src/controller/access/users.rs | 63 +
src/controller/agent/create/mod.rs | 418 ++
src/controller/agent/create/tests.rs | 61 +
src/controller/agent/delete/mod.rs | 195 +
src/controller/agent/details/mod.rs | 84 +
src/controller/agent/determination/mod.rs | 99 +
src/controller/agent/determination/tests.rs | 131 +
src/controller/agent/help/mod.rs | 79 +
src/controller/agent/list/mod.rs | 39 +
src/controller/agent/mod.rs | 54 +
src/controller/cfg/common/generic_setting.rs | 35 +
src/controller/cfg/common/mod.rs | 1 +
src/controller/cfg/controller_type.rs | 74 +
src/controller/cfg/determination/mod.rs | 141 +
.../cfg/determination/speech_to_text/mod.rs | 87 +
.../cfg/determination/speech_to_text/tests.rs | 136 +
src/controller/cfg/determination/tests.rs | 212 +
.../cfg/determination/text_generation/mod.rs | 201 +
.../determination/text_generation/tests.rs | 332 ++
.../cfg/determination/text_to_speech/mod.rs | 158 +
.../cfg/determination/text_to_speech/tests.rs | 252 +
src/controller/cfg/dispatching/mod.rs | 124 +
.../cfg/dispatching/speech_to_text.rs | 78 +
.../cfg/dispatching/text_generation.rs | 155 +
.../cfg/dispatching/text_to_speech.rs | 134 +
.../cfg/global_config/generic_setting.rs | 38 +
src/controller/cfg/global_config/handler.rs | 162 +
src/controller/cfg/global_config/mod.rs | 2 +
src/controller/cfg/help.rs | 547 ++
src/controller/cfg/mod.rs | 12 +
.../cfg/room_config/generic_setting.rs | 38 +
src/controller/cfg/room_config/handler.rs | 141 +
src/controller/cfg/room_config/mod.rs | 2 +
src/controller/cfg/status.rs | 759 +++
src/controller/chat_completion/mod.rs | 599 +++
src/controller/controller_type.rs | 27 +
src/controller/determination/mod.rs | 154 +
src/controller/determination/tests.rs | 196 +
src/controller/dispatching.rs | 108 +
src/controller/help/mod.rs | 90 +
src/controller/image/generation.rs | 179 +
src/controller/image/mod.rs | 2 +
src/controller/image/prompt.rs | 111 +
src/controller/join/mod.rs | 28 +
src/controller/mod.rs | 19 +
src/controller/provider/mod.rs | 97 +
src/controller/reaction/mod.rs | 50 +
src/controller/reaction/text_to_speech.rs | 100 +
src/controller/usage/mod.rs | 21 +
src/controller/utils/agent.rs | 63 +
src/controller/utils/mod.rs | 29 +
src/controller/utils/text_to_speech.rs | 168 +
src/conversation/llm/entity.rs | 17 +
src/conversation/llm/mod.rs | 10 +
src/conversation/llm/tests.rs | 84 +
src/conversation/llm/tokenization.rs | 287 ++
src/conversation/llm/utils.rs | 51 +
src/conversation/matrix/entity.rs | 38 +
src/conversation/matrix/mod.rs | 11 +
.../matrix/room_display_name_fetcher.rs | 56 +
src/conversation/matrix/room_event_fetcher.rs | 46 +
src/conversation/matrix/utils/mod.rs | 284 ++
src/conversation/matrix/utils/tests.rs | 218 +
src/conversation/matrix_llm_bridge.rs | 34 +
src/conversation/mod.rs | 5 +
.../delayed_catch_up_marker_manager.rs | 181 +
src/entity/catch_up_marker/entity.rs | 37 +
src/entity/catch_up_marker/mod.rs | 11 +
src/entity/cfg/config.rs | 401 ++
src/entity/cfg/defaults.rs | 35 +
src/entity/cfg/env.rs | 38 +
src/entity/cfg/mod.rs | 5 +
src/entity/globalconfig/entity.rs | 64 +
src/entity/globalconfig/mod.rs | 8 +
src/entity/message_context.rs | 122 +
src/entity/message_payload.rs | 47 +
src/entity/mod.rs | 15 +
src/entity/room_config_context.rs | 183 +
src/entity/roomconfig/defaults.rs | 17 +
src/entity/roomconfig/entity/handler.rs | 60 +
src/entity/roomconfig/entity/mod.rs | 89 +
.../roomconfig/entity/speech_to_text.rs | 59 +
.../roomconfig/entity/text_generation.rs | 111 +
.../roomconfig/entity/text_to_speech.rs | 110 +
src/entity/roomconfig/mod.rs | 13 +
src/entity/thread_context.rs | 13 +
src/entity/trigger_event_info.rs | 27 +
src/lib.rs | 10 +
src/main.rs | 31 +
src/strings/access.rs | 25 +
src/strings/agent.rs | 345 ++
src/strings/cfg.rs | 356 ++
src/strings/error.rs | 15 +
src/strings/global_config.rs | 67 +
src/strings/help/access.rs | 80 +
src/strings/help/agent.rs | 60 +
src/strings/help/cfg.rs | 273 +
src/strings/help/mod.rs | 17 +
src/strings/help/provider.rs | 7 +
src/strings/help/usage.rs | 7 +
src/strings/image_generation.rs | 15 +
src/strings/introduction.rs | 146 +
src/strings/mod.rs | 19 +
src/strings/provider.rs | 110 +
src/strings/room_config.rs | 67 +
src/strings/speech_to_text.rs | 11 +
src/strings/text_to_speech.rs | 11 +
src/strings/usage.rs | 62 +
src/utils/mod.rs | 3 +
src/utils/status.rs | 11 +
src/utils/text.rs | 19 +
src/utils/text_to_speech.rs | 69 +
220 files changed, 26033 insertions(+)
create mode 100644 .dockerignore
create mode 100644 .editorconfig
create mode 100644 .github/workflows/workflow.yml
create mode 100644 .gitignore
create mode 100644 CHANGELOG.md
create mode 100644 Cargo.lock
create mode 100644 Cargo.toml
create mode 100644 Dockerfile
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 docs/README.md
create mode 100644 docs/access.md
create mode 100644 docs/agents.md
create mode 100644 docs/configuration/README.md
create mode 100644 docs/configuration/handlers.md
create mode 100644 docs/configuration/image-generation.md
create mode 100644 docs/configuration/speech-to-text.md
create mode 100644 docs/configuration/text-generation.md
create mode 100644 docs/configuration/text-to-speech.md
create mode 100644 docs/development.md
create mode 100644 docs/features.md
create mode 100644 docs/installation.md
create mode 100644 docs/providers.md
create mode 100644 docs/sample-provider-configs/anthropic.yml
create mode 100644 docs/sample-provider-configs/groq.yml
create mode 100644 docs/sample-provider-configs/localai.yml
create mode 100644 docs/sample-provider-configs/mistral.yml
create mode 100644 docs/sample-provider-configs/ollama.yml
create mode 100644 docs/sample-provider-configs/openai-compatible.yml
create mode 100644 docs/sample-provider-configs/openai.yml
create mode 100644 docs/sample-provider-configs/openrouter.yml
create mode 100644 docs/sample-provider-configs/together-ai.yml
create mode 100644 docs/screenshots/agent-creation.webp
create mode 100644 docs/screenshots/config-status-handlers.webp
create mode 100644 docs/screenshots/image-generation.webp
create mode 100644 docs/screenshots/introduction-and-general-usage.webp
create mode 100644 docs/screenshots/speech-to-text-default-flow.webp
create mode 100644 docs/screenshots/speech-to-text-language.webp
create mode 100644 docs/screenshots/speech-to-text-transcribe-only-mode.webp
create mode 100644 docs/screenshots/sticker-generation.webp
create mode 100644 docs/screenshots/text-generation-prefix-requirement.webp
create mode 100644 docs/screenshots/text-generation.webp
create mode 100644 docs/screenshots/text-to-speech-only-mode.webp
create mode 100644 docs/screenshots/text-to-speech-seamless-voice-interaction.webp
create mode 100644 docs/usage.md
create mode 100644 etc/app/config.yml.dist
create mode 100644 etc/assets/baibot-torso-768.png
create mode 100644 etc/assets/baibot-torso.svg
create mode 100644 etc/assets/baibot.png
create mode 100644 etc/assets/baibot.svg
create mode 100644 etc/assets/baibot.xcf
create mode 100644 etc/services/core/compose.yml
create mode 100644 etc/services/core/element-web/config.json
create mode 100644 etc/services/core/element-web/nginx.conf
create mode 100644 etc/services/core/synapse/config/homeserver.yaml
create mode 100644 etc/services/core/synapse/config/synapse.127.0.0.1.nip.io.log.config
create mode 100644 etc/services/core/synapse/config/synapse.127.0.0.1.nip.io.signing.key
create mode 100644 etc/services/env.dist
create mode 100644 etc/services/localai/compose.yml
create mode 100644 etc/services/ollama/compose.yml
create mode 100644 justfile
create mode 100644 src/agent/definition.rs
create mode 100644 src/agent/identifier.rs
create mode 100644 src/agent/instantiation.rs
create mode 100644 src/agent/manager.rs
create mode 100644 src/agent/mod.rs
create mode 100644 src/agent/provider/anthropic/config.rs
create mode 100644 src/agent/provider/anthropic/controller.rs
create mode 100644 src/agent/provider/anthropic/mod.rs
create mode 100644 src/agent/provider/anthropic/utils.rs
create mode 100644 src/agent/provider/config.rs
create mode 100644 src/agent/provider/controller.rs
create mode 100644 src/agent/provider/entity/agent_provider.rs
create mode 100644 src/agent/provider/entity/image_generation.rs
create mode 100644 src/agent/provider/entity/mod.rs
create mode 100644 src/agent/provider/entity/ping.rs
create mode 100644 src/agent/provider/entity/speech_to_text.rs
create mode 100644 src/agent/provider/entity/text_generation.rs
create mode 100644 src/agent/provider/entity/text_to_speech.rs
create mode 100644 src/agent/provider/groq/mod.rs
create mode 100644 src/agent/provider/localai/mod.rs
create mode 100644 src/agent/provider/mistral/mod.rs
create mode 100644 src/agent/provider/mod.rs
create mode 100644 src/agent/provider/ollama/mod.rs
create mode 100644 src/agent/provider/openai/config.rs
create mode 100644 src/agent/provider/openai/controller.rs
create mode 100644 src/agent/provider/openai/mod.rs
create mode 100644 src/agent/provider/openai/utils.rs
create mode 100644 src/agent/provider/openai_compat/config.rs
create mode 100644 src/agent/provider/openai_compat/controller.rs
create mode 100644 src/agent/provider/openai_compat/mod.rs
create mode 100644 src/agent/provider/openai_compat/utils.rs
create mode 100644 src/agent/provider/openrouter/mod.rs
create mode 100644 src/agent/provider/togetherai/mod.rs
create mode 100644 src/agent/purpose.rs
create mode 100644 src/agent/utils.rs
create mode 100644 src/bot/implementation.rs
create mode 100644 src/bot/load_config.rs
create mode 100644 src/bot/messaging.rs
create mode 100644 src/bot/mod.rs
create mode 100644 src/bot/reacting.rs
create mode 100644 src/bot/rooms.rs
create mode 100644 src/controller/access/determination/mod.rs
create mode 100644 src/controller/access/determination/tests.rs
create mode 100644 src/controller/access/dispatching.rs
create mode 100644 src/controller/access/help.rs
create mode 100644 src/controller/access/mod.rs
create mode 100644 src/controller/access/room_local_agent_managers.rs
create mode 100644 src/controller/access/users.rs
create mode 100644 src/controller/agent/create/mod.rs
create mode 100644 src/controller/agent/create/tests.rs
create mode 100644 src/controller/agent/delete/mod.rs
create mode 100644 src/controller/agent/details/mod.rs
create mode 100644 src/controller/agent/determination/mod.rs
create mode 100644 src/controller/agent/determination/tests.rs
create mode 100644 src/controller/agent/help/mod.rs
create mode 100644 src/controller/agent/list/mod.rs
create mode 100644 src/controller/agent/mod.rs
create mode 100644 src/controller/cfg/common/generic_setting.rs
create mode 100644 src/controller/cfg/common/mod.rs
create mode 100644 src/controller/cfg/controller_type.rs
create mode 100644 src/controller/cfg/determination/mod.rs
create mode 100644 src/controller/cfg/determination/speech_to_text/mod.rs
create mode 100644 src/controller/cfg/determination/speech_to_text/tests.rs
create mode 100644 src/controller/cfg/determination/tests.rs
create mode 100644 src/controller/cfg/determination/text_generation/mod.rs
create mode 100644 src/controller/cfg/determination/text_generation/tests.rs
create mode 100644 src/controller/cfg/determination/text_to_speech/mod.rs
create mode 100644 src/controller/cfg/determination/text_to_speech/tests.rs
create mode 100644 src/controller/cfg/dispatching/mod.rs
create mode 100644 src/controller/cfg/dispatching/speech_to_text.rs
create mode 100644 src/controller/cfg/dispatching/text_generation.rs
create mode 100644 src/controller/cfg/dispatching/text_to_speech.rs
create mode 100644 src/controller/cfg/global_config/generic_setting.rs
create mode 100644 src/controller/cfg/global_config/handler.rs
create mode 100644 src/controller/cfg/global_config/mod.rs
create mode 100644 src/controller/cfg/help.rs
create mode 100644 src/controller/cfg/mod.rs
create mode 100644 src/controller/cfg/room_config/generic_setting.rs
create mode 100644 src/controller/cfg/room_config/handler.rs
create mode 100644 src/controller/cfg/room_config/mod.rs
create mode 100644 src/controller/cfg/status.rs
create mode 100644 src/controller/chat_completion/mod.rs
create mode 100644 src/controller/controller_type.rs
create mode 100644 src/controller/determination/mod.rs
create mode 100644 src/controller/determination/tests.rs
create mode 100644 src/controller/dispatching.rs
create mode 100644 src/controller/help/mod.rs
create mode 100644 src/controller/image/generation.rs
create mode 100644 src/controller/image/mod.rs
create mode 100644 src/controller/image/prompt.rs
create mode 100644 src/controller/join/mod.rs
create mode 100644 src/controller/mod.rs
create mode 100644 src/controller/provider/mod.rs
create mode 100644 src/controller/reaction/mod.rs
create mode 100644 src/controller/reaction/text_to_speech.rs
create mode 100644 src/controller/usage/mod.rs
create mode 100644 src/controller/utils/agent.rs
create mode 100644 src/controller/utils/mod.rs
create mode 100644 src/controller/utils/text_to_speech.rs
create mode 100644 src/conversation/llm/entity.rs
create mode 100644 src/conversation/llm/mod.rs
create mode 100644 src/conversation/llm/tests.rs
create mode 100644 src/conversation/llm/tokenization.rs
create mode 100644 src/conversation/llm/utils.rs
create mode 100644 src/conversation/matrix/entity.rs
create mode 100644 src/conversation/matrix/mod.rs
create mode 100644 src/conversation/matrix/room_display_name_fetcher.rs
create mode 100644 src/conversation/matrix/room_event_fetcher.rs
create mode 100644 src/conversation/matrix/utils/mod.rs
create mode 100644 src/conversation/matrix/utils/tests.rs
create mode 100644 src/conversation/matrix_llm_bridge.rs
create mode 100644 src/conversation/mod.rs
create mode 100644 src/entity/catch_up_marker/delayed_catch_up_marker_manager.rs
create mode 100644 src/entity/catch_up_marker/entity.rs
create mode 100644 src/entity/catch_up_marker/mod.rs
create mode 100644 src/entity/cfg/config.rs
create mode 100644 src/entity/cfg/defaults.rs
create mode 100644 src/entity/cfg/env.rs
create mode 100644 src/entity/cfg/mod.rs
create mode 100644 src/entity/globalconfig/entity.rs
create mode 100644 src/entity/globalconfig/mod.rs
create mode 100644 src/entity/message_context.rs
create mode 100644 src/entity/message_payload.rs
create mode 100644 src/entity/mod.rs
create mode 100644 src/entity/room_config_context.rs
create mode 100644 src/entity/roomconfig/defaults.rs
create mode 100644 src/entity/roomconfig/entity/handler.rs
create mode 100644 src/entity/roomconfig/entity/mod.rs
create mode 100644 src/entity/roomconfig/entity/speech_to_text.rs
create mode 100644 src/entity/roomconfig/entity/text_generation.rs
create mode 100644 src/entity/roomconfig/entity/text_to_speech.rs
create mode 100644 src/entity/roomconfig/mod.rs
create mode 100644 src/entity/thread_context.rs
create mode 100644 src/entity/trigger_event_info.rs
create mode 100644 src/lib.rs
create mode 100644 src/main.rs
create mode 100644 src/strings/access.rs
create mode 100644 src/strings/agent.rs
create mode 100644 src/strings/cfg.rs
create mode 100644 src/strings/error.rs
create mode 100644 src/strings/global_config.rs
create mode 100644 src/strings/help/access.rs
create mode 100644 src/strings/help/agent.rs
create mode 100644 src/strings/help/cfg.rs
create mode 100644 src/strings/help/mod.rs
create mode 100644 src/strings/help/provider.rs
create mode 100644 src/strings/help/usage.rs
create mode 100644 src/strings/image_generation.rs
create mode 100644 src/strings/introduction.rs
create mode 100644 src/strings/mod.rs
create mode 100644 src/strings/provider.rs
create mode 100644 src/strings/room_config.rs
create mode 100644 src/strings/speech_to_text.rs
create mode 100644 src/strings/text_to_speech.rs
create mode 100644 src/strings/usage.rs
create mode 100644 src/utils/mod.rs
create mode 100644 src/utils/status.rs
create mode 100644 src/utils/text.rs
create mode 100644 src/utils/text_to_speech.rs
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..9d8c1be
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+/target
+/var
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d5c9df2
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,32 @@
+# This file is the top-most EditorConfig file
+root = true
+
+# All Files
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = tab
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+#########################
+# File Extension Settings
+#########################
+
+[*.{yml,yaml,yml.dist}]
+indent_style = space
+indent_size = 2
+
+[*.rs]
+indent_style = space
+indent_size = 4
+
+# Markdown Files
+#
+# Two spaces at the end of a line in Markdown mean "new line",
+# so trimming trailing whitespace for such files can cause breakage.
+[*.md]
+trim_trailing_whitespace = false
+indent_style = space
+indent_size = 2
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
new file mode 100644
index 0000000..a13fef4
--- /dev/null
+++ b/.github/workflows/workflow.yml
@@ -0,0 +1,51 @@
+name: CI (main and tags)
+on:
+ push:
+ branches: [ "main" ]
+ tags: [ "v*" ]
+ schedule:
+ - cron: '0 0 * * 1'
+permissions:
+ checks: write
+ contents: write
+ packages: write
+ pull-requests: read
+jobs:
+ test-and-clippy:
+ name: Unit testing and linting
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - run: cargo test --all-features
+ - run: cargo clippy
+
+ build-publish:
+ name: Build and Publish
+ runs-on: self-hosted
+ steps:
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v1
+ - name: Login to ghcr.io
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: |
+ ghcr.io/${{ github.repository }}
+ registry.etke.cc/${{ github.repository }}
+ tags: |
+ type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
+ type=semver,pattern={{raw}}
+ - name: Build and push
+ uses: docker/build-push-action@v6
+ with:
+ platforms: linux/amd64,linux/arm64
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9d8c1be
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/var
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..685a84a
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1 @@
+There's nothing here yet.
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..4484b15
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,4380 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "accessory"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde"
+dependencies = [
+ "macroific",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "addr2line"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "aes"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
+
+[[package]]
+name = "anthropic-rs"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5ef53259d9d4d8575d885bee6e50d0eab7579225da6e1789b2b77b5405b4d28"
+dependencies = [
+ "futures-util",
+ "reqwest 0.12.7",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8"
+
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "aquamarine"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e"
+dependencies = [
+ "include_dir",
+ "itertools 0.10.5",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "as_variant"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38fa22307249f86fb7fad906fcae77f2564caeb56d7209103c551cd1cf4798f"
+
+[[package]]
+name = "assign"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002"
+
+[[package]]
+name = "async-channel"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-convert"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d416feee97712e43152cd42874de162b8f9b77295b1c85e5d92725cc8310bae"
+dependencies = [
+ "async-trait",
+]
+
+[[package]]
+name = "async-openai"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e1df052c2bd7b241fc828bc2fda74ce9a7ef05e0a593c37275aaaba52caf49d"
+dependencies = [
+ "async-convert",
+ "backoff",
+ "base64 0.21.7",
+ "derive_builder 0.12.0",
+ "futures",
+ "rand",
+ "reqwest 0.11.27",
+ "reqwest-eventsource 0.4.0",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "async-openai"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f7e40b158d5356b3497edb35610a408cb6339e68c91c7521bc68fbec83b2521"
+dependencies = [
+ "async-convert",
+ "backoff",
+ "base64 0.22.1",
+ "bytes",
+ "derive_builder 0.20.1",
+ "eventsource-stream",
+ "futures",
+ "rand",
+ "reqwest 0.12.7",
+ "reqwest-eventsource 0.6.0",
+ "secrecy",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "backoff"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
+dependencies = [
+ "futures-core",
+ "getrandom",
+ "instant",
+ "pin-project-lite",
+ "rand",
+ "tokio",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide 0.7.4",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "baibot"
+version = "1.0.0"
+dependencies = [
+ "anthropic-rs",
+ "anyhow",
+ "async-openai 0.24.0",
+ "base64 0.22.1",
+ "matrix-sdk",
+ "mxidwc",
+ "mxlink",
+ "openai_api_rust",
+ "quick_cache",
+ "regex",
+ "serde",
+ "serde_json",
+ "serde_yaml",
+ "tempfile",
+ "tiktoken-rs",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "url",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "bitmaps"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6"
+
+[[package]]
+name = "blake3"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "bstr"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
+dependencies = [
+ "memchr",
+ "regex-automata 0.4.7",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
+
+[[package]]
+name = "bytesize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
+
+[[package]]
+name = "cbc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "cc"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg-vis"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3a2c3bf5fc10fe2ca157564fbe08a4cb2b0a7d2ff3fe2f9683e65d5e7c7859c"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead",
+ "chacha20",
+ "cipher",
+ "poly1305",
+ "zeroize",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "const_panic"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7782af8f90fe69a4bb41e460abe1727d493403d8b2cc43201a3a3e906b24379f"
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "typenum",
+]
+
+[[package]]
+name = "ctr"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "rustc_version",
+ "serde",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "darling"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+dependencies = [
+ "darling_core 0.14.4",
+ "darling_macro 0.14.4",
+]
+
+[[package]]
+name = "darling"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+dependencies = [
+ "darling_core 0.20.10",
+ "darling_macro 0.20.10",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.10.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.11.1",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+dependencies = [
+ "darling_core 0.14.4",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.20.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+dependencies = [
+ "darling_core 0.20.10",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "deadpool"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490"
+dependencies = [
+ "async-trait",
+ "deadpool-runtime",
+ "num_cpus",
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-runtime"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
+dependencies = [
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-sqlite"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8010e36e12f3be22543a5e478b4af20aeead9a700dd69581a5e050a070fc22c"
+dependencies = [
+ "deadpool",
+ "deadpool-sync",
+ "rusqlite",
+]
+
+[[package]]
+name = "deadpool-sync"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04"
+dependencies = [
+ "deadpool-runtime",
+]
+
+[[package]]
+name = "delegate-display"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66"
+dependencies = [
+ "macroific",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid",
+ "der_derive",
+ "flagset",
+ "zeroize",
+]
+
+[[package]]
+name = "der_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
+dependencies = [
+ "derive_builder_macro 0.12.0",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b"
+dependencies = [
+ "derive_builder_macro 0.20.1",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
+dependencies = [
+ "darling 0.14.4",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38"
+dependencies = [
+ "darling 0.20.10",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
+dependencies = [
+ "derive_builder_core 0.12.0",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc"
+dependencies = [
+ "derive_builder_core 0.20.1",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "serde",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "event-listener"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
+dependencies = [
+ "event-listener 5.3.1",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "eventsource-stream"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab"
+dependencies = [
+ "futures-core",
+ "nom",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "eyeball"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93bd0ebf93d61d6332d3c09a96e97975968a44e19a64c947bde06e6baff383f"
+dependencies = [
+ "futures-core",
+ "readlock",
+ "tracing",
+]
+
+[[package]]
+name = "eyeball-im"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9326c8d9f6d59d18935412608b4514cc661e4e068011bb2f523f6c8b1cfa3bd4"
+dependencies = [
+ "futures-core",
+ "imbl",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fancy-regex"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7493d4c459da9f84325ad297371a6b2b8a162800873a22e3b6b6512e61d18c05"
+dependencies = [
+ "bit-set",
+ "regex",
+]
+
+[[package]]
+name = "fancy_constructor"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2"
+dependencies = [
+ "macroific",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+
+[[package]]
+name = "flagset"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
+
+[[package]]
+name = "flate2"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide 0.8.0",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+
+[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.12",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.1.0",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+dependencies = [
+ "hashbrown",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "http"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.12",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http 1.1.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.26",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.6",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
+dependencies = [
+ "futures-util",
+ "http 0.2.12",
+ "hyper 0.14.30",
+ "rustls 0.21.12",
+ "tokio",
+ "tokio-rustls 0.24.1",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
+dependencies = [
+ "futures-util",
+ "http 1.1.0",
+ "hyper 1.4.1",
+ "hyper-util",
+ "rustls 0.23.12",
+ "rustls-native-certs 0.8.0",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.0",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper 0.14.30",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper 1.4.1",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "hyper 1.4.1",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "imbl"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085"
+dependencies = [
+ "bitmaps",
+ "imbl-sized-chunks",
+ "rand_core",
+ "rand_xoshiro",
+ "serde",
+ "version_check",
+]
+
+[[package]]
+name = "imbl-sized-chunks"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63"
+dependencies = [
+ "bitmaps",
+]
+
+[[package]]
+name = "include_dir"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
+dependencies = [
+ "include_dir_macros",
+]
+
+[[package]]
+name = "include_dir_macros"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "indexed_db_futures"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706"
+dependencies = [
+ "accessory",
+ "cfg-if",
+ "delegate-display",
+ "fancy_constructor",
+ "js-sys",
+ "uuid",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+ "serde",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "block-padding",
+ "generic-array",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.70"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "js_int"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d937f95470b270ce8b8950207715d71aa8e153c0d44c6684d59397ed4949160a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "js_option"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68421373957a1593a767013698dbf206e2b221eefe97a44d98d18672ff38423c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "konst"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50a0ba6de5f7af397afff922f22c149ff605c766cd3269cf6c1cd5e466dbe3b9"
+dependencies = [
+ "const_panic",
+ "konst_kernel",
+ "typewit",
+]
+
+[[package]]
+name = "konst_kernel"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be0a455a1719220fd6adf756088e1c69a85bf14b6a9e24537a5cc04f503edb2b"
+dependencies = [
+ "typewit",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.158"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "macroific"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd"
+dependencies = [
+ "macroific_attr_parse",
+ "macroific_core",
+ "macroific_macro",
+]
+
+[[package]]
+name = "macroific_attr_parse"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67"
+dependencies = [
+ "cfg-if",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "macroific_core"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "macroific_macro"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae"
+dependencies = [
+ "macroific_attr_parse",
+ "macroific_core",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matrix-pickle"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb521190328c57a2051f70250beb874dc0fac6bcd22b615f7f9700b7b4fb826"
+dependencies = [
+ "matrix-pickle-derive",
+ "thiserror",
+]
+
+[[package]]
+name = "matrix-pickle-derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fb3c7231cbb7fbbc50871615edebf65183b382cdaa1fe21c5e88a12617de8e"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "matrix-sdk"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "336687e5fc8b33661a31681e988a67e9a3090c7fb1a8323a7f71eeaabad642ec"
+dependencies = [
+ "anymap2",
+ "aquamarine",
+ "as_variant",
+ "async-channel",
+ "async-stream",
+ "async-trait",
+ "backoff",
+ "bytes",
+ "bytesize",
+ "cfg-vis",
+ "event-listener 4.0.3",
+ "eyeball",
+ "eyeball-im",
+ "futures-core",
+ "futures-util",
+ "gloo-timers",
+ "http 0.2.12",
+ "imbl",
+ "indexmap",
+ "matrix-sdk-base",
+ "matrix-sdk-common",
+ "matrix-sdk-indexeddb",
+ "matrix-sdk-sqlite",
+ "mime",
+ "mime2ext",
+ "reqwest 0.11.27",
+ "ruma",
+ "serde",
+ "serde_html_form",
+ "serde_json",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tokio-util",
+ "tracing",
+ "url",
+ "urlencoding",
+ "zeroize",
+]
+
+[[package]]
+name = "matrix-sdk-base"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00891954d0826a94f1d130f46cbca64176003a234c1be5d9d282970d31cf0c87"
+dependencies = [
+ "as_variant",
+ "async-trait",
+ "bitflags 2.6.0",
+ "eyeball",
+ "eyeball-im",
+ "futures-util",
+ "matrix-sdk-common",
+ "matrix-sdk-crypto",
+ "matrix-sdk-store-encryption",
+ "once_cell",
+ "ruma",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "matrix-sdk-common"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb365a626ab6f6c6a2422cfe2565522f19accb06706c6d04bca8f0f71df29c9f"
+dependencies = [
+ "async-trait",
+ "futures-core",
+ "futures-util",
+ "gloo-timers",
+ "instant",
+ "ruma",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "matrix-sdk-crypto"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e03a64d12a83ebe33bb55de9b77faef4ce27006f946f8468c3311f311f39ab8b"
+dependencies = [
+ "aes",
+ "as_variant",
+ "async-trait",
+ "bs58",
+ "byteorder",
+ "cbc",
+ "cfg-if",
+ "ctr",
+ "eyeball",
+ "futures-core",
+ "futures-util",
+ "hkdf",
+ "hmac",
+ "itertools 0.12.1",
+ "matrix-sdk-common",
+ "pbkdf2",
+ "rand",
+ "rmp-serde",
+ "ruma",
+ "serde",
+ "serde_json",
+ "sha2",
+ "subtle",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tracing",
+ "ulid",
+ "vodozemac",
+ "zeroize",
+]
+
+[[package]]
+name = "matrix-sdk-indexeddb"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad388005c5d4ed2ff38f405d52aa7fa606f4e1ab51baf5f2504721124ed4a58b"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "base64 0.21.7",
+ "getrandom",
+ "gloo-utils",
+ "indexed_db_futures",
+ "js-sys",
+ "matrix-sdk-base",
+ "matrix-sdk-crypto",
+ "matrix-sdk-store-encryption",
+ "ruma",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "matrix-sdk-sqlite"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a98d034dd5aa85b4da6500e60c7d5b9328a37632cf40ba38bbe2c77cea3f14"
+dependencies = [
+ "async-trait",
+ "deadpool-sqlite",
+ "itertools 0.12.1",
+ "matrix-sdk-base",
+ "matrix-sdk-crypto",
+ "matrix-sdk-store-encryption",
+ "rmp-serde",
+ "ruma",
+ "rusqlite",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "vodozemac",
+]
+
+[[package]]
+name = "matrix-sdk-store-encryption"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a7e3162e9f982a4c57ab46df01a4775f697dec8899738bf62d7e97b63faa61c"
+dependencies = [
+ "blake3",
+ "chacha20poly1305",
+ "displaydoc",
+ "getrandom",
+ "hmac",
+ "pbkdf2",
+ "rand",
+ "rmp-serde",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime2ext"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "515a63dc9666c865e848b043ab52fe9a5c713ae89cde4b5fbaae67cfd614b93a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "mxidwc"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1b70c6b8e11b266a470e65d7c018ccfacdf7822449968a37fcdeb258614a7ae"
+dependencies = [
+ "regex",
+]
+
+[[package]]
+name = "mxlink"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80cb97e517e3bc8e85b4787ed87037cd324526862ca9375c6d038699010bf6c2"
+dependencies = [
+ "base64 0.22.1",
+ "chacha20poly1305",
+ "hex",
+ "js_int",
+ "matrix-sdk",
+ "mime",
+ "quick_cache",
+ "rand",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "native-tls"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
+[[package]]
+name = "openai_api_rust"
+version = "0.1.9"
+source = "git+https://github.com/etkecc/openai_api_rust.git?branch=next#a9183fc2e699c3d33c635c2aed3f9174f9d9cd74"
+dependencies = [
+ "log",
+ "mime",
+ "rand",
+ "serde",
+ "serde_json",
+ "ureq",
+]
+
+[[package]]
+name = "openssl"
+version = "0.10.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest",
+ "hmac",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs7"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7"
+dependencies = [
+ "der",
+ "spki",
+ "x509-cert",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8"
+dependencies = [
+ "toml_edit 0.20.7",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit 0.22.20",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac"
+dependencies = [
+ "anyhow",
+ "itertools 0.13.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
+dependencies = [
+ "bitflags 2.6.0",
+ "memchr",
+ "unicase",
+]
+
+[[package]]
+name = "quick_cache"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ec0b6fed0a0ff01fa82d0c8982389375dd59c72dae84d4f8a15b1a894c273f7"
+dependencies = [
+ "ahash",
+ "equivalent",
+ "hashbrown",
+ "parking_lot",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.0.0",
+ "rustls 0.23.12",
+ "socket2",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
+dependencies = [
+ "bytes",
+ "rand",
+ "ring",
+ "rustc-hash 2.0.0",
+ "rustls 0.23.12",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b"
+dependencies = [
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xoshiro"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "readlock"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "072cfe5b1d2dcd38d20e18f85e9c9978b6cc08f0b373e9f1fff1541335622974"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+dependencies = [
+ "bitflags 2.6.0",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.7",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.4",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "reqwest"
+version = "0.11.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
+dependencies = [
+ "base64 0.21.7",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.26",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "hyper 0.14.30",
+ "hyper-rustls 0.24.2",
+ "hyper-tls 0.5.0",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "mime_guess",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls 0.21.12",
+ "rustls-native-certs 0.6.3",
+ "rustls-pemfile 1.0.4",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 0.1.2",
+ "system-configuration 0.5.1",
+ "tokio",
+ "tokio-native-tls",
+ "tokio-rustls 0.24.1",
+ "tokio-util",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "winreg",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.12.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.4.6",
+ "http 1.1.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.4.1",
+ "hyper-rustls 0.27.3",
+ "hyper-tls 0.6.0",
+ "hyper-util",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "mime_guess",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.12",
+ "rustls-native-certs 0.7.3",
+ "rustls-pemfile 2.1.3",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper 1.0.1",
+ "system-configuration 0.6.1",
+ "tokio",
+ "tokio-native-tls",
+ "tokio-rustls 0.26.0",
+ "tokio-util",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+ "windows-registry",
+]
+
+[[package]]
+name = "reqwest-eventsource"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f03f570355882dd8d15acc3a313841e6e90eddbc76a93c748fd82cc13ba9f51"
+dependencies = [
+ "eventsource-stream",
+ "futures-core",
+ "futures-timer",
+ "mime",
+ "nom",
+ "pin-project-lite",
+ "reqwest 0.11.27",
+ "thiserror",
+]
+
+[[package]]
+name = "reqwest-eventsource"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde"
+dependencies = [
+ "eventsource-stream",
+ "futures-core",
+ "futures-timer",
+ "mime",
+ "nom",
+ "pin-project-lite",
+ "reqwest 0.12.7",
+ "thiserror",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rmp"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "ruma"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2779c38df072964c63476259d9300efb07d0d1a7178c6469893636ce0c547a36"
+dependencies = [
+ "assign",
+ "js_int",
+ "js_option",
+ "ruma-client-api",
+ "ruma-common",
+ "ruma-events",
+ "ruma-federation-api",
+]
+
+[[package]]
+name = "ruma-client-api"
+version = "0.17.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "641837258fa214a70823477514954ef0f5d3bc6ae8e1d5d85081856a33103386"
+dependencies = [
+ "assign",
+ "bytes",
+ "http 0.2.12",
+ "js_int",
+ "js_option",
+ "maplit",
+ "ruma-common",
+ "ruma-events",
+ "serde",
+ "serde_html_form",
+ "serde_json",
+]
+
+[[package]]
+name = "ruma-common"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bca4c33c50e47b4cdceeac71bdef0c04153b0e29aa992d9030ec14a62323e85"
+dependencies = [
+ "as_variant",
+ "base64 0.21.7",
+ "bytes",
+ "form_urlencoded",
+ "getrandom",
+ "http 0.2.12",
+ "indexmap",
+ "js-sys",
+ "js_int",
+ "konst",
+ "percent-encoding",
+ "rand",
+ "regex",
+ "ruma-identifiers-validation",
+ "ruma-macros",
+ "serde",
+ "serde_html_form",
+ "serde_json",
+ "thiserror",
+ "tracing",
+ "url",
+ "uuid",
+ "wildmatch",
+]
+
+[[package]]
+name = "ruma-events"
+version = "0.27.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d20a52770e5a9fb30b7a1c14ba8b3dcf76dadc01674e58e40094f78e6bd5e3f1"
+dependencies = [
+ "as_variant",
+ "indexmap",
+ "js_int",
+ "js_option",
+ "percent-encoding",
+ "pulldown-cmark",
+ "regex",
+ "ruma-common",
+ "ruma-identifiers-validation",
+ "ruma-macros",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tracing",
+ "url",
+ "wildmatch",
+]
+
+[[package]]
+name = "ruma-federation-api"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1901c1f27bc327652d58af2a130c73acef3198abeccd24cee97f7267fdf3fe7"
+dependencies = [
+ "js_int",
+ "ruma-common",
+ "ruma-events",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ruma-identifiers-validation"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa38974f5901ed4e00e10aec57b9ad3b4d6d6c1a1ae683c51b88700b9f4ffba"
+dependencies = [
+ "js_int",
+ "thiserror",
+]
+
+[[package]]
+name = "ruma-macros"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0280534a4b3e34416f883285fac4f9c408cd0b737890ae66f3e7a7056d14be80"
+dependencies = [
+ "once_cell",
+ "proc-macro-crate 2.0.0",
+ "proc-macro2",
+ "quote",
+ "ruma-identifiers-validation",
+ "serde",
+ "syn 2.0.77",
+ "toml",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
+dependencies = [
+ "bitflags 2.6.0",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc-hash"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
+dependencies = [
+ "bitflags 2.6.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki 0.101.7",
+ "sct",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
+dependencies = [
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki 0.102.7",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 1.0.4",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.1.3",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 2.1.3",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64 0.21.7",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
+dependencies = [
+ "base64 0.22.1",
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "secrecy"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e"
+dependencies = [
+ "serde",
+ "zeroize",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags 2.6.0",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_bytes"
+version = "0.11.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "serde_html_form"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5"
+dependencies = [
+ "form_urlencoded",
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.128"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_yaml"
+version = "0.9.34+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
+dependencies = [
+ "indexmap",
+ "itoa",
+ "ryu",
+ "serde",
+ "unsafe-libyaml",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys 0.5.0",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags 2.6.0",
+ "core-foundation",
+ "system-configuration-sys 0.6.0",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tiktoken-rs"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c314e7ce51440f9e8f5a497394682a57b7c323d0f4d0a6b1b13c429056e0e234"
+dependencies = [
+ "anyhow",
+ "async-openai 0.14.3",
+ "base64 0.21.7",
+ "bstr",
+ "fancy-regex",
+ "lazy_static",
+ "parking_lot",
+ "rustc-hash 1.1.0",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls 0.21.12",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+dependencies = [
+ "rustls 0.23.12",
+ "rustls-pki-types",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.22.20",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.20.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.6.18",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "typewit"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24"
+dependencies = [
+ "typewit_proc_macros",
+]
+
+[[package]]
+name = "typewit_proc_macros"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
+
+[[package]]
+name = "ulid"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289"
+dependencies = [
+ "getrandom",
+ "rand",
+ "web-time",
+]
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "unsafe-libyaml"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "ureq"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
+dependencies = [
+ "base64 0.22.1",
+ "flate2",
+ "log",
+ "once_cell",
+ "rustls 0.23.12",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "url",
+ "webpki-roots",
+]
+
+[[package]]
+name = "url"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
+[[package]]
+name = "uuid"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
+dependencies = [
+ "getrandom",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "vodozemac"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "051d4af70b53b42adf2aac459a305851b8d754f210aaf11ab509e1065beff422"
+dependencies = [
+ "aes",
+ "arrayvec",
+ "base64 0.22.1",
+ "base64ct",
+ "cbc",
+ "chacha20poly1305",
+ "curve25519-dalek",
+ "ed25519-dalek",
+ "getrandom",
+ "hkdf",
+ "hmac",
+ "matrix-pickle",
+ "pkcs7",
+ "prost",
+ "rand",
+ "serde",
+ "serde_bytes",
+ "serde_json",
+ "sha2",
+ "subtle",
+ "thiserror",
+ "x25519-dalek",
+ "zeroize",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+
+[[package]]
+name = "wasm-streams"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.70"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.26.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "wildmatch"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result",
+ "windows-strings",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winnow"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "x25519-dalek"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+dependencies = [
+ "curve25519-dalek",
+ "rand_core",
+ "serde",
+ "zeroize",
+]
+
+[[package]]
+name = "x509-cert"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
+dependencies = [
+ "const-oid",
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..754b640
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+name = "baibot"
+description = "A Matrix bot for using diffent capabilities (text-generation, text-to-speech, speech-to-text, image-generation, etc.) of AI / Large Language Models"
+authors = ["Slavi Pantaleev "]
+repository = "https://github.com/etkecc/baibot"
+license = "AGPL-3.0-or-later"
+readme = "README.md"
+keywords = ["matrix", "chat", "LLM", "openai", "anthropic", "localai"]
+exclude = [".editorconfig", "justfile", "docs/", "etc/", "Dockerfile", ".dockerignore", ".gitignore"]
+version = "1.0.0"
+edition = "2021"
+
+[lib]
+name = "baibot"
+path = "src/lib.rs"
+
+[dependencies]
+anthropic-rs = "0.1.*"
+anyhow = "1.0.*"
+async-openai = "0.24.*"
+base64 = "0.22.*"
+# We'd rather not depend on this, but we cannot use the ruma-events EventContent macro without it.
+matrix-sdk = { version = "0.7.1", default-features = false }
+mxidwc = "1.0.*"
+mxlink = "1.0.*"
+openai_api_rust = { git = "https://github.com/etkecc/openai_api_rust.git", branch = "next" }
+quick_cache = "0.6.*"
+regex = "1.10.*"
+serde = { version = "1.0.*", features = ["derive"], default-features = false }
+serde_json = "1.0.*"
+serde_yaml = "0.9.*"
+tempfile = "3.12.*"
+tiktoken-rs = { version = "0.5.*", features = ["async-openai"] }
+tokio = { version = "1.40.*", features = ["rt", "rt-multi-thread", "macros"] }
+tracing = "0.1.*"
+tracing-subscriber = { version = "0.3.*", features = ["env-filter"] }
+url = "2.5.*"
+
+[profile.release]
+strip = true
+opt-level = "z"
+lto = "thin"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7da367c
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,43 @@
+#######################################
+# #
+# Stage 1: building #
+# #
+#######################################
+
+FROM docker.io/rust:1.80.1-slim-bookworm AS build
+
+RUN apt-get update && apt-get install -y build-essential pkg-config libssl-dev libsqlite3-dev
+
+ENV CARGO_HOME=/cargo
+ENV CARGO_TARGET_DIR=/target
+
+WORKDIR /app
+
+COPY . /app
+
+RUN --mount=type=cache,target=/cargo,sharing=locked \
+ --mount=type=cache,target=/target,sharing=locked \
+ cargo build --release
+
+# Move it out of the mounted cache, so we can copy it in the next stage.
+RUN --mount=type=cache,target=/target,sharing=locked \
+ cp /target/release/baibot /baibot
+
+
+#######################################
+# #
+# Stage 2: packaging #
+# #
+#######################################
+
+FROM docker.io/debian:bookworm-slim
+
+RUN apt-get update && apt-get install -y ca-certificates sqlite3
+
+WORKDIR /app
+
+COPY --from=build /baibot .
+
+ENTRYPOINT ["/bin/sh", "-c"]
+
+CMD ["/app/baibot"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0ad25db
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..254ab09
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+
+
+
baibot
+
+
+đ¤ baibot is an [AI](https://en.wikipedia.org/wiki/Artificial_intelligence) ([Large Language Model](https://en.wikipedia.org/wiki/Large_language_model)) bot for [Matrix](https://matrix.org/) built by [etke.cc](https://etke.cc/) (managed Matrix servers).
+
+The name is pronounced 'bye' and is a play on [AI](https://en.wikipedia.org/wiki/Artificial_intelligence), referencing the fictional character [đ§đŦ Bai Ganyo](https://en.wikipedia.org/wiki/Bay_Ganyo).
+
+It's designed as a more private and [featureful](#-features) alternative to [matrix-chatgpt-bot](https://github.com/matrixgpt/matrix-chatgpt-bot).
+It's influenced by [chaz](https://github.com/arcuru/chaz), but does **not** use the [AIChat](https://github.com/sigoden/aichat) CLI tool and instead does everything in-process, without forking.
+
+
+## đ Features
+
+- đ¨ Encourages **[provider](./docs/providers.md) choice** ([Anthropic](./docs/providers.md#anthropic), [Groq](./docs/providers.md#groq), [LocalAI](./docs/providers.md#localai), [OpenAI](./docs/providers.md#openai) and [âī¸ many more](./docs/providers.md#ī¸-providers)) as well as **[mixing & matching models](./docs/features.md#-mixing--matching-models)**:
+
+- Supports **different use purposes** (depending on the [âī¸ provider](./docs/providers.md) & model):
+
+ - [đŦ text-generation](./docs/features.md#-text-generation): communicating with you via text
+ - [đĻģ speech-to-text](./docs/features.md#-speech-to-text): turning your voice messages into text
+ - [đŖī¸ text-to-speech](./docs/features.md#ī¸-text-to-speech): turning bot or users text messages into voice messages
+ - [đī¸ image-generation](./docs/features.md#-image-generation): generating images based on instructions
+
+- đĒ Supports [seamless voice interaction](./docs/features.md#seamless-voice-interaction) (turning user voice messages into text, answering in text, then turning that text back into voice)
+
+- đĻģ Supports [transcribe-only mode](./docs/features.md#transcribe-only-mode) (turning user voice messages into text, without doing text-generation)
+
+- đŖī¸ Supports [text-to-speech-only mode](./docs/features.md#text-to-speech-only-mode) (turning user text messages into voice, without doing text-generation)
+
+- đ Supports [encryption](./docs/features.md#-encryption) for Matrix communication and Account-Data-stored configuration
+
+- âģī¸ Supports [context-management](./docs/configuration/text-generation.md#ī¸-context-management) handling on some models (automatically adjusting the message history length, etc.)
+
+- đ ī¸ Allows **customizing much of the bot's [configuration](./docs/configuration/README.md)** at runtime (using commands sent via chat)
+
+- đĨ **Actively maintained** by the team at [etke.cc](https://etke.cc/)
+
+
+## đŧī¸ Screenshots
+
+
+
+You can find more screenshots on the the [đ Features](./docs/features.md) and other [đ Documentation](./docs/README.md) pages, as well as in the [docs/screenshots](./docs/screenshots) directory.
+
+
+## đ Getting Started
+
+đ˛ For a quick experiment, you can refer to the [đ§âđģ development documentation](./docs/development.md) which contains information on how to build and run the bot (and its various dependency services) locally.
+
+For a real installation, see the [đ Installation](./docs/installation.md) documentation which contains information on [đ Running in a container](./docs/installation.md#-running-in-a-container) and [đĨī¸ī¸ī¸ī¸ī¸ Running a binary](./docs/installation.md#-running-a-binary).
+
+
+## đ Documentation
+
+See the bot's [đ documentation](./docs/README.md) for more information on how to use and configure the bot.
+
+
+## đģ Development
+
+See the bot's [đ§âđģ development documentation](./docs/development.md) for more information on how to develop on the bot.
+
+
+## đ Changes
+
+This bot evolves over time, sometimes with backward-incompatible changes.
+
+When updating the bot, refer to [the changelog](CHANGELOG.md) to catch up with what's new.
+
+
+## đ Support
+
+- Matrix room: [#baibot:etke.cc](https://matrix.to/#/#baibot:etke.cc)
+
+- GitHub issues: [etkecc/baibot/issues](https://github.com/etkecc/baibot/issues)
+
+- (for [etke.cc](https://etke.cc/) customers): etke.cc [support](https://etke.cc/contacts/)
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..a962d09
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,9 @@
+# Table of Contents
+
+- [đ Access](./access.md)
+- [đ¤ Agents](./agents.md) and [âī¸ Providers](./providers.md)
+- [đ ī¸ Configuration](./configuration/README.md)
+- [đ Features](./features.md)
+- [đ Usage](./usage.md)
+- [đ Installation](./installation.md)
+- [đģ Development](./development.md)
diff --git a/docs/access.md b/docs/access.md
new file mode 100644
index 0000000..ab6c03c
--- /dev/null
+++ b/docs/access.md
@@ -0,0 +1,46 @@
+## đ Access
+
+This bot employs access control to decide who can use its services and manage its configuration.
+
+
+### đ Joining rooms
+
+The bot automatically joins rooms when invited by someone considered a bot [user](#-users).
+
+
+### đĨ Users
+
+The bot will ignore messages (and room invitations) from unallowed users.
+
+Users can **use all the bot's [features](./features.md)** ([đŦ Text Generation](./features.md#-text-generation), [đĻģ Speech-to-Text](./features.md#-speech-to-text), etc.), but **cannot manage the bot's configuration**.
+
+The bot can be used by users that match some [dynamically](./configuration/README.md#dynamic-configuration) configured [Matrix user id](https://spec.matrix.org/v1.11/#users) patterns.
+
+The following commands are available:
+- **Show** the currently allowed users: `!bai access users`
+- **Set** the list of allowed users: `!bai access set-users SPACE_SEPARATED_PATTERNS`
+
+Example patterns: `@*:example.com @*:another.com @someone:company.org`
+
+
+### đŽââī¸ Administrators
+
+Administrators can **manage the bot's configuration and access control**.
+
+The bot can be administrated by users that match some [statically](./configuration/README.md#static-configuration) configured [Matrix user id](https://spec.matrix.org/v1.11/#users) patterns.
+
+Administrators cannot be changed without adjusting the bot's configuration on the server.
+
+
+### đŧ Room-local agent managers
+
+Room-local agent managers are users privileged to **create their own [agents](./agents.md)** (see `!bai agent`) in rooms.
+Letting regular users create agents which contact arbitrary network services **may be a security issue**.
+
+No room-local agent manager patterns are configured, so new agents can only be created by administrators.
+
+The following commands are available:
+- **Show** the currently allowed users: `!bai access room-local-agent-managers`
+- **Set** the list of allowed users: `!bai access set-room-local-agent-managers SPACE_SEPARATED_PATTERNS`
+
+Example patterns: `@*:synapse.127.0.0.1.nip.io @*:another.com @someone:company.org`
diff --git a/docs/agents.md b/docs/agents.md
new file mode 100644
index 0000000..6ae1c77
--- /dev/null
+++ b/docs/agents.md
@@ -0,0 +1,61 @@
+## đ¤ Agents
+
+An agent is an instantiation and configuration of some [âī¸ provider](./providers.md).
+It can support different capabilities (text-generation, speech-to-text, etc.) depending on the provider used and on the configuration of the agent.
+
+Agents can be set as **[đ¤ handlers](./configuration/handlers.md) for various purposes** (text-generation, speech-to-text, etc.) globally or in specific rooms. Send a `!bai config status` command to see the current configuration.
+
+Agents can be **defined [statically](./configuration/README.md#static-configuration)** (in the server configuration) **or dynamically** (via commands sent to the bot).
+
+When [creating agents](#creating-agents) dynamically, you can do it **per-room or globally**.
+Globally-defined agents can be used by any authorized bot user in any room, while room-local agents can only be used in the room where they were defined.
+
+Agent configuration (like all other configuration) is stored in the Matrix Account Data of the bot user and is **potentially encrypted** (if enabled in the configuration), so that your configuration data is safe even on untrusted homeservers.
+
+
+### Listing agents
+
+To **list** all available agents: `!bai agent list`
+
+
+#### Creating agents
+
+See a [đŧī¸ Screenshot of the agent creation process](./screenshots/agent-creation.webp).
+
+To **create** a new agent, you need to specify the [provider](./providers.md) and an agent id of your choosing.
+
+- **Create** a new agent:
+ - (Accessible in **this room only**) `!bai agent create-room-local PROVIDER_ID AGENT_ID`
+ - (Accessible in **all rooms**) `!bai agent create-global PROVIDER AGENT_ID`
+ - Example: `!bai agent create-room-local openai my-openai-agent`
+
+The `AGENT_ID` is a unique identifier for the agent. It can be any string which **doesn't contain spaces and `/`**.
+
+Depending on where the agent is defined (within a room, globally, or [statically](./configuration/README.md#static-configuration)), this id will get a prefix (e.g. `room-local/`, `global/` or `static/`). The combined id (prefix + agent id) makes the **full agent identifier** (refered to as `FULL_AGENT_IDENTIFIER` in commands below).
+
+When creating an agent, you will be given some sample [YAML](https://en.wikipedia.org/wiki/YAML) configuration which you can use to customize the agent's behavior.
+
+This configuration varies depending on the [âī¸ provider](./providers.md) used and the capabilities of the agent. Based on the configuration keys you pass, certain features will be enabled or disabled. For example, if you skip the `image_generation` key for an [OpenAI](./providers.md#openai) agent, it won't be able to generate images (see [đī¸ Image Generation](./features.md#-image-generation)).
+
+After making your modifications to the sample YAML, you submit it back to the bot and the new agent will be created.
+
+**To make use of the agent**, you need to [đ¤ configure it as a handler for a given purpose](./configuration/handlers.md).
+
+
+### Showing agent details
+
+To **show** full details for a given agent: `!bai agent details FULL_AGENT_IDENTIFIER`
+
+This command requires a full agent identifier (e.g. `room-local/agent-id`).
+
+
+### Deleting agents
+
+To **delete** an agent: `!bai agent delete FULL_AGENT_IDENTIFIER`
+
+This command requires a full agent identifier (e.g. `room-local/agent-id`).
+
+
+### Updating agents
+
+To **update** a given agent's configuration: show the agent's [details](#showing-agent-details) (current configuration), then [delete](#deleting-agents) it and finally [re-create](#creating-agents) it.
diff --git a/docs/configuration/README.md b/docs/configuration/README.md
new file mode 100644
index 0000000..0ca2555
--- /dev/null
+++ b/docs/configuration/README.md
@@ -0,0 +1,48 @@
+## đ ī¸ Configuration
+
+The bot's behavior is controlled by a combination of [static](#static-configuration) and [dynamic](#dynamic-configuration) configuration.
+
+
+### Static configuration
+
+The bot can be configured using a [YAML](https://en.wikipedia.org/wiki/YAML) configuration file as well as [environment variables](https://en.wikipedia.org/wiki/Environment_variable).
+
+When running the bot locally (during [đ§âđģ development](../development.md)), the bot's configuration is read from the `var/app/config.yml` file.
+This file is created from the template found in [etc/app/config.yml.dist](../../etc/app/config.yml.dist).
+
+Certain keys can be left unset, in which case [đ hardcoded defaults](../../src/entity/cfg/defaults.rs) would be used.
+
+Each configuration key found in the YAML configuration can be overridden by setting an environment variable (dots should be replaced with `_`). Example:
+
+- to override `command_prefix`, set an environment variable `BAIBOT_COMMAND_PREFIX`
+- to override `homeserver.server_name`, set an environment variable `BAIBOT_HOMESERVER_SERVER_NAME`
+
+The static configuration contains an `initial_global_config` key, which is used to populate the bot's global configuration (stored as [dynamic configuration](#dynamic-configuration)) the first time the bot starts. Modifying this subsequently will not have any effect. After initial global configuration creation, it's expected to be managed dynamically via chat commands.
+
+
+### Dynamic configuration
+
+Besides the bot's [static configuration](#static-configuration), **the bot can also be configured dynamically at runtime (via chat messages)**.
+
+This includes changes to [đ Access](../access.md), [đ¤ Agents](../agents.md) and [đ ī¸ Room Settings](#room-settings).
+
+
+#### Room Settings
+
+Room Settings come from 3 different levels with priority in the following order (higher to lower):
+
+- đ per-room (`!bai config room ..` commands)
+- đ globally (`!bai config global ..` commands)
+- đ as [hardcoded defaults](../../src/entity/cfg/defaults.rs)
+
+You can adjust the following settings per room and/or globally:
+
+- [đŦ Text Generation](text-generation.md)
+- [đĻģ Speech-to-Text](speech-to-text.md)
+- [đŖī¸ Text-to-Speech](text-to-speech.md)
+- [đī¸ Image Generation](image-generation.md)
+- [đ¤ Handlers](handlers.md)
+
+Refer to the bot's help messages (as a response to a `!bai config` help command) for the most up-to-date information on what Room Settings can be configured.
+
+You can **get an overview of the configuration affecting the current room** (a mix of hardcoded defaults, agent defaults, global and room-level settings) by sending a `!bai config status` command to the room.
diff --git a/docs/configuration/handlers.md b/docs/configuration/handlers.md
new file mode 100644
index 0000000..cddc6fa
--- /dev/null
+++ b/docs/configuration/handlers.md
@@ -0,0 +1,32 @@
+## đ¤ Handlers
+
+### Introduction
+
+You can use **different models in different rooms** (e.g. [OpenAI](../providers.md#openai) GPT-4o alongside [Llama](https://en.wikipedia.org/wiki/Llama_(language_model)) running on [Groq](../providers.md#groq), etc.)
+
+You can also use **different models within the same room** (e.g. [đŦ text-generation](#-text-generation) handled by one [agent](./agents.md), [đĻģ speech-to-text](#-speech-to-text) handled by another, [đŖī¸ text-to-speech](#ī¸-text-to-speech) by a 3rd, etc.)
+
+The bot supports the following use-purposes:
+
+- [đŦ text-generation](../features.md#-text-generation): communicating with you via text
+- [đĻģ speech-to-text](../features.md#-speech-to-text): turning your voice messages into text
+- [đŖī¸ text-to-speech](../features.md#ī¸-text-to-speech): turning bot or users text messages into voice messages
+- [đī¸ image-generation](../features.md#-image-generation): generating images based on instructions
+
+In a given room, each different purpose can be served by a different [provider](../providers.md) and model. This combination of provider and model configuration is called an [đ¤ agent](../agents.md). Each purpose can be served by a different **handler** agent.
+
+See a [đŧī¸ Screenshot of an example room configuration](./screenshots/config-status-handlers.webp).
+
+
+### Configuring
+
+Handlers can be configured [dynamically](./README.md#dynamic-configuration):
+
+- either per-room (e.g. `!bai config room set-handler text-generation room-local/openai-gpt-4o`)
+- or globally (e.g. `!bai config global set-handler text-generation global/openai-gpt-4o`)
+
+The per-room configuration takes priority over the global configuration.
+
+There's also a `catch-all` purpose that can be used as a fallback handler for messages that don't match any other handler.
+
+đĄ It's a good idea to globally-configure a powerful agent as a catch-all handler, so that the bot can always handle messages of any kind. You can then override individual handlers per room or globally.
diff --git a/docs/configuration/image-generation.md b/docs/configuration/image-generation.md
new file mode 100644
index 0000000..a3237ab
--- /dev/null
+++ b/docs/configuration/image-generation.md
@@ -0,0 +1,9 @@
+
+## đī¸ Image Generation
+
+The Image Generation feature is not configurable at this moment.
+
+You may also wish to see:
+
+- [đ Features / đī¸ Image Generation](../features.md#-image-generation) for a higher-level introduction to the Image Generation features
+- [đ Usage / đī¸ Image Generation](../usage.md#-image-generation) section for more details on how to use the bot for Image Generation in a room
diff --git a/docs/configuration/speech-to-text.md b/docs/configuration/speech-to-text.md
new file mode 100644
index 0000000..18a1d36
--- /dev/null
+++ b/docs/configuration/speech-to-text.md
@@ -0,0 +1,39 @@
+## đĻģ Speech-to-Text
+
+Below are some configuration settings related to Speech-to-Text.
+
+You may also wish to see:
+
+- [đ Features / đĻģ Speech-to-Text](../features.md#-speech-to-text) for a higher-level introduction to the Speech-to-Text features
+- [đ Usage / đĻģ Speech-to-Text](../usage.md#-speech-to-text) section for more details on how to use the bot for Speech-to-Text in a room
+
+
+### đĒ Flow Type
+
+Controls how voice messages sent by [đĨ user](../access.md#-users) are handled.
+
+The following configuration values are recognized:
+
+- (default) `transcribe_and_generate_text`: the bot will turn [đĨ user](../access.md#-users) voice messages into text and then generate text messages via [đŦ Text Generation](../features.md#-text-generation). This is the default setting to allow for [Seamless voice interaction](../features.md#seamless-voice-interaction).
+
+- `ignore`: the bot will ignore all audio messages
+
+- `only_transcribe`: the bot will turn [đĨ user](../access.md#-users) voice messages into text, but will **not** proceed with [đŦ Text Generation](../features.md#-text-generation). Switching to this may be useful in some cases, as in [Transcribe-only mode](../features.md#transcribe-only-mode).
+
+Example: `!bai config room speech-to-text set-flow-type ignore` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+
+### đ¤ Language
+
+Lets you specify the language of the input voice messages, to avoid using auto-detection.
+Supplying the input language using a 2-letter code (e.g. `ja`) as per [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) may improve accuracy & latency.
+
+
+
+In the above example screenshot, even without a language specified, the voice was understood correctly as [Bulgarian](https://en.wikipedia.org/wiki/Bulgarian_language), but was produced in latin, not [Cyrillic](https://en.wikipedia.org/wiki/Cyrillic_script), which is wrong.
+
+If different [đĨ user](../access.md#-users) are using different languages, do not specify a language.
+
+đĄ Certain models (like [OpenAI](../providers.md#openai)'s Whisper) may perform auto-translation if you specify a language, but you're speaking another one. You may abuse this side-effect for performing voice-to-text translation, but be aware that not all models behave this way.
+
+Example (setting it to Japanese): `!bai config room speech-to-text set-language ja` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
diff --git a/docs/configuration/text-generation.md b/docs/configuration/text-generation.md
new file mode 100644
index 0000000..3ec1566
--- /dev/null
+++ b/docs/configuration/text-generation.md
@@ -0,0 +1,75 @@
+
+## đŦ Text Generation
+
+Below are some [đ ī¸ dynamic configuration settings](./README.md#dynamic-configuration) related to Text Generation.
+
+You may also wish to see:
+
+- [đ Features / đŦ Text Generation](../features.md#-text-generation) for a higher-level introduction to the Text Generation features
+- [đ Usage / đŦ Text Generation](../usage.md#-text-generation) section for more details on how to use the bot for Text Generation in a room
+
+
+### đ Prefix Requirement Type
+
+In Direct Message rooms with the bot (1:1 rooms), it most usually makes sense for the bot to respond to **all** of your messages, as shown on this [đŧī¸ screenshot](../screenshots/text-generation.webp).
+
+In group rooms (with multiple users), it may be more appropriate for the bot to only respond to messages that are **prefixed** with the command prefix (e.g. `!bai`), so that other chat exchange in the room will not trigger it. Such a setup is shown on this [đŧī¸ screenshot](../screenshots/text-generation-prefix-requirement.webp).
+
+There are exceptions to these rules, and you can configure the bot to respond only to prefixed messages in a 1:1 room, or to respond to all messages even in a multi-user group room.
+
+To support such use-cases, the bot has a `text-generation prefix-requirement-type` setting, which can be set to:
+
+- (default) `no`: indicates that the bot would not require a prefix and would respond to all messages
+
+- `command_prefix`: indicates that the bot would require that messages be prefixed with the command prefix (e.g. `!bai`) and would ignore all messages that are not prefixed
+
+By default, the bot is **auto-configured (upon joining a new room)** to use the `no` setting in rooms that only include 2 users (you and the bot), and `command_prefix` in rooms with more than 2 users. To prevent surprises, the bot will **not** adjust this setting subsequently. You can manually adjust it via `!bai config room set-prefix-requirement-type VALUE`.
+
+Example: `!bai config room text-generation set-prefix-requirement-type command_prefix` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+Regardless of this configuration, **the bot will also respond to messages which directly [mention](https://spec.matrix.org/latest/client-server-api/#user-and-room-mentions) the bot** (e.g. `@baibot`), even if they are not prefixed. An example of this can be seen on this [đŧī¸ screenshot](../screenshots/text-generation-prefix-requirement.webp).
+
+
+### đĒ Auto Usage
+
+Text generation is enabled by default (the `text-generation auto-usage` setting being set to `always`), but can be set to:
+
+- (default) `always`: generate text for all messages (also see [đ Prefix Requirement Type](#-prefix-requirement-type))
+
+- `never`: never generate text for messages
+
+- `only_for_voice`: only generate text when the original user message was a voice message, later transcribed via [đĻģ Speech-to-Text](../features.md#-speech-to-text)
+
+- `only_for_text`: only generate text when original user message was a text message
+
+Example: `!bai config room text-generation set-auto-usage only_for_voice` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+
+### âģī¸ Context Management
+
+The bot also supports âģī¸ **context management**, which automatically adjusts the message history length, etc.
+
+This feature relies on [tokenization](https://en.wikipedia.org/wiki/Large_language_model#Tokenization) performed by the [tiktoken-rs](https://github.com/zurawiki/tiktoken-rs) library which is [poorly well-maintained](https://github.com/zurawiki/tiktoken-rs/issues/50) and only works well for [OpenAI](../providers.md#openai) models.
+
+This setting is **disabled by default**, but can be enabled via `!bai config room context-management-enabled true` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings)).
+
+
+### â¨ī¸ Prompt Override
+
+You can override the [system prompt](https://huggingface.co/docs/transformers/en/tasks/prompting) configured at the [đ¤ agent](../agents.md) level.
+
+Example (multi-line is supported):
+
+```
+!bai config room text-generation set-prompt-override You're a UI/UX expert. Everything you say needs to consider design and usability.
+
+Where appropriate, you'll mention best practices and common pitfalls.
+```
+
+A prompt override can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings).
+
+### đĄī¸ Temperature Override
+
+You can override the [temperature](https://blogs.novita.ai/what-are-large-language-model-settings-temperature-top-p-and-max-tokens/#what-is-llm-temperature) (randomness / creativity) parameter configured at the [đ¤ agent](../agents.md) level.
+
+Example: `!bai config room text-generation set-temperature-override 3.5` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
diff --git a/docs/configuration/text-to-speech.md b/docs/configuration/text-to-speech.md
new file mode 100644
index 0000000..b794d5a
--- /dev/null
+++ b/docs/configuration/text-to-speech.md
@@ -0,0 +1,63 @@
+
+## đŖī¸ Text-to-Speech
+
+Below are some configuration settings related to Text-to-Speech.
+
+You may also wish to see:
+
+- [đ Features / đŖī¸ Text-to-Speech](../features.md#-text-generation) for a higher-level introduction to the Text-to-Speech features
+- [đ Usage / đŖī¸ Text-to-Speech](../usage.md#-text-generation) section for more details on how to use the bot for Text-to-Speech in a room
+
+
+### đĒ Bot Messages Flow Type
+
+Controls how automatic text-to-speech functions for **messages sent by the bot**.
+
+The following configuration values are recognized:
+
+- (default) `on_demand_for_voice`: the bot will turn its own text messages into audio (voice) messages only after an allowed [đĨ user](../access.md#-users) **reacts** to a bot's message with đŖī¸. To make it easier for users to react without having to hunt for this emoji, the bot will automatically add a đŖī¸ reaction to its own messages which are in response to a user audio (voice) message.
+
+- `on_demand_always`: the bot will turn its own text messages into audio (voice) messages only after an allowed [đĨ user](../access.md#-users) **reacts** to a bot's message with đŖī¸. To make it easier for users to react without having to hunt for this emoji, the bot will automatically add a đŖī¸ reaction to **all of its own messages**.
+
+- `only_for_voice`: the bot will turn its own text messages into audio (voice) messages only if the original user message was a voice message. This is to allow for [Seamless voice interaction](../features.md#seamless-voice-interaction), where you can speak to the bot and then hear its responses
+
+- `never`: the bot will never turn its own text messags into audio (voice) messages
+
+- `always`: the bot will turn all its text messages into audio (voice) messages. This also allows for [Seamless voice interaction](../features.md#seamless-voice-interaction).
+
+Example: `!bai config room text-to-speech set-bot-msgs-flow-type never` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+
+### đĒ User Messages Flow Type
+
+Controls how automatic text-to-speech functions for **messages sent by [đĨ users](../access.md#-users)**.
+
+**Only works when automatic text-generation is disabled** (see [đŦ Text Generation / đĒ Auto Usage](./text-generation.md#-auto-usage)).
+
+The following configuration values are recognized:
+
+- (default) `never`: the bot will never turn [đĨ user](../access.md#-users) text messages into audio (voice) messages
+
+- `on_demand`: the bot will turn [đĨ user](../access.md#-users) text messages into audio (voice) messages if the text message receives a đŖī¸ reaction
+
+- `always`: the bot will turn all [đĨ user](../access.md#-users) text messages into audio (voice) messages. This is to allow for [Text-to-Speech-only mode](../features.md/#text-to-speech-only-mode).
+
+Example: `!bai config room text-to-speech set-user-msgs-flow-type always` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+
+### đ˛ Speed override
+
+The speed override setting lets you speed up/down speech relative to the default speed configured at the [đ¤ agent](../agents.md) level (usually `1.0`).
+
+Values typically range from `0.25` to `4.0`, but may vary depending on the selected model.
+
+Example: `!bai config room text-to-speech set-speed-override 1.5` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
+
+
+### đĢ Voice override
+
+The voice override setting lets you change the voice being used by the text-to-speech model configured at the [đ¤ agent](../agents.md) level (usually `onyx` when using [OpenAI](../providers.md#openai)).
+
+Possible values (e.g. `onyx`) depend on the model you're using. For example, for [OpenAI](../providers.md#openai)'s Whisper model, [these voices](https://platform.openai.com/docs/guides/text-to-speech/voice-options) are available.
+
+Example: `!bai config room text-to-speech set-voice-override nova` (this can also be set globally, see [đ ī¸ Room Settings](./README.md#room-settings))
diff --git a/docs/development.md b/docs/development.md
new file mode 100644
index 0000000..4a9fe50
--- /dev/null
+++ b/docs/development.md
@@ -0,0 +1,134 @@
+## đ§âđģ Development
+
+This documentation page contains information about **running the bot locally for development purposes**.
+This can also **helpful for quickly testing the bot in a containerized environment, with all dependency services included**.
+
+For running the bot against your Matrix server, see the [đ Installation](./installation.md) documentation.
+
+This bot is built in [đĻ Rust](https://www.rust-lang.org/) and uses the [mxlink](https://github.com/etkecc/rust-mxlink) library (built on top of [matrix-rust-sdk](https://github.com/matrix-org/matrix-rust-sdk)).
+
+For local development, we run all dependency services in [đ Docker](https://www.docker.com/) containers via [docker-compose](https://docs.docker.com/compose/).
+
+
+### Prerequisites
+
+- [đ Docker](https://www.docker.com/) and [docker-compose](https://docs.docker.com/compose/)
+- [Just](https://github.com/casey/just)
+- (Optional) [đĻ Rust](https://www.rust-lang.org/) - for compiling and running outside of a container
+- (Optional) an API key for some Large Language Model [âī¸ provider](./providers.md) (e.g. [OpenAI](./providers.md#openai)), though we recommend using [LocalAI](#localai) or [Ollama](#ollama) for local development
+
+
+### Getting started guide
+
+Developing [locally](#running-locally) is possible, but requires a [Rust](https://www.rust-lang.org/) toolchain.
+If this dependency is problematic for you, consider [đ running in a container](#running-in-a-container).
+
+In any case, you will need [đ Docker](https://www.docker.com/) as [dependency services](../etc/services/) run there.
+
+
+#### Running locally
+
+1. Start the core dependency services (Postgres, Synapse, Element Web): `just services-start`
+2. (Only the first time around) Prepare initial app configuration in `var/app/local/config.yml`: `just app-local-prepare`
+3. (Only the first time around) [Prepare your configuration file](#prepare-your-configuration-file)
+4. (Only the first time around) Prepare initial default Matrix user accounts (`admin` and `baibot`): `just users-prepare`
+5. (Optional) Start additional services depending on which [agent provider you've chosen](#choosing-an-agent-provider):
+ - for [LocalAI](#localai):
+ - Start services: `just localai-start`
+ - Wait a while for LocalAI to start up. It has a lot of models to download. Monitor progress using `just localai-tail-logs`
+ - When ready, you'll be able to reach LocalAI's web interface at http://localai.127.0.0.1.nip.io:42027/ (not that you really need it)
+ - for [Ollama](#ollama):
+ - Start services: `just ollama-start`
+ - (Only the first time around) Pull the model configured in `agents.static_definitions` in the configuration file: `just ollama-pull-model gemma2:2b`
+6. Start the bot: `just run-locally`
+7. Go to http://element.127.0.0.1.nip.io:42025/ and login with `admin` / `admin`
+8. Create a new room and invite `@baibot:synapse.127.0.0.1.nip.io`
+9. When done, stop the bot (`Ctrl` + `C`)
+10. Stop the core dependency services: `just services-stop`
+11. (Optional) Stop additional services:
+ - for [LocalAI](#localai): `just localai-stop`
+ - for [Ollama](#ollama): `just ollama-stop`
+
+
+#### Running in a container
+
+You can avoid having a [Rust](https://www.rust-lang.org/) toolchain installed locally and build/run this in a container.
+
+1. Start the core dependency services (Postgres, Synapse, Element Web): `just services-start`
+2. (Only the first time around) Prepare initial app configuration in `var/app/container/config.yml`: `just app-container-prepare`
+3. (Only the first time around) [Prepare your configuration file](#prepare-your-configuration-file)
+4. (Only the first time around) Prepare initial default Matrix user accounts (`admin` and `baibot`): `just users-prepare`
+5. (Optional) Start additional services depending on which [agent provider you've chosen](#choosing-an-agent-provider):
+ - for [LocalAI](#localai):
+ - Start services: `just localai-start`
+ - Wait a while for LocalAI to start up. It has a lot of models to download. Monitor progress using `just localai-tail-logs`
+ - When ready, you'll be able to reach LocalAI's web interface at http://localai.127.0.0.1.nip.io:42027/ (not that you really need it)
+ - for [Ollama](#ollama):
+ - Start services: `just ollama-start`
+ - (Only the first time around) Pull the model configured in `agents.static_definitions` in the configuration file: `just ollama-pull-model gemma2:2b`
+6. Start the bot: `just run-in-container`
+7. Go to http://element.127.0.0.1.nip.io:42025/ and login with `admin` / `admin`
+8. Create a new room and invite `@baibot:synapse.127.0.0.1.nip.io`
+9. When done, stop the bot (`Ctrl` + `C`)
+10. Stop the dependency services: `just services-stop`
+11. (Optional) Stop additional services:
+ - for [LocalAI](#localai): `just localai-stop`
+ - for [Ollama](#ollama): `just ollama-stop`
+
+
+#### Prepare your configuration file
+
+This is about editing your configuration. The initial configuration is created based on `etc/app/config.yml.dist` when you run `just app-local-prepare` or `just app-container-prepare`.
+
+Depending on whether you run locally or in a container, your configuration lives in a different file (`var/app/local/config.yml` and `var/app/container/config.yml`, respectively).
+
+Before starting the bot, you may wish to adjust this configuration.
+
+
+##### Choosing an agent provider
+
+You can create [đ¤ agents](./agents.md) either [statically](./configuration/README.md#static-configuration) or [dynamically](./configuration/README.md#dynamic-configuration) using any of the supported [âī¸ providers](./providers.md).
+
+For getting started most quickly (and locally), we recommend using [LocalAI](#localai) or [Ollama](#ollama). These services are already configured to run as [local services via docker-compose](../etc/services/).
+
+**Ollama is most lightweight** (~2GB for the container image + ~1.6GB for the model), but supports only [đŦ text-generation](./features.md#-text-generation).
+
+**LocalAI requires 4x more disk space** (~6GB for the container image + ~12GB for the models), but supports [đŦ text-generation](./features.md#-text-generation), [đŖī¸ text-to-speech](./features.md#ī¸-text-to-speech), [đĻģ speech-to-text](./features.md#-speech-to-text) and [đŧī¸ image-generation](./features.md#ī¸-image-generation).
+
+**OpenAI supports all of these capabilities** as well and does not require powerful hardware or lots of disk space. However, it requires signup and an API key.
+
+For local testing, **we recommend LocalAI**, because it runs fully locally and supports more features than Ollama.
+
+###### LocalAI
+
+[LocalAI](./providers.md#localai) supports all [đ features](./features.md) of the bot.
+
+If you decided to go with [LocalAI](./providers.md#localai):
+
+- enable the `localai` entry in the `agents.static_definitions` list in the configuration file
+- adjust the `initial_global_config.handler.catch_all` setting in the configuration file (`null` -> `static/localai`)
+
+By default, we configure LocalAI to use the [All-In-One images](https://localai.io/basics/container/#all-in-one-images) running on the CPU.
+Performance is not great, but it should work reasonably well on good hardware.
+
+If you'd like to use GPU acceleration, you may adjust the `SERVICE_LOCALAI_IMAGE_NAME` variable in [var/services/env](../var/services/env) (this file is automatically prepared for you based on [etc/services/env.dist](../etc/services/env.dist)) to use [other available LocalAI All-In-One images](https://localai.io/basics/container/#available-aio-images).
+
+###### Ollama
+
+[Ollama](./providers.md#ollama) only supports [đŦ text-generation](./features.md#-text-generation).
+
+If you decided to go with [Ollama](./providers.md#ollama):
+
+- enable the `ollama` entry in the `agents.static_definitions` list in the configuration file
+- adjust the `initial_global_config.handler.catch_all` setting in the configuration file (`null` -> `static/ollama`)
+
+The [gemma2:2b](https://ollama.com/library/gemma2:2b) model was chosen as a default, because it's smallest/lightest and should run well under [Ollama](./providers.md#ollama) on most machines.
+
+###### OpenAI
+
+[OpenAI](./providers.md#openai) supports all [đ features](./features.md) of the bot.
+
+If you decided to go with [OpenAI](./providers.md#openai):
+
+- enable the `openai` entry in the `agents.static_definitions` list in the configuration file
+- adjust the `initial_global_config.handler.catch_all` setting in the configuration file (`null` -> `static/openai`)
diff --git a/docs/features.md b/docs/features.md
new file mode 100644
index 0000000..f39ca39
--- /dev/null
+++ b/docs/features.md
@@ -0,0 +1,150 @@
+## đ Features
+
+### đ¨ Mixing & matching models
+
+You can use **different models in different rooms** (e.g. [OpenAI](./providers.md#openai) GPT-4o alongside [Llama](https://en.wikipedia.org/wiki/Llama_(language_model)) running on [Groq](./providers.md#groq), etc.)
+
+You can also use **different models within the same room** (e.g. [đŦ text-generation](#-text-generation) handled by one [đ¤ agent](./agents.md), [đĻģ speech-to-text](#-speech-to-text) handled by another, [đŖī¸ text-to-speech](#ī¸-text-to-speech) by a 3rd, etc.)
+
+The bot supports the following use-purposes:
+
+- [đŦ text-generation](#-text-generation): communicating with you via text
+- [đĻģ speech-to-text](#-speech-to-text): turning your voice messages into text
+- [đŖī¸ text-to-speech](#ī¸-text-to-speech): turning bot or users text messages into voice messages
+- [đī¸ image-generation](#-image-generation): generating images based on instructions
+
+In a given room, each different purpose can be served by a different [âī¸ provider](./providers.md) and model. This combination of provider and model configuration is called an [đ¤ agent](./agents.md). Each purpose can be served by a different **handler** agent.
+
+See a [đŧī¸ Screenshot of an example room configuration](./screenshots/config-status-handlers.webp).
+
+For more information about configuring handlers, see the [đ¤ Handlers / Configuring](./configuration/handlers.md#configuring) documentation section.
+
+
+### đŦ Text Generation
+
+Text Generation is the bot's ability to **respond to users' text messages with text**.
+
+
+
+In multi-user (group) rooms, to avoid disturbing the normal conversation between people, the bot is auto-configured to only respond to messages starting with the command prefix (`!bai`) or direct mentions via the [đŦ Text Generation / đ Prefix Requirement Type](./configuration/text-generation.md#-prefix-requirement-type) setting.
+
+A few other features (like [đŖī¸ Text-to-Speech](#ī¸-text-to-speech) and [đĻģ Speech-to-Text](#-speech-to-text)) combine well with Text Generation, so you **don't necessarily need to communicate with the bot via text** (with [Seamless voice interaction](#seamless-voice-interaction), you can communicate only with voice).
+
+You may also wish to see:
+
+- [đ ī¸ Configuration / đŦ Text Generation](./configuration/README.md#-text-generation) for configuration options related to Text Generation
+- [đ Usage / đŦ Text Generation](./usage.md#-text-generation) section for more details on how to use the bot for Text Generation in a room
+
+
+### đŖī¸ Text-to-Speech
+
+Text-to-Speech is the bot's ability to **turn text messages into voice messages**.
+
+It can be performed **on the bot's own text messages** (responses to yours due to [đŦ Text Generation](#-text-generation)) and/or **on your own text messages**.
+
+Text-to-Speech can be enabled to be done automatically or on-demand (only after reacting to a message with đŖī¸), and is configurable for different message types ([đĒ Bot Messages Flow Type](./configuration/README.md#-bot-messages-flow-type) vs [đĒ User Messages Flow Type](./configuration/README.md#-user-messages-flow-type)).
+
+By default, the bot **doesn't** perform text-to-speech. It can be configured for [Seamless voice interaction](#seamless-voice-interaction), where you can **speak to the bot** (instead of typing) and then **hear its responses**.
+
+Another use-case is to have the bot operate in [Text-to-Speech-only mode](#text-to-speech-only-mode).
+
+- [đ ī¸ Configuration / đŖī¸ Text-to-Speech](./configuration/README.md#-text-to-speech) for configuration options related to Text-to-Speech
+- [đ Usage / đŖī¸ Text-to-Speech](./usage.md#-text-to-speech) section for more details on how to use the bot for Text-to-Speech in a room
+
+
+#### Text-to-Speech-only mode
+
+You may wish to have the bot **automatically turn your text messages into voice messages**, but **without** doing [đŦ Text Generation](#-text-generation).
+
+
+
+This could be useful in a room with others, where you'd like to post text messages and have people in the room consume them more easily (by listening to audio).
+
+To allow for this use-case, you can:
+
+- disable [đŦ Text Generation](#-text-generation) (via [đŦ Text Generation / đĒ Auto Usage](./configuration/text-generation.md#-auto-usage) setting): `!bai config room text-generation set-auto-usage never`
+
+- enable [đŖī¸ Text-to-Speech](#ī¸-text-to-speech) for user messages (via [đŖī¸ Text-to-Speech / đĒ User Messages Flow Type](./configuration/text-to-speech.md#-user-messages-flow-type)): `!bai config room text-to-speech set-user-msgs-flow-type always` (or `on_demand`)
+
+
+### đĻģ Speech-to-Text
+
+Speech-to-Text is the bot's ability to **turn voice messages into text**.
+
+
+
+The default flow is shown in the screenshot above: your voice messages are transcribed to text and [đŦ Text Generation](#-text-generation) is performed. By default, the bot offers [đŖī¸ Text-to-Speech](#ī¸-text-to-speech) for its answers via a đŖī¸ emoji. You can click it to trigger text-to-speech on-demand.
+
+You may also configure the bot for [Seamless voice interaction](#seamless-voice-interaction) or [Transcribe-only mode](#transcribe-only-mode), etc.
+
+
+#### Seamless voice interaction
+
+The bot can perform seamless voice interaction (đŖī¸-to-đŖī¸), allowing you to **speak to the bot** (instead of typing) and then **hear its responses**.
+
+
+
+The flow is like this:
+
+1. đ¤ You sending a voice message
+2. đ¤ The bot:
+ - (default) first turning your **voice message into text** ([đĻģ Speech-to-Text](#-speech-to-text)) and posting it as a reply. This lets you you see what the bot heard.
+ - (default) then **answering in text** ([đŦ Text Generation](#-text-generation)). This lets you read/skim text, if you so prefer.
+ - (can be enabled) finally **turning the answer's text into a voice message** ([đŖī¸ Text-to-Speech](#ī¸-text-to-speech))
+3. đ¤ You continuing the conversation via text or voice messages
+
+â ī¸ Certain clients (like [Element](https://element.io/)) only support sending voice messages as top-level room messages, not as thread replies. Until this client limitation is fixed, Element users can only send the 1st message as a voice message - subsequent replies in the same conversation thread will need to be sent as text messages.
+
+By default, the last part of the aforementioned flow is **not enabled**, because we assume **a saner default is to reply with text and merely *offer* text-to-speech to those who want it**. Offering is done by the bot reacting to its own message with đŖī¸, and letting you click this emoji to trigger text-to-speech on-demand.
+
+To enable automatic text-to-speech for the bot's messages, set the [đŖī¸ Text-to-Speech / đĒ Bot Messages Flow Type](./configuration/text-to-speech.md#-bot-messages-flow-type) setting to `only_for_voice` or `always` (e.g. `!bai config room text-to-speech set-bot-msgs-flow-type only_for_voice`).
+
+
+#### Transcribe-only mode
+
+If you'd like to have the bot **only turn voice messages into text** (without generating text messages or voice messages), you can configure the bot for that.
+
+
+
+To operate in this mode, you can:
+
+- disable [đŦ Text Generation](#-text-generation) (via [đŦ Text Generation / đĒ Auto Usage](./configuration/text-generation.md#-auto-usage) setting): `!bai config room text-generation set-auto-usage never`
+
+- adjust the [đĻģ Speech-to-Text / đĒ Flow Type](./configuration/speech-to-text.md#-flow-type) setting to make the bot only transcribe (without doing [đŦ Text Generation](#-text-generation)): `!bai config room speech-to-text set-flow-type only_transcribe`
+
+
+### đī¸ Image Generation
+
+Image generation is the bot's ability to **generate images** based on text prompts.
+
+See a [đŧī¸ Screenshot of the Image Generation feature](./screenshots/image-generation.webp).
+
+You may also wish to see:
+
+- [đ ī¸ Configuration / đī¸ Image Generation](./configuration/README.md#-image-generation) for configuration options related to Image Generation
+- [đ Usage / đī¸ Image Generation](./usage.md#-image-generation) section for more details on how to use the bot for Image Generation in a room
+- [đĢĩ Sticker Generation](#-sticker-generation) - a special case of Image Generation
+
+
+### đĢĩ Sticker Generation
+
+Sticker generation is the bot's ability to **generate sticker** images based on text prompts. It's a special case of [đī¸ Image Generation](#ī¸-image-generation).
+
+See a [đŧī¸ Screenshot of the Sticker Generation feature](./screenshots/sticker-generation.webp).
+
+See [đ Usage / đī¸ Image Generation / Generating Stickers](./usage.md#generating-stickers) for details.
+
+
+### đ Encryption
+
+#### Message exchange
+
+The bot works in both **unencrypted and encrypted Matrix rooms**.
+
+If configured, the bot can make use of **Matrix's Secure Storage (Recovery) feature**, so that it can restore its encryption keys even its local database gets lost.
+
+#### Configuration
+
+The bot also stores its [đ ī¸ configuration](./configuration/README.md) (both đ per-room and đglobally) in Matrix Account Data, which is **generally stored as plain-text in the server**.
+
+To overcome this Matrix limitation, the bot can **optionally encrypt the configuration data** before storing it in Account Data. This allows for the bot to be used securely even against untrusted servers, without leaking sensitive configuration data to them.
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000..3ec4070
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,93 @@
+## đ Installation
+
+âī¸ The easiest way to use the bot is to **get a managed Matrix server from [etke.cc](https://etke.cc/)** and order baibot via the [order form](https://etke.cc/order/). Existing customers can request the inclusion of this additional service by [contacting support](https://etke.cc/contacts/).
+
+đģ If you're managing your Matrix server with the help of the [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy) Ansible playbook, you can easily **install the bot via the Ansible playbook**. See the playbook's [Setting up baibot](https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-playbook-bot-baibot.md) documentation page.
+
+đ In other cases, we **recommend using our [prebuilt container images](https://github.com/etkecc/baibot/pkgs/container/baibot) and [running in a container](#-running-in-a-container)**. You can also [build a container image](#building-a-container-image) yourself.
+
+đ¨ If containers are not your thing, you can [build a binary](#-building-a-binary) yourself and [run it](#-running-a-binary).
+
+đ˛ For a quick experiment, you can refer to the [đ§âđģ development documentation](./development.md) which contains information on how to build and run the bot (and its various dependency services) locally.
+
+
+### đ Building a container image
+
+We provide prebuilt container images for the `amd64` and `arm64` architectures, so **you don't necessarily need to build images yourself** and can jump to [Running in a container](#-running-in-a-container).
+
+If you nevertheless wish to build a container image yourself, you can do so by running `just build-container-image`.
+This will build and tag your container image as `localhost/baibot:latest`.
+
+
+### đ Running in a container
+
+We recommend using a **tagged-release** (e.g. `v1.0.0`, not `latest`) of our [prebuilt container images](https://github.com/etkecc/baibot/pkgs/container/baibot), but you can also [build a container image](#-building-a-container-image) yourself.
+
+You should:
+
+- [đ ī¸ prepare a configuration file](#-preparing-a-configuration-file) (e.g. `cp etc/app/config.yml.dist /path/to/config.yml` & edit it)
+- prepare a data directory (`mkdir /path/to/data`)
+
+The example below uses [đ Docker](https://www.docker.com/) to run the container, but other container runtimes like [Podman](https://podman.io/) should work as well.
+
+```sh
+# Adjust the version tag to point to the latest available tagged version.
+# If building your own container image name, adjust to something like `localhost/baibot:latest`.
+CONTAINER_IMAGE_NAME=ghcr.io/etkecc/baibot:v1.0.0
+
+/usr/bin/env docker run \
+ -it \
+ --rm \
+ --name=baibot \
+ --user=$(id -u):$(id -g) \
+ --cap-drop=ALL \
+ --read-only \
+ --env BAIBOT_PERSISTENCE_DATA_DIR_PATH=/data \
+ --mount type=bind,src=/path/to/config.yml,dst=/app/config.yml,ro \
+ --mount type=bind,src=/path/to/data,dst=/data \
+ $CONTAINER_IMAGE_NAME
+```
+
+đĄ If you've defined the `persistence.data_dir_path` setting in the `config.yml` file, you can skip the `BAIBOT_PERSISTENCE_DATA_DIR_PATH` environment variable.
+
+
+### đ¨ Building a binary
+
+To build a binary, you need a [đĻ Rust](https://www.rust-lang.org/) toolchain.
+
+Consult the [Dockerfile](../Dockerfile) file to learn what some of the build dependencies are (e.g. `libssl-dev`, `libsqlite3-dev`, etc., on Debian-based distros).
+
+You can build a binary from the current project's source code:
+
+- in `debug` mode via: `just build-debug`, yielding a binary in `target/debug/baibot`
+- (recommended) in `release` mode via: `just build-release`, yielding a binary in `target/release/baibot`
+
+đĄ Unless you're [đ§âđģ developing](./development.md), you probably wish to build in release mode, as that provides a much smaller and more optimized binary.
+
+đĻ You can also install from the [baibot](https://crates.io/crates/baibot) crate published to [crates.io](https://crates.io) with the help of the [cargo](https://doc.rust-lang.org/cargo/) package manager by running: `cargo install baibot`.
+
+
+### đĨī¸ Running a binary
+
+Once you've [đ¨ built a binary](#-building-a-binary) and [đ ī¸ prepared a configuration file](#-preparing-a-configuration-file), you can run it.
+
+Consult the [Dockerfile](../Dockerfile) file to learn what some of the runtime dependencies are (e.g. `ca-certificates`, `sqlite3`, etc., on Debian-based distros).
+
+You can run the binary like this:
+
+```sh
+BAIBOT_CONFIG_FILE_PATH=/path/to/config.yml \
+BAIBOT_PERSISTENCE_DATA_DIR_PATH=/path/to/data \
+./target/release/baibot
+```
+
+đĄ If you've defined the `persistence.data_dir_path` setting in the `config.yml` file, you can skip the `BAIBOT_PERSISTENCE_DATA_DIR_PATH` environment variable.
+
+đĄ If your `config.yml` file is in your working directory (which may be different than the directory the binary lives in), you can skip the `BAIBOT_CONFIG_FILE_PATH` environment variable.
+
+
+### đ ī¸ Preparing a configuration file
+
+For an introduction to the configuration file, see the [đ ī¸ Configuration](./configuration/README.md) page.
+
+Generally, you need to copy the configuration file template ([etc/app/config.yml.dist](../etc/app/config.yml.dist)) and make modifications as needed.
diff --git a/docs/providers.md b/docs/providers.md
new file mode 100644
index 0000000..c93f58f
--- /dev/null
+++ b/docs/providers.md
@@ -0,0 +1,170 @@
+## âī¸ Providers
+
+[đ¤ Agents](./agents.md) are powered by a provider. The provider could be a **local service** or a **cloud service**.
+
+The list of supported providers is below.
+
+
+### Table of contents
+
+- [How to choose a provider](#how-to-choose-a-provider)
+- [How to use a provider](#how-to-use-a-provider)
+- [Supported providers](#supported-providers)
+ - [Anthropic](#anthropic)
+ - [Groq](#groq)
+ - [LocalAI](#localai)
+ - [Mistral](#mistral)
+ - [Ollama](#ollama)
+ - [OpenAI](#openai)
+ - [OpenAI Compatible](#openai-compatible)
+ - [OpenRouter](#openrouter)
+ - [Together AI](#together-ai)
+
+
+### How to choose a provider
+
+If you're not sure which provider to start with, we **recommend [OpenAI](#openai)** as it's the most popular and has the **widest range of capabilities**: [đŦ text-generation](./features.md#-text-generation), [đī¸ image-generation](./features.md#ī¸-image-generation), [đĻģ speech-to-text](./features.md#-speech-to-text), [đŖī¸ text-to-speech](./features.md#ī¸-text-to-speech).
+
+You don't need to choose just one though. The bot supports [mixing & matching models](./features.md#-mixing--matching-models), so you can use multiple providers at the same time.
+
+
+### How to use a provider
+
+- sign up for it
+- obtain an API key
+- [create a new agent](./agents.md#creating-agents)
+- set it as a handler for some types of messages (see [Mixing & matching models](./features.md#-mixing--matching-models)) for a specific room or globally
+
+
+### Supported providers
+
+### Anthropic
+
+[Anthropic](https://www.anthropic.com/) is an American AI company founded by former OpenAI engineers and providing powerful language models.
+
+- đ Identifier: `anthropic`
+- đ Links: [đ Home page](https://www.anthropic.com/), [đ Wiki](https://en.wikipedia.org/wiki/Anthropic), [đ¤ Sign up](https://console.anthropic.com/), [đ Models list](https://docs.anthropic.com/en/docs/about-claude/models)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local anthropic my-anthropic-agent`
+ - create a global agent: `!bai agent create-global anthropic my-anthropic-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/anthropic.yml).
+
+
+### Groq
+
+[Groq](https://groq.com/) is an American company developing optimized Language Processing Units (LPU) and offering cloud service which runs various models (built by others) with very high performance.
+
+- đ Identifier: `groq`
+- đ Links: [đ Home page](https://groq.com/), [đ Wiki](https://en.wikipedia.org/wiki/Groq), [đ¤ Sign up](https://console.groq.com/login), [đ Models list](https://console.groq.com/docs/models)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation), [đĻģ speech-to-text](./features.md#-speech-to-text)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local groq my-groq-agent`
+ - create a global agent: `!bai agent create-global groq my-groq-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/groq.yml).
+
+
+### LocalAI
+
+[LocalAI](https://localai.io/) is the free, Open Source OpenAI alternative. LocalAI act as a drop-in replacement REST API thatâs compatible with OpenAI API specifications for local inferencing. It allows you to run LLMs, generate images, audio (and not only) locally or on-prem with consumer grade hardware, supporting multiple model families and architectures.
+
+- đ Identifier: `localai`
+- đ Links: [đ Home page](https://localai.io/), [đ Models list](https://localai.io/gallery.html)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation), [đŖī¸ text-to-speech](./features.md#ī¸-text-to-speech), [đĻģ speech-to-text](./features.md#-speech-to-text)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local localai my-localai-agent`
+ - create a global agent: `!bai agent create-global localai my-localai-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/localai.yml).
+
+
+### Mistral
+
+[Mistral AI](https://mistral.ai/) is a research lab based in Europe (France) which produces their own language models.
+
+- đ Identifier: `mistral`
+- đ Links: [đ Home page](https://mistral.ai/), [đ Wiki](https://en.wikipedia.org/wiki/Mistral_AI), [đ¤ Sign up](https://auth.mistral.ai/ui/registration), [đ Models list](https://docs.mistral.ai/getting-started/models/)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local mistral my-mistral-agent`
+ - create a global agent: `!bai agent create-global mistral my-mistral-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/mistral.yml).
+
+
+### Ollama
+
+[Ollama](https://ollama.com/) lets you run various models in a [self-hosted](https://github.com/ollama/ollama?tab=readme-ov-file#ollama) way. This is more advanced and requires powerful hardware for running some of the better models, but ensures your data stays with you.
+
+- đ Identifier: `ollama`
+- đ Links: [đ Home page](https://ollama.com/), [đ Models list](https://ollama.com/library)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local ollama my-ollama-agent`
+ - create a global agent: `!bai agent create-global ollama my-ollama-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/ollama.yml).
+
+
+### OpenAI
+
+[OpenAI](https://openai.com/) is an American AI company providing powerful language models.
+
+Use this provider either with the OpenAI API or with other OpenAI-compatible API services which **fully** adhere to the [OpenAI API spec](https://github.com/openai/openai-openapi/).
+For services which are not fully compatible with the OpenAI API, consider using the [OpenAI Compatible](#openai-compatible) provider.
+
+- đ Identifier: `openai`
+- đ Links: [đ Home page](https://openai.com/), [đ Wiki](https://en.wikipedia.org/wiki/OpenAI), [đ¤ Sign up](https://platform.openai.com/signup), [đ Models list](https://platform.openai.com/docs/models)
+- đ Capabilities: [đī¸ image-generation](./features.md#ī¸-image-generation), [đŦ text-generation](./features.md#-text-generation), [đŖī¸ text-to-speech](./features.md#ī¸-text-to-speech), [đĻģ speech-to-text](./features.md#-speech-to-text)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local openai my-openai-agent`
+ - create a global agent: `!bai agent create-global openai my-openai-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/openai.yml).
+
+
+### OpenAI Compatible
+
+This provider allows you to use OpenAI-compatible API services like [OpenRouter](https://openrouter.ai/), [Together AI](https://www.together.ai/), etc.
+
+Some of these popular services already have **shortcut** providers (leading to this one behind the scenes) - this make it easier to get started.
+
+This provider is just as featureful as the [OpenAI](#openai) provider, but is more compatible with services which do not fully adhere to the [OpenAI API spec](https://github.com/openai/openai-openapi/).
+
+- đ Identifier: `openai-compatible`
+- đ Capabilities: [đī¸ image-generation](./features.md#ī¸-image-generation), [đŦ text-generation](./features.md#-text-generation), [đŖī¸ text-to-speech](./features.md#ī¸-text-to-speech), [đĻģ speech-to-text](./features.md#-speech-to-text)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local openai-compatible my-openai-compatible-agent`
+ - create a global agent: `!bai agent create-global openai-compatible my-openai-compatible-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/openai-compatible.yml).
+
+
+### OpenRouter
+
+[OpenRouter](https://openrouter.ai/) is a unified interface for LLMs. The platform scouts for the lowest prices and best latencies/throughputs across dozens of providers, and lets you choose how to [prioritize](https://openrouter.ai/docs/provider-routing) them.
+
+- đ Identifier: `openrouter`
+- đ Links: [đ Home page](https://openrouter.ai/), [đ¤ Sign up](https://openrouter.ai/), [đ Models list](https://openrouter.ai/models)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local openrouter my-openrouter-agent`
+ - create a global agent: `!bai agent create-global openrouter my-openrouter-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/openrouter.yml).
+
+
+### Together AI
+
+[Together AI](https://www.together.ai/) makes it easy to run or [fine-tune](https://docs.together.ai/docs/fine-tuning-overview) leading open source models with only a few lines of code.
+
+- đ Identifier: `together-ai`
+- đ Links: [đ Home page](https://www.together.ai/), [đ¤ Sign up](https://api.together.ai/signup), [đ Models list](https://api.together.xyz/models)
+- đ Capabilities: [đŦ text-generation](./features.md#-text-generation)
+- đ˛ Quick start:
+ - create a room-local agent: `!bai agent create-room-local together-ai my-together-ai-agent`
+ - create a global agent: `!bai agent create-global together-ai my-together-ai-agent`
+
+đĄ When creating an agent, the bot will show you an up-to-date sample configuration for this provider which looks [like this](../sample-provider-configs/together-ai.yml).
diff --git a/docs/sample-provider-configs/anthropic.yml b/docs/sample-provider-configs/anthropic.yml
new file mode 100644
index 0000000..b290156
--- /dev/null
+++ b/docs/sample-provider-configs/anthropic.yml
@@ -0,0 +1,8 @@
+base_url: https://api.anthropic.com/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: claude-3-5-sonnet-20240620
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 8192
+ max_context_tokens: 204800
diff --git a/docs/sample-provider-configs/groq.yml b/docs/sample-provider-configs/groq.yml
new file mode 100644
index 0000000..a56fdfc
--- /dev/null
+++ b/docs/sample-provider-configs/groq.yml
@@ -0,0 +1,10 @@
+base_url: https://api.groq.com/openai/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: llama3-70b-8192
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 4096
+ max_context_tokens: 131072
+speech_to_text:
+ model_id: whisper-large-v3
diff --git a/docs/sample-provider-configs/localai.yml b/docs/sample-provider-configs/localai.yml
new file mode 100644
index 0000000..29d63c0
--- /dev/null
+++ b/docs/sample-provider-configs/localai.yml
@@ -0,0 +1,20 @@
+base_url: http://my-localai-self-hosted-service:8080/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: gpt-4
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 4096
+ max_context_tokens: 128000
+speech_to_text:
+ model_id: whisper-1
+text_to_speech:
+ model_id: tts-1
+ voice: onyx
+ speed: 1.0
+ response_format: opus
+image_generation:
+ model_id: stablediffusion
+ style: vivid
+ size: 1024x1024
+ quality: standard
diff --git a/docs/sample-provider-configs/mistral.yml b/docs/sample-provider-configs/mistral.yml
new file mode 100644
index 0000000..5039316
--- /dev/null
+++ b/docs/sample-provider-configs/mistral.yml
@@ -0,0 +1,8 @@
+base_url: https://api.mistral.ai/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: mistral-large-latest
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 4096
+ max_context_tokens: 128000
diff --git a/docs/sample-provider-configs/ollama.yml b/docs/sample-provider-configs/ollama.yml
new file mode 100644
index 0000000..d794859
--- /dev/null
+++ b/docs/sample-provider-configs/ollama.yml
@@ -0,0 +1,8 @@
+base_url: http://my-ollama-self-hosted-service:11434/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: gemma2:2b
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 4096
+ max_context_tokens: 128000
diff --git a/docs/sample-provider-configs/openai-compatible.yml b/docs/sample-provider-configs/openai-compatible.yml
new file mode 100644
index 0000000..c8852c3
--- /dev/null
+++ b/docs/sample-provider-configs/openai-compatible.yml
@@ -0,0 +1,10 @@
+base_url: ''
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: some-model
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 4096
+ max_context_tokens: 128000
+speech_to_text:
+ model_id: whisper-1
diff --git a/docs/sample-provider-configs/openai.yml b/docs/sample-provider-configs/openai.yml
new file mode 100644
index 0000000..612cdbe
--- /dev/null
+++ b/docs/sample-provider-configs/openai.yml
@@ -0,0 +1,20 @@
+base_url: https://api.openai.com/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: gpt-4o-2024-08-06
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 16384
+ max_context_tokens: 128000
+speech_to_text:
+ model_id: whisper-1
+text_to_speech:
+ model_id: tts-1-hd
+ voice: onyx
+ speed: 1.0
+ response_format: opus
+image_generation:
+ model_id: dall-e-3
+ style: vivid
+ size: 1024x1024
+ quality: standard
diff --git a/docs/sample-provider-configs/openrouter.yml b/docs/sample-provider-configs/openrouter.yml
new file mode 100644
index 0000000..561b40e
--- /dev/null
+++ b/docs/sample-provider-configs/openrouter.yml
@@ -0,0 +1,8 @@
+base_url: https://openrouter.ai/api/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: mattshumer/reflection-70b:free
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 2048
+ max_context_tokens: 8192
diff --git a/docs/sample-provider-configs/together-ai.yml b/docs/sample-provider-configs/together-ai.yml
new file mode 100644
index 0000000..5a9b8e8
--- /dev/null
+++ b/docs/sample-provider-configs/together-ai.yml
@@ -0,0 +1,8 @@
+base_url: https://api.together.xyz/v1
+api_key: YOUR_API_KEY_HERE
+text_generation:
+ model_id: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo
+ prompt: You are a brief, but helpful bot.
+ temperature: 1.0
+ max_response_tokens: 2048
+ max_context_tokens: 8192
diff --git a/docs/screenshots/agent-creation.webp b/docs/screenshots/agent-creation.webp
new file mode 100644
index 0000000000000000000000000000000000000000..96851c042f72a1b17436c2df5039f32d8038d954
GIT binary patch
literal 91060
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