"""Entry point for running the Discord bot with direct ChatService injection. This script constructs the same adapter stack as the FastAPI app but runs the Discord bot instead of a web server. The bot calls ChatService directly — no HTTP roundtrip to the API. """ import asyncio import logging from adapters.outbound.chroma_rules import ChromaRuleRepository from adapters.outbound.gitea_issues import GiteaIssueTracker from adapters.outbound.openrouter import OpenRouterLLM from adapters.outbound.sqlite_convos import SQLiteConversationStore from adapters.inbound.discord_bot import run_bot from config.settings import Settings from domain.services import ChatService logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) async def _init_and_run() -> None: settings = Settings() if not settings.discord_bot_token: raise ValueError("DISCORD_BOT_TOKEN is required") logger.info("Initialising adapters for Discord bot...") # Vector store chroma_repo = ChromaRuleRepository( persist_dir=settings.chroma_dir, embedding_model=settings.embedding_model, ) logger.info("ChromaDB ready (%d rules)", chroma_repo.count()) # Conversation store conv_store = SQLiteConversationStore(db_url=settings.db_url) await conv_store.init_db() logger.info("SQLite conversation store ready") # LLM llm = None if settings.openrouter_api_key: llm = OpenRouterLLM( api_key=settings.openrouter_api_key, model=settings.openrouter_model, ) logger.info("OpenRouter LLM ready (model: %s)", settings.openrouter_model) else: logger.warning("OPENROUTER_API_KEY not set — LLM disabled") # Gitea gitea = None if settings.gitea_token: gitea = GiteaIssueTracker( token=settings.gitea_token, owner=settings.gitea_owner, repo=settings.gitea_repo, base_url=settings.gitea_base_url, ) # Service service = ChatService( rules=chroma_repo, llm=llm, # type: ignore[arg-type] conversations=conv_store, issues=gitea, top_k_rules=settings.top_k_rules, ) logger.info("Starting Discord bot...") run_bot( token=settings.discord_bot_token, chat_service=service, guild_id=settings.discord_guild_id, ) def main() -> None: asyncio.run(_init_and_run()) if __name__ == "__main__": main()