Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,44 @@ from stackone_ai import StackOneToolSet
toolset = StackOneToolSet() # Uses STACKONE_API_KEY env var
# Or explicitly: toolset = StackOneToolSet(api_key="your-api-key")

# Get HRIS-related tools
# Get HRIS-related tools with glob patterns
tools = toolset.get_tools("hris_*", account_id="your-account-id")
# Exclude certain tools with negative patterns
tools = toolset.get_tools(["hris_*", "!hris_delete_*"])

# Use a specific tool
# Use a specific tool with the new call method
employee_tool = tools.get_tool("hris_get_employee")
# Call with keyword arguments
employee = employee_tool.call(id="employee-id")
# Or with traditional execute method
employee = employee_tool.execute({"id": "employee-id"})
```

## Meta Tools (Beta)

Meta tools enable dynamic tool discovery and execution without hardcoding tool names:

```python
# Get meta tools for dynamic discovery
tools = toolset.get_tools("hris_*")
meta_tools = tools.meta_tools()

# Search for relevant tools using natural language
filter_tool = meta_tools.get_tool("meta_filter_relevant_tools")
results = filter_tool.call(query="manage employees", limit=5)

# Execute discovered tools dynamically
execute_tool = meta_tools.get_tool("meta_execute_tool")
result = execute_tool.call(toolName="hris_list_employees", params={"limit": 10})
```

## Features

- Unified interface for multiple SaaS tools
- AI-friendly tool descriptions and parameters
- **Tool Calling**: Direct method calling with `tool.call()` for intuitive usage
- **Glob Pattern Filtering**: Advanced tool filtering with patterns like `"hris_*"` and exclusions `"!hris_delete_*"`
- **Meta Tools** (Beta): Dynamic tool discovery and execution based on natural language queries
- Integration with popular AI frameworks:
- OpenAI Functions
- LangChain Tools
Expand Down
247 changes: 247 additions & 0 deletions examples/meta_tools_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
#!/usr/bin/env python
"""
Example demonstrating meta tools for dynamic tool discovery and execution.

Meta tools allow AI agents to search for relevant tools based on natural language queries
and execute them dynamically without hardcoding tool names.
"""

import os

from dotenv import load_dotenv

from stackone_ai import StackOneToolSet

# Load environment variables
load_dotenv()


def example_meta_tools_basic():
"""Basic example of using meta tools for tool discovery"""
print("🔍 Example 1: Dynamic tool discovery\n")

# Initialize StackOne toolset
toolset = StackOneToolSet()

# Get all available tools (you can also use pattern like "hris_*")
all_tools = toolset.get_tools("hris_*")
print(f"Total HRIS tools available: {len(all_tools)}")

# Get meta tools for dynamic discovery
meta_tools = all_tools.meta_tools()

# Get the filter tool to search for relevant tools
filter_tool = meta_tools.get_tool("meta_filter_relevant_tools")
if filter_tool:
# Search for employee management tools
result = filter_tool.call(query="manage employees create update list", limit=5, minScore=0.0)

print("Found relevant tools:")
for tool in result.get("tools", []):
print(f" - {tool['name']} (score: {tool['score']:.2f}): {tool['description']}")

print()


def example_meta_tools_with_execution():
"""Example of discovering and executing tools dynamically"""
print("🚀 Example 2: Dynamic tool execution\n")

# Initialize toolset
toolset = StackOneToolSet()

# Get all tools
all_tools = toolset.get_tools()
meta_tools = all_tools.meta_tools()

# Step 1: Search for relevant tools
filter_tool = meta_tools.get_tool("meta_filter_relevant_tools")
execute_tool = meta_tools.get_tool("meta_execute_tool")

if filter_tool and execute_tool:
# Find tools for listing employees
search_result = filter_tool.call(query="list all employees", limit=1)

tools_found = search_result.get("tools", [])
if tools_found:
best_tool = tools_found[0]
print(f"Best matching tool: {best_tool['name']}")
print(f"Description: {best_tool['description']}")
print(f"Relevance score: {best_tool['score']:.2f}")

# Step 2: Execute the found tool
try:
print(f"\nExecuting {best_tool['name']}...")
result = execute_tool.call(toolName=best_tool["name"], params={"limit": 5})
print(f"Execution result: {result}")
except Exception as e:
print(f"Execution failed (expected in example): {e}")

print()


def example_tool_calling():
"""Example of the new tool calling functionality"""
print("📞 Example 3: Tool calling functionality\n")

# Initialize toolset
toolset = StackOneToolSet()

# Get a specific tool
tool = toolset.get_tool("hris_list_employees")

if tool:
print(f"Tool: {tool.name}")
print(f"Description: {tool.description}")

# New calling methods
try:
# Method 1: Call with keyword arguments
result = tool.call(limit=10, page=1)
print(f"Called with kwargs: {result}")
except Exception as e:
print(f"Call with kwargs (expected to fail in example): {e}")

try:
# Method 2: Call with dictionary
result = tool.call({"limit": 10, "page": 1})
print(f"Called with dict: {result}")
except Exception as e:
print(f"Call with dict (expected to fail in example): {e}")

print()


def example_with_openai():
"""Example of using meta tools with OpenAI"""
print("🤖 Example 4: Using meta tools with OpenAI\n")

try:
from openai import OpenAI

# Initialize OpenAI client
client = OpenAI()

# Initialize StackOne toolset
toolset = StackOneToolSet()

# Get HRIS tools and their meta tools
hris_tools = toolset.get_tools("hris_*")
meta_tools = hris_tools.meta_tools()

# Convert to OpenAI format
openai_tools = meta_tools.to_openai()

# Create a chat completion with meta tools
response = client.chat.completions.create(
model="gpt-4",
messages=[
{
"role": "system",
"content": "You are an HR assistant. Use meta_filter_relevant_tools to find appropriate tools, then meta_execute_tool to execute them.",
},
{"role": "user", "content": "Can you help me find tools for managing employee records?"},
],
tools=openai_tools,
tool_choice="auto",
)

print("OpenAI Response:", response.choices[0].message.content)

if response.choices[0].message.tool_calls:
print("\nTool calls made:")
for tool_call in response.choices[0].message.tool_calls:
print(f" - {tool_call.function.name}")

except ImportError:
print("OpenAI library not installed. Install with: pip install openai")
except Exception as e:
print(f"OpenAI example failed: {e}")

print()


def example_with_langchain():
"""Example of using tools with LangChain"""
print("🔗 Example 5: Using tools with LangChain\n")

try:
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# Initialize StackOne toolset
toolset = StackOneToolSet()

# Get tools and convert to LangChain format
tools = toolset.get_tools("hris_list_*")
langchain_tools = tools.to_langchain()

# Get meta tools as well
meta_tools = tools.meta_tools()
langchain_meta_tools = meta_tools.to_langchain()

# Combine all tools
all_langchain_tools = list(langchain_tools) + list(langchain_meta_tools)

print(f"Available tools for LangChain: {len(all_langchain_tools)}")
for tool in all_langchain_tools:
print(f" - {tool.name}: {tool.description}")

# Create LangChain agent
llm = ChatOpenAI(model="gpt-4", temperature=0)

prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are an HR assistant. Use the meta tools to discover and execute relevant tools.",
),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
]
)

agent = create_tool_calling_agent(llm, all_langchain_tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=all_langchain_tools, verbose=True)

# Run the agent
result = agent_executor.invoke({"input": "Find tools that can list employee data"})

print(f"\nAgent result: {result['output']}")

except ImportError as e:
print(f"LangChain dependencies not installed: {e}")
print("Install with: pip install langchain-openai")
except Exception as e:
print(f"LangChain example failed: {e}")

print()


def main():
"""Run all examples"""
print("=" * 60)
print("StackOne AI SDK - Meta Tools & Tool Calling Examples")
print("=" * 60)
print()

# Basic examples that work without external APIs
example_meta_tools_basic()
example_meta_tools_with_execution()
example_tool_calling()

# Examples that require OpenAI API
if os.getenv("OPENAI_API_KEY"):
example_with_openai()
example_with_langchain()
else:
print("ℹ️ Set OPENAI_API_KEY to run OpenAI and LangChain examples\n")

print("=" * 60)
print("Examples completed!")
print("=" * 60)


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ dependencies = [
"requests>=2.32.3",
"langchain-core>=0.1.0",
"mcp[cli]>=1.3.0",
"bm25s>=0.2.2",
"numpy>=1.24.0",
]

[project.scripts]
Expand Down Expand Up @@ -104,3 +106,7 @@ warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true

[[tool.mypy.overrides]]
module = "bm25s"
ignore_missing_imports = true
3 changes: 2 additions & 1 deletion stackone_ai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""StackOne AI SDK"""

from .models import StackOneTool, Tools
from .toolset import StackOneToolSet

__all__ = ["StackOneToolSet"]
__all__ = ["StackOneToolSet", "StackOneTool", "Tools"]
__version__ = "0.0.4"
Loading