-
Notifications
You must be signed in to change notification settings - Fork 0
feat: support Python 3.9+ with optional MCP server support #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
1a9bda2
f9d6988
ccfd709
4254aaf
aa7f568
52bd25e
0e7fae6
0a14357
c9df82a
3eb2ea2
9f4024c
03b8438
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,20 @@ on: | |
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] | ||
| include: | ||
| - python-version: "3.9" | ||
| test-extras: "--all-extras --no-extra mcp" | ||
| - python-version: "3.10" | ||
| test-extras: "--all-extras" | ||
| - python-version: "3.11" | ||
| test-extras: "--all-extras" | ||
| - python-version: "3.12" | ||
| test-extras: "--all-extras" | ||
| - python-version: "3.13" | ||
| test-extras: "--all-extras" | ||
|
||
| env: | ||
| STACKONE_API_KEY: ${{ secrets.STACKONE_API_KEY }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
|
|
@@ -17,11 +31,11 @@ jobs: | |
| - name: Install uv | ||
| uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3 | ||
| with: | ||
| python-version: "3.11" | ||
| python-version: ${{ matrix.python-version }} | ||
| enable-cache: true | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --all-extras | ||
| run: uv sync ${{ matrix.test-extras }} | ||
|
|
||
| - name: Run tests | ||
| run: uv run pytest | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,12 +18,32 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug | |||||
| - CrewAI Tools | ||||||
| - LangGraph Tool Node | ||||||
|
|
||||||
| ## Requirements | ||||||
|
|
||||||
| - Python 3.9+ (core SDK functionality) | ||||||
| - Python 3.10+ (for MCP server and CrewAI examples) | ||||||
|
|
||||||
| ## Installation | ||||||
|
|
||||||
| ### Basic Installation | ||||||
|
|
||||||
| ```bash | ||||||
| pip install stackone-ai | ||||||
| ``` | ||||||
|
|
||||||
| ### Optional Features | ||||||
|
|
||||||
| ```bash | ||||||
| # Install with MCP server support (requires Python 3.10+) | ||||||
| pip install stackone-ai[mcp] | ||||||
|
||||||
| pip install stackone-ai[mcp] | |
| pip install "stackone-ai[mcp]" |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote extras in pip install to prevent shell globbing errors in some shells.
Prompt for AI agents
Address the following comment on README.md at line 41:
<comment>Quote extras in pip install to prevent shell globbing errors in some shells.</comment>
<file context>
@@ -18,12 +18,32 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
- CrewAI Tools
- LangGraph Tool Node
+## Requirements
+
+- Python 3.9+ (core SDK functionality)
+- Python 3.10+ (for MCP server and CrewAI examples)
+
## Installation
</file context>
| pip install stackone-ai[examples] | |
| pip install "stackone-ai[examples]" |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote combined extras in pip install to avoid shell globbing issues.
Prompt for AI agents
Address the following comment on README.md at line 44:
<comment>Quote combined extras in pip install to avoid shell globbing issues.</comment>
<file context>
@@ -18,12 +18,32 @@ StackOne AI provides a unified interface for accessing various SaaS tools throug
- CrewAI Tools
- LangGraph Tool Node
+## Requirements
+
+- Python 3.9+ (core SDK functionality)
+- Python 3.10+ (for MCP server and CrewAI examples)
+
## Installation
</file context>
| pip install stackone-ai[mcp,examples] | |
| pip install "stackone-ai[mcp,examples]" |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote extras in the inline pip install example to avoid shell globbing issues in zsh and similar shells.
Prompt for AI agents
Address the following comment on README.md at line 109:
<comment>Quote extras in the inline pip install example to avoid shell globbing issues in zsh and similar shells.</comment>
<file context>
@@ -82,10 +102,12 @@ for tool_call in response.tool_calls:
</details>
<details>
-<summary>CrewAI Integration</summary>
+<summary>CrewAI Integration (Python 3.10+)</summary>
CrewAI uses LangChain tools natively, making integration seamless:
+> **Note**: CrewAI requires Python 3.10+. Install with `pip install stackone-ai[examples]`
</file context>
| > **Note**: CrewAI requires Python 3.10+. Install with `pip install stackone-ai[examples]` | |
| > **Note**: CrewAI requires Python 3.10+. Install with `pip install "stackone-ai[examples]"` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ name = "stackone-ai" | |
| version = "0.3.1" | ||
| description = "agents performing actions on your SaaS" | ||
| readme = "README.md" | ||
| requires-python = ">=3.11" | ||
| requires-python = ">=3.9" | ||
| authors = [ | ||
| { name = "StackOne", email = "[email protected]" } | ||
| ] | ||
|
|
@@ -12,16 +12,21 @@ classifiers = [ | |
| "Intended Audience :: Developers", | ||
| "License :: OSI Approved :: MIT License", | ||
| "Programming Language :: Python :: 3", | ||
| "Programming Language :: Python :: 3.9", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.11", | ||
| "Programming Language :: Python :: 3.12", | ||
| "Programming Language :: Python :: 3.13", | ||
| "Topic :: Software Development :: Libraries :: Python Modules", | ||
| ] | ||
| dependencies = [ | ||
| "pydantic>=2.10.6", | ||
| "requests>=2.32.3", | ||
| "langchain-core>=0.1.0", | ||
| "mcp[cli]>=1.3.0", | ||
| "bm25s>=0.2.2", | ||
| "numpy>=1.24.0", | ||
| "typing-extensions>=4.0.0", | ||
| "eval-type-backport; python_version<'3.10'", | ||
| ] | ||
|
|
||
| [project.scripts] | ||
|
|
@@ -41,8 +46,12 @@ packages = ["stackone_ai"] | |
| "py.typed" = "py.typed" | ||
|
|
||
| [project.optional-dependencies] | ||
| # TODO: Remove python_version conditions when Python 3.9 support is dropped | ||
| mcp = [ | ||
| "mcp[cli]>=1.3.0; python_version>='3.10'", | ||
| ] | ||
| examples = [ | ||
| "crewai>=0.102.0", | ||
| "crewai>=0.102.0; python_version>='3.10'", | ||
| "langchain-openai>=0.3.6", | ||
| "openai>=1.63.2", | ||
| "python-dotenv>=1.0.1", | ||
|
|
@@ -82,7 +91,7 @@ markers = [ | |
|
|
||
| [tool.ruff] | ||
| line-length = 110 | ||
| target-version = "py311" | ||
| target-version = "py39" | ||
|
|
||
| [tool.ruff.lint] | ||
| select = [ | ||
|
|
@@ -96,7 +105,7 @@ select = [ | |
| ] | ||
|
|
||
| [tool.mypy] | ||
| python_version = "3.11" | ||
| python_version = "3.9" | ||
| disallow_untyped_defs = true | ||
| disallow_incomplete_defs = true | ||
| check_untyped_defs = true | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,22 @@ | ||
| # TODO: Remove when Python 3.9 support is dropped | ||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| import base64 | ||
| import json | ||
| from collections.abc import Sequence | ||
| from enum import Enum | ||
| from functools import partial | ||
| from typing import Annotated, Any, TypeAlias, cast | ||
| from typing import Annotated, Any, cast | ||
|
|
||
| import requests | ||
| from langchain_core.tools import BaseTool | ||
| from pydantic import BaseModel, BeforeValidator, Field, PrivateAttr | ||
| from requests.exceptions import RequestException | ||
|
|
||
| # TODO: Remove when Python 3.9 support is dropped | ||
| from typing_extensions import TypeAlias | ||
|
|
||
| # Type aliases for common types | ||
| JsonDict: TypeAlias = dict[str, Any] | ||
| Headers: TypeAlias = dict[str, str] | ||
|
|
@@ -140,21 +146,20 @@ def _prepare_request_params(self, kwargs: JsonDict) -> tuple[str, JsonDict, Json | |
| for key, value in kwargs.items(): | ||
| param_location = self._execute_config.parameter_locations.get(key) | ||
|
|
||
| match param_location: | ||
| case ParameterLocation.PATH: | ||
| if param_location == ParameterLocation.PATH: | ||
| url = url.replace(f"{{{key}}}", str(value)) | ||
ryoppippi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| elif param_location == ParameterLocation.QUERY: | ||
| query_params[key] = value | ||
| elif param_location in (ParameterLocation.BODY, ParameterLocation.FILE): | ||
| body_params[key] = value | ||
| else: | ||
| # Default behavior | ||
| if f"{{{key}}}" in url: | ||
| url = url.replace(f"{{{key}}}", str(value)) | ||
| case ParameterLocation.QUERY: | ||
| elif self._execute_config.method in {"GET", "DELETE"}: | ||
| query_params[key] = value | ||
| case ParameterLocation.BODY | ParameterLocation.FILE: | ||
| else: | ||
| body_params[key] = value | ||
| case _: | ||
| # Default behavior | ||
| if f"{{{key}}}" in url: | ||
| url = url.replace(f"{{{key}}}", str(value)) | ||
| elif self._execute_config.method in {"GET", "DELETE"}: | ||
| query_params[key] = value | ||
| else: | ||
| body_params[key] = value | ||
|
|
||
| return url, body_params, query_params | ||
|
|
||
|
|
@@ -355,13 +360,12 @@ def to_langchain(self) -> BaseTool: | |
| python_type: type = str # Default to str | ||
| if isinstance(details, dict): | ||
| type_str = details.get("type", "string") | ||
| match type_str: | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not supported in 3.9 |
||
| case "number": | ||
| python_type = float | ||
| case "integer": | ||
| python_type = int | ||
| case "boolean": | ||
| python_type = bool | ||
| if type_str == "number": | ||
| python_type = float | ||
| elif type_str == "integer": | ||
| python_type = int | ||
| elif type_str == "boolean": | ||
| python_type = bool | ||
|
|
||
| field = Field(description=details.get("description", "")) | ||
| else: | ||
|
|
@@ -480,7 +484,7 @@ def to_langchain(self) -> Sequence[BaseTool]: | |
| """ | ||
| return [tool.to_langchain() for tool in self.tools] | ||
|
|
||
| def meta_tools(self) -> "Tools": | ||
| def meta_tools(self) -> Tools: | ||
| """Return meta tools for tool discovery and execution | ||
|
|
||
| Meta tools enable dynamic tool discovery and execution based on natural language queries. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.