Skip to content

feat(tools): add OptionsAhoyTool for equity-compensation tax calculations#6148

Open
AlvisoOculus wants to merge 4 commits into
crewAIInc:mainfrom
AlvisoOculus:feat/optionsahoy-tool
Open

feat(tools): add OptionsAhoyTool for equity-compensation tax calculations#6148
AlvisoOculus wants to merge 4 commits into
crewAIInc:mainfrom
AlvisoOculus:feat/optionsahoy-tool

Conversation

@AlvisoOculus

@AlvisoOculus AlvisoOculus commented Jun 13, 2026

Copy link
Copy Markdown

Summary

Adds OptionsAhoyTool, a built-in tool that computes the tax outcome of common equity-compensation decisions using the OptionsAhoy public REST API. Each calculation runs against the federal tax code and all fifty states plus the District of Columbia.

The API is keyless, so the tool needs no API key and no new dependency. It uses requests, which is already a core dependency.

A single tool exposes seven calculators through a calculator selector and an inputs object:

  • amt-iso: optimize a multi-year incentive stock option (ISO) exercise schedule under the alternative minimum tax (AMT)
  • nso: tax and after-tax proceeds of exercising non-qualified stock options (NSOs), holding versus selling
  • rsu-sell-vs-hold: selling vested restricted stock units (RSUs) at vest versus holding, on an after-tax, risk-adjusted basis
  • concentration: analyze a concentrated single-stock position and the after-tax cost of diversifying it
  • protective-put: price a protective put hedge at a given downside protection level and tenor
  • qsbs: check qualified small business stock (QSBS) eligibility and the resulting capital-gains exclusion
  • equity-funding: plan which equity lots to sell, and when, to fund a cash goal by a target date at the least after-tax cost

This follows the existing single-API tool pattern in the catalog (for example SerperDevTool, which also wraps one provider's HTTP endpoint with requests).

Usage

from crewai_tools import OptionsAhoyTool

tool = OptionsAhoyTool()

result = tool.run(
    calculator="qsbs",
    inputs={
        "acquisitionDate": "2018-01-01",
        "saleDate": "2026-02-01",
        "entityType": "us-c-corp",
        "acquisitionMethod": "original-issuance",
        "assetCategory": "under-50m",
        "industry": "tech-software",
        "activeBusiness": "yes",
        "adjustedBasis": 10000,
        "expectedGain": 2000000,
        "stateCode": "CA",
        "ordinaryIncome": 250000,
        "filingStatus": "single",
    },
)

Changes

  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/ — tool, package __init__.py, and README
  • Registered OptionsAhoyTool in crewai_tools/tools/__init__.py and crewai_tools/__init__.py
  • lib/crewai-tools/tests/tools/optionsahoy_tool_test.py — unit tests (mocked HTTP, no network, no key)

Testing

  • uv run pytest tests/tools/optionsahoy_tool_test.py — 7 passed
  • uv run ruff check and ruff format --check — clean
  • uv run pyright on the new files — 0 errors

Summary by CodeRabbit

  • New Features

    • Added OptionsAhoyTool to integrate with the OptionsAhoy REST API, exposing multiple financial calculators and configurable base URL/timeout.
  • Documentation

    • Added a comprehensive guide covering supported calculators, input formats, usage examples, installation, and configuration.
  • Tests

    • Added unit tests covering initialization, request payload behavior, validation of required inputs, and error handling.

…ions

Add a keyless OptionsAhoyTool that wraps the OptionsAhoy public REST API.
A single tool exposes seven calculators via a calculator selector and an
inputs object: incentive stock option (ISO) exercise under the alternative
minimum tax (AMT), non-qualified stock option (NSO) exercise, restricted
stock unit (RSU) sell-versus-hold, single-stock concentration, protective
put pricing, qualified small business stock (QSBS) eligibility, and funding
a cash goal from equity lots.

Uses the existing requests dependency, requires no API key, and registers
in the package exports. Includes a README and mocked unit tests with no
network access.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new OptionsAhoyTool that integrates with the public OptionsAhoy REST API (seven calculators). It includes input schema and validation, payload construction (preserving nullable terminationDate), HTTP POST execution with structured error handling, tests, README, and package exports.

Changes

OptionsAhoy Tool Integration

Layer / File(s) Summary
Tool schema and type contracts
lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/optionsahoy_tool.py
CalculatorName literal, REQUIRED_FIELDS map, _KEEP_NULL set, and OptionsAhoyToolSchema Pydantic model define supported calculators and validate calculator and inputs.
Tool implementation
lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/optionsahoy_tool.py
OptionsAhoyTool provides name, description, args_schema, base_url, timeout; _check_required validates inputs, _build_payload filters None (keeps terminationDate), _run POSTs to /api/v1/{calculator} and returns indented JSON or structured error JSON on failures.
Test suite
lib/crewai-tools/tests/tools/optionsahoy_tool_test.py
Fixture yields OptionsAhoyTool; tests cover defaults/overrides, correct endpoint and payload/timeout, serialization rules (omit None vs preserve terminationDate), missing/None required-field validation, HTTP error parsing, and request-exception handling.
Documentation and package wiring
lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/README.md, lib/crewai-tools/src/crewai_tools/__init__.py, lib/crewai-tools/src/crewai_tools/tools/__init__.py
README documents calculators, inputs, examples, and configuration; package __init__ files import and export OptionsAhoyTool.
sequenceDiagram
  participant Caller
  participant OptionsAhoyTool
  participant Validator
  participant PayloadBuilder
  participant OptionsAhoyAPI

  Caller->>OptionsAhoyTool: run(calculator, inputs)
  OptionsAhoyTool->>Validator: _check_required(calculator, inputs)
  Validator-->>OptionsAhoyTool: validated
  OptionsAhoyTool->>PayloadBuilder: _build_payload(inputs)
  PayloadBuilder-->>OptionsAhoyTool: payload (None filtered except terminationDate)
  OptionsAhoyTool->>OptionsAhoyAPI: POST /api/v1/{calculator} with JSON + timeout
  alt Success (200 + JSON)
    OptionsAhoyAPI-->>OptionsAhoyTool: JSON response
    OptionsAhoyTool-->>Caller: pretty JSON string (result)
  else HTTPError
    OptionsAhoyAPI-->>OptionsAhoyTool: error response
    OptionsAhoyTool-->>Caller: structured error JSON (status + parsed body)
  else Request Exception
    OptionsAhoyAPI-->>OptionsAhoyTool: connection/timeout error
    OptionsAhoyTool-->>Caller: structured error JSON (exception message)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 New tool hops in to call OptionsAhoy’s door,
Seven calculators hum, inputs checked to the core,
Payloads trim None while keeping one null friend,
Tests stand guard so responses stay well-penned.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: addition of OptionsAhoyTool for equity-compensation tax calculations, which matches the PR's primary objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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/optionsahoy_tool/optionsahoy_tool.py`:
- Around line 159-173: The pre-flight check in _check_required only tests for
presence of keys and therefore misses required inputs that are present but set
to None (which _build_payload later strips), so update _check_required to treat
a required field as missing when it is either not present in inputs or its value
is None (except for keys explicitly allowed to be null via _KEEP_NULL, e.g.,
terminationDate); change the missing list comprehension to something like:
missing = [field for field in required if field not in inputs or
(inputs.get(field) is None and field not in _KEEP_NULL)] so validation fails for
None-valued required fields.
🪄 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: 1745a862-741a-4085-a267-ab15bf6d3ff3

📥 Commits

Reviewing files that changed from the base of the PR and between d80719d and d46b5a9.

📒 Files selected for processing (6)
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/README.md
  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/optionsahoy_tool.py
  • lib/crewai-tools/tests/tools/optionsahoy_tool_test.py

@AlvisoOculus

Copy link
Copy Markdown
Author

Thanks for the catch. Fixed: _check_required now treats a required field that is present but None as missing, since _build_payload strips None values before the request (the documented exception is terminationDate, which is intentionally kept as null and stays in _KEEP_NULL). Added a regression test (test_optionsahoy_tool_none_required_field_raises) that passes a required field as None and asserts the validation error.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lib/crewai-tools/tests/tools/optionsahoy_tool_test.py (1)

100-114: ⚡ Quick win

Tighten the regression assertion to the specific missing field.

The current regex is broad and can pass for unrelated missing-field failures. Assert that strike is the reported missing required field to keep this regression precise.

Suggested test assertion update
-    with pytest.raises(ValueError, match="missing required input field"):
+    with pytest.raises(
+        ValueError, match=r"missing required input field\(s\): strike"
+    ):
🤖 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/optionsahoy_tool_test.py` around lines 100 -
114, The test's ValueError assertion is too generic; update the pytest.raises
match to assert the error specifically names the missing "strike" field by
changing match="missing required input field" to a tighter pattern that includes
"strike" (for example match="missing required input field.*strike" or an exact
message mentioning strike) for the tool.run call so the regression verifies that
"strike" is the reported missing field.
🤖 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.

Nitpick comments:
In `@lib/crewai-tools/tests/tools/optionsahoy_tool_test.py`:
- Around line 100-114: The test's ValueError assertion is too generic; update
the pytest.raises match to assert the error specifically names the missing
"strike" field by changing match="missing required input field" to a tighter
pattern that includes "strike" (for example match="missing required input
field.*strike" or an exact message mentioning strike) for the tool.run call so
the regression verifies that "strike" is the reported missing field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b556726-1bbd-49c3-a5ed-c1a53b87020b

📥 Commits

Reviewing files that changed from the base of the PR and between d46b5a9 and 67e8014.

📒 Files selected for processing (2)
  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/optionsahoy_tool.py
  • lib/crewai-tools/tests/tools/optionsahoy_tool_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/crewai-tools/src/crewai_tools/tools/optionsahoy_tool/optionsahoy_tool.py

@AlvisoOculus

Copy link
Copy Markdown
Author

Thanks for the review. The one item CodeRabbit flagged is addressed: required fields whose value is None are now treated as missing during pre-flight validation (previously only key presence was checked), with a regression test covering it. The intentionally-nullable terminationDate is preserved. CodeRabbit has since approved and the checks are green.

The branch has fallen a little behind main but is still mergeable with no conflicts. I am glad to rebase or update it if you would like it current. Is there anything else needed before this can be merged?

@AlvisoOculus

Copy link
Copy Markdown
Author

Quick status nudge on this one. It's approved by the review bot and now conflict-free with main after a rebase. CI looks like it may be waiting on a maintainer to approve the workflow run for an outside contributor.

Is there anything else you'd like changed before this can be considered for merge? Happy to address any feedback. Thanks for crewAI.

@greysonlalonde if you have a moment to take a look, much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant