feat(stackone): add StackOne AI integration#3
Conversation
Add integration with StackOne unified API platform for HRIS, ATS, CRM, and other business systems. - Add `tool_from_stackone()` for single tool creation - Add `StackOneToolset` for bulk tool registration with pattern matching - Require stackone-ai>=2.1.1 (Python 3.10+) - Add documentation and examples - Add comprehensive unit tests with 100% coverage
d77e04f to
888b2d4
Compare
- Fix StackOne URL from .co to .com - Update description to reflect current positioning as "AI Integration Gateway"
glebedel
left a comment
There was a problem hiding this comment.
We should update the examples but also add the meta tools! That would be a great value-add and differentiator for pydantic too because they might not have this concept at all.
eg. via import search_tool & execute_tool and that could take any tools, stackone tools for sure but also any other pydantic tools (eg. let's say the exa one)
Add search_tool, execute_tool, and feedback_tool functions that wrap StackOne's utility tools API (tool_search, tool_execute, tool_feedback). Extend StackOneToolset with new options: - include_utility_tools: enables dynamic tool discovery mode - include_feedback_tool: adds feedback collection capability - hybrid_alpha: configures BM25/TF-IDF search weight Update documentation and examples to reflect the new API.
Update minimum version requirement to 2.3.0 which includes the utility_tools API (tool_search, tool_execute, tool_feedback).
- Update StackOneToolset tests to match new implementation that uses _tool_from_stackone_tool directly instead of tool_from_stackone - Add test for include_utility_tools option - Mark docstring examples with test="skip" to avoid linting issues
- Remove `{test="skip"}` from docstring examples
- Simplify StackOneToolset example to match ExaToolset pattern
- Move Args documentation from class docstring to __init__ method
- Remove verbose examples from search_tool, execute_tool, feedback_tool
- Fix pyright type error in test file with ignore comment
- Replace hris_* with stackone_* in filter_pattern example - Replace hris_list_employees/hris_get_employee with stackone_* tools - Update description to remove 'unified interface' terminology
…_*/stackone_* Per PR review feedback, update all examples and tests to use actual StackOne provider naming patterns like 'bamboohr_*' and 'workday_*' instead of fictional 'hris_*' or 'stackone_*' patterns. - Update docs/toolsets.md examples - Update docs/third-party-tools.md examples - Update examples/stackone_integration.py - Update tests/test_ext_stackone.py - Update docstrings in pydantic_ai_slim/pydantic_ai/ext/stackone.py
- Format test file with ruff to fix line length issues
- Add {test="skip"} to StackOneToolset docstring example to skip
example test that requires stackone-ai package
Add stackone.py and test_ext_stackone.py to coverage omit list, similar to aci.py and exa.py which are also external integrations.
|
Managed to get the CI passing now also noticed that in the Docs we are adding too many tools strategies there. @glebedel Comparing to the others they just added how to use a toolset and multiple tools and thats's it .. having the Dynamic Discovery, Feedback and Search (semantic/lexical) would be great but we are adding too much on the page in the docs. |
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="pydantic_ai_slim/pydantic_ai/ext/stackone.py">
<violation number="1" location="pydantic_ai_slim/pydantic_ai/ext/stackone.py:117">
P2: Detect `execute_config["account_ids"]` by key presence, not truthiness, so mutual-exclusion and resolution logic work correctly for falsy values.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…listed-as-third-party-toolset-on-pydantic # Conflicts: # docs/install.md
|
|
||
| ## StackOne Tools {#stackone-tools} | ||
|
|
||
| [StackOne](https://www.stackone.com) is Integration Infrastructure for AI Agents, providing tools for 200+ enterprise applications. If you'd like to use a StackOne tool with Pydantic AI, you can use the [`tool_from_stackone`][pydantic_ai.ext.stackone.tool_from_stackone] convenience method. You will need to install the `stackone-ai` package (requires Python 3.10+), set `STACKONE_API_KEY` in your environment, and provide one or more connected account IDs via `STACKONE_ACCOUNT_ID` or by passing `account_ids=...` directly (a single string or a list). |
There was a problem hiding this comment.
We need users to sets STACKONE_ACCOUNT_ID or pass account_ids either single or multiple IDS .. I think only API Key is not enough we need account ID(s) .. I think, you question is to rephrase the docs? as at the moment it sounds like account_ids are required?
| worker_tool = tool_from_stackone('workday_list_workers') | ||
|
|
||
| agent = Agent('openai:gpt-5.4', tools=[worker_tool]) | ||
|
|
||
| result = agent.run_sync('List the first 5 workers') | ||
| print(result.output) | ||
| ``` | ||
|
|
||
| For multiple StackOne tools, use the [`StackOneToolset`][pydantic_ai.ext.stackone.StackOneToolset] [toolset](toolsets.md#stackone-tools): | ||
|
|
||
| ```python {test="skip"} | ||
| from pydantic_ai import Agent | ||
| from pydantic_ai.ext.stackone import StackOneToolset | ||
|
|
||
| toolset = StackOneToolset(filter_pattern='workday_list_worker*') | ||
|
|
||
| agent = Agent('openai:gpt-5.4', toolsets=[toolset]) |
There was a problem hiding this comment.
are these the first examples that we want to expose?
There was a problem hiding this comment.
Previously, it used to be bamboohr, I changes it to Workday .. Or do you want it to be more simplified example? I added filter pattern as Workday returns more than 128 tools which is limit for OpenAI Specs .. Should we change example or how we represent it?
| === "Search & Execute" | ||
|
|
||
| The agent receives two meta tools, `tool_search` and `tool_execute`. The model calls `tool_search` with a natural-language query (e.g. `"list employees"`), picks a result, and calls `tool_execute` with the tool name and parameters. Prompt size stays constant regardless of catalog size. Requires `stackone-ai >= 2.5.0`. | ||
|
|
||
| ```python {test="skip"} | ||
| toolset = StackOneToolset(mode='search_and_execute') | ||
| agent = Agent('openai:gpt-5.4', toolsets=[toolset]) | ||
| ``` |
There was a problem hiding this comment.
feel like it's confusing to have search as well as search_and_execute suggested - in which case should they use each should be described or we should expose one?
There was a problem hiding this comment.
I was wondered about this as well.. Should we just show search tools for now and remove the search_and_execute for now? Or just hide from docs? Or recommend user which one to use in what situation by updating doc providing more context?
There was a problem hiding this comment.
Pydantic also have own Tool Search so I thought having execute will be good but wasn't sure.
|
Discussed with @willleeney and decided we have search and execute there with other strategies demonstrated so keep it minimal |
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="docs/third-party-tools.md">
<violation number="1" location="docs/third-party-tools.md:137">
P3: The example comment says this is a single-tool setup without a toolset, but the code still passes `toolsets=[toolset]`. Make the code match the documented usage.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| # Or a single tool without a toolset | ||
| worker_tool = tool_from_stackone('workday_list_workers') | ||
|
|
||
| agent = Agent('openai:gpt-5.4', toolsets=[toolset], tools=[worker_tool]) |
There was a problem hiding this comment.
P3: The example comment says this is a single-tool setup without a toolset, but the code still passes toolsets=[toolset]. Make the code match the documented usage.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/third-party-tools.md, line 137:
<comment>The example comment says this is a single-tool setup without a toolset, but the code still passes `toolsets=[toolset]`. Make the code match the documented usage.</comment>
<file context>
@@ -102,68 +102,62 @@ agent = Agent('openai:gpt-5.2', toolsets=[toolset])
- search='auto',
- )
- agent = Agent('openai:gpt-5.4', toolsets=[toolset])
+ agent = Agent('openai:gpt-5.4', toolsets=[toolset], tools=[worker_tool])
```
</file context>
| agent = Agent('openai:gpt-5.4', toolsets=[toolset], tools=[worker_tool]) | |
| agent = Agent('openai:gpt-5.4', tools=[worker_tool]) |
…ird-party-tools.md
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
| ``` | ||
|
|
||
| !!! note | ||
| Bare `StackOneToolset()` loads every tool for your connected account(s). Large catalogs may exceed the model's per-request tool-count limit — narrow with `tools=` or `filter_pattern=`, or use `mode='search_and_execute'` for on-demand discovery. |
There was a problem hiding this comment.
I'd suggest search_and_execute as the first option. Also link to where the examples for each are given?
| !!! note | ||
| Bare `StackOneToolset()` loads every tool for your connected account(s). Large catalogs may exceed the model's per-request tool-count limit — narrow with `tools=` or `filter_pattern=`, or use `mode='search_and_execute'` for on-demand discovery. | ||
|
|
||
| For more control, narrow the toolset, use meta-tools for large catalogs, or connect multiple accounts: |
There was a problem hiding this comment.
shouldn't be using meta-tools anymore
| # Or a single tool without a toolset | ||
| worker_tool = tool_from_stackone('workday_list_workers') | ||
|
|
||
| agent = Agent('openai:gpt-5.4', toolsets=[toolset], tools=[worker_tool]) |
There was a problem hiding this comment.
would say explicitly that you can use both in combination as well
|
|
||
| === "Search & Execute" | ||
|
|
||
| The agent receives two meta tools, `tool_search` and `tool_execute`. The model decides the search query at runtime, picks a result, and calls `tool_execute` with the tool name and parameters. Prompt size stays constant regardless of catalog size. Requires `stackone-ai >= 2.5.0`. |
There was a problem hiding this comment.
I'd say "instead of giving every tool available, we can only expose two tools ... " to make it obvious what is going on
|
|
||
| === "Search & Execute" | ||
|
|
||
| The agent receives two meta tools, `tool_search` and `tool_execute`. The model decides the search query at runtime, picks a result, and calls `tool_execute` with the tool name and parameters. Prompt size stays constant regardless of catalog size. Requires `stackone-ai >= 2.5.0`. |
There was a problem hiding this comment.
also I would have something here written that semantic calls our api for a model for doing the search whereas local uses BM-25 to do the search to explicitly define the difference between what local and non-local actually means
| agent = Agent('openai:gpt-5.4', toolsets=[toolset]) | ||
| ``` | ||
|
|
||
| `search_config['method']` picks the strategy: `auto` (default, tries semantic then falls back to local), `semantic` (LLM-powered embedding search only), or `local` (on-device BM25+TF-IDF). |
There was a problem hiding this comment.
is it BM-25 + TF-IDF still or isn't it just BM-25?
but also do the written stuff about the description before you show the code of how to do it
|
Thank @willleeney for feedback on the docs .. Agreed on all the points and made changes accordingly .. |
Summary
Add StackOne AI integration enabling AI agents to access 200+ SaaS integrations (HRIS, ATS, LMS, CRM, IAM, etc.) through a unified API.
Changes
tool_from_stackone()for individual tool integrationStackOneToolsetfor bulk registration with glob pattern filteringNew Files
pydantic_ai_slim/pydantic_ai/ext/stackone.py- Core integration moduletests/test_ext_stackone.py- Unit testsexamples/stackone_integration.py- Usage examplesDocumentation Updates
docs/install.md- Addedstackoneextradocs/third-party-tools.md- Added StackOne tools sectiondocs/toolsets.md- Added StackOne toolset sectionDependencies
stackone-ai>=2.1.1(requires Python 3.10+, same as Pydantic AI)Test plan
uv run pytest tests/test_ext_stackone.py)Jira: ENG-10848