feat(nodes): add tool_insforge — InsForge backend agent-tool node - #1714
feat(nodes): add tool_insforge — InsForge backend agent-tool node#1714chinesepowered wants to merge 2 commits into
Conversation
Exposes an InsForge project's REST API to agents: PostgREST-style record
select / insert / upsert / update / delete, database function (RPC)
calls, and storage bucket listing, download URLs and object deletes.
InsForge cannot be handled the way db_postgres handles Supabase. That
preset works because Supabase hands out a real Postgres connection
string; a managed InsForge project documents no direct connection, only
a PostgREST-style REST API, so there is nothing for psycopg to dial.
Self-hosted deployments that do expose their own Postgres are already
served by db_postgres and store_postgres.
Safety properties:
* Read-only by default. Every mutating tool is gated behind an
allow_writes switch that ships off, so binding this node to an agent
cannot change the backend until an operator opts in. rpc_call is
gated with them since nothing in a function's name reveals whether
it mutates.
* records_update and records_delete refuse to run without a filter.
PostgREST applies an empty filter set to every row, so an omitted
filter would rewrite or empty the whole table.
* Table, bucket and function names must be bare identifiers; object
keys may be path-like but reject '..' segments and are escaped per
segment, so no agent-supplied value can redirect a request off the
configured host.
* Filters are validated against the documented PostgREST operator set
rather than passed through as arbitrary query parameters.
* Inserts bypass the retry helper, since a retried insert would
silently duplicate rows.
* Storage downloads return a URL rather than file bytes.
Adds request_with_retry to the shared http_retry helper for PATCH /
DELETE / PUT, which the REST surface needs and which post_with_retry and
get_with_retry did not cover. Purely additive: the existing helpers are
untouched and their tests still pass.
File upload is deliberately not implemented — the current API negotiates
uploads through a multi-step upload-strategy handshake whose response
shape could not be verified against a live project, and the single-call
alternative is deprecated upstream.
🤖 Internal: Discord sync markerAuto-managed by the Discord notification workflow. Stores the linked Discord message ID and forum thread ID. Do not edit or delete. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds the ChangesInsForge agent tool
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Agent
participant IInstance
participant insforge_client
participant InsForgeREST
Agent->>IInstance: Invoke an InsForge tool
IInstance->>insforge_client: Validate inputs and build request
insforge_client->>InsForgeREST: Send authenticated REST request
InsForgeREST-->>insforge_client: Return response
insforge_client-->>IInstance: Parse result or error
IInstance-->>Agent: Return structured output
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@nodes/src/nodes/tool_insforge/IInstance.py`:
- Around line 371-374: Update the storage_list_objects limit schema description
to interpolate DEFAULT_LIMIT and MAX_LIMIT rather than hardcoding 100 and 1000.
Import DEFAULT_LIMIT alongside MAX_LIMIT from .insforge_client, matching the
existing records_select schema pattern.
In `@nodes/src/nodes/tool_insforge/insforge_client.py`:
- Around line 280-303: Broaden the exception handling in the request flow around
get_with_retry, requests.post, and request_with_retry by adding a fallback for
requests.exceptions.RequestException after the specific HTTPError, Timeout, and
ConnectionError handlers. Translate other request-related failures into the
documented actionable ValueError instead of allowing raw transport exceptions to
escape.
In `@nodes/src/nodes/tool_insforge/README.md`:
- Around line 90-96: Update the HTTP failure mapping section to qualify the
429/5xx retry behavior by naming the retryable methods, consistent with the
exception documented near line 83. Explicitly state that POST failures are
surfaced without retry, while preserving the existing exponential-backoff
behavior for supported non-POST methods.
- Around line 67-73: Align the README filter grammar with the validation
performed by encode_filters: either add operator-specific validation so in
requires parenthesized set values and other operators reject unsupported
comma-separated forms, or revise the documentation to describe the broader
grammar currently accepted. Ensure the documented rejection behavior and
examples accurately match the implemented validation.
In `@nodes/src/nodes/tool_insforge/requirements.txt`:
- Line 1: Update the dependency declaration in requirements.txt from unpinned
requests to the minimum safe constraint requests>=2.34.2.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b97be456-60b7-400a-a753-7da2ac16230b
⛔ Files ignored due to path filters (1)
nodes/src/nodes/tool_insforge/insforge.svgis excluded by!**/*.svg
📒 Files selected for processing (12)
docs/README-nodes.mdnodes/src/nodes/tool_insforge/IGlobal.pynodes/src/nodes/tool_insforge/IInstance.pynodes/src/nodes/tool_insforge/README.mdnodes/src/nodes/tool_insforge/__init__.pynodes/src/nodes/tool_insforge/insforge_client.pynodes/src/nodes/tool_insforge/requirements.txtnodes/src/nodes/tool_insforge/services.jsonnodes/test/test_tool_insforge.pypackages/ai/src/ai/common/utils/__init__.pypackages/ai/src/ai/common/utils/http_retry.pypackages/ai/tests/ai/common/utils/test_http_retry.py
- encode_filters now validates the two operator-specific shapes it documents: 'in' requires a parenthesised set and 'is' one of null/true/false/unknown. "in.1,2,3" is the mistake a model actually makes, and PostgREST answers it with a 400 that names no column. - call() catches requests.exceptions.RequestException as a fallback, so the rarer transport failures reach the agent as a reportable message instead of a raw traceback. - README: qualify the 429/5xx retry claim, which omitted that POST is exempt, and describe what the filter validation actually checks. Same correction in the call() docstring. - storage_list_objects interpolates DEFAULT_LIMIT/MAX_LIMIT rather than restating 100/1000. - Pin requests>=2.34.2, matching tool_github, tool_n8n and tool_cognee. - Test stubs model requests' exception hierarchy instead of aliasing the classes to a flat Exception, which let a fallback handler swallow cases the specific handlers own.
|
Agreed with all five. Fixed in 8cf5ee3. Filter grammar vs. validation (README) — right, and the gap was worth closing in code rather than in prose. I left POST retry exemption (README) — correct, the bullet was unqualified. It now names the retried methods and states the POST exemption, cross-referencing the Safety section. The same unqualified claim was in the
Writing the test for it surfaced something in the test file: the import stubs aliased
83 tests pass (was 74), ruff clean. |
|
On the inconclusive Linked Issues check: the icon is present and committed — |
Closes #1711
What
Adds
tool_insforge, an agent-tool node for InsForge — an open-source backend platform (Postgres + auth + storage + edge functions + realtime). Binds to an agent's tool channel like the othertool_*nodes; no data lanes.Why this isn't a
services.insforge.jsonpresetThe obvious question first, since this repo already set the precedent.
db_postgreshandles Supabase as a branded preset:That works because Supabase hands out a real Postgres connection string. A managed InsForge project documents no direct connection — only a PostgREST-style REST API (
GET/POST/PATCH/DELETE /api/database/records/{table},POST /api/database/rpc/{fn}, Bearer auth). There is nothing forpsycopgto dial, so the preset route isn't available.Self-hosted deployments that do expose their own Postgres are already served by
db_postgres/store_postgres; the README says so, and points there for bulk pipeline work.Tools
Read (always available):
records_select,storage_list_buckets,storage_list_objects,storage_get_download_urlWrite (require
allow_writes):records_insert,records_upsert,records_update,records_delete,rpc_call,storage_delete_objectDatabase tools return a uniform
{count, rows, query}envelope so an agent sees one contract regardless of which it called.Safety
allow_writesships off, so binding this node to an agent cannot change the backend until an operator opts in.rpc_callis gated with the writes because nothing in a function's name reveals whether it mutates.records_update/records_deleterefuse to run without at least one filter — PostgREST applies an empty filter set to every row, so an omitted filter would rewrite or empty the whole table...segments and are escaped per segment. No agent-supplied value can redirect a request off the configured host.Shared helper change
Adds
request_with_retrytoai.common.utils.http_retryfor PATCH / DELETE / PUT, which this REST surface needs and whichpost_with_retry/get_with_retrydidn't cover. The module already factored out_retrying/_is_retryablefor exactly this kind of reuse.Purely additive — the existing helpers are untouched. Their 11 tests still pass, and 7 new ones cover the addition.
Testing
pytest nodes/test/test_tool_insforge.py— 74 passed (services.json schema, base-URL normalisation, filter grammar, path-traversal guards, write gating, unfiltered-write refusal, config/env resolution)pytest packages/ai/tests/ai/common/utils/test_http_retry.py— 18 passed (11 pre-existing + 7 new)ruff check/ruff format --check— clean across all touched filesTests follow the
test_tool_n8n.pyconvention: inject import stubs only for modules not already present, then remove them so nothing leaks into the shared session.Deliberately out of scope
upload-strategyhandshake whose response shape I could not verify against a live project, and the single-callPUTalternative is deprecated upstream. Left out rather than guessed at — happy to add it if someone with a project can confirm the contract.store_insforgewould have to go through the admin raw-SQL endpoint, which isn't appropriate for a pipeline node.llm_openai_apialready presets arbitrary OpenAI-compatible endpoints.Notes for review
Prefersemantics come from the published REST docs; the tests cover the node's own logic, not the wire contract. Worth a sanity check from anyone with an account.currentColor. Swap it for the official asset if the project would rather ship that.docs/README-nodes.mdis updated by hand per its own note. The header's service count was already stale before this change, so I left it alone rather than half-correct it.Summary by CodeRabbit
New Features
Documentation
Tests