Add TinyFish integration tools#2125
Conversation
📝 WalkthroughWalkthroughAdds TinyFish web-search, page-fetch, and browser-automation tools: configuration toggles and pricing fields, three Tool implementations that proxy via IntegrationClient, fake-backend routes and tests, runtime registration and integration tests, and documentation/catalog updates. ChangesTinyFish Integration Tools
Sequence Diagram(s)sequenceDiagram
participant Agent
participant TinyFishSearchTool
participant TinyFishFetchTool
participant TinyFishAgentRunTool
participant IntegrationClient
participant Backend
Agent->>TinyFishSearchTool: invoke(search args)
TinyFishSearchTool->>IntegrationClient: POST /agent-integrations/tinyfish/search
IntegrationClient->>Backend: forward search request
Backend->>IntegrationClient: search response
IntegrationClient->>TinyFishSearchTool: response
TinyFishSearchTool->>Agent: ToolResult
Agent->>TinyFishFetchTool: invoke(fetch args)
TinyFishFetchTool->>IntegrationClient: POST /agent-integrations/tinyfish/fetch
IntegrationClient->>Backend: forward fetch request
Backend->>IntegrationClient: fetch response
IntegrationClient->>TinyFishFetchTool: response
TinyFishFetchTool->>Agent: ToolResult
Agent->>TinyFishAgentRunTool: invoke(agent run args)
TinyFishAgentRunTool->>IntegrationClient: POST /agent-integrations/tinyfish/agent/run
IntegrationClient->>Backend: forward agent.run request
Backend->>IntegrationClient: agent.run response
IntegrationClient->>TinyFishAgentRunTool: response
TinyFishAgentRunTool->>Agent: ToolResult
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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: 1
🧹 Nitpick comments (3)
src/openhuman/integrations/tinyfish.rs (3)
310-418: ⚡ Quick winAdd debug logging on the error path.
The fetch tool logs at debug level on entry (line 339) but omits logging when the POST fails at line 416.
📊 Observability improvement
match self .client .post::<TinyFishFetchResponse>("/agent-integrations/tinyfish/fetch", &body) .await { Ok(resp) => { // ... existing success handling ... } - Err(e) => Ok(ToolResult::error(format!("TinyFish fetch failed: {e}"))), + Err(e) => { + tracing::debug!(error = %e, "[tinyfish_fetch] request failed"); + Ok(ToolResult::error(format!("TinyFish fetch failed: {e}"))) + } }As per coding guidelines: use tracing at debug or trace level for error paths.
🤖 Prompt for 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. In `@src/openhuman/integrations/tinyfish.rs` around lines 310 - 418, The execute method logs entry but doesn't emit debug info when the POST fails; update the error path that handles the client.post::<TinyFishFetchResponse> call (the Err(e) arm) to emit a tracing::debug (or tracing::error) with the error details and relevant context (e.g., url_count or normalized_urls and request body) before returning ToolResult::error, so failures are recorded with the actual error and helpful context for debugging.
493-563: ⚡ Quick winUse debug level and add error-path logging for consistency.
Line 527 uses
infolevel, while search/fetch usedebug. Line 558 returns an error without logging it first.📊 Observability improvements
- tracing::info!(url = url, "[tinyfish_agent_run] starting automation"); + tracing::debug!(url = url, "[tinyfish_agent_run] starting automation"); match self .client .post::<TinyFishAgentRunResponse>("/agent-integrations/tinyfish/agent/run", &body) .await { Ok(resp) => { // ... existing success handling ... } - Err(e) => Ok(ToolResult::error(format!( - "TinyFish automation failed: {e}" - ))), + Err(e) => { + tracing::debug!(error = %e, "[tinyfish_agent_run] request failed"); + Ok(ToolResult::error(format!("TinyFish automation failed: {e}"))) + } }As per coding guidelines: use tracing at debug or trace level for RPC entry/exit and error paths.
🤖 Prompt for 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. In `@src/openhuman/integrations/tinyfish.rs` around lines 493 - 563, The execute method currently logs the RPC start at info level and returns errors without logging; change tracing::info!(url = url, "[tinyfish_agent_run] starting automation") to tracing::debug! (or trace per guidelines) and add a tracing::debug/error log in the Err(e) branch that includes the url and the error (and any run_id if available) before returning ToolResult::error; also add a debug log on successful response exit (e.g., include resp.run_id, resp.status, resp.error) to keep entry/exit and error-path observability consistent in execute.
184-249: ⚡ Quick winConsider debug-level logging on error path and aligning success-path log level.
Line 200 correctly uses
debuglevel for the happy path, but line 247 returns an error without logging it first. Line 527 inagent_runusesinfoinstead ofdebug.📊 Observability improvements
Add debug logging on the error path:
match self .client .post::<TinyFishSearchResponse>("/agent-integrations/tinyfish/search", &body) .await { Ok(resp) => { // ... existing success handling ... } - Err(e) => Ok(ToolResult::error(format!("TinyFish search failed: {e}"))), + Err(e) => { + tracing::debug!(error = %e, "[tinyfish_search] request failed"); + Ok(ToolResult::error(format!("TinyFish search failed: {e}"))) + } }As per coding guidelines: use tracing at debug or trace level for RPC entry/exit and error paths.
🤖 Prompt for 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. In `@src/openhuman/integrations/tinyfish.rs` around lines 184 - 249, The execute method currently logs the happy path with tracing::debug but does not log errors; add a tracing::debug (or tracing::trace if you prefer finer verbosity) in the Err branch of TinyFishIntegration::execute before returning ToolResult::error to record the error and request context (include query and body summary); ensure the existing tracing::debug at the start of execute remains. Also update the logging call in agent_run (which currently uses tracing::info) to tracing::debug so RPC entry/exit and error paths use the same debug-level convention.
🤖 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 `@docs/TEST-COVERAGE-MATRIX.md`:
- Line 259: The summary totals in the "## Summary" block of
TEST-COVERAGE-MATRIX.md are out of date after adding the new "7.2.3 | TinyFish
Integration Tools" row; open that file, locate the "## Summary" table and
increment the overall covered leaf count and the total leaf count to reflect the
newly covered leaf (the new row references
src/openhuman/integrations/tinyfish_tests.rs and
src/openhuman/tools/ops_tests.rs::all_tools_executes_tinyfish_family_against_fake_backend),
then verify any derived percentages or aggregate subtotals in the same summary
table are recalculated accordingly so the matrix remains internally consistent.
---
Nitpick comments:
In `@src/openhuman/integrations/tinyfish.rs`:
- Around line 310-418: The execute method logs entry but doesn't emit debug info
when the POST fails; update the error path that handles the
client.post::<TinyFishFetchResponse> call (the Err(e) arm) to emit a
tracing::debug (or tracing::error) with the error details and relevant context
(e.g., url_count or normalized_urls and request body) before returning
ToolResult::error, so failures are recorded with the actual error and helpful
context for debugging.
- Around line 493-563: The execute method currently logs the RPC start at info
level and returns errors without logging; change tracing::info!(url = url,
"[tinyfish_agent_run] starting automation") to tracing::debug! (or trace per
guidelines) and add a tracing::debug/error log in the Err(e) branch that
includes the url and the error (and any run_id if available) before returning
ToolResult::error; also add a debug log on successful response exit (e.g.,
include resp.run_id, resp.status, resp.error) to keep entry/exit and error-path
observability consistent in execute.
- Around line 184-249: The execute method currently logs the happy path with
tracing::debug but does not log errors; add a tracing::debug (or tracing::trace
if you prefer finer verbosity) in the Err branch of TinyFishIntegration::execute
before returning ToolResult::error to record the error and request context
(include query and body summary); ensure the existing tracing::debug at the
start of execute remains. Also update the logging call in agent_run (which
currently uses tracing::info) to tracing::debug so RPC entry/exit and error
paths use the same debug-level convention.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 091e4172-6c73-43ef-9a68-575ed5d39977
📒 Files selected for processing (12)
docs/TEST-COVERAGE-MATRIX.mdsrc/openhuman/about_app/catalog.rssrc/openhuman/composio/client_tests.rssrc/openhuman/config/schema/tools.rssrc/openhuman/integrations/mod.rssrc/openhuman/integrations/test_support.rssrc/openhuman/integrations/test_support_test.rssrc/openhuman/integrations/tinyfish.rssrc/openhuman/integrations/tinyfish_tests.rssrc/openhuman/integrations/types.rssrc/openhuman/tools/ops.rssrc/openhuman/tools/ops_tests.rs
# Conflicts: # src/openhuman/config/schema/tools.rs
Extends `IntegrationToggle` with `mode` ("managed" | "byo") and
`api_key`. Adds `is_active()` that returns true iff:
- `enabled = true` AND `mode = "managed"` (current behavior — the
OpenHuman backend owns the upstream key), OR
- `enabled = true` AND `mode = "byo"` AND `api_key` is a non-empty
trimmed string.
Tool registration for apify, google_places, parallel, tinyfish,
stock_prices, and twilio now goes through `is_active()` so a BYO
toggle without a key never registers the tools — matching the rule
the upcoming Search Engines settings panel will surface to users.
Default behavior is unchanged: integrations default to managed mode
with no api_key, so existing configs continue to register tools as
soon as `enabled = true`.
Follow-up: a Search Engines settings panel (TinyFish, Parallel,
Google Places) needs a write-capable config RPC analogous to
composio_set_api_key / composio_clear_api_key. Tracked separately.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@src/openhuman/config/schema/tools.rs`:
- Around line 562-576: IntegrationToggle currently derives Debug which will
print the sensitive api_key; remove the automatic Debug derive and provide a
manual impl Debug for IntegrationToggle (or drop Debug entirely) that redacts or
omits the api_key field when formatting, similar to the
PolymarketClobCredentials implementation later in this file; keep the existing
derives for Clone/Serialize/Deserialize/JsonSchema and ensure the manual Debug
impl references the struct name and prints enabled and mode but replaces api_key
with a constant like "<redacted>" or nothing.
- Around line 583-594: The is_active helper currently treats any mode other than
INTEGRATION_MODE_BYO as active; change it to fail closed by only returning true
for known, valid modes. Update the match on self.mode.as_str() in is_active:
keep the INTEGRATION_MODE_BYO arm that validates api_key using
api_key.as_deref().map(|s| !s.trim().is_empty()).unwrap_or(false), add an
explicit arm for the legitimate managed mode (e.g., INTEGRATION_MODE_MANAGED)
that returns true, and make the wildcard (_) arm return false so unknown/typo
modes are treated as inactive; ensure you reference the enabled field early as
the current guard remains.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dca5efbf-fe62-4316-b892-584733e9665e
📒 Files selected for processing (3)
docs/TEST-COVERAGE-MATRIX.mdsrc/openhuman/about_app/catalog.rssrc/openhuman/config/schema/tools.rs
✅ Files skipped from review due to trivial changes (1)
- docs/TEST-COVERAGE-MATRIX.md
🚧 Files skipped from review as they are similar to previous changes (1)
- src/openhuman/about_app/catalog.rs
| #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] | ||
| #[serde(default)] | ||
| pub struct IntegrationToggle { | ||
| #[serde(default = "defaults::default_true")] | ||
| pub enabled: bool, | ||
| /// Routing mode. One of [`INTEGRATION_MODE_MANAGED`] (default — the | ||
| /// OpenHuman backend proxies the call) or [`INTEGRATION_MODE_BYO`] | ||
| /// (the user's own API key is required and tools refuse to | ||
| /// register without it). | ||
| #[serde(default = "default_integration_mode")] | ||
| pub mode: String, | ||
| /// API key for [`INTEGRATION_MODE_BYO`]. Ignored in managed mode. | ||
| /// Trimmed empty / `None` ⇒ no BYO key configured. | ||
| #[serde(default)] | ||
| pub api_key: Option<String>, |
There was a problem hiding this comment.
Redact api_key from Debug.
IntegrationToggle now contains a secret but still derives Debug, so any {:?} log of this config will print the raw BYO key. Please switch to a manual Debug impl, like PolymarketClobCredentials does later in this file, or drop the derive entirely.
Proposed fix
-#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
+#[derive(Clone, Serialize, Deserialize, JsonSchema)]
#[serde(default)]
pub struct IntegrationToggle {
#[serde(default = "defaults::default_true")]
pub enabled: bool,
@@
#[serde(default)]
pub api_key: Option<String>,
}
+
+impl std::fmt::Debug for IntegrationToggle {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("IntegrationToggle")
+ .field("enabled", &self.enabled)
+ .field("mode", &self.mode)
+ .field("api_key", &self.api_key.as_ref().map(|_| "<redacted>"))
+ .finish()
+ }
+}As per coding guidelines, "Never log secrets, raw JWTs, API keys, or full PII — redact or omit sensitive fields."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] | |
| #[serde(default)] | |
| pub struct IntegrationToggle { | |
| #[serde(default = "defaults::default_true")] | |
| pub enabled: bool, | |
| /// Routing mode. One of [`INTEGRATION_MODE_MANAGED`] (default — the | |
| /// OpenHuman backend proxies the call) or [`INTEGRATION_MODE_BYO`] | |
| /// (the user's own API key is required and tools refuse to | |
| /// register without it). | |
| #[serde(default = "default_integration_mode")] | |
| pub mode: String, | |
| /// API key for [`INTEGRATION_MODE_BYO`]. Ignored in managed mode. | |
| /// Trimmed empty / `None` ⇒ no BYO key configured. | |
| #[serde(default)] | |
| pub api_key: Option<String>, | |
| #[derive(Clone, Serialize, Deserialize, JsonSchema)] | |
| #[serde(default)] | |
| pub struct IntegrationToggle { | |
| #[serde(default = "defaults::default_true")] | |
| pub enabled: bool, | |
| /// Routing mode. One of [`INTEGRATION_MODE_MANAGED`] (default — the | |
| /// OpenHuman backend proxies the call) or [`INTEGRATION_MODE_BYO`] | |
| /// (the user's own API key is required and tools refuse to | |
| /// register without it). | |
| #[serde(default = "default_integration_mode")] | |
| pub mode: String, | |
| /// API key for [`INTEGRATION_MODE_BYO`]. Ignored in managed mode. | |
| /// Trimmed empty / `None` ⇒ no BYO key configured. | |
| #[serde(default)] | |
| pub api_key: Option<String>, | |
| } | |
| impl std::fmt::Debug for IntegrationToggle { | |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
| f.debug_struct("IntegrationToggle") | |
| .field("enabled", &self.enabled) | |
| .field("mode", &self.mode) | |
| .field("api_key", &self.api_key.as_ref().map(|_| "<redacted>")) | |
| .finish() | |
| } | |
| } |
🤖 Prompt for 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.
In `@src/openhuman/config/schema/tools.rs` around lines 562 - 576,
IntegrationToggle currently derives Debug which will print the sensitive
api_key; remove the automatic Debug derive and provide a manual impl Debug for
IntegrationToggle (or drop Debug entirely) that redacts or omits the api_key
field when formatting, similar to the PolymarketClobCredentials implementation
later in this file; keep the existing derives for
Clone/Serialize/Deserialize/JsonSchema and ensure the manual Debug impl
references the struct name and prints enabled and mode but replaces api_key with
a constant like "<redacted>" or nothing.
| pub fn is_active(&self) -> bool { | ||
| if !self.enabled { | ||
| return false; | ||
| } | ||
| match self.mode.as_str() { | ||
| INTEGRATION_MODE_BYO => self | ||
| .api_key | ||
| .as_deref() | ||
| .map(|s| !s.trim().is_empty()) | ||
| .unwrap_or(false), | ||
| _ => true, | ||
| } |
There was a problem hiding this comment.
Make unknown integration modes fail closed.
Any mode other than exact "byo" currently returns true, so a typo like "manged" or "BYO" still registers the integration as active. Since this helper is the registration gate, invalid values should be rejected or treated as inactive instead of silently enabling managed routing.
Proposed fix
pub fn is_active(&self) -> bool {
if !self.enabled {
return false;
}
match self.mode.as_str() {
+ INTEGRATION_MODE_MANAGED => true,
INTEGRATION_MODE_BYO => self
.api_key
.as_deref()
.map(|s| !s.trim().is_empty())
.unwrap_or(false),
- _ => true,
+ _ => false,
}
}🤖 Prompt for 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.
In `@src/openhuman/config/schema/tools.rs` around lines 583 - 594, The is_active
helper currently treats any mode other than INTEGRATION_MODE_BYO as active;
change it to fail closed by only returning true for known, valid modes. Update
the match on self.mode.as_str() in is_active: keep the INTEGRATION_MODE_BYO arm
that validates api_key using api_key.as_deref().map(|s|
!s.trim().is_empty()).unwrap_or(false), add an explicit arm for the legitimate
managed mode (e.g., INTEGRATION_MODE_MANAGED) that returns true, and make the
wildcard (_) arm return false so unknown/typo modes are treated as inactive;
ensure you reference the enabled field early as the current guard remains.
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Summary
tinyfish_search,tinyfish_fetch, andtinyfish_agent_runbehind the standardintegrations.tinyfish.enabledtoggle.Problem
OpenHuman has backend-proxied integration surfaces for providers like Apify, Parallel, Google Places, stock data, and Twilio, but no first-class TinyFish entry point. TinyFish maps cleanly onto the existing integration-tool model and gives the agent a single provider for web search, browser-rendered page extraction, and goal-based site automation.
Solution
This PR adds a Rust-side TinyFish integration module that follows the existing
IntegrationClientpattern. The client continues to call OpenHuman backend routes, so TinyFish API keys, rate limits, and billing remain server-side.The new tool family covers:
tinyfish_search: ranked search results with optional location/language/page/thumbnail parameters.tinyfish_fetch: browser-rendered extraction for up to 10 known URLs, with format/link/image-link options.tinyfish_agent_run: executable goal-based browser automation with optional structured output, stealth profile, proxy country, and vault scoping fields.Tests use the existing fake integration backend and do not call TinyFish directly.
Submission Checklist
7.2.3 TinyFish Integration Toolsand recalculated the summary totals indocs/TEST-COVERAGE-MATRIX.md## RelatedCloses #NNNin the## Relatedsection: N/A — no linked issueImpact
Runtime impact is limited to core tool registration when the user is signed in and
integrations.tinyfish.enabledis true. There are no new client-side secrets or SDK dependencies. The OpenHuman backend must expose the corresponding/agent-integrations/tinyfish/*proxy routes for live execution.Related
7.2.3/agent-integrations/tinyfish/search,/agent-integrations/tinyfish/fetch, and/agent-integrations/tinyfish/agent/runif not already deployed.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
feat/tinyfish-integration98094e099f26613ed1a421ce4c6d12292e6f36a8Validation Run
pnpm --filter openhuman-app format:checkpnpm typecheckcargo test --manifest-path Cargo.toml tinyfish --libcargo test --manifest-path Cargo.toml all_tools_registers_integration_families_when_enabled_and_signed_in --libcargo test --manifest-path Cargo.toml fake_backend --libcargo test --manifest-path Cargo.toml pricing_for_config_short_circuits_in_direct_mode --libcargo test --manifest-path Cargo.toml integration_pricing_defaults_on_missing_fields --libcargo fmt --manifest-path Cargo.toml -- --checkpnpm --filter openhuman-app rust:format:checkandpnpm --filter openhuman-app rust:checkninjaprerequisite; eslint reported existing warnings but no errors.Validation Blocked
Behavior Changes
Parity Contract
IntegrationClientdispatch path.Duplicate / Superseded PR Handling
Summary by CodeRabbit