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.
@@ -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()