Bridgic is a project that enables developers to develop agents/workflows in a more natural and flexible way, consisting of several Python packages.
Project description
Bridgic is an innovative programming framework designed to create agentic systems, ranging from deterministic workflows to autonomous agents. It introduces a new paradigm that simplifies the development of agentic systems.
✨ The name "Bridgic" is inspired by the idea of "Bridging Logic and Magic". It means seamlessly uniting the precision of logic (deterministic execution flows) with the creativity of magic (highly autonomous AI).
🔗 Features
- Orchestration: Bridgic helps manage the execution flow of your AI applications by leveraging both predefined dependencies and dynamic routing.
- Parameter Binding: There are three ways to pass data among workers—Arguments Mapping, Arguments Injection, and Inputs Propagation—thereby eliminating the complexity of global state management.
- Dynamic Routing: Bridgic enables conditional branching and intelligent decision-making through an easy-to-use
ferry_to()API that adapts to runtime dynamics. - Dynamic Topology: The topology can be changed at runtime in Bridgic to support highly autonomous AI applications.
- Modularity: In Bridgic, a complex agentic system can be composed by reusing components through hierarchical nesting.
- Human-in-the-Loop: A Bridgic-style agentic system can request feedback from human whenever needed to dynamically adjust its execution logic.
- Serialization: Bridgic employs a scalable serialization and deserialization mechanism to achieve state persistence and recovery, enabling human-in-the-loop in long-running AI systems.
- Systematic Integration: A wide range of tools and LLMs can be seamlessly integrated into the Bridgic world, in a systematic way.
- Customization: What Bridgic provides is not a "black box" approach. You have full control over every aspect of your AI applications, such as prompts, context windows, the control flow, and more.
📦 Installation
Python 3.9 or higher version is required.
pip install bridgic
🚀 Development Paradigm
Bridgic provides several core mechanisms for building agentic systems.
Here are simple examples demonstrating each key feature:
1. Static Declaration
The @worker decorator can define workers and their dependencies simultaneously within a GraphAutoma subclass. This declarative approach allows you to express complex workflows as a graph of interconnected tasks, where each worker represents a specific processing step and the execution order is automatically determined by dependencies.
from bridgic.core.automa import GraphAutoma, worker
class TextProcessor(GraphAutoma):
@worker(is_start=True)
async def load_text(self, text: str) -> str:
return text.strip()
@worker(dependencies=["load_text"])
async def count_words(self, text: str) -> int:
return len(text.split())
@worker(dependencies=["count_words"], is_output=True)
async def generate_summary(self, word_count: int) -> dict:
return {"word_count": word_count}
async def main():
"""Run the text processor example."""
processor = TextProcessor()
sample_text = "Hello world. This is a sample text. It contains multiple sentences."
result = await processor.arun(text=sample_text)
print(f"Final result: {result}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Final result: {'word_count': 11}
In this example, the text processing workflow demonstrates how static dependencies create a clear data flow: load_text → count_words → generate_summary. The framework automatically handles the execution order based on the declared dependencies, making it easy to reason about the workflow logic.
2. Dynamic Routing
Use ferry_to() for dynamic routing based on runtime conditions. This enables intelligent decision-making by allowing the system to adapt its behavior according to the content or context of incoming requests, thereby creating highly autonomous agents that can select among different processing paths.
from bridgic.core.automa import GraphAutoma, worker
class SmartRouter(GraphAutoma):
@worker(is_start=True)
async def analyze_request(self, request: str) -> str:
print(f"Analyzing request: {request}")
if "?" in request:
print("Detected question - routing to Q&A handler")
self.ferry_to("handle_question", question=request)
else:
print("Standard request - routing to general handler")
self.ferry_to("handle_general", question=request)
@worker()
async def handle_question(self, question: str) -> str:
print("❓ QUESTION: Processing question")
response = f"ANSWER: Based on your question '{question}', here's what I think..."
print(f"Response: {response}")
return response
@worker()
async def handle_general(self, question: str) -> str:
print("📝 GENERAL: Processing standard question")
response = f"ACKNOWLEDGED: {question}"
print(f"Response: {response}")
return response
async def main():
"""Run the smart router example."""
router = SmartRouter()
test_requests = [
"What is the weather like today?",
"Create a poeom about love."
]
for request in test_requests:
print(f"\n--- Processing: {request} ---")
await router.arun(request=request)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
--- Processing: What is the weather like today? ---
Analyzing request: What is the weather like today?
Detected question - routing to Q&A handler
❓ QUESTION: Processing question
Response: ANSWER: Based on your question 'What is the weather like today?', here's what I think...
--- Processing: Create a poeom about love. ---
Analyzing request: Create a poeom about love.
Standard request - routing to general handler
📝 GENERAL: Processing standard question
Response: ACKNOWLEDGED: Create a poeom about love.
The smart router example showcases how ferry_to() enables conditional execution paths. The system analyzes each request and dynamically chooses the appropriate handler, demonstrating how agents can make intelligent routing decisions based on the nature of incoming data.
3. Parameter Binding
Use parameter binding to control how data flows between workers with different mapping rules. This flexible parameter binding mechanism allows you to precisely control how data is passed between workers, supporting various data transformation patterns and enabling complex data processing pipelines.
from bridgic.core.automa import GraphAutoma, worker
from bridgic.core.automa.args import ArgsMappingRule, From
class DataProcessor(GraphAutoma):
@worker(is_start=True)
async def generate_data(self, bound: int) -> list:
return list(range(1, bound + 1))
@worker(dependencies=["generate_data"], args_mapping_rule=ArgsMappingRule.AS_IS)
async def calculate(self, data: list) -> dict:
return {"sum": sum(data), "count": len(data)}
@worker(dependencies=["calculate"], args_mapping_rule=ArgsMappingRule.UNPACK, is_output=True)
async def output(self, bound: From(key="generate_data"), sum: int, count: int) -> str:
return f"The bound is {bound}. The length of data is {count} and their sum is {sum}."
async def main():
"""Run the data processor example."""
processor = DataProcessor()
result = await processor.arun(bound=5)
print(f"Final result: {result}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Final result: The bound is 5. The length of data is 5 and their sum is 15.
This parameter binding example demonstrates the power of flexible data flow control. The AS_IS rule passes data as-is, while UNPACK spreads elements of a dict (or list) as individual arguments, and From() allows access to data from any worker without direct dependencies. This enables sophisticated data processing patterns in which workers can access information from multiple sources.
🤖 Building Complex Agentic System
By combining these features, you can build a Bridgic-style agentic system that can:
- Execute well-defined workflows through declarative dependencies;
- Adapt intelligently to different situations according to runtime conditions;
- Process complex data across multiple steps, supporting transformations and aggregations.
Whether you're building simple automation scripts or complex autonomous agents, Bridgic provides the tools to define your logic clearly while retaining the flexibility required for intelligent, adaptive behavior.
More examples will be added in the near future. :)
📚 Documents
For more about development skills of Bridgic, see:
📄 License
This repository is licensed under the MIT License.
🤝 Contributing
For contribution guidelines and instructions, please see CONTRIBUTING.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bridgic-0.1.1.tar.gz.
File metadata
- Download URL: bridgic-0.1.1.tar.gz
- Upload date:
- Size: 5.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9fd046046189240afea3c4a344e4e75c108caecf39d8e29cc60633033b785a0
|
|
| MD5 |
0ed9b5ef28dd281acd77eb846d9824a0
|
|
| BLAKE2b-256 |
1a8648e65edee6f2d5154a565a7a3d6698e590d8ac443b725d60655cb68fd2d4
|
File details
Details for the file bridgic-0.1.1-py3-none-any.whl.
File metadata
- Download URL: bridgic-0.1.1-py3-none-any.whl
- Upload date:
- Size: 5.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ab68a9e9d3c9fb3c881313cb23fffababf9a0ca77d8e8dec7963d1e79c94662
|
|
| MD5 |
696a2bf7b1b745d2f08b8cefd0a124f4
|
|
| BLAKE2b-256 |
f10a5a90a8970d48c50e63efeaba31c15d05076601c3c55244f57fa05fd570bb
|