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
238 changes: 98 additions & 140 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,179 +1,137 @@
<div align="center">
<img src="impossibly-logo-full.png" alt="Impossibly Logo" width="600">

<p><strong>Build, orchestrate and scale agents impossibly fast</strong></p>

[![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)
</div>

---

# 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.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
50 changes: 37 additions & 13 deletions src/impossibly/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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)
Expand All @@ -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

Expand All @@ -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


Expand Down