-
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" | ||
|
Contributor
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. do we need all of these, can we just do 3.9 & 3.10 assuming backwards compatibility ? Maybe 3.9/3.10 & the latest LTE
Contributor
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. oh 3.14 is the latest |
||
| 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] | ||||||
|
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. Quote extras in pip install to avoid shell globbing (e.g., zsh) causing 'no matches found'. Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| # Install with CrewAI examples (requires Python 3.10+) | ||||||
| pip install stackone-ai[examples] | ||||||
|
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. Quote extras in pip install to prevent shell globbing errors in some shells. Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| # Install everything | ||||||
| pip install stackone-ai[mcp,examples] | ||||||
|
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. Quote combined extras in pip install to avoid shell globbing issues. Prompt for AI agents
Suggested change
|
||||||
| ``` | ||||||
|
|
||||||
| ## Quick Start | ||||||
|
|
||||||
| ```python | ||||||
|
|
@@ -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]` | ||||||
|
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. Quote extras in the inline pip install example to avoid shell globbing issues in zsh and similar shells. Prompt for AI agents
Suggested change
|
||||||
|
|
||||||
| ```python | ||||||
| from crewai import Agent, Crew, Task | ||||||
| from stackone_ai import StackOneToolSet | ||||||
|
|
||||||
| 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
|
||
| 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: | ||
|
Contributor
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.