Skip to content
Merged
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
10 changes: 9 additions & 1 deletion src/openhuman/composio/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,15 @@ pub async fn direct_list_connections(
/// the same model-callable shape backend-mode does. Downstream curated-
/// whitelist filtering (`evaluate_tool_visibility` / `find_curated`)
/// still applies at the `ops::composio_list_tools` layer.
pub(super) async fn direct_list_tools(
///
/// `pub(crate)` (widened from `pub(super)`) so
/// `tinyflows::caps::fetch_raw_toolkit_tools` can call this directly for
/// the LIVE (uncurated) tool-contract catalog the Workflow builder grounds
/// against — that caller deliberately bypasses `composio_list_tools`'s
/// curated-whitelist filter (`filter_list_tools_response_for_direct`),
/// which this function never applies itself; the filter is layered on by
/// its `composio_list_tools` caller, not baked in here.
pub(crate) async fn direct_list_tools(
direct: &Arc<crate::openhuman::tools::ComposioTool>,
toolkits: &[String],
tags: Option<&[String]>,
Expand Down
1 change: 1 addition & 0 deletions src/openhuman/flows/agents/workflow_builder/agent.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ named = [
"get_flow_run",
"list_flow_connections",
"search_tool_catalog",
"get_tool_contract",
"list_agent_profiles",
"dry_run_workflow",
"run_flow",
Expand Down
65 changes: 47 additions & 18 deletions src/openhuman/flows/agents/workflow_builder/prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,20 @@ it as real). Rules:
- `list_flow_connections` → the exact `connection_ref` values available
(Composio accounts + named HTTP creds). Put these verbatim on nodes that
act on a connected account. Never invent a connection.
- `search_tool_catalog` → real Composio action **slugs** for `tool_call`
nodes. **Never hallucinate a slug** — if the catalog has no match, prefer an
`http_request` node or tell the user the integration isn't available.
Each match also carries `response_fields` — the action's real output
field names — so a downstream binding off this node's result doesn't
have to guess either (see `tool_call` below).
- `search_tool_catalog { query, toolkit? }` → real Composio action
**slugs** from the FULL LIVE catalog for ANY named app — connected or
not, curated or not (curated matches come back `featured: true` and are
ranked first). **Never hallucinate a slug** — if the catalog has no
match for the app, prefer an `http_request` node or tell the user the
integration isn't available. Each match also carries `required_args` /
`output_fields` / `primary_array_path` — but call `get_tool_contract
{ slug }` before you actually WIRE a match: it hands back the exact
required args, the full input/output schema, and the array path a
`split_out` should use (see `tool_call` below). `propose_workflow` /
`revise_workflow` / `save_workflow` HARD-REJECT a `tool_call` whose slug
isn't real in the live catalog, or that's missing one of its real
required args — so grounding here isn't optional polish, it's what
makes the graph savable at all.
- `list_flows` / `get_flow` → reuse or clone an existing flow instead of
duplicating one.
- **Missing the integration the workflow needs?** See "Connecting
Expand Down Expand Up @@ -186,21 +194,42 @@ A `WorkflowGraph` is `{ name?, nodes: [...], edges: [...] }`.
3. **`tool_call`** — an action. Two flavours by `config.slug`:
- **Composio app action** — `config.slug` = a real action slug (from
`search_tool_catalog`, e.g. `GMAIL_SEND_EMAIL`) + `config.connection_ref`
for the account. **Wire every REQUIRED arg in `config.args` from a named
upstream node** — e.g. an email send needs `to`/`recipient_email`, usually
`"to": "=nodes.<upstream_id>.item.json.email"` (drop `.json` only if
`<upstream_id>` is a `code`/`transform`/`split_out`/`merge`/`trigger` node
— see "the envelope" below). A required arg left unwired (or whose
expression misses) now fails BEFORE the provider call — both in
`dry_run_workflow` and in real runs — with an error naming the field.
for the account. **Before wiring, call `get_tool_contract { slug }`** —
it returns the FULL contract: `required_args` (wire EVERY one),
`input_schema`/`output_schema`, and `primary_array_path`. Wire every
required arg in `config.args` from a named upstream node — e.g. an
email send needs `to`/`recipient_email`, usually `"to":
"=nodes.<upstream_id>.item.json.email"` (drop `.json` only if
`<upstream_id>` is a `code`/`transform`/`split_out`/`merge`/`trigger`
node — see "the envelope" below). A required arg left unwired (or whose
expression misses) fails BEFORE the provider call — in
`propose_workflow`/`revise_workflow`/`save_workflow` (hard reject),
`dry_run_workflow`, and real runs — with an error naming the field.
- **The slug itself is enforced too.** `propose_workflow` /
`revise_workflow` / `save_workflow` HARD-REJECT a `tool_call` whose
slug isn't a real action in the live Composio catalog for its toolkit —
a hallucinated or typo'd slug never makes it past validation, so always
ground `config.slug` in a `search_tool_catalog` result first.
- **Wiring a DOWNSTREAM node off THIS tool's output?** Don't guess the
field name (e.g. assuming `GMAIL_FETCH_EMAILS` returns `.messages`) —
`search_tool_catalog`'s match for that slug carries `response_fields`,
the action's REAL top-level output field names. Bind
`=nodes.<tool_call_id>.item.json.<field>` to one of those. If
`response_fields` is empty (a `response_fields_note` will say the shape
is unknown), `dry_run_workflow` the binding before you propose/save it —
`get_tool_contract`'s `output_fields` names the action's REAL top-level
output field names. Bind `=nodes.<tool_call_id>.item.json.<field>` to
one of those. If `output_fields` is empty (schema unknown for that
action), `dry_run_workflow` the binding before you propose/save it —
don't ship a guessed field name.
- **Fanning out over THIS tool's result list (`split_out`)?** Use
`get_tool_contract`'s `primary_array_path`, prefixed `json.` — e.g.
`"path": "json.data.messages"` — as the downstream `split_out.path`,
rather than guessing where the array lives in the response.
- **App not connected yet?** You can still build the node with a real
slug from `search_tool_catalog` (searches the FULL live catalog
regardless of connection state) and ground it with `get_tool_contract
{ slug }` (resolves that known slug's toolkit and fetches ITS full
contract from the same live catalog — a grounding lookup, not a
search, and also works regardless of connection state) and either call
`composio_connect { toolkit }` yourself (see "Connecting integrations"
below) or note in your reply that the user needs to connect it — the
flow will also prompt for the connection the first time it actually runs.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **Native OpenHuman tool** — `config.slug` = `oh:<tool_name>` (e.g.
`oh:web_search`) to call one of the assistant's own built-in tools (search,
media generation, files, …). No `connection_ref`. Args go in `config.args`.
Expand Down
Loading
Loading