stackone-ai 2.8.0__tar.gz → 2.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stackone_ai-2.9.0/.release-please-manifest.json +3 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/CHANGELOG.md +7 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/PKG-INFO +41 -1
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/README.md +38 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/pydantic_ai_integration.py +12 -24
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/pyproject.toml +2 -1
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/__init__.py +1 -1
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/models.py +44 -1
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/toolset.py +44 -1
- stackone_ai-2.9.0/tests/test_integrations_pydantic_ai.py +183 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/uv.lock +6 -2
- stackone_ai-2.8.0/.release-please-manifest.json +0 -3
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/development-workflow.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/examples-standards.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/git-workflow.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/nix-workflow.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/no-relative-imports.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/package-installation.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/release-please-standards.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.claude/rules/uv-scripts.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/development-workflow.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/examples-standards.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/git-workflow.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/no-relative-imports.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/package-installation.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/release-please-standards.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.cursor/rules/uv-scripts.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.env.example +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.envrc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/actions/setup-nix/action.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/dependabot.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/workflows/ci.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/workflows/nix-flake-update.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/workflows/nix-flake.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.github/workflows/release.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.gitignore +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.gitleaks.toml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.gitmodules +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.mcp.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/.release-please-config.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/CLAUDE.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/LICENSE +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/auth_management.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/crewai_integration.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/langchain_integration.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/langgraph_integration.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/openai_integration.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/search_tools.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/examples/test_examples.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/flake.lock +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/flake.nix +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/justfile +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/scripts/benchmark_search.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/constants.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/feedback/__init__.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/feedback/tool.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/integrations/__init__.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/integrations/langgraph.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/local_search.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/py.typed +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/semantic_search.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/utils/__init__.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/utils/normalize.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/stackone_ai/utils/tfidf_index.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/conftest.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/mocks/serve.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_agent_tools.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_feedback.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_fetch_tools.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_integrations_langgraph.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_local_search.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_models.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_semantic_search.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_tfidf_index.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_tool_calling.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/tests/test_toolset.py +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/development-workflow.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/file-operations.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/git-workflow.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/pnpm-usage.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/typescript-patterns.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/rules/typescript-testing.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.claude/skills/orama-integration/SKILL.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/development-workflow.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/file-operations.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/git-workflow.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/orama-integration.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/pnpm-usage.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/typescript-patterns.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.cursor/rules/typescript-testing.mdc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.envrc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.git +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/CODEOWNERS +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/actions/setup-nix/action.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/check-title.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/ci.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/claude.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/dry-publish.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/nix-flake.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.github/workflows/release.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.gitignore +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.gitleaks.toml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.mcp.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.oxfmtrc.jsonc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/.oxlintrc.jsonc +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/AGENTS.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/CHANGELOG.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/CLAUDE.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/LICENSE +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/README.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/README.md +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/ai-sdk-integration.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/ai-sdk-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/anthropic-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/claude-agent-sdk-integration.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/claude-agent-sdk-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/fetch-tools-debug.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/fetch-tools.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/fetch-tools.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/meta-tools.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/openai-integration.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/openai-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/openai-responses-integration.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/openai-responses-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/package.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/tanstack-ai-integration.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/tanstack-ai-integration.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/examples/tsconfig.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/flake.lock +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/flake.nix +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/knip.config.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/lefthook.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.example-api.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.mcp.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.openai.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.stackone-ai.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.stackone-rpc.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/handlers.utils.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/mcp-server.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/mocks/node.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/package.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/pnpm-lock.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/pnpm-workspace.yaml +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/consts.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/feedback.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/feedback.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/headers.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/headers.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/index.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/mcp-client.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/mcp-client.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/requestBuilder.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/requestBuilder.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/rpc-client.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/rpc-client.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/schema.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/tool.test-d.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/tool.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/tool.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/toolsets.test-d.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/toolsets.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/toolsets.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/types.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/array.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/array.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/error-stackone-api.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/error-stackone-api.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/error-stackone.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/error-stackone.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/tfidf-index.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/tfidf-index.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/try-import.test.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/try-import.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/src/utils/type.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/tsconfig.json +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/tsdown.config.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/vitest.config.ts +0 -0
- {stackone_ai-2.8.0 → stackone_ai-2.9.0}/vendor/stackone-ai-node/vitest.setup.ts +0 -0
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.9.0](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v2.8.0...stackone-ai-v2.9.0) (2026-04-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **integrations:** add native Pydantic AI support ([#184](https://github.com/StackOneHQ/stackone-ai-python/issues/184)) ([8216ab6](https://github.com/StackOneHQ/stackone-ai-python/commit/8216ab6c0776b0753cf3e68461bf5e8a9b044b06))
|
|
9
|
+
|
|
3
10
|
## [2.8.0](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v2.7.0...stackone-ai-v2.8.0) (2026-04-20)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackone-ai
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: agents performing actions on your SaaS
|
|
5
5
|
Author-email: StackOne <support@stackone.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -29,6 +29,8 @@ Requires-Dist: pydantic-ai>=1.83.0; extra == 'examples'
|
|
|
29
29
|
Requires-Dist: python-dotenv>=1.0.1; extra == 'examples'
|
|
30
30
|
Provides-Extra: mcp
|
|
31
31
|
Requires-Dist: mcp>=1.3.0; extra == 'mcp'
|
|
32
|
+
Provides-Extra: pydantic-ai
|
|
33
|
+
Requires-Dist: pydantic-ai-slim<2.0.0,>=1.83.0; extra == 'pydantic-ai'
|
|
32
34
|
Description-Content-Type: text/markdown
|
|
33
35
|
|
|
34
36
|
# StackOne AI SDK
|
|
@@ -59,6 +61,7 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
|
|
|
59
61
|
- LangChain Tools
|
|
60
62
|
- CrewAI Tools
|
|
61
63
|
- LangGraph Tool Node
|
|
64
|
+
- Pydantic AI Toolset
|
|
62
65
|
|
|
63
66
|
## Requirements
|
|
64
67
|
|
|
@@ -228,6 +231,43 @@ for tool_call in response.tool_calls:
|
|
|
228
231
|
|
|
229
232
|
</details>
|
|
230
233
|
|
|
234
|
+
<details>
|
|
235
|
+
<summary>Pydantic AI Integration</summary>
|
|
236
|
+
|
|
237
|
+
StackOne tools convert to Pydantic AI `Tool` instances via `.to_pydantic_ai()`, parallel to `.to_openai()` and `.to_langchain()`:
|
|
238
|
+
|
|
239
|
+
Prerequisites:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
pip install 'stackone-ai[pydantic-ai]'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
import os
|
|
247
|
+
from pydantic_ai import Agent
|
|
248
|
+
from stackone_ai import StackOneToolSet
|
|
249
|
+
|
|
250
|
+
toolset = StackOneToolSet()
|
|
251
|
+
tools = toolset.fetch_tools(
|
|
252
|
+
actions=["workday_list_workers", "workday_get_worker"],
|
|
253
|
+
account_ids=[os.environ["STACKONE_ACCOUNT_ID"]],
|
|
254
|
+
).to_pydantic_ai()
|
|
255
|
+
|
|
256
|
+
agent = Agent("openai:gpt-5.4", tools=tools)
|
|
257
|
+
result = agent.run_sync("List the first 5 employees")
|
|
258
|
+
print(result.output)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
For the full catalog (or the meta search/execute tools), use the `.pydantic_ai()` method on `StackOneToolSet` — parallel to `.openai()` / `.langchain()`:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
toolset = StackOneToolSet()
|
|
265
|
+
tools = toolset.pydantic_ai(account_ids=[os.environ["STACKONE_ACCOUNT_ID"]])
|
|
266
|
+
# or pydantic_ai(mode="search_and_execute") for agent-driven discovery
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
</details>
|
|
270
|
+
|
|
231
271
|
<details>
|
|
232
272
|
<summary>LangGraph Integration</summary>
|
|
233
273
|
|
|
@@ -26,6 +26,7 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
|
|
|
26
26
|
- LangChain Tools
|
|
27
27
|
- CrewAI Tools
|
|
28
28
|
- LangGraph Tool Node
|
|
29
|
+
- Pydantic AI Toolset
|
|
29
30
|
|
|
30
31
|
## Requirements
|
|
31
32
|
|
|
@@ -195,6 +196,43 @@ for tool_call in response.tool_calls:
|
|
|
195
196
|
|
|
196
197
|
</details>
|
|
197
198
|
|
|
199
|
+
<details>
|
|
200
|
+
<summary>Pydantic AI Integration</summary>
|
|
201
|
+
|
|
202
|
+
StackOne tools convert to Pydantic AI `Tool` instances via `.to_pydantic_ai()`, parallel to `.to_openai()` and `.to_langchain()`:
|
|
203
|
+
|
|
204
|
+
Prerequisites:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
pip install 'stackone-ai[pydantic-ai]'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import os
|
|
212
|
+
from pydantic_ai import Agent
|
|
213
|
+
from stackone_ai import StackOneToolSet
|
|
214
|
+
|
|
215
|
+
toolset = StackOneToolSet()
|
|
216
|
+
tools = toolset.fetch_tools(
|
|
217
|
+
actions=["workday_list_workers", "workday_get_worker"],
|
|
218
|
+
account_ids=[os.environ["STACKONE_ACCOUNT_ID"]],
|
|
219
|
+
).to_pydantic_ai()
|
|
220
|
+
|
|
221
|
+
agent = Agent("openai:gpt-5.4", tools=tools)
|
|
222
|
+
result = agent.run_sync("List the first 5 employees")
|
|
223
|
+
print(result.output)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
For the full catalog (or the meta search/execute tools), use the `.pydantic_ai()` method on `StackOneToolSet` — parallel to `.openai()` / `.langchain()`:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
toolset = StackOneToolSet()
|
|
230
|
+
tools = toolset.pydantic_ai(account_ids=[os.environ["STACKONE_ACCOUNT_ID"]])
|
|
231
|
+
# or pydantic_ai(mode="search_and_execute") for agent-driven discovery
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
</details>
|
|
235
|
+
|
|
198
236
|
<details>
|
|
199
237
|
<summary>LangGraph Integration</summary>
|
|
200
238
|
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
```bash
|
|
4
4
|
uv run examples/pydantic_ai_integration.py
|
|
5
5
|
```
|
|
6
|
+
|
|
7
|
+
Install with `pip install 'stackone-ai[pydantic-ai]'` (or
|
|
8
|
+
`pip install 'stackone-ai[examples]'` to run this file).
|
|
6
9
|
"""
|
|
7
10
|
|
|
8
11
|
from __future__ import annotations
|
|
9
12
|
|
|
10
|
-
import json
|
|
11
13
|
import os
|
|
12
14
|
|
|
13
15
|
try:
|
|
@@ -18,31 +20,15 @@ except ModuleNotFoundError:
|
|
|
18
20
|
pass
|
|
19
21
|
|
|
20
22
|
try:
|
|
21
|
-
from pydantic_ai import Agent
|
|
23
|
+
from pydantic_ai import Agent
|
|
22
24
|
except ImportError:
|
|
23
|
-
print("Install pydantic-ai to run this example: pip install pydantic-ai")
|
|
25
|
+
print("Install pydantic-ai to run this example: pip install 'stackone-ai[pydantic-ai]'")
|
|
24
26
|
raise SystemExit(1) from None
|
|
25
27
|
|
|
26
28
|
from stackone_ai import StackOneToolSet
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
def _to_pydantic_ai_tool(stackone_tool) -> Tool:
|
|
30
|
-
"""Convert a StackOneTool to a Pydantic AI Tool with the proper JSON schema."""
|
|
31
|
-
params_schema = stackone_tool.to_openai_function()["function"]["parameters"]
|
|
32
|
-
|
|
33
|
-
def execute(**kwargs: object) -> str:
|
|
34
|
-
return json.dumps(stackone_tool.execute(kwargs))
|
|
35
|
-
|
|
36
|
-
return Tool.from_schema(
|
|
37
|
-
execute,
|
|
38
|
-
name=stackone_tool.name,
|
|
39
|
-
description=stackone_tool.description,
|
|
40
|
-
json_schema=params_schema,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
31
|
def pydantic_ai_integration() -> None:
|
|
45
|
-
account_id = os.getenv("STACKONE_ACCOUNT_ID")
|
|
46
32
|
for var in ["STACKONE_API_KEY", "STACKONE_ACCOUNT_ID", "OPENAI_API_KEY"]:
|
|
47
33
|
if not os.getenv(var):
|
|
48
34
|
print(f"Set {var} to run this example.")
|
|
@@ -51,12 +37,14 @@ def pydantic_ai_integration() -> None:
|
|
|
51
37
|
toolset = StackOneToolSet()
|
|
52
38
|
tools = toolset.fetch_tools(
|
|
53
39
|
actions=["workday_list_workers", "workday_get_worker", "workday_get_current_user"],
|
|
54
|
-
account_ids=[
|
|
55
|
-
)
|
|
56
|
-
pydantic_tools = [_to_pydantic_ai_tool(t) for t in tools]
|
|
57
|
-
print(f"Loaded {len(pydantic_tools)} tools for Pydantic AI.")
|
|
40
|
+
account_ids=[os.environ["STACKONE_ACCOUNT_ID"]],
|
|
41
|
+
).to_pydantic_ai()
|
|
58
42
|
|
|
59
|
-
agent = Agent(
|
|
43
|
+
agent = Agent(
|
|
44
|
+
"openai:gpt-5.4",
|
|
45
|
+
system_prompt="You are a helpful HR assistant.",
|
|
46
|
+
tools=tools,
|
|
47
|
+
)
|
|
60
48
|
result = agent.run_sync("List the first 5 employees")
|
|
61
49
|
print(f"Result:\n{result.output}")
|
|
62
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stackone-ai"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.9.0"
|
|
4
4
|
description = "agents performing actions on your SaaS"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -37,6 +37,7 @@ packages = ["stackone_ai"]
|
|
|
37
37
|
|
|
38
38
|
[project.optional-dependencies]
|
|
39
39
|
mcp = ["mcp>=1.3.0"]
|
|
40
|
+
pydantic-ai = ["pydantic-ai-slim>=1.83.0,<2.0.0"]
|
|
40
41
|
examples = [
|
|
41
42
|
"crewai>=0.102.0",
|
|
42
43
|
"langchain-openai>=1.1.14",
|
|
@@ -6,13 +6,16 @@ import logging
|
|
|
6
6
|
from collections.abc import Sequence
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
from enum import Enum
|
|
9
|
-
from typing import Annotated, Any, ClassVar, TypeAlias, cast
|
|
9
|
+
from typing import TYPE_CHECKING, Annotated, Any, ClassVar, TypeAlias, cast
|
|
10
10
|
from urllib.parse import quote
|
|
11
11
|
|
|
12
12
|
import httpx
|
|
13
13
|
from langchain_core.tools import BaseTool
|
|
14
14
|
from pydantic import BaseModel, BeforeValidator, Field, PrivateAttr
|
|
15
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from pydantic_ai.tools import Tool as PydanticAITool
|
|
18
|
+
|
|
16
19
|
# Type aliases for common types
|
|
17
20
|
JsonDict: TypeAlias = dict[str, Any]
|
|
18
21
|
Headers: TypeAlias = dict[str, str]
|
|
@@ -467,6 +470,36 @@ class StackOneTool(BaseModel):
|
|
|
467
470
|
|
|
468
471
|
return StackOneLangChainTool()
|
|
469
472
|
|
|
473
|
+
def to_pydantic_ai_tool(self) -> PydanticAITool:
|
|
474
|
+
"""Convert this tool to a Pydantic AI ``Tool``.
|
|
475
|
+
|
|
476
|
+
Requires ``stackone-ai[pydantic-ai]`` (installs ``pydantic-ai-slim``).
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
A ``pydantic_ai.tools.Tool`` ready to pass to ``Agent(tools=[...])``.
|
|
480
|
+
"""
|
|
481
|
+
try:
|
|
482
|
+
from pydantic_ai.tools import Tool
|
|
483
|
+
except ImportError as e:
|
|
484
|
+
raise ImportError(
|
|
485
|
+
"Install `pydantic-ai-slim` (or `stackone-ai[pydantic-ai]`) "
|
|
486
|
+
"to use the Pydantic AI integration."
|
|
487
|
+
) from e
|
|
488
|
+
|
|
489
|
+
openai_function = self.to_openai_function()
|
|
490
|
+
json_schema = openai_function["function"]["parameters"]
|
|
491
|
+
parent_tool = self
|
|
492
|
+
|
|
493
|
+
def implementation(**kwargs: Any) -> Any:
|
|
494
|
+
return parent_tool.execute(kwargs)
|
|
495
|
+
|
|
496
|
+
return Tool.from_schema(
|
|
497
|
+
function=implementation,
|
|
498
|
+
name=self.name,
|
|
499
|
+
description=self.description,
|
|
500
|
+
json_schema=json_schema,
|
|
501
|
+
)
|
|
502
|
+
|
|
470
503
|
def set_account_id(self, account_id: str | None) -> None:
|
|
471
504
|
"""Set the account ID for this tool
|
|
472
505
|
|
|
@@ -577,3 +610,13 @@ class Tools:
|
|
|
577
610
|
Sequence of tools in LangChain format
|
|
578
611
|
"""
|
|
579
612
|
return [tool.to_langchain() for tool in self.tools]
|
|
613
|
+
|
|
614
|
+
def to_pydantic_ai(self) -> list[PydanticAITool]:
|
|
615
|
+
"""Convert all tools to Pydantic AI ``Tool`` instances.
|
|
616
|
+
|
|
617
|
+
Requires ``stackone-ai[pydantic-ai]`` (installs ``pydantic-ai-slim``).
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
List of ``pydantic_ai.tools.Tool`` ready to pass to ``Agent(tools=[...])``.
|
|
621
|
+
"""
|
|
622
|
+
return [tool.to_pydantic_ai_tool() for tool in self.tools]
|
|
@@ -11,7 +11,7 @@ import threading
|
|
|
11
11
|
from collections.abc import Coroutine, Sequence
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
from importlib import metadata
|
|
14
|
-
from typing import Any, Literal, TypedDict, TypeVar
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Literal, TypedDict, TypeVar
|
|
15
15
|
|
|
16
16
|
from pydantic import BaseModel, Field, PrivateAttr, ValidationError, field_validator
|
|
17
17
|
|
|
@@ -32,6 +32,9 @@ from stackone_ai.semantic_search import (
|
|
|
32
32
|
)
|
|
33
33
|
from stackone_ai.utils.normalize import _normalize_action_name
|
|
34
34
|
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from pydantic_ai.tools import Tool as PydanticAITool
|
|
37
|
+
|
|
35
38
|
logger = logging.getLogger("stackone.tools")
|
|
36
39
|
|
|
37
40
|
SearchMode = Literal["auto", "semantic", "local"]
|
|
@@ -752,6 +755,46 @@ class StackOneToolSet:
|
|
|
752
755
|
|
|
753
756
|
return self.fetch_tools(account_ids=effective_account_ids).to_langchain()
|
|
754
757
|
|
|
758
|
+
def pydantic_ai(
|
|
759
|
+
self,
|
|
760
|
+
*,
|
|
761
|
+
mode: Literal["search_and_execute"] | None = None,
|
|
762
|
+
account_ids: list[str] | None = None,
|
|
763
|
+
) -> list[PydanticAITool]:
|
|
764
|
+
"""Get tools as Pydantic AI ``Tool`` instances.
|
|
765
|
+
|
|
766
|
+
Args:
|
|
767
|
+
mode: Tool mode.
|
|
768
|
+
``None`` (default): fetch all tools and convert to Pydantic AI tools.
|
|
769
|
+
``"search_and_execute"``: return two meta tools (tool_search + tool_execute)
|
|
770
|
+
that let the LLM discover and execute tools on-demand.
|
|
771
|
+
account_ids: Account IDs to scope tools. Overrides the ``execute``
|
|
772
|
+
config from the constructor.
|
|
773
|
+
|
|
774
|
+
Returns:
|
|
775
|
+
List of Pydantic AI ``Tool`` objects ready to pass to ``Agent(tools=...)``.
|
|
776
|
+
|
|
777
|
+
Requires ``stackone-ai[pydantic-ai]`` (installs ``pydantic-ai-slim``).
|
|
778
|
+
|
|
779
|
+
Examples::
|
|
780
|
+
|
|
781
|
+
# All tools
|
|
782
|
+
toolset = StackOneToolSet()
|
|
783
|
+
tools = toolset.pydantic_ai()
|
|
784
|
+
agent = Agent("openai:gpt-5.4", tools=tools)
|
|
785
|
+
|
|
786
|
+
# Meta tools for agent-driven discovery
|
|
787
|
+
tools = toolset.pydantic_ai(mode="search_and_execute")
|
|
788
|
+
"""
|
|
789
|
+
effective_account_ids = account_ids or (
|
|
790
|
+
self._execute_config.get("account_ids") if self._execute_config else None
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
if mode == "search_and_execute":
|
|
794
|
+
return self._build_tools(account_ids=effective_account_ids).to_pydantic_ai()
|
|
795
|
+
|
|
796
|
+
return self.fetch_tools(account_ids=effective_account_ids).to_pydantic_ai()
|
|
797
|
+
|
|
755
798
|
def execute(
|
|
756
799
|
self,
|
|
757
800
|
tool_name: str,
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Tests for the Pydantic AI integration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from unittest.mock import MagicMock
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
pytest.importorskip("pydantic_ai", reason="pydantic-ai not installed")
|
|
11
|
+
|
|
12
|
+
from pydantic_ai import Agent # noqa: E402
|
|
13
|
+
from pydantic_ai.tools import Tool # noqa: E402
|
|
14
|
+
|
|
15
|
+
from stackone_ai import StackOneToolSet # noqa: E402
|
|
16
|
+
from stackone_ai.models import ExecuteConfig, StackOneTool, ToolParameters, Tools # noqa: E402
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def sample_tool() -> StackOneTool:
|
|
21
|
+
return StackOneTool(
|
|
22
|
+
description="List employees",
|
|
23
|
+
parameters=ToolParameters(
|
|
24
|
+
type="object",
|
|
25
|
+
properties={"limit": {"type": "integer", "description": "Max results"}},
|
|
26
|
+
),
|
|
27
|
+
_execute_config=ExecuteConfig(
|
|
28
|
+
headers={},
|
|
29
|
+
method="GET",
|
|
30
|
+
url="https://api.example.com/employees",
|
|
31
|
+
name="bamboohr_list_employees",
|
|
32
|
+
),
|
|
33
|
+
_api_key="test_key",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture
|
|
38
|
+
def second_tool() -> StackOneTool:
|
|
39
|
+
return StackOneTool(
|
|
40
|
+
description="Get an employee",
|
|
41
|
+
parameters=ToolParameters(type="object", properties={}),
|
|
42
|
+
_execute_config=ExecuteConfig(
|
|
43
|
+
headers={},
|
|
44
|
+
method="GET",
|
|
45
|
+
url="https://api.example.com/employees/{id}",
|
|
46
|
+
name="bamboohr_get_employee",
|
|
47
|
+
),
|
|
48
|
+
_api_key="test_key",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# --- StackOneTool.to_pydantic_ai_tool() ---
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_tool_to_pydantic_ai_tool_returns_tool(sample_tool: StackOneTool):
|
|
56
|
+
tool = sample_tool.to_pydantic_ai_tool()
|
|
57
|
+
assert isinstance(tool, Tool)
|
|
58
|
+
assert tool.name == "bamboohr_list_employees"
|
|
59
|
+
assert tool.description == "List employees"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_tool_to_pydantic_ai_tool_schema_matches_openai(sample_tool: StackOneTool):
|
|
63
|
+
tool = sample_tool.to_pydantic_ai_tool()
|
|
64
|
+
assert tool.function_schema.json_schema == sample_tool.to_openai_function()["function"]["parameters"]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_tool_to_pydantic_ai_tool_executes_through_stackone(
|
|
68
|
+
sample_tool: StackOneTool, monkeypatch: pytest.MonkeyPatch
|
|
69
|
+
):
|
|
70
|
+
captured: dict[str, Any] = {}
|
|
71
|
+
|
|
72
|
+
def fake_execute(self: StackOneTool, arguments: dict[str, Any]) -> Any:
|
|
73
|
+
captured["args"] = arguments
|
|
74
|
+
return {"ok": True}
|
|
75
|
+
|
|
76
|
+
monkeypatch.setattr(StackOneTool, "execute", fake_execute)
|
|
77
|
+
|
|
78
|
+
tool = sample_tool.to_pydantic_ai_tool()
|
|
79
|
+
result = tool.function(limit=10) # type: ignore[operator]
|
|
80
|
+
|
|
81
|
+
assert captured["args"] == {"limit": 10}
|
|
82
|
+
assert result == {"ok": True}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --- Tools.to_pydantic_ai() ---
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_tools_to_pydantic_ai_converts_all(sample_tool: StackOneTool, second_tool: StackOneTool):
|
|
89
|
+
collection = Tools([sample_tool, second_tool])
|
|
90
|
+
tools = collection.to_pydantic_ai()
|
|
91
|
+
|
|
92
|
+
assert len(tools) == 2
|
|
93
|
+
assert [t.name for t in tools] == ["bamboohr_list_employees", "bamboohr_get_employee"]
|
|
94
|
+
assert all(isinstance(t, Tool) for t in tools)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_tools_to_pydantic_ai_empty_collection():
|
|
98
|
+
collection = Tools([])
|
|
99
|
+
assert collection.to_pydantic_ai() == []
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_tools_to_pydantic_ai_with_agent(sample_tool: StackOneTool, monkeypatch: pytest.MonkeyPatch):
|
|
103
|
+
monkeypatch.setattr(StackOneTool, "execute", lambda self, arguments: {"employee_ids": ["1", "2"]})
|
|
104
|
+
tools = Tools([sample_tool]).to_pydantic_ai()
|
|
105
|
+
|
|
106
|
+
agent = Agent("test", tools=tools)
|
|
107
|
+
result = agent.run_sync("list employees")
|
|
108
|
+
|
|
109
|
+
assert "bamboohr_list_employees" in result.output
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# --- StackOneToolSet.pydantic_ai() ---
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_toolset_pydantic_ai_default_uses_fetch_tools(sample_tool: StackOneTool):
|
|
116
|
+
toolset = StackOneToolSet.__new__(StackOneToolSet)
|
|
117
|
+
toolset.fetch_tools = MagicMock(return_value=Tools([sample_tool])) # type: ignore[method-assign]
|
|
118
|
+
toolset._build_tools = MagicMock() # type: ignore[method-assign]
|
|
119
|
+
toolset._execute_config = None # type: ignore[attr-defined]
|
|
120
|
+
|
|
121
|
+
tools = toolset.pydantic_ai()
|
|
122
|
+
|
|
123
|
+
toolset.fetch_tools.assert_called_once_with(account_ids=None)
|
|
124
|
+
toolset._build_tools.assert_not_called()
|
|
125
|
+
assert len(tools) == 1
|
|
126
|
+
assert tools[0].name == "bamboohr_list_employees"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_toolset_pydantic_ai_search_and_execute_uses_build_tools(sample_tool: StackOneTool):
|
|
130
|
+
toolset = StackOneToolSet.__new__(StackOneToolSet)
|
|
131
|
+
toolset.fetch_tools = MagicMock() # type: ignore[method-assign]
|
|
132
|
+
toolset._build_tools = MagicMock(return_value=Tools([sample_tool])) # type: ignore[method-assign]
|
|
133
|
+
toolset._execute_config = None # type: ignore[attr-defined]
|
|
134
|
+
|
|
135
|
+
tools = toolset.pydantic_ai(mode="search_and_execute")
|
|
136
|
+
|
|
137
|
+
toolset._build_tools.assert_called_once_with(account_ids=None)
|
|
138
|
+
toolset.fetch_tools.assert_not_called()
|
|
139
|
+
assert len(tools) == 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_toolset_pydantic_ai_account_ids_override(sample_tool: StackOneTool):
|
|
143
|
+
toolset = StackOneToolSet.__new__(StackOneToolSet)
|
|
144
|
+
toolset.fetch_tools = MagicMock(return_value=Tools([sample_tool])) # type: ignore[method-assign]
|
|
145
|
+
toolset._build_tools = MagicMock() # type: ignore[method-assign]
|
|
146
|
+
toolset._execute_config = {"account_ids": ["from-config"]} # type: ignore[attr-defined]
|
|
147
|
+
|
|
148
|
+
toolset.pydantic_ai(account_ids=["override"])
|
|
149
|
+
|
|
150
|
+
toolset.fetch_tools.assert_called_once_with(account_ids=["override"])
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_toolset_pydantic_ai_falls_back_to_execute_config_account_ids(sample_tool: StackOneTool):
|
|
154
|
+
toolset = StackOneToolSet.__new__(StackOneToolSet)
|
|
155
|
+
toolset.fetch_tools = MagicMock(return_value=Tools([sample_tool])) # type: ignore[method-assign]
|
|
156
|
+
toolset._build_tools = MagicMock() # type: ignore[method-assign]
|
|
157
|
+
toolset._execute_config = {"account_ids": ["from-config"]} # type: ignore[attr-defined]
|
|
158
|
+
|
|
159
|
+
toolset.pydantic_ai()
|
|
160
|
+
|
|
161
|
+
toolset.fetch_tools.assert_called_once_with(account_ids=["from-config"])
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# --- Import error path ---
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_to_pydantic_ai_tool_raises_helpful_import_error_when_package_missing(
|
|
168
|
+
sample_tool: StackOneTool, monkeypatch: pytest.MonkeyPatch
|
|
169
|
+
):
|
|
170
|
+
"""If `pydantic_ai` is not importable, the helper surfaces a clean install hint."""
|
|
171
|
+
import builtins
|
|
172
|
+
|
|
173
|
+
real_import = builtins.__import__
|
|
174
|
+
|
|
175
|
+
def fake_import(name: str, *args: Any, **kwargs: Any):
|
|
176
|
+
if name == "pydantic_ai.tools" or name.startswith("pydantic_ai"):
|
|
177
|
+
raise ImportError("No module named 'pydantic_ai'")
|
|
178
|
+
return real_import(name, *args, **kwargs)
|
|
179
|
+
|
|
180
|
+
monkeypatch.setattr(builtins, "__import__", fake_import)
|
|
181
|
+
|
|
182
|
+
with pytest.raises(ImportError, match="stackone-ai\\[pydantic-ai\\]"):
|
|
183
|
+
sample_tool.to_pydantic_ai_tool()
|
|
@@ -4762,7 +4762,7 @@ wheels = [
|
|
|
4762
4762
|
|
|
4763
4763
|
[[package]]
|
|
4764
4764
|
name = "stackone-ai"
|
|
4765
|
-
version = "2.
|
|
4765
|
+
version = "2.9.0"
|
|
4766
4766
|
source = { editable = "." }
|
|
4767
4767
|
dependencies = [
|
|
4768
4768
|
{ name = "bm25s" },
|
|
@@ -4786,6 +4786,9 @@ examples = [
|
|
|
4786
4786
|
mcp = [
|
|
4787
4787
|
{ name = "mcp" },
|
|
4788
4788
|
]
|
|
4789
|
+
pydantic-ai = [
|
|
4790
|
+
{ name = "pydantic-ai-slim" },
|
|
4791
|
+
]
|
|
4789
4792
|
|
|
4790
4793
|
[package.dev-dependencies]
|
|
4791
4794
|
dev = [
|
|
@@ -4813,10 +4816,11 @@ requires-dist = [
|
|
|
4813
4816
|
{ name = "openai", marker = "extra == 'examples'", specifier = ">=2.32.0" },
|
|
4814
4817
|
{ name = "pydantic", specifier = ">=2.10.6" },
|
|
4815
4818
|
{ name = "pydantic-ai", marker = "extra == 'examples'", specifier = ">=1.83.0" },
|
|
4819
|
+
{ name = "pydantic-ai-slim", marker = "extra == 'pydantic-ai'", specifier = ">=1.83.0,<2.0.0" },
|
|
4816
4820
|
{ name = "python-dotenv", marker = "extra == 'examples'", specifier = ">=1.0.1" },
|
|
4817
4821
|
{ name = "typing-extensions", specifier = ">=4.0.0" },
|
|
4818
4822
|
]
|
|
4819
|
-
provides-extras = ["mcp", "examples"]
|
|
4823
|
+
provides-extras = ["mcp", "pydantic-ai", "examples"]
|
|
4820
4824
|
|
|
4821
4825
|
[package.metadata.requires-dev]
|
|
4822
4826
|
dev = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|