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.
Files changed (83) hide show
  1. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.github/workflows/docs.yml +1 -1
  2. stackone_ai-0.3.3/.github/workflows/lint.yml +42 -0
  3. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.github/workflows/release.yml +1 -1
  4. stackone_ai-0.3.3/.github/workflows/test.yml +37 -0
  5. stackone_ai-0.3.3/.release-please-manifest.json +3 -0
  6. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/CHANGELOG.md +25 -0
  7. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/PKG-INFO +166 -5
  8. stackone_ai-0.3.3/README.md +307 -0
  9. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/pyproject.toml +28 -6
  10. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/__init__.py +6 -2
  11. stackone_ai-0.3.3/stackone_ai/feedback/__init__.py +5 -0
  12. stackone_ai-0.3.3/stackone_ai/feedback/tool.py +238 -0
  13. stackone_ai-0.3.3/stackone_ai/integrations/__init__.py +20 -0
  14. stackone_ai-0.3.3/stackone_ai/integrations/langgraph.py +94 -0
  15. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/meta_tools.py +8 -3
  16. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/models.py +114 -74
  17. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/server.py +21 -6
  18. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/specs/parser.py +4 -1
  19. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/toolset.py +14 -0
  20. stackone_ai-0.3.3/tests/test_feedback.py +269 -0
  21. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_tool_calling.py +0 -18
  22. stackone_ai-0.3.3/uv.lock +5969 -0
  23. stackone_ai-0.3.1/.github/workflows/lint.yml +0 -29
  24. stackone_ai-0.3.1/.github/workflows/test.yml +0 -27
  25. stackone_ai-0.3.1/.release-please-manifest.json +0 -3
  26. stackone_ai-0.3.1/README.md +0 -154
  27. stackone_ai-0.3.1/examples/langgraph_tool_node.py +0 -39
  28. stackone_ai-0.3.1/uv.lock +0 -4038
  29. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/cursor-rules-location.mdc +0 -0
  30. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/examples-standards.mdc +0 -0
  31. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/no-relative-imports.mdc +0 -0
  32. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/package-installation.mdc +0 -0
  33. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/release-please-standards.mdc +0 -0
  34. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.cursor/rules/uv-scripts.mdc +0 -0
  35. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.env_example +0 -0
  36. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.gitignore +0 -0
  37. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.mcp.json +0 -0
  38. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.pre-commit-config.yaml +0 -0
  39. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.release-please-config.json +0 -0
  40. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/.vscode/settings.json +0 -0
  41. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/CLAUDE.md +0 -0
  42. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/LICENSE +0 -0
  43. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/Makefile +0 -0
  44. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/available_tools.py +0 -0
  45. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/crewai_integration.py +0 -0
  46. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/custom_base_url.py +0 -0
  47. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/error_handling.py +0 -0
  48. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/file_uploads.py +0 -0
  49. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/index.py +0 -0
  50. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/langchain_integration.py +0 -0
  51. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/mcp_server.py +0 -0
  52. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/meta_tools_example.py +0 -0
  53. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/openai_integration.py +0 -0
  54. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/stackone_account_ids.py +0 -0
  55. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/examples/test_examples.py +0 -0
  56. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/mkdocs.yml +0 -0
  57. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/py.typed +0 -0
  58. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/build_docs.py +0 -0
  59. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/pull_oas.py +0 -0
  60. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/scripts/update_version.py +0 -0
  61. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/constants.py +0 -0
  62. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/ats.json +0 -0
  63. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/core.json +0 -0
  64. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/crm.json +0 -0
  65. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/documents.json +0 -0
  66. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/hris.json +0 -0
  67. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/iam.json +0 -0
  68. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/lms.json +0 -0
  69. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/oas/marketing.json +0 -0
  70. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/py.typed +0 -0
  71. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/stackone_ai/specs/loader.py +0 -0
  72. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/ats_tools.json +0 -0
  73. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/core_tools.json +0 -0
  74. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/crm_tools.json +0 -0
  75. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/documents_tools.json +0 -0
  76. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/hris_tools.json +0 -0
  77. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/iam_tools.json +0 -0
  78. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/lms_tools.json +0 -0
  79. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/snapshots/test_parser/test_parse_all_oas_specs/marketing_tools.json +0 -0
  80. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_meta_tools.py +0 -0
  81. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_models.py +0 -0
  82. {stackone_ai-0.3.1 → stackone_ai-0.3.3}/tests/test_parser.py +0 -0
  83. {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@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
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@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
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
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.3.3"
3
+ }
@@ -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.1
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.11
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>CrewAI Integration</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
+ [![PyPI version](https://badge.fury.io/py/stackone-ai.svg)](https://badge.fury.io/py/stackone-ai)
4
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/StackOneHQ/stackone-ai-python)](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