Integrations
The flexus_client_kit.integrations module provides ready-to-use integrations for common services.
Available Integrations
| Module | Purpose | Example Bot |
|---|---|---|
fi_pdoc | Policy document CRUD | frog, lawyerrat |
fi_mongo_store | Personal MongoDB storage | frog, lawyerrat |
fi_slack | Slack messenger | karen |
fi_discord2 | Discord messenger | karen |
fi_gmail | Gmail integration | - |
fi_report | PDF report generation | adspy |
fi_widget | UI widget rendering | lawyerrat |
fi_repo_reader | Git repo browsing | karen |
fi_pdoc — Policy Documents
Policy documents are structured JSON files that bots can read and write.
Setup
from flexus_client_kit.integrations import fi_pdoc
# Initialize in main looppdoc = fi_pdoc.IntegrationPdoc(rcx, rcx.persona.ws_root_group_id)Tool Definition
from flexus_client_kit.integrations import fi_pdoc
TOOLS = [ fi_pdoc.POLICY_DOCUMENT_TOOL, # ... other tools]
@rcx.on_tool_call(fi_pdoc.POLICY_DOCUMENT_TOOL.name)async def handle_pdoc(toolcall, args): return await pdoc.called_by_model(toolcall, args)Tool Actions
| Action | Description |
|---|---|
read | Read document at path |
write | Create/update document |
list | List documents in directory |
delete | Delete document |
Example Usage
LLM can use the tool like this:
{ "action": "write", "path": "/reports/weekly", "content": {"report": {"meta": {"date": "2024-01-15"}, "title": "Weekly Report", "data": "..."}}}Policy documents should have a top-level key containing a meta object. This structure enables custom forms: {"report": {"meta": {...}, ...}}
fi_mongo_store — Personal MongoDB
Each bot gets its own MongoDB database for storing data.
Setup
from pymongo import AsyncMongoClientfrom flexus_client_kit import ckit_mongofrom flexus_client_kit.integrations import fi_mongo_store
# Get MongoDB connectionmongo_conn_str = await ckit_mongo.mongo_fetch_creds(fclient, rcx.persona.persona_id)mongo = AsyncMongoClient(mongo_conn_str)db = mongo[rcx.persona.persona_id + "_db"]personal_mongo = db["personal_mongo"]Tool Definition
TOOLS = [ fi_mongo_store.MONGO_STORE_TOOL,]
@rcx.on_tool_call(fi_mongo_store.MONGO_STORE_TOOL.name)async def handle_mongo(toolcall, args): return await fi_mongo_store.handle_mongo_store( rcx.workdir, personal_mongo, toolcall, args, )Tool Actions
| Action | Description |
|---|---|
save | Store key-value pair |
load | Retrieve by key |
list | List all keys |
delete | Remove by key |
fi_slack — Slack Integration
Full-featured Slack integration with reactive message handling.
Setup
from flexus_client_kit.integrations import fi_slack
slack = fi_slack.IntegrationSlack( fclient, rcx, SLACK_BOT_TOKEN=setup["SLACK_BOT_TOKEN"], SLACK_APP_TOKEN=setup["SLACK_APP_TOKEN"], should_join=setup.get("slack_should_join", []),)Activity Callback
Handle incoming Slack messages:
from flexus_client_kit import ckit_kanban
def handle_slack_activity(activity, posted): # Create kanban task for each message await ckit_kanban.bot_kanban_post_into_inbox( client=fclient, persona_id=rcx.persona.persona_id, title=f"Slack: {activity.text[:50]}...", description=activity.text, payload_json=json.dumps({ "source": "slack", "channel": activity.channel, "ts": activity.ts, "user": activity.user, }), )
slack.set_activity_callback(handle_slack_activity)Start Listening
# Join specified channelsawait slack.join_channels()
# Start reactive listener (runs in background)await slack.start_reactive()
# In finally block:await slack.close()Sending Messages
await slack.send_message( channel="#general", text="Hello from Flexus!", thread_ts=original_message_ts, # Optional: reply in thread)Captured Threads
When a Slack thread is “captured”, messages flow directly to a Flexus thread:
@rcx.on_updated_messageasync def on_message(msg): # Check if bot posted something that should go to Slack await slack.look_assistant_might_have_posted_something(msg)fi_discord2 — Discord Integration
Discord integration with similar patterns to Slack.
Setup
from flexus_client_kit.integrations import fi_discord2
discord = fi_discord2.IntegrationDiscord( fclient, rcx, DISCORD_BOT_TOKEN=setup["DISCORD_BOT_TOKEN"], watch_channels=setup.get("discord_watch_channels", []),)
discord.set_activity_callback(handle_discord_activity)await discord.join_channels()await discord.start_reactive()Discord thread creation works differently from Slack. See the karen bot for implementation details.
fi_report — PDF Reports
Generate PDF reports from structured data.
Usage
from flexus_client_kit.integrations.report import fi_report
# Create reportreport = fi_report.ReportBuilder()report.add_title("Monthly Analysis")report.add_section("Summary", summary_text)report.add_table("Metrics", metrics_data)report.add_chart("Trends", chart_data)
# Generate PDFpdf_bytes = report.build()
# Save to policy document or return to userfi_widget — UI Widgets
Render interactive widgets in the chat UI.
Tool Definition
from flexus_client_kit.integrations import fi_widget
TOOLS = [ fi_widget.PRINT_WIDGET_TOOL,]
@rcx.on_tool_call(fi_widget.PRINT_WIDGET_TOOL.name)async def handle_widget(toolcall, args): return await fi_widget.handle_print_widget(toolcall, args)Widget Types
| Type | Description |
|---|---|
table | Data table |
chart | Various chart types |
form | Input form |
progress | Progress indicator |
fi_repo_reader — Git Repository Browser
Read files from git repositories.
Setup
from flexus_client_kit.integrations import fi_repo_reader
TOOLS = [ fi_repo_reader.REPO_READER_TOOL,]
@rcx.on_tool_call(fi_repo_reader.REPO_READER_TOOL.name)async def handle_repo(toolcall, args): return await fi_repo_reader.handle_repo_reader( workdir=rcx.workdir, args=args, )Tool Actions
| Action | Description |
|---|---|
clone | Clone a repository |
list_files | List files in directory |
read_file | Read file contents |
search | Search for patterns |
Setup Schema for Integrations
Define setup fields for integration credentials:
mybot_setup_schema = { # Slack integration "SLACK_BOT_TOKEN": { "bs_type": "string_long", "bs_default": "", "bs_group": "slack", "bs_description": "Bot User OAuth Token (xoxb-...)", }, "SLACK_APP_TOKEN": { "bs_type": "string_long", "bs_default": "", "bs_group": "slack", "bs_description": "App-Level Token (xapp-...)", }, "slack_should_join": { "bs_type": "string_multiline", "bs_default": "", "bs_group": "slack", "bs_description": "Channels to join (one per line)", },
# Discord integration "DISCORD_BOT_TOKEN": { "bs_type": "string_long", "bs_default": "", "bs_group": "discord", "bs_description": "Discord Bot Token", }, "discord_watch_channels": { "bs_type": "string_multiline", "bs_default": "", "bs_group": "discord", "bs_description": "Channel IDs to watch (one per line)", },}Complete Integration Example
from flexus_client_kit import ckit_bot_exec, ckit_shutdown, ckit_mongo, ckit_kanbanfrom flexus_client_kit.integrations import fi_pdoc, fi_mongo_store, fi_slackfrom pymongo import AsyncMongoClient
TOOLS = [ fi_pdoc.POLICY_DOCUMENT_TOOL, fi_mongo_store.MONGO_STORE_TOOL,]
async def main_loop(fclient, rcx): setup = ckit_bot_exec.official_setup_mixing_procedure(install.setup_schema, rcx.persona.persona_setup)
# MongoDB mongo_conn = await ckit_mongo.mongo_fetch_creds(fclient, rcx.persona.persona_id) mongo = AsyncMongoClient(mongo_conn) db = mongo[rcx.persona.persona_id + "_db"] personal_mongo = db["personal_mongo"]
# Policy Documents pdoc = fi_pdoc.IntegrationPdoc(rcx, rcx.persona.ws_root_group_id)
# Slack (if configured) slack = None if setup.get("SLACK_BOT_TOKEN"): slack = fi_slack.IntegrationSlack( fclient, rcx, SLACK_BOT_TOKEN=setup["SLACK_BOT_TOKEN"], SLACK_APP_TOKEN=setup["SLACK_APP_TOKEN"], should_join=setup.get("slack_should_join", "").split("\n"), ) slack.set_activity_callback( lambda a, p: ckit_kanban.bot_kanban_post_into_inbox( fclient, rcx.persona.persona_id, title=f"Slack: {a.text[:50]}", description=a.text, payload_json=json.dumps({"slack_ts": a.ts}), ) ) await slack.join_channels() await slack.start_reactive()
# Tool handlers @rcx.on_tool_call(fi_pdoc.POLICY_DOCUMENT_TOOL.name) async def handle_pdoc(toolcall, args): return await pdoc.called_by_model(toolcall, args)
@rcx.on_tool_call(fi_mongo_store.MONGO_STORE_TOOL.name) async def handle_mongo(toolcall, args): return await fi_mongo_store.handle_mongo_store(rcx.workdir, personal_mongo, toolcall, args)
@rcx.on_updated_message async def on_msg(msg): if slack: await slack.look_assistant_might_have_posted_something(msg)
try: while not ckit_shutdown.shutdown_event.is_set(): await rcx.unpark_collected_events(sleep_if_no_work=10.0) finally: if slack: await slack.close() mongo.close()