Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions flexus_client_kit/ckit_cloudtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
CLOUDTOOLS_VECDB = {"flexus_vector_search", "flexus_read_original"}
CLOUDTOOLS_PYTHON = {"python_execute"}
CLOUDTOOLS_WEB = {"web"}
CLOUDTOOLS_NOT_KANBAN = CLOUDTOOLS_VECDB | CLOUDTOOLS_PYTHON | CLOUDTOOLS_WEB
CLOUDTOOLS_AGENTS = {"flexus_hand_over_task"}
CLOUDTOOLS_NOT_KANBAN = CLOUDTOOLS_VECDB | CLOUDTOOLS_PYTHON | CLOUDTOOLS_WEB | CLOUDTOOLS_AGENTS

CLOUDTOOLS_MCP = {"mcp_*"}

CLOUDTOOLS_QUITE_A_LOT = KANBAN_ADVANCED | CLOUDTOOLS_NOT_KANBAN | CLOUDTOOLS_MCP

CLOUDTOOLS_ALL_KNOWN = KANBAN_ALL | CLOUDTOOLS_NOT_KANBAN # unsable in a bot
CLOUDTOOLS_ALL_KNOWN = KANBAN_ALL | CLOUDTOOLS_NOT_KANBAN # unusable in a bot


def gql_error_4xx_to_model_reraise_5xx(e: gql.transport.exceptions.TransportQueryError, label: str) -> str:
Expand Down
Empty file.
57 changes: 57 additions & 0 deletions flexus_simple_bots/integration_tester/autonomous__failed.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
messages:
- role: user
content: Work on the assigned task.
- role: assistant
tool_calls:
- function:
arguments: '{"op":"assign_to_this_chat","args":{"batch":["int_test_002"]}}'
name: flexus_kanban_advanced
id: call_assign_002
type: function
- role: tool
call_id: call_assign_002
content: |-
Assigned task int_test_002 to this chat and also moved it to 'inprogress' column

{
"id": "int_test_002",
"title": "Test integrations batch 1/1",
"details": {
"description": "Integrations: newsapi\nTool mapping: newsapi->newsapi"
}
}
- role: assistant
tool_calls:
- function:
arguments: '{"op":"help","args":{}}'
name: newsapi
id: call_help_002
type: function
- role: tool
call_id: call_help_002
content: '[HELP OUTPUT - NOT A TEST] newsapi help'
- role: assistant
tool_calls:
- function:
arguments: '{"op":"status","args":{}}'
name: newsapi
id: call_status_002
type: function
- role: tool
call_id: call_status_002
content: 'Error [AUTH_ERROR]: API key invalid or unauthorized'
- role: assistant
tool_calls:
- function:
arguments: '{"op":"resolve","args":{"task_id":"int_test_002","resolution_code":"FAILED","resolution_summary":"| Integration | Status | Details |\n|-------------|--------|---------|\n| newsapi | FAILED | op=status: Error [AUTH_ERROR]: API key invalid or unauthorized |"}}'
name: flexus_kanban_advanced
id: call_resolve_002
type: function
- role: tool
call_id: call_resolve_002
content: Task int_test_002 resolved as FAILED
- role: assistant
content: |-
| Integration | Status | Details |
|-------------|--------|---------|
| newsapi | FAILED | op=status: Error [AUTH_ERROR]: API key invalid or unauthorized |
57 changes: 57 additions & 0 deletions flexus_simple_bots/integration_tester/autonomous__untested.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
messages:
- role: user
content: Work on the assigned task.
- role: assistant
tool_calls:
- function:
arguments: '{"op":"assign_to_this_chat","args":{"batch":["int_test_001"]}}'
name: flexus_kanban_advanced
id: call_assign_001
type: function
- role: tool
call_id: call_assign_001
content: |-
Assigned task int_test_001 to this chat and also moved it to 'inprogress' column

{
"id": "int_test_001",
"title": "Test integrations batch 1/1",
"details": {
"description": "Integrations: newsapi\nTool mapping: newsapi->newsapi"
}
}
- role: assistant
tool_calls:
- function:
arguments: '{"op":"help","args":{}}'
name: newsapi
id: call_help_001
type: function
- role: tool
call_id: call_help_001
content: '[HELP OUTPUT - NOT A TEST] newsapi help'
- role: assistant
tool_calls:
- function:
arguments: '{"op":"list_methods","args":{}}'
name: newsapi
id: call_methods_001
type: function
- role: tool
call_id: call_methods_001
content: 'api_key_hint=***dc9, method_ids=[newsapi.everything.v1, newsapi.top_headlines.v1, newsapi.sources.v1]'
- role: assistant
tool_calls:
- function:
arguments: '{"op":"resolve","args":{"task_id":"int_test_001","resolution_code":"FAILED","resolution_summary":"| Integration | Status | Details |\n|-------------|--------|---------|\n| newsapi | UNTESTED | help and list_methods only; no real API test; api_key_hint=***dc9 |"}}'
name: flexus_kanban_advanced
id: call_resolve_001
type: function
- role: tool
call_id: call_resolve_001
content: Task int_test_001 resolved as FAILED
- role: assistant
content: |-
| Integration | Status | Details |
|-------------|--------|---------|
| newsapi | UNTESTED | help and list_methods only; no real API test; api_key_hint=***dc9 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions flexus_simple_bots/integration_tester/integration_tester__s1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
messages:
- role: user
content: test all integrations
- role: assistant
tool_calls:
- function:
arguments: '{"requested":"all","batch_size":5,"configured_only":true}'
name: integration_plan_batches
id: call_plan_001
type: function
- role: tool
call_id: call_plan_001
content: |-
{
"ok": true,
"requested": ["all"],
"supported": ["newsapi", "resend"],
"configured": ["newsapi", "resend"],
"configured_only": true,
"selected": ["newsapi", "resend"],
"unsupported": [],
"batch_size": 5,
"batches": [["newsapi", "resend"]],
"task_specs": [
{
"title": "Test integrations batch 1/1",
"description": "Integrations: newsapi,resend\nTool mapping: newsapi->newsapi, resend->email_setup_domain",
"integrations": ["newsapi", "resend"]
}
]
}
- role: assistant
tool_calls:
- function:
arguments: '{"description":"Integrations: newsapi,resend\nTool mapping: newsapi->newsapi, resend->email_setup_domain","fexp_name":"autonomous","title":"Test integrations batch 1/1","to_bot":"Integration Tester"}'
name: flexus_hand_over_task
id: call_handover_001
type: function
- role: tool
call_id: call_handover_001
content: |-
✓ Task handed over to 'Integration Tester'
Task ID: int_test_001
- role: assistant
content: |-
Created 1 batch task for 2 integrations (newsapi, resend).

Task handed over to autonomous worker for execution.
148 changes: 148 additions & 0 deletions flexus_simple_bots/integration_tester/integration_tester_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import asyncio
import json
import sys
from pathlib import Path

_repo_root = Path(__file__).parents[2]
if str(_repo_root) not in sys.path:
sys.path.insert(0, str(_repo_root))

from flexus_client_kit import ckit_bot_exec, ckit_client, ckit_shutdown, ckit_integrations_db
from flexus_client_kit import ckit_bot_version
from flexus_simple_bots.integration_tester import integration_tester_shared as shared
from flexus_simple_bots.integration_tester import integration_tester_install

BOT_NAME = ckit_bot_version.bot_name_from_file(__file__)
BOT_VERSION = (Path(__file__).parents[1] / "VERSION").read_text().strip()
SETUP_SCHEMA = json.loads((Path(__file__).parent / "setup_schema.json").read_text())


async def integration_tester_main_loop(
fclient: ckit_client.FlexusClient,
rcx: ckit_bot_exec.RobotContext,
) -> None:
setup = ckit_bot_exec.official_setup_mixing_procedure(SETUP_SCHEMA, rcx.persona.persona_setup)
shared.load_env_config(setup)

integr_records = shared.INTEGRATION_TESTER_INTEGRATIONS
setup_allow = shared._setup_allowlist_names(setup)
if setup_allow:
allow = set(setup_allow)
integr_records = [r for r in integr_records if r.integr_name in allow]

await ckit_integrations_db.main_loop_integrations_init(integr_records, rcx, setup)
supported_integrations = sorted({r.integr_name for r in integr_records})

for name, reg in shared.INTEGRATION_REGISTRY.items():
if name not in supported_integrations:
continue
obj = reg["integration_cls"](*reg["integration_args"](fclient, rcx, setup))
rcx.on_tool_call(reg["tool"].name)(shared.IntegrationHandler(reg, obj))

@rcx.on_tool_call(shared.PLAN_BATCHES_TOOL.name)
async def toolcall_plan_batches(toolcall, model_produced_args):
args = model_produced_args or {}
req = shared._requested_names(str(args.get("requested", "all")))
bs = args.get("batch_size", 5)
configured_only = bool(args.get("configured_only", True))
try:
bs = int(bs)
except (TypeError, ValueError):
bs = 5

configured = {x["name"] for x in shared.get_configured_integrations()}
selected = []
unsupported = []

if "all" in req:
pool = [x for x in supported_integrations if (x in configured or not configured_only)]
selected = pool
else:
for x in req:
if x not in supported_integrations:
unsupported.append(x)
continue
if configured_only and x not in configured:
continue
if x not in selected:
selected.append(x)

batches = shared._chunk_names(selected, bs)
task_specs = []
total = len(batches)
for i, b in enumerate(batches, start=1):
tool_map = ", ".join(f"{name}->{shared.INTEGRATION_REGISTRY[name]['tool'].name}" for name in b)
task_specs.append({
"title": f"Test integrations batch {i}/{total}",
"description": f"Integrations: {','.join(b)}\nTool mapping: {tool_map}",
"integrations": b,
})

return json.dumps({
"ok": True,
"requested": req,
"supported": supported_integrations,
"configured": sorted(configured),
"configured_only": configured_only,
"selected": selected,
"unsupported": unsupported,
"batch_size": bs,
"batches": batches,
"task_specs": task_specs,
}, indent=2)

configured = shared.get_configured_integrations()
shared.logger.info(f"Integration Tester started. Configured integrations: {[i['name'] for i in configured]}")

@rcx.on_updated_task
async def on_task_update(action, old_task, new_task):
task = new_task or old_task
if not task:
shared.logger.info(f"TASK UPDATE: {action} with no task payload")
return
col = task.calc_bucket()
title = task.ktask_title
tid = task.ktask_id
if col == "inprogress":
shared.logger.info(f"TASK ASSIGNED: {title} (id={tid}) - will test now")
elif col == "done":
shared.logger.info(f"TASK COMPLETED: {title} (id={tid})")
else:
shared.logger.info(f"TASK UPDATE: {title} moved to {col} (id={tid})")

while not ckit_shutdown.shutdown_event.is_set():
await rcx.unpark_collected_events(sleep_if_no_work=10.0)

shared.logger.info(f"{rcx.persona.persona_id} exit")


def main():
scenario_fn = ckit_bot_exec.parse_bot_args()
fclient = ckit_client.FlexusClient(
ckit_client.bot_service_name(BOT_NAME, BOT_VERSION),
endpoint="/v1/jailed-bot",
)

from dotenv import load_dotenv
load_dotenv()

async def _install_compat(client: ckit_client.FlexusClient) -> int:
await integration_tester_install.install(
client,
bot_name=BOT_NAME,
bot_version=BOT_VERSION,
tools=shared.TOOLS,
)
return 0

asyncio.run(ckit_bot_exec.run_bots_in_this_group(
fclient,
bot_main_loop=integration_tester_main_loop,
inprocess_tools=shared.TOOLS,
scenario_fn=scenario_fn,
install_func=_install_compat,
))


if __name__ == "__main__":
main()
Loading