diff --git a/README.md b/README.md index 956a697..a64582d 100644 --- a/README.md +++ b/README.md @@ -1,179 +1,137 @@ +
+ Impossibly Logo + +

Build, orchestrate and scale agents impossibly fast

+ + [![PyPI version](https://badge.fury.io/py/impossibly.svg)](https://badge.fury.io/py/impossibly) + [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) + [![GitHub stars](https://img.shields.io/badge/github-stars-yellow?style=social&logo=github)](https://github.com/jacksongrove/impossibly) + [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + [![Documentation](https://img.shields.io/badge/docs-impossibly.dev-blue)](https://impossibly.dev) + + [**Documentation**](https://impossibly.dev) | [**Examples**](examples/) | [**Discord**](https://discord.gg/impossibly) | [**GitHub**](https://github.com/jacksongrove/impossibly) +
+ +--- + # Impossibly -Impossibly is an agentic orchestration framework for rapidly building agentic architectures in Python. It accelerates agentic development, empowering developers to craft architectures that enable LLMs to excel in higher-order tasks like idea generation and critical thinking. -This library is designed to be used as a backend for AI apps and automations, providing support for all major LLM providers and locally-hosted model endpoints. +Impossibly is a lean Python library for building and orchestrating production AI agents. Declare tools as plain Python functions, keep a tiny dependency footprint, and skip the boilerplate—the stable core ships with first-class multimodal support and built-in tracing so you can ship and debug impossibly fast. -# Getting Started -## Installation +🎯 Why Impossibly? +--- +1. Stable core without surprise breakages. +2. Ultra-lean footprint. No dependency bloat. +3. Declare tools as plain Python functions. +4. Multi-modal out of the box. +5. Powerful agents without the boilerplate. It's time to build impossibly fast. -Install the base package: -```bash -pip install impossibly -``` - -Or install with specific integrations: -```bash -# Minimal installations with specific providers -pip install "impossibly[openai]" # Only OpenAI support -pip install "impossibly[anthropic]" # Only Anthropic support -pip install "impossibly[all]" # All LLM integrations - -# For testing and development -pip install "impossibly[test]" # All LLM integrations + testing tools -pip install "impossibly[dev]" # All LLM integrations + testing + dev tools -``` +## 🚀 Quick Start -## Imports -Import the components you need: ```python from impossibly import Agent, Graph, START, END -``` - -## Setting Up Environment Variables -1. Copy the `.env.template` file to a new file named `.env`: - ```bash - cp .env.template .env - ``` - -2. Fill in your API keys and configurations in the `.env` file: - ``` - OPENAI_API_KEY=your_actual_api_key_here - ANTHROPIC_API_KEY=your_anthropic_api_key_here - ``` -3. The library will automatically load these variables when needed. At minimum, you'll need the API key for your preferred LLM provider. - -## Initalize Clients for LLM APIs -Done in the format standard to your API. - -## Create Agents -Initalize the agents you'd like to call with a simple schema: -``` +# Create an Impossibly agent agent = Agent( - client=client, - model="gpt-4o", - name="Agent", - system_prompt="You are a friendly assistant.", - description="This agent is an example agent." - ) -``` - -## Define how agents are executed with a Graph -Graphs connect agents together using nodes and edges, routing the execution flow and all needed information through the graph. Each node represents an agent and each edge represents a conditional under which that agent is called. This conditional can be defined in natural language for each agent, within its `system_prompt`. - -In the case of multiple edges branching from one node, agents can understand their routing options using the `description` field of connecting nodes. + client=openai_client, + model="gpt-4o", + system_prompt="You are a helpful assistant", + tools=[web_search, calculator, database] +) -Every graph accepts user input at the `START` and returns a response to the user at the `END`. - -With this basic understanding, a graph can be created in just a few lines. -``` +# Build a reasoning workflow graph = Graph() - -graph.add_node(agent) -graph.add_node(summarizer_agent) - graph.add_edge(START, agent) -graph.add_edge(agent, summarizer_agent) -graph.add_edge(summarizer_agent, END) -``` +graph.add_edge(agent, END) -## Run your Graph -You're done! Prompt your agentic architecture. -``` -graph.invoke("Hello there!") +# Execute with autonomous reasoning +result = graph.invoke("Analyze current market trends and provide strategic recommendations") ``` -# Development -## Running Tests +## ✨ Core Features -To run the tests, first install the package with test dependencies: +### One-line Agent Instantiation +Define agents and the tools available to them in a single object instantiation: +- **Native Multi-modal Support**: Agents can work with both text and images straight out of the box +- **Native Routing**: Under-the-hood prompt-injection to ensure intelligent decision making and routing +- **Custom Functions:** +Build functions simply with Python, then connect them as tools to your agents -```bash -# Install with test dependencies -pip install -e ".[test]" -``` - -Then run the tests using the CLI command that gets installed with the package: - -```bash -# Run all tests -impossibly run - -# Run just feature tests -impossibly run --path features/ +### Graph-Based Orchestration +Agents connected with a visual and intuitive workflow design: +- **Conditional Logic**: Route based on agent decisions +- **Monitoring**: Track agent performance and decisions -# Run tests in Docker -impossibly run --docker - -# Get help -impossibly run --help -``` +### Tool Integration +Seamless connection to external systems and data with native Python functions: +- **User-Created Tools**: Connect to any API, service, database and more with self-defined Python functions +- **Own Your Tools**: Core updates won't break your functions—full control and easy fixes +- **Custom Tools**: Build domain-specific capabilities with Python -See [tests/README.md](tests/README.md) for more details on the testing framework and available options. +### Designed for Multi-Agent Architectures +Specialized agents working together on complex tasks: +- **Role-Based Design**: Each agent has a specific expertise +- **Coordinated Workflows**: Agents pass work between each other +- **Quality Assurance**: Multiple agents validate and improve results +- **Scalable Architecture**: Add agents as complexity grows -## Local Development and Running Examples +## 📚 Examples -If you want to develop locally and test the examples, follow these steps: +Explore practical implementations in the `/examples` directory: -### Building the Package Locally +- **SQL Agent**: Autonomous database analysis with iterative reasoning +- **Research Agent**: Multi-step research with source validation +- **Conversational Agents**: Context-aware dialogue systems +- **Tool Agents**: Specialized agents for specific tasks +- **Mixture of Experts**: Dynamic agent selection based on task requirements -1. Clone the repository: - ```bash - git clone https://github.com/jacksongrove/impossibly.git - cd impossibly - ``` +## 🛠 Installation -2. Create and activate a virtual environment (optional but recommended): - ```bash - python -m venv .venv - source .venv/bin/activate # On Windows: .venv\Scripts\activate - ``` - -3. Install development dependencies: - ```bash - pip install -e ".[dev]" - ``` +```bash +# Base installation +pip install impossibly -4. Build the package locally: - ```bash - python -m build - ``` - This will create distributions in the `dist/` directory. +# With specific LLM providers only +pip install "impossibly[openai]" +pip install "impossibly[anthropic]" +pip install "impossibly[all]" -### Installing the Local Build to Run Examples +# For development & contributions +pip install "impossibly[dev]" +``` -There are two approaches to use your local build: +## 🧪 Testing -#### Option 1: Install in Development Mode (Recommended) +```bash +# Install with test dependencies +pip install -e ".[test]" -This allows changes to the source code to be immediately reflected without reinstalling: +# Run test suite +impossibly run -```bash -pip install -e . +# Run in Docker +impossibly run --docker ``` -#### Option 2: Install the Built Wheel - -If you want to test the exact distribution that would be uploaded to PyPI: +## 📖 Documentation -```bash -pip install dist/impossibly-0.1.0-py3-none-any.whl -``` +Visit **[impossibly.dev](https://impossibly.dev)** for: +- Complete API documentation +- Agentic AI tutorials and guides +- Framework comparisons (LangGraph, CrewAI, AutoGen) +- Real-world case studies +- Best practices for building reliable agents -### Running Examples +## 🤝 Community -Once you've installed the package using either method, you can run the examples: +- **Discord**: [Join our community](https://discord.gg/impossibly) +- **GitHub**: [Contribute to the project](https://github.com/jacksongrove/impossibly) +- **Documentation**: [Learn more at impossibly.dev](https://impossibly.dev) -```bash -# Set up your environment variables first -cp .env.template .env -# Edit .env to add your API keys +## 📄 License -# Run an example -python examples/image_agent/image_agent.py +MIT License - see [LICENSE](LICENSE) for details. -# Or try another example -python examples/web_search_agent/web_search_agent.py -``` +--- -Make sure the required dependencies for each example are installed and the necessary API keys are in your `.env` file. +**Ready to build, orchestrate and scale AI agents impossibly fast?** Start with the [documentation](https://impossibly.dev) and join the community pushing the boundaries of autonomous AI. diff --git a/pyproject.toml b/pyproject.toml index bbd4d76..20ee49a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta" [project] name = "impossibly" -version = "0.1.0" -description = "An agentic architecture for idea generation & critical thinking" +version = "0.1.1" +description = "Build, orchestrate and scale agents impossibly fast" readme = "README.md" requires-python = ">=3.9" license = "MIT" diff --git a/src/impossibly/graph.py b/src/impossibly/graph.py index 37d6586..10502d3 100644 --- a/src/impossibly/graph.py +++ b/src/impossibly/graph.py @@ -98,10 +98,9 @@ def add_edge(self, node1: Union[Agent, List[Agent]], node2: Union[Agent, List[Ag if n1 not in self.edges: raise ValueError(f"{n1} is not a valid node in the graph. Please add it first.") for n2 in node2: - if n1 is not n2: - if n2 not in self.edges: - raise ValueError(f"{n2} is not a valid node in the graph. Please add it first.") - self.edges[n1].append(n2) + if n2 not in self.edges and n2 != END: + raise ValueError(f"{n2} is not a valid node in the graph. Please add it first.") + self.edges[n1].append(n2) def invoke(self, user_prompt: str = "", files: list[str] = [], show_thinking: bool = False) -> str: @@ -141,6 +140,7 @@ async def _invoke_async(self, user_prompt: str = "", files: list[str] = [], show # Execute each node in the graph until END is reached curr_node = self.edges[START][0] prompt = user_prompt + original_prompt = user_prompt # Store original task for self-loops author = 'user' selected_files = files while curr_node != END: @@ -155,6 +155,7 @@ async def _invoke_async(self, user_prompt: str = "", files: list[str] = [], show i = 0 if len(self.edges[curr_node]) > 1: route_idx, output = self._get_route(curr_node, output) + i = route_idx # Route files intended to be passed selected_files, output = self._get_files(files, output) @@ -167,9 +168,26 @@ async def _invoke_async(self, user_prompt: str = "", files: list[str] = [], show await global_memory.add(curr_node, self.edges[curr_node][i], output) # Continue executing through the graph until END is reached - curr_node = self.edges[curr_node][i] - prompt = output - author = 'user' + next_node = self.edges[curr_node][i] + + # Handle self-loops: reset conversation to maintain tool-calling behavior + if next_node == curr_node: + # Self-loop: Reset message history and use fresh context + if hasattr(curr_node, 'client') and hasattr(curr_node.client, 'messages') and curr_node.client.messages: + # Keep only the system message (first message) + system_msg = curr_node.client.messages[0] + curr_node.client.messages = [system_msg] + + # Create fresh prompt with task context and progress + cleaned_output = output.replace(f'\\\\{curr_node.name}\\\\', '').strip() + prompt = f"{original_prompt}\n\nProgress so far: {cleaned_output}\n\nContinue with your task." + author = 'user' + else: + # Different agent: pass the full output + prompt = output + author = 'user' + + curr_node = next_node return None @@ -191,21 +209,27 @@ def _get_route(self, node: Agent, output: str) -> tuple[int, str]: - str: The output string with the routing command removed. ''' options = self.edges[node] - # Regex from back of list to find agent names in delimited text + # Regex to find agent names - handle both single and double backslashes + # Try double backslashes first, then single backslashes match = re.search(r'(?<=\\\\)(.*?)(?=\\\\)', output, re.DOTALL) + if not match: + match = re.search(r'(?<=\\)(.*?)(?=\\)', output, re.DOTALL) + if match: - # Remove the last instance of the command from the output - output = re.sub(r'\\\\' + re.escape(match.group(1)) + r'\\\\', '', output, count=1) + # Remove the routing command from the output (handle both patterns) + command = match.group(1) + output = re.sub(r'\\\\?' + re.escape(command) + r'\\\\?', '', output, count=1) # Get index of the desired agent in the node's edge list # Check if the match is the END command - if match.group(1) == 'END': + if command == 'END': return options.index(END), output else: for i, option in enumerate(options): - if option.name == match.group(1): + if option.name == command: return i, output - print("No route found. Choosing random route.") + + # No route found, choose default route return 0, output