feat(tools): add Truth Bear GAUGE tools (official-source signals, offline-verifiable, no API key) - #6752
Conversation
…line-verifiable, no API key) Adds three tools for querying official-record signals from US federal agencies and named registries (USGS, NOAA, NWS, EPA, SEC EDGAR, openFDA, EIA, BLS and others): - TruthBearCoverageTool (free): does a signal line exist, how many objects it covers, how fresh those readings are, and whether the source agency is on schedule or overdue. - TruthBearCatalogTool (free): which entity ids are valid for a signal line. - TruthBearRecordTool (paid via x402): requests one official record and returns the HTTP 402 payment challenge untouched. Standard library only - no new dependency. No API key for the free tools. No code here holds keys, signs, or moves funds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdded three CrewAI tools for Truth Bear GAUGE coverage, catalog, and official-record queries. The tools expose JSON responses, validate catalog filters, preserve x402 payment challenges, and are documented, tested, and exported through the package API. ChangesTruth Bear GAUGE integration
Sequence Diagram(s)sequenceDiagram
participant CrewAIAgent
participant TruthBearTool
participant TruthBearAPI
participant Wallet
CrewAIAgent->>TruthBearTool: Submit GAUGE query
TruthBearTool->>TruthBearAPI: Request coverage, catalog, or record data
TruthBearAPI-->>TruthBearTool: Return JSON or x402 challenge
TruthBearTool-->>CrewAIAgent: Return serialized response
CrewAIAgent->>Wallet: Settle x402 challenge when required
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: 3
🧹 Nitpick comments (3)
lib/crewai-tools/tool.specs.json (1)
26662-26754: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAdd the required filter constraint to the generated JSON Schema for
TruthBearCatalogInput.
TruthBearCatalogInputenforcessignal_idorindustryat runtime via_require_a_filter, butTruthBearCatalogInputintool.specs.jsonstill exposes norequiredfields oranyOf/if-thenconstraint. Schema-only callers can plan an invalid catalog request by omitting both filters. Add a JSON SchemaanyOfbetweensignal_idandindustry.🤖 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 `@lib/crewai-tools/tool.specs.json` around lines 26662 - 26754, Update the TruthBearCatalogInput run_params_schema to require at least one filter by adding an anyOf constraint referencing signal_id and industry. Preserve both properties as optional individually while ensuring schema-only callers cannot omit both filters.lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py (2)
96-123: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAssert complete pass-through payloads for both record paths.
Both tests validate selected fields only. The API contract requires preservation of the complete x402 challenge and successful record.
lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py#L96-L123: assertresult["challenge"] == challenge.lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py#L125-L133: assertresult == payload.🤖 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 `@lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py` around lines 96 - 123, Strengthen the pass-through assertions in lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py at lines 96-123 by asserting result["challenge"] exactly equals challenge, preserving the complete x402 payload. At lines 125-133, assert the record-path result exactly equals payload, preserving the complete successful response.
73-87: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a behavior test for the
industryfilter.The catalog contract accepts either
signal_idorindustry. The current tests cover onlysignal_idand the missing-filter error. Add a test that callsTruthBearCatalogTool().run(industry="hydrology")and verifies the returned signal and entities.As per coding guidelines,
**/tests/**/*.py: Write unit tests for new functionality that focus on behavior rather than implementation details.🤖 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 `@lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py` around lines 73 - 87, Add a unit test alongside test_catalog_filtered that calls TruthBearCatalogTool().run(industry="hydrology") with a mocked catalog response, then verifies the returned signal and entity values. Keep the assertion focused on the observable filtered-catalog behavior rather than implementation details.Source: Coding guidelines
🤖 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 `@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md`:
- Line 3: Update the first section heading in Examples.md from the H3 “Tool
Initialization” heading to an H2, preserving its existing text and icon so the
document follows the H1-to-H2 heading hierarchy.
In `@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md`:
- Line 27: Update the TruthBearCatalogTool description in
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md:27 to
state that catalog filtering accepts signal_id or industry, not entity. Update
the catalog lookup description in
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md:64 with
the same contract; the signal_id and entity pair applies only to
TruthBearRecordTool.
In
`@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/truthbear_gauge_tool.py`:
- Around line 37-44: Update the HTTP error helper to return the status code
separately from the parsed payload, preserving the 402 response body unchanged.
In the coverage flow, use the separate status to treat all non-2xx responses as
errors instead of interpreting missing signals as not found, and update
TruthBearRecordTool to return the 402 payload directly without wrapping it.
---
Nitpick comments:
In `@lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py`:
- Around line 96-123: Strengthen the pass-through assertions in
lib/crewai-tools/tests/tools/truthbear_gauge_tool_test.py at lines 96-123 by
asserting result["challenge"] exactly equals challenge, preserving the complete
x402 payload. At lines 125-133, assert the record-path result exactly equals
payload, preserving the complete successful response.
- Around line 73-87: Add a unit test alongside test_catalog_filtered that calls
TruthBearCatalogTool().run(industry="hydrology") with a mocked catalog response,
then verifies the returned signal and entity values. Keep the assertion focused
on the observable filtered-catalog behavior rather than implementation details.
In `@lib/crewai-tools/tool.specs.json`:
- Around line 26662-26754: Update the TruthBearCatalogInput run_params_schema to
require at least one filter by adding an anyOf constraint referencing signal_id
and industry. Preserve both properties as optional individually while ensuring
schema-only callers cannot omit both filters.
🪄 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 Plus
Run ID: c1c2d5c2-ac85-40af-bf8a-f391861a7491
📒 Files selected for processing (8)
lib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.mdlib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/truthbear_gauge_tool.pylib/crewai-tools/tests/tools/truthbear_gauge_tool_test.pylib/crewai-tools/tool.specs.json
| @@ -0,0 +1,137 @@ | |||
| # 🐻 Truth Bear GAUGE Tools — Usage Examples | |||
|
|
|||
| ### 🔧 Tool Initialization | |||
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use an H2 for the first section heading.
Line 3 skips from the H1 at Line 1 to an H3. Change ### 🔧 Tool Initialization to ## 🔧 Tool Initialization to satisfy markdownlint MD001.
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)
[warning] 3-3: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 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 `@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md` at
line 3, Update the first section heading in Examples.md from the H3 “Tool
Initialization” heading to an H2, preserving its existing text and icon so the
document follows the H1-to-H2 heading hierarchy.
Source: Linters/SAST tools
| | Tool | Cost | Purpose | | ||
| | ---- | ---- | ------- | | ||
| | `TruthBearCoverageTool` | free | Does a signal line exist, how many objects it covers, how fresh those readings are, and whether the source agency is on schedule or overdue | | ||
| | `TruthBearCatalogTool` | free | Which entity ids are valid for a signal line — `signal_id` and `entity` must be used as a pair | |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Use the actual catalog contract in both documents.
TruthBearCatalogTool accepts one of signal_id or industry. It does not accept entity. The signal_id plus entity pair applies to TruthBearRecordTool.
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md#L27-L27: describe catalog filtering withsignal_idorindustry.lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md#L64-L64: state that the catalog lookup usessignal_idorindustry.
📍 Affects 2 files
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md#L27-L27(this comment)lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md#L64-L64
🤖 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 `@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md` at
line 27, Update the TruthBearCatalogTool description in
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/README.md:27 to
state that catalog filtering accepts signal_id or industry, not entity. Update
the catalog lookup description in
lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/Examples.md:64 with
the same contract; the signal_id and entity pair applies only to
TruthBearRecordTool.
| except urllib.error.HTTPError as exc: | ||
| body = exc.read().decode("utf-8", errors="replace") | ||
| try: | ||
| payload = json.loads(body) | ||
| except json.JSONDecodeError: | ||
| payload = {"raw": body[:2000]} | ||
| # In x402 a 402 is not a failure, it is the price quote. Return it intact. | ||
| return {"http_status": exc.code, **payload} |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Keep the HTTP status separate from the response payload.
This helper converts every HTTP error into a payload dictionary. A 429 or 5xx response from /gauge/coverage then has no signals field, so Lines 80-82 incorrectly report found: false. The record tool also wraps the 402 body at Lines 166-174, but the PR objective requires payment challenges to be returned unchanged.
Return (status, payload) internally. Handle non-2xx coverage responses as errors. Return the 402 payload itself from TruthBearRecordTool.
Proposed fix
-def _get_json(path: str, params: Optional[dict] = None) -> dict:
+def _get_json(path: str, params: Optional[dict] = None) -> tuple[int, dict]:
...
try:
with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT) as resp:
- return json.loads(resp.read().decode("utf-8"))
+ return resp.status, json.loads(resp.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
...
- return {"http_status": exc.code, **payload}
+ return exc.code, payload- data = _get_json("/gauge/coverage", params)
+ status, data = _get_json("/gauge/coverage", params)
+ if not 200 <= status < 300:
+ return json.dumps({"http_status": status, "error": data}, ensure_ascii=False)- data = _get_json("/gauge", {"signal_id": args.signal_id, "entity": args.entity})
- if data.get("http_status") == 402:
- return json.dumps(
- {
- "payment_required": True,
- "note": "Settle this x402 challenge with your own wallet, then retry the same URL.",
- "challenge": {k: v for k, v in data.items() if k != "http_status"},
- },
- ensure_ascii=False,
- )
+ _, data = _get_json("/gauge", {"signal_id": args.signal_id, "entity": args.entity})
return json.dumps(data, ensure_ascii=False)📝 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.
| except urllib.error.HTTPError as exc: | |
| body = exc.read().decode("utf-8", errors="replace") | |
| try: | |
| payload = json.loads(body) | |
| except json.JSONDecodeError: | |
| payload = {"raw": body[:2000]} | |
| # In x402 a 402 is not a failure, it is the price quote. Return it intact. | |
| return {"http_status": exc.code, **payload} | |
| except urllib.error.HTTPError as exc: | |
| body = exc.read().decode("utf-8", errors="replace") | |
| try: | |
| payload = json.loads(body) | |
| except json.JSONDecodeError: | |
| payload = {"raw": body[:2000]} | |
| # In x402 a 402 is not a failure, it is the price quote. Return it intact. | |
| return exc.code, payload |
🤖 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
`@lib/crewai-tools/src/crewai_tools/tools/truthbear_gauge_tool/truthbear_gauge_tool.py`
around lines 37 - 44, Update the HTTP error helper to return the status code
separately from the parsed payload, preserving the 402 response body unchanged.
In the coverage flow, use the separate status to treat all non-2xx responses as
errors instead of interpreting missing signals as not found, and update
TruthBearRecordTool to return the 402 payload directly without wrapping it.
What this adds
Three tools for querying Truth Bear GAUGE, a pay-per-call source of official-record signals (US federal agencies and named registries: USGS, NOAA, NWS, EPA, SEC EDGAR, openFDA, EIA, BLS, FAA, US CBP, USTR, USITC, Federal Register, plus IMF PortWatch and UNFCCC feeds).
TruthBearCoverageToolTruthBearCatalogToolsignal_idandentitymust be used as a pair)TruthBearRecordToolWhy it may be useful here
Every paid record ships a canonical sha256
record_hashthat the caller can recompute offline from the delivered payload, so an agent does not have to trust the endpoint it just paid. Readings are stated against the official threshold ladder published by the source agency rather than a scale we invented, and each record carries a same-season percentile so an agent can tell routine from unusual.The service is descriptive only: no forecast, no recommendation, no adjudication.
Notes for reviewers
urllib,json) pluspydantic, which crewai-tools already requires.TruthBearRecordToolreturns the x402 challenge (network, asset, amount, recipient) to the caller and stops. Settling it is the caller's own wallet's job; nothing here holds keys or signs anything.TruthBearCatalogToolrejects a call with neithersignal_idnorindustry.signalslist with HTTP 200, not an error — the tool converts that to an explicit{"found": false}so the agent does not mistake an empty answer for a failed call.tool.specs.jsonwas regenerated withpython -m crewai_tools.generate_tool_specs; the diff is additive only (96 → 99 tools).Testing
tests/tools/truthbear_gauge_tool_test.pycovers the three tools with mocked HTTP: coverage hit, coverage miss (emptysignals), the required-filter guard on the catalog tool, the 402 challenge being returned intact rather than raised as an error, and a paid success passing through unchanged.Run under this repo's own pytest configuration, including
--block-network, so the mocks are verified to make no real network calls.The endpoint behaviour the tools rely on was also verified against the live service before opening this PR: coverage returns 1 signal for a valid id and 0 for an unknown one; a filtered catalog response is 1.8 KB with 10 entities; the paid endpoint answers 402 with a well-formed
acceptsentry carrying network, asset, payTo and amount.