BiXFlow 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bixflow-0.9.0/AUTHORS.md +68 -0
- bixflow-0.9.0/BiXFlow/__init__.py +19 -0
- bixflow-0.9.0/BiXFlow/cli.py +256 -0
- bixflow-0.9.0/BiXFlow/client.py +387 -0
- bixflow-0.9.0/BiXFlow/config.py +144 -0
- bixflow-0.9.0/BiXFlow/exceptions.py +39 -0
- bixflow-0.9.0/BiXFlow/utils.py +309 -0
- bixflow-0.9.0/BiXFlow/workflow.py +953 -0
- bixflow-0.9.0/BiXFlow.egg-info/PKG-INFO +586 -0
- bixflow-0.9.0/BiXFlow.egg-info/SOURCES.txt +28 -0
- bixflow-0.9.0/BiXFlow.egg-info/dependency_links.txt +1 -0
- bixflow-0.9.0/BiXFlow.egg-info/entry_points.txt +2 -0
- bixflow-0.9.0/BiXFlow.egg-info/requires.txt +15 -0
- bixflow-0.9.0/BiXFlow.egg-info/top_level.txt +4 -0
- bixflow-0.9.0/LICENSE +21 -0
- bixflow-0.9.0/NOTICE +114 -0
- bixflow-0.9.0/PKG-INFO +586 -0
- bixflow-0.9.0/README.md +527 -0
- bixflow-0.9.0/licenses.json +0 -0
- bixflow-0.9.0/mcps/__init__.py +0 -0
- bixflow-0.9.0/scripts/__init__.py +0 -0
- bixflow-0.9.0/scripts/demo_cli.py +49 -0
- bixflow-0.9.0/scripts/license_check.py +275 -0
- bixflow-0.9.0/scripts/release.py +147 -0
- bixflow-0.9.0/setup.cfg +4 -0
- bixflow-0.9.0/setup.py +67 -0
- bixflow-0.9.0/tests/__init__.py +0 -0
- bixflow-0.9.0/tests/test_mcps.py +174 -0
- bixflow-0.9.0/tests/test_nested_workflows.py +272 -0
- bixflow-0.9.0/tests/test_workflow.py +133 -0
bixflow-0.9.0/AUTHORS.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Authors and Contributors
|
|
2
|
+
|
|
3
|
+
## Core Team
|
|
4
|
+
|
|
5
|
+
BiXFlow is developed and maintained by:
|
|
6
|
+
|
|
7
|
+
### Bixing Development Team
|
|
8
|
+
|
|
9
|
+
- **Organization**: China Mobile Research Institute
|
|
10
|
+
- **Contact**: bixing_support@chinamobile.com
|
|
11
|
+
|
|
12
|
+
## Contributors
|
|
13
|
+
|
|
14
|
+
We thank all the people who have contributed to BiXFlow:
|
|
15
|
+
|
|
16
|
+
### Code Contributors
|
|
17
|
+
|
|
18
|
+
This list is updated automatically based on Git history.
|
|
19
|
+
|
|
20
|
+
### Documentation Contributors
|
|
21
|
+
|
|
22
|
+
- All contributors who have helped improve the documentation
|
|
23
|
+
|
|
24
|
+
## Acknowledgments
|
|
25
|
+
|
|
26
|
+
### Model Context Protocol (MCP)
|
|
27
|
+
|
|
28
|
+
BiXFlow is built upon the [Model Context Protocol](https://modelcontextprotocol.io/). We thank the MCP community for creating this powerful protocol.
|
|
29
|
+
|
|
30
|
+
### Open Source Libraries
|
|
31
|
+
|
|
32
|
+
BiXFlow uses and depends on the following open source projects:
|
|
33
|
+
|
|
34
|
+
- **mcp** - Model Context Protocol implementation
|
|
35
|
+
- **uvicorn** - ASGI server
|
|
36
|
+
- **httpx** - Async HTTP client
|
|
37
|
+
- **jinja2** - Template engine
|
|
38
|
+
- **pyyaml** - YAML parser
|
|
39
|
+
- **pandas** - Data manipulation library
|
|
40
|
+
- **openpyxl** - Excel file handling
|
|
41
|
+
- **pytest** - Testing framework
|
|
42
|
+
- **black** - Code formatter
|
|
43
|
+
- **flake8** - Code linter
|
|
44
|
+
|
|
45
|
+
### Inspiration and Ideas
|
|
46
|
+
|
|
47
|
+
We draw inspiration from various workflow orchestration and automation tools in the open source community.
|
|
48
|
+
|
|
49
|
+
## Becoming a Contributor
|
|
50
|
+
|
|
51
|
+
We welcome contributions from everyone! See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to BiXFlow.
|
|
52
|
+
|
|
53
|
+
### How to Get Listed
|
|
54
|
+
|
|
55
|
+
Contributors are automatically recognized through:
|
|
56
|
+
|
|
57
|
+
- Pull requests that are merged
|
|
58
|
+
- Issue reports that lead to improvements
|
|
59
|
+
- Documentation improvements
|
|
60
|
+
- Bug reports and feature suggestions
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
Thank you to all our contributors! 🙏
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""BiXFlow: A framework for executing workflows through Model Context Protocol servers.
|
|
2
|
+
|
|
3
|
+
This package provides a high-level interface for executing workflows defined in YAML format
|
|
4
|
+
through MCP servers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .client import MCPClient
|
|
8
|
+
from .workflow import (
|
|
9
|
+
BiXFlowExecutor,
|
|
10
|
+
run_workflow_from_file,
|
|
11
|
+
run_workflow_from_file_sync,
|
|
12
|
+
run_workflow_from_content,
|
|
13
|
+
run_workflow_from_content_sync
|
|
14
|
+
)
|
|
15
|
+
from .config import MCPConfig, get_config
|
|
16
|
+
from .exceptions import BiXFlowError, WorkflowNotFoundError, InvalidWorkflowError, MCPClientError, ConfigurationError
|
|
17
|
+
|
|
18
|
+
__version__ = "0.9.0"
|
|
19
|
+
__author__ = "BiXFlow Development Team"
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Command-line interface for BiXFlow."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from BiXFlow import run_workflow_from_file_sync, run_workflow_from_content_sync, __version__
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_args_from_json(json_str: str) -> Dict[str, Any]:
|
|
13
|
+
"""Load arguments from JSON string."""
|
|
14
|
+
try:
|
|
15
|
+
# Handle escaped quotes in command line arguments
|
|
16
|
+
if '\\"' in json_str:
|
|
17
|
+
json_str = json_str.replace('\\"', '"')
|
|
18
|
+
return json.loads(json_str)
|
|
19
|
+
except json.JSONDecodeError as e:
|
|
20
|
+
raise ValueError(f"Invalid JSON for arguments: {e}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def print_table_result(result: Dict[str, Any]) -> None:
|
|
24
|
+
"""Print result in table format."""
|
|
25
|
+
if result.get('status') == 'done':
|
|
26
|
+
print("Workflow Execution Result:")
|
|
27
|
+
print("=" * 50)
|
|
28
|
+
print(f"Status: {result.get('status', 'N/A')}")
|
|
29
|
+
print(f"Message: {result.get('message', 'N/A')}")
|
|
30
|
+
|
|
31
|
+
data = result.get('data', {})
|
|
32
|
+
if isinstance(data, dict):
|
|
33
|
+
print("\nData:")
|
|
34
|
+
for key, value in data.items():
|
|
35
|
+
print(f" {key}: {value}")
|
|
36
|
+
else:
|
|
37
|
+
print(f"\nData: {data}")
|
|
38
|
+
else:
|
|
39
|
+
print("Workflow Execution Result:")
|
|
40
|
+
print("=" * 50)
|
|
41
|
+
print(f"Status: {result.get('status', 'N/A')}")
|
|
42
|
+
print(f"Data: {result.get('data', 'N/A')}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def print_simple_workflows(workflows_dir: Path) -> None:
|
|
46
|
+
"""Print workflows in simple format."""
|
|
47
|
+
print("Available workflows:")
|
|
48
|
+
for service_dir in workflows_dir.iterdir():
|
|
49
|
+
if service_dir.is_dir():
|
|
50
|
+
print(f" Service: {service_dir.name}")
|
|
51
|
+
for workflow_file in service_dir.glob("*.yaml"):
|
|
52
|
+
workflow_name = workflow_file.stem
|
|
53
|
+
print(f" - {workflow_name}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def print_detailed_workflows(workflows_dir: Path) -> None:
|
|
57
|
+
"""Print workflows in detailed format."""
|
|
58
|
+
print("Available workflows (detailed):")
|
|
59
|
+
for service_dir in workflows_dir.iterdir():
|
|
60
|
+
if service_dir.is_dir():
|
|
61
|
+
print(f"\nService: {service_dir.name}")
|
|
62
|
+
print("-" * (len(service_dir.name) + 9))
|
|
63
|
+
for workflow_file in service_dir.glob("*.yaml"):
|
|
64
|
+
workflow_name = workflow_file.stem
|
|
65
|
+
# Try to read workflow file to get description
|
|
66
|
+
try:
|
|
67
|
+
with open(workflow_file, 'r', encoding='utf-8') as f:
|
|
68
|
+
import yaml
|
|
69
|
+
workflow_content = yaml.safe_load(f)
|
|
70
|
+
description = workflow_content.get('description', 'No description')
|
|
71
|
+
display_name = workflow_content.get('display_name', workflow_name)
|
|
72
|
+
print(f" {workflow_name}:")
|
|
73
|
+
print(f" Display Name: {display_name}")
|
|
74
|
+
print(f" Description: {description}")
|
|
75
|
+
except Exception:
|
|
76
|
+
print(f" {workflow_name}:")
|
|
77
|
+
print(f" Display Name: {workflow_name}")
|
|
78
|
+
print(f" Description: No description available")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main() -> None:
|
|
82
|
+
"""Main CLI entry point."""
|
|
83
|
+
parser = argparse.ArgumentParser(
|
|
84
|
+
description="BiXFlow - Execute workflows through Model Context Protocol servers",
|
|
85
|
+
prog="BiXFlow"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Add version argument
|
|
89
|
+
parser.add_argument(
|
|
90
|
+
"-v", "--version",
|
|
91
|
+
action="version",
|
|
92
|
+
version=f"%(prog)s {__version__}"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Global options
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"--format",
|
|
98
|
+
choices=["json", "table"],
|
|
99
|
+
default="json",
|
|
100
|
+
help="Output format (default: json)"
|
|
101
|
+
)
|
|
102
|
+
parser.add_argument(
|
|
103
|
+
"--verbose", "-V",
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="Enable verbose output"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
109
|
+
|
|
110
|
+
# Run named workflow command
|
|
111
|
+
run_named_parser = subparsers.add_parser(
|
|
112
|
+
"run-named",
|
|
113
|
+
help="Run a named workflow from the standard workflows directory"
|
|
114
|
+
)
|
|
115
|
+
run_named_parser.add_argument(
|
|
116
|
+
"service_name",
|
|
117
|
+
help="Name of the service directory under workflows/"
|
|
118
|
+
)
|
|
119
|
+
run_named_parser.add_argument(
|
|
120
|
+
"workflow_name",
|
|
121
|
+
help="Name of the workflow (without .yaml extension)"
|
|
122
|
+
)
|
|
123
|
+
run_named_parser.add_argument(
|
|
124
|
+
"--args",
|
|
125
|
+
help="JSON string of arguments to pass to the workflow"
|
|
126
|
+
)
|
|
127
|
+
run_named_parser.add_argument(
|
|
128
|
+
"--config",
|
|
129
|
+
help="Path to the MCP servers configuration file"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Run workflow from file command
|
|
133
|
+
run_parser = subparsers.add_parser(
|
|
134
|
+
"run",
|
|
135
|
+
help="Run a workflow from a YAML file"
|
|
136
|
+
)
|
|
137
|
+
run_parser.add_argument(
|
|
138
|
+
"workflow_path",
|
|
139
|
+
help="Path to the workflow YAML file"
|
|
140
|
+
)
|
|
141
|
+
run_parser.add_argument(
|
|
142
|
+
"--args",
|
|
143
|
+
help="JSON string of arguments to pass to the workflow"
|
|
144
|
+
)
|
|
145
|
+
run_parser.add_argument(
|
|
146
|
+
"--config",
|
|
147
|
+
help="Path to the MCP servers configuration file"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Run workflow from content command
|
|
151
|
+
run_content_parser = subparsers.add_parser(
|
|
152
|
+
"run-content",
|
|
153
|
+
help="Run a workflow from YAML content"
|
|
154
|
+
)
|
|
155
|
+
run_content_parser.add_argument(
|
|
156
|
+
"workflow_content",
|
|
157
|
+
help="YAML content defining the workflow"
|
|
158
|
+
)
|
|
159
|
+
run_content_parser.add_argument(
|
|
160
|
+
"--args",
|
|
161
|
+
help="JSON string of arguments to pass to the workflow"
|
|
162
|
+
)
|
|
163
|
+
run_content_parser.add_argument(
|
|
164
|
+
"--config",
|
|
165
|
+
help="Path to the MCP servers configuration file or JSON string"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# List workflows command
|
|
169
|
+
list_parser = subparsers.add_parser(
|
|
170
|
+
"list-workflows",
|
|
171
|
+
help="List available workflows"
|
|
172
|
+
)
|
|
173
|
+
list_parser.add_argument(
|
|
174
|
+
"--format",
|
|
175
|
+
choices=["simple", "detailed"],
|
|
176
|
+
default="simple",
|
|
177
|
+
help="Output format for workflow list (default: simple)"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
args = parser.parse_args()
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
# Handle global verbose option
|
|
184
|
+
if args.verbose:
|
|
185
|
+
print(f"Running BiXFlow CLI version {__version__}")
|
|
186
|
+
|
|
187
|
+
if args.command == "run":
|
|
188
|
+
# Parse arguments if provided
|
|
189
|
+
workflow_args = {}
|
|
190
|
+
if args.args:
|
|
191
|
+
workflow_args = load_args_from_json(args.args)
|
|
192
|
+
|
|
193
|
+
# Run the workflow
|
|
194
|
+
result = run_workflow_from_file_sync(
|
|
195
|
+
workflow_path=args.workflow_path,
|
|
196
|
+
mcp_config=args.config,
|
|
197
|
+
args=workflow_args
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Print result based on format
|
|
201
|
+
if args.format == "table":
|
|
202
|
+
print_table_result(result)
|
|
203
|
+
else:
|
|
204
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
205
|
+
|
|
206
|
+
elif args.command == "run-content":
|
|
207
|
+
# Parse arguments if provided
|
|
208
|
+
workflow_args = {}
|
|
209
|
+
if args.args:
|
|
210
|
+
workflow_args = load_args_from_json(args.args)
|
|
211
|
+
|
|
212
|
+
# Parse config if provided as JSON string
|
|
213
|
+
config = args.config
|
|
214
|
+
if config and config.startswith('{') and config.endswith('}'):
|
|
215
|
+
try:
|
|
216
|
+
config = json.loads(config)
|
|
217
|
+
except json.JSONDecodeError:
|
|
218
|
+
# If it's not valid JSON, treat as file path
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
# Process escape sequences in workflow content
|
|
222
|
+
workflow_content = args.workflow_content.encode().decode('unicode_escape')
|
|
223
|
+
|
|
224
|
+
# Run the workflow
|
|
225
|
+
result = run_workflow_from_content_sync(
|
|
226
|
+
workflow_content=workflow_content,
|
|
227
|
+
mcp_config=config,
|
|
228
|
+
args=workflow_args
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Print result based on format
|
|
232
|
+
if args.format == "table":
|
|
233
|
+
print_table_result(result)
|
|
234
|
+
else:
|
|
235
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
236
|
+
|
|
237
|
+
elif args.command == "list-workflows":
|
|
238
|
+
# List available workflows
|
|
239
|
+
workflows_dir = Path("workflows")
|
|
240
|
+
if workflows_dir.exists():
|
|
241
|
+
if args.format == "detailed":
|
|
242
|
+
print_detailed_workflows(workflows_dir)
|
|
243
|
+
else:
|
|
244
|
+
print_simple_workflows(workflows_dir)
|
|
245
|
+
else:
|
|
246
|
+
print("No workflows directory found.")
|
|
247
|
+
else:
|
|
248
|
+
parser.print_help()
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
252
|
+
sys.exit(1)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
if __name__ == "__main__":
|
|
256
|
+
main()
|