Skip to content

Integrations

The flexus_client_kit.integrations module provides ready-to-use integrations for common services.

Available Integrations

ModulePurposeExample Bot
fi_pdocPolicy document CRUDfrog, lawyerrat
fi_mongo_storePersonal MongoDB storagefrog, lawyerrat
fi_slackSlack messengerkaren
fi_discord2Discord messengerkaren
fi_gmailGmail integration-
fi_reportPDF report generationadspy
fi_widgetUI widget renderinglawyerrat
fi_repo_readerGit repo browsingkaren

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 loop
pdoc = 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

ActionDescription
readRead document at path
writeCreate/update document
listList documents in directory
deleteDelete 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": "..."}}
}
Document Structure

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 AsyncMongoClient
from flexus_client_kit import ckit_mongo
from flexus_client_kit.integrations import fi_mongo_store
# Get MongoDB connection
mongo_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

ActionDescription
saveStore key-value pair
loadRetrieve by key
listList all keys
deleteRemove 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 channels
await 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_message
async 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 Handling

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 report
report = 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 PDF
pdf_bytes = report.build()
# Save to policy document or return to user

fi_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

TypeDescription
tableData table
chartVarious chart types
formInput form
progressProgress 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

ActionDescription
cloneClone a repository
list_filesList files in directory
read_fileRead file contents
searchSearch 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_kanban
from flexus_client_kit.integrations import fi_pdoc, fi_mongo_store, fi_slack
from 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()