stackone-ai 0.3.1__tar.gz → 0.3.3__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-0.3.1 → stackone_ai-0.3.3}/.github/workflows/docs.yml +1 -1
- stackone_ai-0.3.3/.github/workflows/lint.yml +42 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.github/workflows/release.yml +1 -1
- stackone_ai-0.3.3/.github/workflows/test.yml +37 -0
- stackone_ai-0.3.3/.release-please-manifest.json +3 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/CHANGELOG.md +25 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/PKG-INFO +166 -5
- stackone_ai-0.3.3/README.md +307 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/pyproject.toml +28 -6
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/__init__.py +6 -2
- stackone_ai-0.3.3/stackone_ai/feedback/__init__.py +5 -0
- stackone_ai-0.3.3/stackone_ai/feedback/tool.py +238 -0
- stackone_ai-0.3.3/stackone_ai/integrations/__init__.py +20 -0
- stackone_ai-0.3.3/stackone_ai/integrations/langgraph.py +94 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/meta_tools.py +8 -3
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/models.py +114 -74
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/server.py +21 -6
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/specs/parser.py +4 -1
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/toolset.py +14 -0
- stackone_ai-0.3.3/tests/test_feedback.py +269 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_tool_calling.py +0 -18
- stackone_ai-0.3.3/uv.lock +5969 -0
- stackone_ai-0.3.1/.github/workflows/lint.yml +0 -29
- stackone_ai-0.3.1/.github/workflows/test.yml +0 -27
- stackone_ai-0.3.1/.release-please-manifest.json +0 -3
- stackone_ai-0.3.1/README.md +0 -154
- stackone_ai-0.3.1/examples/langgraph_tool_node.py +0 -39
- stackone_ai-0.3.1/uv.lock +0 -4038
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/cursor-rules-location.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/examples-standards.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/no-relative-imports.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/package-installation.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/release-please-standards.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/uv-scripts.mdc +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.env_example +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.gitignore +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.mcp.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.pre-commit-config.yaml +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.release-please-config.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.vscode/settings.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/CLAUDE.md +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/LICENSE +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/Makefile +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/available_tools.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/crewai_integration.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/custom_base_url.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/error_handling.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/file_uploads.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/index.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/langchain_integration.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/mcp_server.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/meta_tools_example.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/openai_integration.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/stackone_account_ids.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/test_examples.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/mkdocs.yml +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/py.typed +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/build_docs.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/pull_oas.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/update_version.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/constants.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/ats.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/core.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/crm.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/documents.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/hris.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/iam.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/lms.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/marketing.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/py.typed +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/specs/loader.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/ats_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/core_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/crm_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/documents_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/hris_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/iam_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/lms_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/marketing_tools.json +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_meta_tools.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_models.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_parser.py +0 -0
- {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_toolset.py +0 -0
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
16
16
|
|
|
17
17
|
- name: Install uv
|
|
18
|
-
uses: astral-sh/setup-uv@
|
|
18
|
+
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
|
19
19
|
with:
|
|
20
20
|
python-version: "3.11"
|
|
21
21
|
enable-cache: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Lint & Type Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.9", "3.10"]
|
|
14
|
+
include:
|
|
15
|
+
- python-version: "3.9"
|
|
16
|
+
sync-extras: "--all-extras --no-extra mcp"
|
|
17
|
+
- python-version: "3.10"
|
|
18
|
+
sync-extras: "--all-extras"
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
enable-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync ${{ matrix.sync-extras }}
|
|
30
|
+
|
|
31
|
+
- name: Run Ruff
|
|
32
|
+
uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
|
|
33
|
+
with:
|
|
34
|
+
args: check .
|
|
35
|
+
|
|
36
|
+
- name: Run Mypy
|
|
37
|
+
run: |
|
|
38
|
+
if [[ "${{ matrix.python-version }}" == "3.9" ]]; then
|
|
39
|
+
uv run mypy stackone_ai --exclude stackone_ai/server.py
|
|
40
|
+
else
|
|
41
|
+
uv run mypy stackone_ai
|
|
42
|
+
fi
|
|
@@ -25,7 +25,7 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
- name: Install uv
|
|
27
27
|
if: ${{ steps.release.outputs.release_created }}
|
|
28
|
-
uses: astral-sh/setup-uv@
|
|
28
|
+
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
|
29
29
|
with:
|
|
30
30
|
python-version: "3.11"
|
|
31
31
|
enable-cache: true
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.9", "3.10", "3.13"]
|
|
14
|
+
include:
|
|
15
|
+
- python-version: "3.9"
|
|
16
|
+
test-extras: "--all-extras --no-extra mcp"
|
|
17
|
+
- python-version: "3.10"
|
|
18
|
+
test-extras: "--all-extras"
|
|
19
|
+
- python-version: "3.13"
|
|
20
|
+
test-extras: "--all-extras"
|
|
21
|
+
env:
|
|
22
|
+
STACKONE_API_KEY: ${{ secrets.STACKONE_API_KEY }}
|
|
23
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 # v6.6.1
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
enable-cache: true
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: uv sync ${{ matrix.test-extras }}
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: uv run pytest
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.3](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v0.3.2...stackone-ai-v0.3.3) (2025-10-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* feedback tool ([#36](https://github.com/StackOneHQ/stackone-ai-python/issues/36)) ([9179918](https://github.com/StackOneHQ/stackone-ai-python/commit/9179918104c0ec4cfe0488713ca325f0e8e7c6f1))
|
|
9
|
+
* LangGraph integration helpers and example ([#33](https://github.com/StackOneHQ/stackone-ai-python/issues/33)) ([983e2f7](https://github.com/StackOneHQ/stackone-ai-python/commit/983e2f7e6551e3722f235ea534ae61f24644350e))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* remove async method ([#31](https://github.com/StackOneHQ/stackone-ai-python/issues/31)) ([370699e](https://github.com/StackOneHQ/stackone-ai-python/commit/370699e390e4a46d8b4ae664fed8f5de6395eb9d))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
|
|
19
|
+
* use uv for installing ([#30](https://github.com/StackOneHQ/stackone-ai-python/issues/30)) ([3c5d8fb](https://github.com/StackOneHQ/stackone-ai-python/commit/3c5d8fb54e61f8f730098e97f8bf2dfc78cf3bec))
|
|
20
|
+
|
|
21
|
+
## [0.3.2](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v0.3.1...stackone-ai-v0.3.2) (2025-08-26)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
|
|
26
|
+
* support Python 3.9+ with optional MCP server support ([#28](https://github.com/StackOneHQ/stackone-ai-python/issues/28)) ([1a37776](https://github.com/StackOneHQ/stackone-ai-python/commit/1a377768c15223e25dbaf1e0bcd0c0e8bb0df2e8))
|
|
27
|
+
|
|
3
28
|
## [0.3.1](https://github.com/StackOneHQ/stackone-ai-python/compare/stackone-ai-v0.3.0...stackone-ai-v0.3.1) (2025-08-19)
|
|
4
29
|
|
|
5
30
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackone-ai
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Summary: agents performing actions on your SaaS
|
|
5
5
|
Author-email: StackOne <support@stackone.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -8,24 +8,32 @@ Classifier: Development Status :: 4 - Beta
|
|
|
8
8
|
Classifier: Intended Audience :: Developers
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
-
Requires-Python: >=3.
|
|
17
|
+
Requires-Python: >=3.9
|
|
14
18
|
Requires-Dist: bm25s>=0.2.2
|
|
19
|
+
Requires-Dist: eval-type-backport; python_version < '3.10'
|
|
15
20
|
Requires-Dist: langchain-core>=0.1.0
|
|
16
|
-
Requires-Dist: mcp[cli]>=1.3.0
|
|
17
21
|
Requires-Dist: numpy>=1.24.0
|
|
18
22
|
Requires-Dist: pydantic>=2.10.6
|
|
19
23
|
Requires-Dist: requests>=2.32.3
|
|
24
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
20
25
|
Provides-Extra: docs
|
|
21
26
|
Requires-Dist: mkdocs-terminal>=4.7.0; extra == 'docs'
|
|
22
27
|
Requires-Dist: pygments>=2.12; extra == 'docs'
|
|
23
28
|
Requires-Dist: pymdown-extensions; extra == 'docs'
|
|
24
29
|
Provides-Extra: examples
|
|
25
|
-
Requires-Dist: crewai>=0.102.0; extra == 'examples'
|
|
30
|
+
Requires-Dist: crewai>=0.102.0; (python_version >= '3.10') and extra == 'examples'
|
|
26
31
|
Requires-Dist: langchain-openai>=0.3.6; extra == 'examples'
|
|
32
|
+
Requires-Dist: langgraph>=0.2.0; extra == 'examples'
|
|
27
33
|
Requires-Dist: openai>=1.63.2; extra == 'examples'
|
|
28
34
|
Requires-Dist: python-dotenv>=1.0.1; extra == 'examples'
|
|
35
|
+
Provides-Extra: mcp
|
|
36
|
+
Requires-Dist: mcp[cli]>=1.3.0; (python_version >= '3.10') and extra == 'mcp'
|
|
29
37
|
Description-Content-Type: text/markdown
|
|
30
38
|
|
|
31
39
|
# StackOne AI SDK
|
|
@@ -48,10 +56,39 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
|
|
|
48
56
|
- CrewAI Tools
|
|
49
57
|
- LangGraph Tool Node
|
|
50
58
|
|
|
59
|
+
## Requirements
|
|
60
|
+
|
|
61
|
+
- Python 3.9+ (core SDK functionality)
|
|
62
|
+
- Python 3.10+ (for MCP server and CrewAI examples)
|
|
63
|
+
|
|
51
64
|
## Installation
|
|
52
65
|
|
|
66
|
+
### Basic Installation
|
|
67
|
+
|
|
53
68
|
```bash
|
|
54
69
|
pip install stackone-ai
|
|
70
|
+
|
|
71
|
+
# Or with uv
|
|
72
|
+
uv add stackone-ai
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Optional Features
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Install with MCP server support (requires Python 3.10+)
|
|
79
|
+
uv add 'stackone-ai[mcp]'
|
|
80
|
+
# or
|
|
81
|
+
pip install 'stackone-ai[mcp]'
|
|
82
|
+
|
|
83
|
+
# Install with CrewAI examples (requires Python 3.10+)
|
|
84
|
+
uv add 'stackone-ai[examples]'
|
|
85
|
+
# or
|
|
86
|
+
pip install 'stackone-ai[examples]'
|
|
87
|
+
|
|
88
|
+
# Install everything
|
|
89
|
+
uv add 'stackone-ai[mcp,examples]'
|
|
90
|
+
# or
|
|
91
|
+
pip install 'stackone-ai[mcp,examples]'
|
|
55
92
|
```
|
|
56
93
|
|
|
57
94
|
## Quick Start
|
|
@@ -76,6 +113,51 @@ employee = employee_tool.call(id="employee-id")
|
|
|
76
113
|
employee = employee_tool.execute({"id": "employee-id"})
|
|
77
114
|
```
|
|
78
115
|
|
|
116
|
+
## Implicit Feedback (Beta)
|
|
117
|
+
|
|
118
|
+
The Python SDK can emit implicit behavioural feedback to LangSmith so you can triage low-quality tool results without manually tagging runs.
|
|
119
|
+
|
|
120
|
+
### Automatic configuration
|
|
121
|
+
|
|
122
|
+
Set `LANGSMITH_API_KEY` in your environment and the SDK will initialise the implicit feedback manager on first tool execution. You can optionally fine-tune behaviour with:
|
|
123
|
+
|
|
124
|
+
- `STACKONE_IMPLICIT_FEEDBACK_ENABLED` (`true`/`false`, defaults to `true` when an API key is present)
|
|
125
|
+
- `STACKONE_IMPLICIT_FEEDBACK_PROJECT` to pin a LangSmith project name
|
|
126
|
+
- `STACKONE_IMPLICIT_FEEDBACK_TAGS` with a comma-separated list of tags applied to every run
|
|
127
|
+
|
|
128
|
+
### Manual configuration
|
|
129
|
+
|
|
130
|
+
If you want custom session or user resolvers, call `configure_implicit_feedback` during start-up:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from stackone_ai import configure_implicit_feedback
|
|
134
|
+
|
|
135
|
+
configure_implicit_feedback(
|
|
136
|
+
api_key="/path/to/langsmith.key",
|
|
137
|
+
project_name="stackone-agents",
|
|
138
|
+
default_tags=["python-sdk"],
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Providing your own `session_resolver`/`user_resolver` callbacks lets you derive identifiers from the request context before events are sent to LangSmith.
|
|
143
|
+
|
|
144
|
+
### Attaching session context to tool calls
|
|
145
|
+
|
|
146
|
+
Both `tool.execute` and `tool.call` accept an `options` keyword that is excluded from the API request but forwarded to the feedback manager:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
tool.execute(
|
|
150
|
+
{"id": "employee-id"},
|
|
151
|
+
options={
|
|
152
|
+
"feedback_session_id": "chat-42",
|
|
153
|
+
"feedback_user_id": "user-123",
|
|
154
|
+
"feedback_metadata": {"conversation_id": "abc"},
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
When two calls for the same session happen within a few seconds, the SDK emits a `refinement_needed` event, and you can inspect suitability scores directly in LangSmith.
|
|
160
|
+
|
|
79
161
|
## Integration Examples
|
|
80
162
|
|
|
81
163
|
<details>
|
|
@@ -112,10 +194,64 @@ for tool_call in response.tool_calls:
|
|
|
112
194
|
</details>
|
|
113
195
|
|
|
114
196
|
<details>
|
|
115
|
-
<summary>
|
|
197
|
+
<summary>LangGraph Integration</summary>
|
|
198
|
+
|
|
199
|
+
StackOne tools convert to LangChain tools, which LangGraph consumes via its prebuilt nodes:
|
|
200
|
+
|
|
201
|
+
Prerequisites:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
pip install langgraph langchain-openai
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from langchain_openai import ChatOpenAI
|
|
209
|
+
from typing import Annotated
|
|
210
|
+
from typing_extensions import TypedDict
|
|
211
|
+
|
|
212
|
+
from langgraph.graph import StateGraph, START, END
|
|
213
|
+
from langgraph.graph.message import add_messages
|
|
214
|
+
from langgraph.prebuilt import tools_condition
|
|
215
|
+
|
|
216
|
+
from stackone_ai import StackOneToolSet
|
|
217
|
+
from stackone_ai.integrations.langgraph import to_tool_node, bind_model_with_tools
|
|
218
|
+
|
|
219
|
+
# Prepare tools
|
|
220
|
+
toolset = StackOneToolSet()
|
|
221
|
+
tools = toolset.get_tools("hris_*", account_id="your-account-id")
|
|
222
|
+
langchain_tools = tools.to_langchain()
|
|
223
|
+
|
|
224
|
+
class State(TypedDict):
|
|
225
|
+
messages: Annotated[list, add_messages]
|
|
226
|
+
|
|
227
|
+
# Build a small agent loop: LLM -> maybe tools -> back to LLM
|
|
228
|
+
graph = StateGraph(State)
|
|
229
|
+
graph.add_node("tools", to_tool_node(langchain_tools))
|
|
230
|
+
|
|
231
|
+
def call_llm(state: dict):
|
|
232
|
+
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
233
|
+
llm = bind_model_with_tools(llm, langchain_tools)
|
|
234
|
+
resp = llm.invoke(state["messages"]) # returns AIMessage with optional tool_calls
|
|
235
|
+
return {"messages": state["messages"] + [resp]}
|
|
236
|
+
|
|
237
|
+
graph.add_node("llm", call_llm)
|
|
238
|
+
graph.add_edge(START, "llm")
|
|
239
|
+
graph.add_conditional_edges("llm", tools_condition)
|
|
240
|
+
graph.add_edge("tools", "llm")
|
|
241
|
+
app = graph.compile()
|
|
242
|
+
|
|
243
|
+
_ = app.invoke({"messages": [("user", "Get employee with id emp123") ]})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
</details>
|
|
247
|
+
|
|
248
|
+
<details>
|
|
249
|
+
<summary>CrewAI Integration (Python 3.10+)</summary>
|
|
116
250
|
|
|
117
251
|
CrewAI uses LangChain tools natively, making integration seamless:
|
|
118
252
|
|
|
253
|
+
> **Note**: CrewAI requires Python 3.10+. Install with `pip install 'stackone-ai[examples]'`
|
|
254
|
+
|
|
119
255
|
```python
|
|
120
256
|
from crewai import Agent, Crew, Task
|
|
121
257
|
from stackone_ai import StackOneToolSet
|
|
@@ -147,6 +283,31 @@ result = crew.kickoff()
|
|
|
147
283
|
|
|
148
284
|
</details>
|
|
149
285
|
|
|
286
|
+
## Feedback Collection
|
|
287
|
+
|
|
288
|
+
The SDK includes a feedback collection tool (`meta_collect_tool_feedback`) that allows users to submit feedback about their experience with StackOne tools. This tool is automatically included in the toolset and is designed to be invoked by AI agents after user permission.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from stackone_ai import StackOneToolSet
|
|
292
|
+
|
|
293
|
+
toolset = StackOneToolSet()
|
|
294
|
+
|
|
295
|
+
# Get the feedback tool (included with "meta_*" pattern or all tools)
|
|
296
|
+
tools = toolset.get_tools("meta_*")
|
|
297
|
+
feedback_tool = tools.get_tool("meta_collect_tool_feedback")
|
|
298
|
+
|
|
299
|
+
# Submit feedback (typically invoked by AI after user consent)
|
|
300
|
+
result = feedback_tool.call(
|
|
301
|
+
feedback="The HRIS tools are working great! Very fast response times.",
|
|
302
|
+
account_id="acc_123456",
|
|
303
|
+
tool_names=["hris_list_employees", "hris_get_employee"]
|
|
304
|
+
)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Important**: The AI agent should always ask for user permission before submitting feedback:
|
|
308
|
+
- "Are you ok with sending feedback to StackOne? The LLM will take care of sending it."
|
|
309
|
+
- Only call the tool after the user explicitly agrees.
|
|
310
|
+
|
|
150
311
|
## Meta Tools (Beta)
|
|
151
312
|
|
|
152
313
|
Meta tools enable dynamic tool discovery and execution without hardcoding tool names:
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# StackOne AI SDK
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/stackone-ai)
|
|
4
|
+
[](https://github.com/StackOneHQ/stackone-ai-python/releases)
|
|
5
|
+
|
|
6
|
+
StackOne AI provides a unified interface for accessing various SaaS tools through AI-friendly APIs.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Unified interface for multiple SaaS tools
|
|
11
|
+
- AI-friendly tool descriptions and parameters
|
|
12
|
+
- **Tool Calling**: Direct method calling with `tool.call()` for intuitive usage
|
|
13
|
+
- **Glob Pattern Filtering**: Advanced tool filtering with patterns like `"hris_*"` and exclusions `"!hris_delete_*"`
|
|
14
|
+
- **Meta Tools** (Beta): Dynamic tool discovery and execution based on natural language queries
|
|
15
|
+
- Integration with popular AI frameworks:
|
|
16
|
+
- OpenAI Functions
|
|
17
|
+
- LangChain Tools
|
|
18
|
+
- CrewAI Tools
|
|
19
|
+
- LangGraph Tool Node
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Python 3.9+ (core SDK functionality)
|
|
24
|
+
- Python 3.10+ (for MCP server and CrewAI examples)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
### Basic Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install stackone-ai
|
|
32
|
+
|
|
33
|
+
# Or with uv
|
|
34
|
+
uv add stackone-ai
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Optional Features
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Install with MCP server support (requires Python 3.10+)
|
|
41
|
+
uv add 'stackone-ai[mcp]'
|
|
42
|
+
# or
|
|
43
|
+
pip install 'stackone-ai[mcp]'
|
|
44
|
+
|
|
45
|
+
# Install with CrewAI examples (requires Python 3.10+)
|
|
46
|
+
uv add 'stackone-ai[examples]'
|
|
47
|
+
# or
|
|
48
|
+
pip install 'stackone-ai[examples]'
|
|
49
|
+
|
|
50
|
+
# Install everything
|
|
51
|
+
uv add 'stackone-ai[mcp,examples]'
|
|
52
|
+
# or
|
|
53
|
+
pip install 'stackone-ai[mcp,examples]'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from stackone_ai import StackOneToolSet
|
|
60
|
+
|
|
61
|
+
# Initialize with API key
|
|
62
|
+
toolset = StackOneToolSet() # Uses STACKONE_API_KEY env var
|
|
63
|
+
# Or explicitly: toolset = StackOneToolSet(api_key="your-api-key")
|
|
64
|
+
|
|
65
|
+
# Get HRIS-related tools with glob patterns
|
|
66
|
+
tools = toolset.get_tools("hris_*", account_id="your-account-id")
|
|
67
|
+
# Exclude certain tools with negative patterns
|
|
68
|
+
tools = toolset.get_tools(["hris_*", "!hris_delete_*"])
|
|
69
|
+
|
|
70
|
+
# Use a specific tool with the new call method
|
|
71
|
+
employee_tool = tools.get_tool("hris_get_employee")
|
|
72
|
+
# Call with keyword arguments
|
|
73
|
+
employee = employee_tool.call(id="employee-id")
|
|
74
|
+
# Or with traditional execute method
|
|
75
|
+
employee = employee_tool.execute({"id": "employee-id"})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Implicit Feedback (Beta)
|
|
79
|
+
|
|
80
|
+
The Python SDK can emit implicit behavioural feedback to LangSmith so you can triage low-quality tool results without manually tagging runs.
|
|
81
|
+
|
|
82
|
+
### Automatic configuration
|
|
83
|
+
|
|
84
|
+
Set `LANGSMITH_API_KEY` in your environment and the SDK will initialise the implicit feedback manager on first tool execution. You can optionally fine-tune behaviour with:
|
|
85
|
+
|
|
86
|
+
- `STACKONE_IMPLICIT_FEEDBACK_ENABLED` (`true`/`false`, defaults to `true` when an API key is present)
|
|
87
|
+
- `STACKONE_IMPLICIT_FEEDBACK_PROJECT` to pin a LangSmith project name
|
|
88
|
+
- `STACKONE_IMPLICIT_FEEDBACK_TAGS` with a comma-separated list of tags applied to every run
|
|
89
|
+
|
|
90
|
+
### Manual configuration
|
|
91
|
+
|
|
92
|
+
If you want custom session or user resolvers, call `configure_implicit_feedback` during start-up:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from stackone_ai import configure_implicit_feedback
|
|
96
|
+
|
|
97
|
+
configure_implicit_feedback(
|
|
98
|
+
api_key="/path/to/langsmith.key",
|
|
99
|
+
project_name="stackone-agents",
|
|
100
|
+
default_tags=["python-sdk"],
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Providing your own `session_resolver`/`user_resolver` callbacks lets you derive identifiers from the request context before events are sent to LangSmith.
|
|
105
|
+
|
|
106
|
+
### Attaching session context to tool calls
|
|
107
|
+
|
|
108
|
+
Both `tool.execute` and `tool.call` accept an `options` keyword that is excluded from the API request but forwarded to the feedback manager:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
tool.execute(
|
|
112
|
+
{"id": "employee-id"},
|
|
113
|
+
options={
|
|
114
|
+
"feedback_session_id": "chat-42",
|
|
115
|
+
"feedback_user_id": "user-123",
|
|
116
|
+
"feedback_metadata": {"conversation_id": "abc"},
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
When two calls for the same session happen within a few seconds, the SDK emits a `refinement_needed` event, and you can inspect suitability scores directly in LangSmith.
|
|
122
|
+
|
|
123
|
+
## Integration Examples
|
|
124
|
+
|
|
125
|
+
<details>
|
|
126
|
+
<summary>LangChain Integration</summary>
|
|
127
|
+
|
|
128
|
+
StackOne tools work seamlessly with LangChain, enabling powerful AI agent workflows:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from langchain_openai import ChatOpenAI
|
|
132
|
+
from stackone_ai import StackOneToolSet
|
|
133
|
+
|
|
134
|
+
# Initialize StackOne tools
|
|
135
|
+
toolset = StackOneToolSet()
|
|
136
|
+
tools = toolset.get_tools("hris_*", account_id="your-account-id")
|
|
137
|
+
|
|
138
|
+
# Convert to LangChain format
|
|
139
|
+
langchain_tools = tools.to_langchain()
|
|
140
|
+
|
|
141
|
+
# Use with LangChain models
|
|
142
|
+
model = ChatOpenAI(model="gpt-4o-mini")
|
|
143
|
+
model_with_tools = model.bind_tools(langchain_tools)
|
|
144
|
+
|
|
145
|
+
# Execute AI-driven tool calls
|
|
146
|
+
response = model_with_tools.invoke("Get employee information for ID: emp123")
|
|
147
|
+
|
|
148
|
+
# Handle tool calls
|
|
149
|
+
for tool_call in response.tool_calls:
|
|
150
|
+
tool = tools.get_tool(tool_call["name"])
|
|
151
|
+
if tool:
|
|
152
|
+
result = tool.execute(tool_call["args"])
|
|
153
|
+
print(f"Result: {result}")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
</details>
|
|
157
|
+
|
|
158
|
+
<details>
|
|
159
|
+
<summary>LangGraph Integration</summary>
|
|
160
|
+
|
|
161
|
+
StackOne tools convert to LangChain tools, which LangGraph consumes via its prebuilt nodes:
|
|
162
|
+
|
|
163
|
+
Prerequisites:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pip install langgraph langchain-openai
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from langchain_openai import ChatOpenAI
|
|
171
|
+
from typing import Annotated
|
|
172
|
+
from typing_extensions import TypedDict
|
|
173
|
+
|
|
174
|
+
from langgraph.graph import StateGraph, START, END
|
|
175
|
+
from langgraph.graph.message import add_messages
|
|
176
|
+
from langgraph.prebuilt import tools_condition
|
|
177
|
+
|
|
178
|
+
from stackone_ai import StackOneToolSet
|
|
179
|
+
from stackone_ai.integrations.langgraph import to_tool_node, bind_model_with_tools
|
|
180
|
+
|
|
181
|
+
# Prepare tools
|
|
182
|
+
toolset = StackOneToolSet()
|
|
183
|
+
tools = toolset.get_tools("hris_*", account_id="your-account-id")
|
|
184
|
+
langchain_tools = tools.to_langchain()
|
|
185
|
+
|
|
186
|
+
class State(TypedDict):
|
|
187
|
+
messages: Annotated[list, add_messages]
|
|
188
|
+
|
|
189
|
+
# Build a small agent loop: LLM -> maybe tools -> back to LLM
|
|
190
|
+
graph = StateGraph(State)
|
|
191
|
+
graph.add_node("tools", to_tool_node(langchain_tools))
|
|
192
|
+
|
|
193
|
+
def call_llm(state: dict):
|
|
194
|
+
llm = ChatOpenAI(model="gpt-4o-mini")
|
|
195
|
+
llm = bind_model_with_tools(llm, langchain_tools)
|
|
196
|
+
resp = llm.invoke(state["messages"]) # returns AIMessage with optional tool_calls
|
|
197
|
+
return {"messages": state["messages"] + [resp]}
|
|
198
|
+
|
|
199
|
+
graph.add_node("llm", call_llm)
|
|
200
|
+
graph.add_edge(START, "llm")
|
|
201
|
+
graph.add_conditional_edges("llm", tools_condition)
|
|
202
|
+
graph.add_edge("tools", "llm")
|
|
203
|
+
app = graph.compile()
|
|
204
|
+
|
|
205
|
+
_ = app.invoke({"messages": [("user", "Get employee with id emp123") ]})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
<details>
|
|
211
|
+
<summary>CrewAI Integration (Python 3.10+)</summary>
|
|
212
|
+
|
|
213
|
+
CrewAI uses LangChain tools natively, making integration seamless:
|
|
214
|
+
|
|
215
|
+
> **Note**: CrewAI requires Python 3.10+. Install with `pip install 'stackone-ai[examples]'`
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from crewai import Agent, Crew, Task
|
|
219
|
+
from stackone_ai import StackOneToolSet
|
|
220
|
+
|
|
221
|
+
# Get tools and convert to LangChain format
|
|
222
|
+
toolset = StackOneToolSet()
|
|
223
|
+
tools = toolset.get_tools("hris_*", account_id="your-account-id")
|
|
224
|
+
langchain_tools = tools.to_langchain()
|
|
225
|
+
|
|
226
|
+
# Create CrewAI agent with StackOne tools
|
|
227
|
+
agent = Agent(
|
|
228
|
+
role="HR Manager",
|
|
229
|
+
goal="Analyze employee data and generate insights",
|
|
230
|
+
backstory="Expert in HR analytics and employee management",
|
|
231
|
+
tools=langchain_tools,
|
|
232
|
+
llm="gpt-4o-mini"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Define task and execute
|
|
236
|
+
task = Task(
|
|
237
|
+
description="Find all employees in the engineering department",
|
|
238
|
+
agent=agent,
|
|
239
|
+
expected_output="List of engineering employees with their details"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
crew = Crew(agents=[agent], tasks=[task])
|
|
243
|
+
result = crew.kickoff()
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
</details>
|
|
247
|
+
|
|
248
|
+
## Feedback Collection
|
|
249
|
+
|
|
250
|
+
The SDK includes a feedback collection tool (`meta_collect_tool_feedback`) that allows users to submit feedback about their experience with StackOne tools. This tool is automatically included in the toolset and is designed to be invoked by AI agents after user permission.
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from stackone_ai import StackOneToolSet
|
|
254
|
+
|
|
255
|
+
toolset = StackOneToolSet()
|
|
256
|
+
|
|
257
|
+
# Get the feedback tool (included with "meta_*" pattern or all tools)
|
|
258
|
+
tools = toolset.get_tools("meta_*")
|
|
259
|
+
feedback_tool = tools.get_tool("meta_collect_tool_feedback")
|
|
260
|
+
|
|
261
|
+
# Submit feedback (typically invoked by AI after user consent)
|
|
262
|
+
result = feedback_tool.call(
|
|
263
|
+
feedback="The HRIS tools are working great! Very fast response times.",
|
|
264
|
+
account_id="acc_123456",
|
|
265
|
+
tool_names=["hris_list_employees", "hris_get_employee"]
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Important**: The AI agent should always ask for user permission before submitting feedback:
|
|
270
|
+
- "Are you ok with sending feedback to StackOne? The LLM will take care of sending it."
|
|
271
|
+
- Only call the tool after the user explicitly agrees.
|
|
272
|
+
|
|
273
|
+
## Meta Tools (Beta)
|
|
274
|
+
|
|
275
|
+
Meta tools enable dynamic tool discovery and execution without hardcoding tool names:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
# Get meta tools for dynamic discovery
|
|
279
|
+
tools = toolset.get_tools("hris_*")
|
|
280
|
+
meta_tools = tools.meta_tools()
|
|
281
|
+
|
|
282
|
+
# Search for relevant tools using natural language
|
|
283
|
+
filter_tool = meta_tools.get_tool("meta_search_tools")
|
|
284
|
+
results = filter_tool.call(query="manage employees", limit=5)
|
|
285
|
+
|
|
286
|
+
# Execute discovered tools dynamically
|
|
287
|
+
execute_tool = meta_tools.get_tool("meta_execute_tool")
|
|
288
|
+
result = execute_tool.call(toolName="hris_list_employees", params={"limit": 10})
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Examples
|
|
292
|
+
|
|
293
|
+
For more examples, check out the [examples/](examples/) directory:
|
|
294
|
+
|
|
295
|
+
- [Error Handling](examples/error_handling.py)
|
|
296
|
+
- [StackOne Account IDs](examples/stackone_account_ids.py)
|
|
297
|
+
- [Available Tools](examples/available_tools.py)
|
|
298
|
+
- [File Uploads](examples/file_uploads.py)
|
|
299
|
+
- [OpenAI Integration](examples/openai_integration.py)
|
|
300
|
+
- [LangChain Integration](examples/langchain_integration.py)
|
|
301
|
+
- [CrewAI Integration](examples/crewai_integration.py)
|
|
302
|
+
- [LangGraph Tool Node](examples/langgraph_tool_node.py)
|
|
303
|
+
- [Meta Tools](examples/meta_tools_example.py)
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
Apache 2.0 License
|