| | |
| | |
| | """ |
| | AGI-HEDGE-FUND - Multi-agent recursive market cognition framework |
| | |
| | This script serves as the entry point for the AGI-HEDGE-FUND system, providing |
| | command-line interface for running the multi-agent market cognition platform. |
| | |
| | Usage: |
| | python -m src.main --mode backtest --start-date 2022-01-01 --end-date 2022-12-31 |
| | python -m src.main --mode live --data-source yahoo --show-trace |
| | python -m src.main --mode analysis --portfolio-file portfolio.json --consensus-graph |
| | |
| | Internal Note: This script encodes the system's entry point while exposing the |
| | recursive cognitive architecture through interpretability flags. |
| | """ |
| |
|
| | import argparse |
| | import datetime |
| | import json |
| | import logging |
| | import os |
| | import sys |
| | from typing import Dict, List, Any, Optional |
| |
|
| | |
| | from agents.base import BaseAgent |
| | from agents.graham import GrahamAgent |
| | from agents.dalio import DalioAgent |
| | from agents.wood import WoodAgent |
| | from agents.ackman import AckmanAgent |
| | from agents.simons import SimonsAgent |
| | from agents.taleb import TalebAgent |
| | from portfolio.manager import PortfolioManager |
| | from market.environment import MarketEnvironment |
| | from llm.router import ModelRouter |
| | from utils.diagnostics import TracingTools, TracingMode, ShellDiagnostics, ShellFailureMap |
| |
|
| |
|
| | |
| | logging.basicConfig( |
| | level=logging.INFO, |
| | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', |
| | handlers=[ |
| | logging.StreamHandler(sys.stdout) |
| | ] |
| | ) |
| |
|
| | logger = logging.getLogger("agi-hedge-fund") |
| |
|
| |
|
| | def parse_args(): |
| | """Parse command line arguments.""" |
| | parser = argparse.ArgumentParser(description='AGI-HEDGE-FUND - Multi-agent recursive market cognition framework') |
| | |
| | |
| | parser.add_argument('--mode', type=str, choices=['backtest', 'live', 'analysis'], default='backtest', |
| | help='Operation mode: backtest, live, or analysis') |
| | |
| | |
| | parser.add_argument('--start-date', type=str, default='2020-01-01', |
| | help='Start date for backtesting (YYYY-MM-DD)') |
| | parser.add_argument('--end-date', type=str, default='2023-01-01', |
| | help='End date for backtesting (YYYY-MM-DD)') |
| | |
| | |
| | parser.add_argument('--initial-capital', type=float, default=100000.0, |
| | help='Initial capital amount') |
| | parser.add_argument('--tickers', type=str, nargs='+', default=['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA'], |
| | help='Stock tickers to analyze') |
| | parser.add_argument('--rebalance-frequency', type=str, choices=['daily', 'weekly', 'monthly'], default='weekly', |
| | help='Portfolio rebalance frequency') |
| | |
| | |
| | parser.add_argument('--data-source', type=str, choices=['yahoo', 'polygon', 'alpha_vantage'], default='yahoo', |
| | help='Market data source') |
| | parser.add_argument('--data-path', type=str, default='data', |
| | help='Path to data directory') |
| | |
| | |
| | parser.add_argument('--agents', type=str, nargs='+', |
| | default=['graham', 'dalio', 'wood', 'ackman', 'simons', 'taleb'], |
| | help='Agents to use') |
| | parser.add_argument('--reasoning-depth', type=int, default=3, |
| | help='Agent reasoning depth') |
| | parser.add_argument('--arbitration-depth', type=int, default=2, |
| | help='Portfolio meta-agent arbitration depth') |
| | |
| | |
| | parser.add_argument('--llm-provider', type=str, choices=['anthropic', 'openai', 'groq', 'ollama', 'deepseek'], |
| | default='anthropic', |
| | help='LLM provider') |
| | |
| | |
| | parser.add_argument('--model', type=str, default=None, |
| | help='Specific LLM model to use') |
| | parser.add_argument('--fallback-providers', type=str, nargs='+', |
| | default=['openai', 'groq'], |
| | help='Fallback LLM providers') |
| | |
| | |
| | parser.add_argument('--output-dir', type=str, default='output', |
| | help='Directory for output files') |
| | parser.add_argument('--portfolio-file', type=str, default=None, |
| | help='Portfolio state file for analysis mode') |
| | |
| | |
| | parser.add_argument('--show-trace', action='store_true', |
| | help='Show reasoning traces') |
| | parser.add_argument('--consensus-graph', action='store_true', |
| | help='Generate consensus graph visualization') |
| | parser.add_argument('--agent-conflict-map', action='store_true', |
| | help='Generate agent conflict map visualization') |
| | parser.add_argument('--attribution-report', action='store_true', |
| | help='Generate attribution report') |
| | parser.add_argument('--shell-failure-map', action='store_true', |
| | help='Show shell failure map') |
| | parser.add_argument('--trace-level', type=str, |
| | choices=['disabled', 'minimal', 'detailed', 'comprehensive', 'symbolic'], |
| | default='minimal', |
| | help='Trace level for diagnostics') |
| | |
| | |
| | parser.add_argument('--max-position-size', type=float, default=0.2, |
| | help='Maximum position size as fraction of portfolio') |
| | parser.add_argument('--min-position-size', type=float, default=0.01, |
| | help='Minimum position size as fraction of portfolio') |
| | parser.add_argument('--risk-budget', type=float, default=0.5, |
| | help='Risk budget (0-1)') |
| | parser.add_argument('--memory-decay', type=float, default=0.2, |
| | help='Memory decay rate for agents') |
| | |
| | |
| | return parser.parse_args() |
| |
|
| |
|
| | def create_agents(args) -> List[BaseAgent]: |
| | """ |
| | Create agent instances based on command-line arguments. |
| | |
| | Args: |
| | args: Command-line arguments |
| | |
| | Returns: |
| | List of agent instances |
| | """ |
| | |
| | model_router = ModelRouter( |
| | provider=args.llm_provider, |
| | model=args.model, |
| | fallback_providers=args.fallback_providers, |
| | ) |
| | |
| | |
| | trace_mode = TracingMode(args.trace_level) |
| | |
| | |
| | agents = [] |
| | |
| | for agent_type in args.agents: |
| | agent_type = agent_type.lower() |
| | |
| | if agent_type == "graham": |
| | agent = GrahamAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | elif agent_type == "dalio": |
| | agent = DalioAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | elif agent_type == "wood": |
| | agent = WoodAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | elif agent_type == "ackman": |
| | agent = AckmanAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | elif agent_type == "simons": |
| | agent = SimonsAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | elif agent_type == "taleb": |
| | agent = TalebAgent( |
| | reasoning_depth=args.reasoning_depth, |
| | memory_decay=args.memory_decay, |
| | initial_capital=args.initial_capital, |
| | model_provider=args.llm_provider, |
| | model_name=args.model, |
| | trace_enabled=args.show_trace, |
| | ) |
| | else: |
| | logger.warning(f"Unknown agent type: {agent_type}") |
| | continue |
| | |
| | agents.append(agent) |
| | |
| | logger.info(f"Created {len(agents)} agents: {', '.join(agent.name for agent in agents)}") |
| | |
| | return agents |
| |
|
| |
|
| | def create_portfolio_manager(agents: List[BaseAgent], args) -> PortfolioManager: |
| | """ |
| | Create portfolio manager instance. |
| | |
| | Args: |
| | agents: List of agent instances |
| | args: Command-line arguments |
| | |
| | Returns: |
| | Portfolio manager instance |
| | """ |
| | |
| | portfolio_manager = PortfolioManager( |
| | agents=agents, |
| | initial_capital=args.initial_capital, |
| | arbitration_depth=args.arbitration_depth, |
| | max_position_size=args.max_position_size, |
| | min_position_size=args.min_position_size, |
| | consensus_threshold=0.6, |
| | show_trace=args.show_trace, |
| | risk_budget=args.risk_budget, |
| | ) |
| | |
| | logger.info(f"Created portfolio manager with {len(agents)} agents") |
| | |
| | return portfolio_manager |
| |
|
| |
|
| | def create_market_environment(args) -> MarketEnvironment: |
| | """ |
| | Create market environment instance. |
| | |
| | Args: |
| | args: Command-line arguments |
| | |
| | Returns: |
| | Market environment instance |
| | """ |
| | |
| | market_env = MarketEnvironment( |
| | data_source=args.data_source, |
| | tickers=args.tickers, |
| | data_path=args.data_path, |
| | start_date=args.start_date if args.mode == "backtest" else None, |
| | end_date=args.end_date if args.mode == "backtest" else None, |
| | ) |
| | |
| | logger.info(f"Created market environment with {len(args.tickers)} tickers") |
| | |
| | return market_env |
| |
|
| |
|
| | def run_backtest(portfolio_manager: PortfolioManager, market_env: MarketEnvironment, args) -> Dict[str, Any]: |
| | """ |
| | Run backtesting simulation. |
| | |
| | Args: |
| | portfolio_manager: Portfolio manager instance |
| | market_env: Market environment instance |
| | args: Command-line arguments |
| | |
| | Returns: |
| | Backtest results |
| | """ |
| | |
| | start_date = datetime.datetime.strptime(args.start_date, "%Y-%m-%d").date() |
| | end_date = datetime.datetime.strptime(args.end_date, "%Y-%m-%d").date() |
| | |
| | |
| | if args.rebalance_frequency == "daily": |
| | rebalance_days = 1 |
| | elif args.rebalance_frequency == "weekly": |
| | rebalance_days = 7 |
| | else: |
| | rebalance_days = 30 |
| | |
| | |
| | results = portfolio_manager.run_simulation( |
| | start_date=args.start_date, |
| | end_date=args.end_date, |
| | data_source=args.data_source, |
| | rebalance_frequency=args.rebalance_frequency, |
| | ) |
| | |
| | logger.info(f"Completed backtest from {args.start_date} to {args.end_date}") |
| | |
| | |
| | os.makedirs(args.output_dir, exist_ok=True) |
| | results_file = os.path.join(args.output_dir, f"backtest_results_{start_date}_{end_date}.json") |
| | |
| | with open(results_file, 'w') as f: |
| | json.dump(results, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved backtest results to {results_file}") |
| | |
| | |
| | if args.consensus_graph: |
| | consensus_graph = portfolio_manager.visualize_consensus_graph() |
| | consensus_file = os.path.join(args.output_dir, f"consensus_graph_{start_date}_{end_date}.json") |
| | |
| | with open(consensus_file, 'w') as f: |
| | json.dump(consensus_graph, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved consensus graph to {consensus_file}") |
| | |
| | if args.agent_conflict_map: |
| | conflict_map = portfolio_manager.visualize_agent_conflict_map() |
| | conflict_file = os.path.join(args.output_dir, f"conflict_map_{start_date}_{end_date}.json") |
| | |
| | with open(conflict_file, 'w') as f: |
| | json.dump(conflict_map, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved agent conflict map to {conflict_file}") |
| | |
| | if args.attribution_report: |
| | |
| | all_signals = [] |
| | for trade_batch in results.get("trades", []): |
| | for trade in trade_batch: |
| | if "signal" in trade: |
| | all_signals.append(trade["signal"]) |
| | |
| | |
| | tracer = TracingTools(agent_id="portfolio", agent_name="Portfolio") |
| | attribution_report = tracer.generate_attribution_report(all_signals) |
| | report_file = os.path.join(args.output_dir, f"attribution_report_{start_date}_{end_date}.json") |
| | |
| | with open(report_file, 'w') as f: |
| | json.dump(attribution_report, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved attribution report to {report_file}") |
| | |
| | |
| | portfolio_state = portfolio_manager.get_portfolio_state() |
| | portfolio_file = os.path.join(args.output_dir, f"portfolio_state_{end_date}.json") |
| | |
| | with open(portfolio_file, 'w') as f: |
| | json.dump(portfolio_state, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved portfolio state to {portfolio_file}") |
| | |
| | return results |
| |
|
| |
|
| | def run_live_analysis(portfolio_manager: PortfolioManager, market_env: MarketEnvironment, args) -> Dict[str, Any]: |
| | """ |
| | Run live market analysis. |
| | |
| | Args: |
| | portfolio_manager: Portfolio manager instance |
| | market_env: Market environment instance |
| | args: Command-line arguments |
| | |
| | Returns: |
| | Analysis results |
| | """ |
| | |
| | market_data = market_env.get_current_market_data() |
| | |
| | |
| | analysis_results = portfolio_manager.process_market_data(market_data) |
| | |
| | logger.info(f"Completed live market analysis for {len(args.tickers)} tickers") |
| | |
| | |
| | os.makedirs(args.output_dir, exist_ok=True) |
| | timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") |
| | results_file = os.path.join(args.output_dir, f"live_analysis_{timestamp}.json") |
| | |
| | with open(results_file, 'w') as f: |
| | json.dump(analysis_results, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved live analysis results to {results_file}") |
| | |
| | |
| | if args.consensus_graph: |
| | consensus_graph = portfolio_manager.visualize_consensus_graph() |
| | consensus_file = os.path.join(args.output_dir, f"consensus_graph_{timestamp}.json") |
| | |
| | with open(consensus_file, 'w') as f: |
| | json.dump(consensus_graph, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved consensus graph to {consensus_file}") |
| | |
| | if args.agent_conflict_map: |
| | conflict_map = portfolio_manager.visualize_agent_conflict_map() |
| | conflict_file = os.path.join(args.output_dir, f"conflict_map_{timestamp}.json") |
| | |
| | with open(conflict_file, 'w') as f: |
| | json.dump(conflict_map, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved agent conflict map to {conflict_file}") |
| | |
| | |
| | consensus_decisions = analysis_results.get("meta_agent", {}).get("consensus_decisions", []) |
| | |
| | if consensus_decisions: |
| | trade_results = portfolio_manager.execute_trades(consensus_decisions) |
| | |
| | |
| | trades_file = os.path.join(args.output_dir, f"trades_{timestamp}.json") |
| | |
| | with open(trades_file, 'w') as f: |
| | json.dump(trade_results, f, indent=2, default=str) |
| | |
| | logger.info(f"Executed {len(trade_results.get('trades', []))} trades and saved results to {trades_file}") |
| | |
| | |
| | portfolio_state = portfolio_manager.get_portfolio_state() |
| | portfolio_file = os.path.join(args.output_dir, f"portfolio_state_{timestamp}.json") |
| | |
| | with open(portfolio_file, 'w') as f: |
| | json.dump(portfolio_state, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved portfolio state to {portfolio_file}") |
| | |
| | return analysis_results |
| |
|
| |
|
| | def run_portfolio_analysis(args) -> Dict[str, Any]: |
| | """ |
| | Run analysis on existing portfolio. |
| | |
| | Args: |
| | args: Command-line arguments |
| | |
| | Returns: |
| | Analysis results |
| | """ |
| | |
| | if not args.portfolio_file or not os.path.exists(args.portfolio_file): |
| | logger.error(f"Portfolio file not found: {args.portfolio_file}") |
| | sys.exit(1) |
| | |
| | |
| | with open(args.portfolio_file, 'r') as f: |
| | portfolio_state = json.load(f) |
| | |
| | |
| | agents = create_agents(args) |
| | |
| | |
| | portfolio_manager = create_portfolio_manager(agents, args) |
| | |
| | |
| | market_env = create_market_environment(args) |
| | |
| | |
| | market_data = market_env.get_current_market_data() |
| | |
| | |
| | analysis_results = portfolio_manager.process_market_data(market_data) |
| | |
| | logger.info(f"Completed portfolio analysis") |
| | |
| | |
| | os.makedirs(args.output_dir, exist_ok=True) |
| | timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") |
| | results_file = os.path.join(args.output_dir, f"portfolio_analysis_{timestamp}.json") |
| | |
| | with open(results_file, 'w') as f: |
| | json.dump(analysis_results, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved portfolio analysis results to {results_file}") |
| | |
| | |
| | if args.consensus_graph: |
| | consensus_graph = portfolio_manager.visualize_consensus_graph() |
| | consensus_file = os.path.join(args.output_dir, f"consensus_graph_{timestamp}.json") |
| | |
| | with open(consensus_file, 'w') as f: |
| | json.dump(consensus_graph, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved consensus graph to {consensus_file}") |
| | |
| | if args.agent_conflict_map: |
| | conflict_map = portfolio_manager.visualize_agent_conflict_map() |
| | conflict_file = os.path.join(args.output_dir, f"conflict_map_{timestamp}.json") |
| | |
| | with open(conflict_file, 'w') as f: |
| | json.dump(conflict_map, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved agent conflict map to {conflict_file}") |
| | |
| | if args.attribution_report: |
| | |
| | agent_performance = portfolio_manager.get_agent_performance() |
| | |
| | |
| | attribution_file = os.path.join(args.output_dir, f"agent_performance_{timestamp}.json") |
| | |
| | with open(attribution_file, 'w') as f: |
| | json.dump(agent_performance, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved agent performance report to {attribution_file}") |
| | |
| | if args.shell_failure_map: |
| | |
| | shell_diagnostics = ShellDiagnostics( |
| | agent_id="portfolio", |
| | agent_name="Portfolio", |
| | tracing_tools=TracingTools( |
| | agent_id="portfolio", |
| | agent_name="Portfolio", |
| | tracing_mode=TracingMode(args.trace_level), |
| | ) |
| | ) |
| | |
| | |
| | failure_map = ShellFailureMap() |
| | |
| | |
| | for agent in agents: |
| | agent_state = agent.get_state_report() |
| | |
| | |
| | for shell_pattern in [ |
| | "NULL_FEATURE", |
| | "CIRCUIT_FRAGMENT", |
| | "META_FAILURE", |
| | "RECURSIVE_FRACTURE", |
| | "ETHICAL_INVERSION", |
| | ]: |
| | try: |
| | from utils.diagnostics import ShellPattern |
| | pattern = getattr(ShellPattern, shell_pattern) |
| | |
| | |
| | failure_data = shell_diagnostics.simulate_shell_failure( |
| | shell_pattern=pattern, |
| | context=agent_state, |
| | ) |
| | |
| | |
| | failure_map.add_failure( |
| | agent_id=agent.id, |
| | agent_name=agent.name, |
| | shell_pattern=pattern, |
| | failure_data=failure_data, |
| | ) |
| | except Exception as e: |
| | logger.error(f"Error simulating shell failure: {e}") |
| | |
| | |
| | failure_viz = failure_map.generate_failure_map_visualization() |
| | failure_file = os.path.join(args.output_dir, f"shell_failure_map_{timestamp}.json") |
| | |
| | with open(failure_file, 'w') as f: |
| | json.dump(failure_viz, f, indent=2, default=str) |
| | |
| | logger.info(f"Saved shell failure map to {failure_file}") |
| | |
| | return analysis_results |
| |
|
| |
|
| | def main(): |
| | """Main entry point.""" |
| | |
| | args = parse_args() |
| | |
| | |
| | os.makedirs(args.output_dir, exist_ok=True) |
| | |
| | |
| | if args.mode == "backtest": |
| | |
| | agents = create_agents(args) |
| | |
| | |
| | portfolio_manager = create_portfolio_manager(agents, args) |
| | |
| | |
| | market_env = create_market_environment(args) |
| | |
| | |
| | results = run_backtest(portfolio_manager, market_env, args) |
| | |
| | |
| | print("\n=== Backtest Results ===") |
| | print(f"Start Date: {args.start_date}") |
| | print(f"End Date: {args.end_date}") |
| | print(f"Initial Capital: ${args.initial_capital:.2f}") |
| | print(f"Final Portfolio Value: ${results.get('final_value', 0):.2f}") |
| | |
| | total_return = (results.get('final_value', 0) / args.initial_capital) - 1 |
| | print(f"Total Return: {total_return:.2%}") |
| | print(f"Number of Trades: {sum(len(batch) for batch in results.get('trades', []))}") |
| | print(f"Results saved to: {args.output_dir}") |
| | |
| | elif args.mode == "live": |
| | |
| | agents = create_agents(args) |
| | |
| | |
| | portfolio_manager = create_portfolio_manager(agents, args) |
| | |
| | |
| | market_env = create_market_environment(args) |
| | |
| | |
| | results = run_live_analysis(portfolio_manager, market_env, args) |
| | |
| | |
| | print("\n=== Live Analysis Results ===") |
| | print(f"Analysis Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") |
| | print(f"Tickers Analyzed: {', '.join(args.tickers)}") |
| | |
| | |
| | consensus_decisions = results.get("meta_agent", {}).get("consensus_decisions", []) |
| | if consensus_decisions: |
| | print("\nConsensus Decisions:") |
| | for decision in consensus_decisions: |
| | ticker = decision.get("ticker", "") |
| | action = decision.get("action", "") |
| | confidence = decision.get("confidence", 0) |
| | quantity = decision.get("quantity", 0) |
| | |
| | print(f" {action.upper()} {quantity} {ticker} (Confidence: {confidence:.2f})") |
| | else: |
| | print("\nNo consensus decisions generated.") |
| | |
| | print(f"Results saved to: {args.output_dir}") |
| | |
| | elif args.mode == "analysis": |
| | |
| | results = run_portfolio_analysis(args) |
| | |
| | |
| | print("\n=== Portfolio Analysis Results ===") |
| | print(f"Analysis Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") |
| | print(f"Portfolio File: {args.portfolio_file}") |
| | |
| | |
| | agent_weights = results.get("meta_agent", {}).get("agent_weights", {}) |
| | if agent_weights: |
| | print("\nAgent Weights:") |
| | for agent_id, weight in agent_weights.items(): |
| | print(f" {agent_id}: {weight:.2f}") |
| | |
| | |
| | consensus_decisions = results.get("meta_agent", {}).get("consensus_decisions", []) |
| | if consensus_decisions: |
| | print("\nRecommended Actions:") |
| | for decision in consensus_decisions: |
| | ticker = decision.get("ticker", "") |
| | action = decision.get("action", "") |
| | confidence = decision.get("confidence", 0) |
| | quantity = decision.get("quantity", 0) |
| | |
| | print(f" {action.upper()} {quantity} {ticker} (Confidence: {confidence:.2f})") |
| | else: |
| | print("\nNo recommended actions.") |
| | |
| | print(f"Results saved to: {args.output_dir}") |
| |
|
| |
|
| | if __name__ == "__main__": |
| | main() |
| |
|