Compare commits
2 Commits
d086ffd80c
...
9fb68683ed
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fb68683ed | |||
| 1ef55d1b16 |
43
README.md
43
README.md
@@ -87,6 +87,49 @@ aitbc --help --language german
|
|||||||
aitbc marketplace list --translate-to french
|
aitbc marketplace list --translate-to french
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🔗 Blockchain Node (Brother Chain)
|
||||||
|
|
||||||
|
A minimal asset-backed blockchain that validates compute receipts and mints AIT tokens.
|
||||||
|
|
||||||
|
### ✅ Current Status
|
||||||
|
- **Chain ID**: `ait-devnet`
|
||||||
|
- **Consensus**: Proof-of-Authority (single proposer)
|
||||||
|
- **RPC Endpoint**: `http://localhost:8026/rpc`
|
||||||
|
- **Health Check**: `http://localhost:8026/health`
|
||||||
|
- **Metrics**: `http://localhost:8026/metrics` (Prometheus format)
|
||||||
|
- **Status**: 🟢 Operational and fully functional
|
||||||
|
|
||||||
|
### 🚀 Quick Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/aitbc/apps/blockchain-node
|
||||||
|
source .venv/bin/activate
|
||||||
|
bash scripts/devnet_up.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The node starts:
|
||||||
|
- Proposer loop (block production)
|
||||||
|
- RPC API on port 8026
|
||||||
|
- Mock coordinator on port 8090 (for testing)
|
||||||
|
|
||||||
|
### 🛠️ CLI Interaction
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check node status
|
||||||
|
aitbc blockchain status
|
||||||
|
|
||||||
|
# Get chain head
|
||||||
|
aitbc blockchain head
|
||||||
|
|
||||||
|
# Check balance
|
||||||
|
aitbc blockchain balance --address <your-address>
|
||||||
|
|
||||||
|
# Fund an address (devnet faucet)
|
||||||
|
aitbc blockchain faucet --address <your-address> --amount 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
For full documentation, see: [`apps/blockchain-node/README.md`](./apps/blockchain-node/README.md)
|
||||||
|
|
||||||
## 🤖 Agent-First Computing
|
## 🤖 Agent-First Computing
|
||||||
|
|
||||||
AITBC creates an ecosystem where AI agents are the primary participants:
|
AITBC creates an ecosystem where AI agents are the primary participants:
|
||||||
|
|||||||
@@ -1,25 +1,169 @@
|
|||||||
# Blockchain Node
|
# Blockchain Node (Brother Chain)
|
||||||
|
|
||||||
## Purpose & Scope
|
Minimal asset-backed blockchain node that validates compute receipts and mints AIT tokens.
|
||||||
|
|
||||||
Minimal asset-backed blockchain node that validates compute receipts and mints AIT tokens as described in `docs/bootstrap/blockchain_node.md`.
|
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Scaffolded. Implementation pending per staged roadmap.
|
✅ **Operational** — Core blockchain functionality implemented and running.
|
||||||
|
|
||||||
## Devnet Tooling
|
### Capabilities
|
||||||
|
- PoA consensus with single proposer (devnet)
|
||||||
|
- Transaction processing (TRANSFER, RECEIPT_CLAIM)
|
||||||
|
- Receipt validation and minting
|
||||||
|
- Gossip-based peer-to-peer networking (in-memory backend)
|
||||||
|
- RESTful RPC API (`/rpc/*`)
|
||||||
|
- Prometheus metrics (`/metrics`)
|
||||||
|
- Health check endpoint (`/health`)
|
||||||
|
- SQLite persistence with Alembic migrations
|
||||||
|
|
||||||
- `scripts/make_genesis.py` — Generate a deterministic devnet genesis file (`data/devnet/genesis.json`).
|
## Quickstart (Devnet)
|
||||||
- `scripts/keygen.py` — Produce throwaway devnet keypairs (printed or written to disk).
|
|
||||||
- `scripts/devnet_up.sh` — Launch the blockchain node and RPC API with a freshly generated genesis file.
|
|
||||||
|
|
||||||
### Quickstart
|
The blockchain node is already set up with a virtualenv. To launch:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd apps/blockchain-node
|
cd /opt/aitbc/apps/blockchain-node
|
||||||
python scripts/make_genesis.py --force
|
source .venv/bin/activate
|
||||||
bash scripts/devnet_up.sh
|
bash scripts/devnet_up.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The script sets `PYTHONPATH=src` and starts the proposer loop plus the FastAPI app (via `uvicorn`). Press `Ctrl+C` to stop the devnet.
|
This will:
|
||||||
|
1. Generate genesis block at `data/devnet/genesis.json`
|
||||||
|
2. Start the blockchain node proposer loop (PID logged)
|
||||||
|
3. Start RPC API on `http://127.0.0.1:8026`
|
||||||
|
4. Start mock coordinator on `http://127.0.0.1:8090`
|
||||||
|
|
||||||
|
Press `Ctrl+C` to stop all processes.
|
||||||
|
|
||||||
|
### Manual Startup
|
||||||
|
|
||||||
|
If you prefer to start components separately:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Terminal 1: Blockchain node
|
||||||
|
cd /opt/aitbc/apps/blockchain-node
|
||||||
|
source .venv/bin/activate
|
||||||
|
PYTHONPATH=src python -m aitbc_chain.main
|
||||||
|
|
||||||
|
# Terminal 2: RPC API
|
||||||
|
cd /opt/aitbc/apps/blockchain-node
|
||||||
|
source .venv/bin/activate
|
||||||
|
PYTHONPATH=src uvicorn aitbc_chain.app:app --host 127.0.0.1 --port 8026
|
||||||
|
|
||||||
|
# Terminal 3: Mock coordinator (optional, for testing)
|
||||||
|
cd /opt/aitbc/apps/blockchain-node
|
||||||
|
source .venv/bin/activate
|
||||||
|
PYTHONPATH=src uvicorn mock_coordinator:app --host 127.0.0.1 --port 8090
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
Once running, the RPC API is available at `http://127.0.0.1:8026/rpc`.
|
||||||
|
|
||||||
|
### Health & Metrics
|
||||||
|
- `GET /health` — Health check with node info
|
||||||
|
- `GET /metrics` — Prometheus-format metrics
|
||||||
|
|
||||||
|
### Blockchain Queries
|
||||||
|
- `GET /rpc/head` — Current chain head block
|
||||||
|
- `GET /rpc/blocks/{height}` — Get block by height
|
||||||
|
- `GET /rpc/blocks-range?start=0&end=10` — Get block range
|
||||||
|
- `GET /rpc/info` — Chain information
|
||||||
|
- `GET /rpc/supply` — Token supply info
|
||||||
|
- `GET /rpc/validators` — List validators
|
||||||
|
- `GET /rpc/state` — Full state dump
|
||||||
|
|
||||||
|
### Transactions
|
||||||
|
- `POST /rpc/sendTx` — Submit transaction (JSON body: `TransactionRequest`)
|
||||||
|
- `GET /rpc/transactions` — Latest transactions
|
||||||
|
- `GET /rpc/tx/{tx_hash}` — Get transaction by hash
|
||||||
|
- `POST /rpc/estimateFee` — Estimate fee for transaction type
|
||||||
|
|
||||||
|
### Receipts (Compute Proofs)
|
||||||
|
- `POST /rpc/submitReceipt` — Submit receipt claim
|
||||||
|
- `GET /rpc/receipts` — Latest receipts
|
||||||
|
- `GET /rpc/receipts/{receipt_id}` — Get receipt by ID
|
||||||
|
|
||||||
|
### Accounts
|
||||||
|
- `GET /rpc/getBalance/{address}` — Account balance
|
||||||
|
- `GET /rpc/address/{address}` — Address details + txs
|
||||||
|
- `GET /rpc/addresses` — List active addresses
|
||||||
|
|
||||||
|
### Admin
|
||||||
|
- `POST /rpc/admin/mintFaucet` — Mint devnet funds (requires admin key)
|
||||||
|
|
||||||
|
### Sync
|
||||||
|
- `GET /rpc/syncStatus` — Chain sync status
|
||||||
|
|
||||||
|
## CLI Integration
|
||||||
|
|
||||||
|
Use the AITBC CLI to interact with the node:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source /opt/aitbc/cli/venv/bin/activate
|
||||||
|
aitbc blockchain status
|
||||||
|
aitbc blockchain head
|
||||||
|
aitbc blockchain balance --address <your-address>
|
||||||
|
aitbc blockchain faucet --address <your-address> --amount 1000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit `.env` in this directory to change:
|
||||||
|
|
||||||
|
```
|
||||||
|
CHAIN_ID=ait-devnet
|
||||||
|
DB_PATH=./data/chain.db
|
||||||
|
RPC_BIND_HOST=0.0.0.0
|
||||||
|
RPC_BIND_PORT=8026
|
||||||
|
P2P_BIND_HOST=0.0.0.0
|
||||||
|
P2P_BIND_PORT=7070
|
||||||
|
PROPOSER_KEY=proposer_key_<timestamp>
|
||||||
|
MINT_PER_UNIT=1000
|
||||||
|
COORDINATOR_RATIO=0.05
|
||||||
|
GOSSIP_BACKEND=memory
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the node after changes.
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
blockchain-node/
|
||||||
|
├── src/aitbc_chain/
|
||||||
|
│ ├── app.py # FastAPI app + routes
|
||||||
|
│ ├── main.py # Proposer loop + startup
|
||||||
|
│ ├── config.py # Settings from .env
|
||||||
|
│ ├── database.py # DB init + session mgmt
|
||||||
|
│ ├── mempool.py # Transaction mempool
|
||||||
|
│ ├── gossip/ # P2P message bus
|
||||||
|
│ ├── consensus/ # PoA proposer logic
|
||||||
|
│ ├── rpc/ # RPC endpoints
|
||||||
|
│ ├── contracts/ # Smart contract logic
|
||||||
|
│ └── models.py # SQLModel definitions
|
||||||
|
├── data/
|
||||||
|
│ └── devnet/
|
||||||
|
│ └── genesis.json # Generated by make_genesis.py
|
||||||
|
├── scripts/
|
||||||
|
│ ├── make_genesis.py # Genesis generator
|
||||||
|
│ ├── devnet_up.sh # Devnet launcher
|
||||||
|
│ └── keygen.py # Keypair generator
|
||||||
|
└── .env # Node configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The node uses proof-of-authority (PoA) consensus with a single proposer for the devnet.
|
||||||
|
- Transactions require a valid signature (ed25519) unless running in test mode.
|
||||||
|
- Receipts represent compute work attestations and mint new AIT tokens to the miner.
|
||||||
|
- Gossip backend defaults to in-memory; for multi-node networks, configure a Redis backend.
|
||||||
|
- RPC API does not require authentication on devnet (add in production).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Port already in use:** Change `RPC_BIND_PORT` in `.env` and restart.
|
||||||
|
|
||||||
|
**Database locked:** Ensure only one node instance is running; delete `data/chain.db` if corrupted.
|
||||||
|
|
||||||
|
**No blocks proposed:** Check proposer logs; ensure `PROPOSER_KEY` is set and no other proposers are conflicting.
|
||||||
|
|
||||||
|
**Mock coordinator not responding:** It's only needed for certain tests; the blockchain node can run standalone.
|
||||||
|
|||||||
@@ -10,29 +10,15 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
# Import advanced analytics with robust path resolution
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
if _src_path not in sys.path:
|
||||||
if _services_path:
|
sys.path.insert(0, _src_path)
|
||||||
if os.path.isdir(_services_path):
|
|
||||||
if _services_path not in sys.path:
|
|
||||||
sys.path.insert(0, _services_path)
|
|
||||||
else:
|
|
||||||
print(f"Warning: AITBC_SERVICES_PATH set but not a directory: {_services_path}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
_computed_services = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
|
||||||
if os.path.isdir(_computed_services) and _computed_services not in sys.path:
|
|
||||||
sys.path.insert(0, _computed_services)
|
|
||||||
else:
|
|
||||||
_fallback = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
|
||||||
if os.path.isdir(_fallback) and _fallback not in sys.path:
|
|
||||||
sys.path.insert(0, _fallback)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from advanced_analytics import (
|
from app.services.advanced_analytics import (
|
||||||
start_analytics_monitoring, stop_analytics_monitoring, get_dashboard_data,
|
start_analytics_monitoring, stop_analytics_monitoring, get_dashboard_data,
|
||||||
create_analytics_alert, get_analytics_summary, advanced_analytics,
|
create_analytics_alert, get_analytics_summary, advanced_analytics,
|
||||||
MetricType, Timeframe
|
MetricType, Timeframe
|
||||||
@@ -43,8 +29,8 @@ except ImportError as e:
|
|||||||
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Required service module 'advanced_analytics' could not be imported: {_import_error}. "
|
f"Required service module 'app.services.advanced_analytics' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api dependencies are installed or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
start_analytics_monitoring = stop_analytics_monitoring = get_dashboard_data = _missing
|
start_analytics_monitoring = stop_analytics_monitoring = get_dashboard_data = _missing
|
||||||
create_analytics_alert = get_analytics_summary = _missing
|
create_analytics_alert = get_analytics_summary = _missing
|
||||||
|
|||||||
@@ -10,29 +10,15 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Import AI surveillance system with robust path resolution
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
if _src_path not in sys.path:
|
||||||
if _services_path:
|
sys.path.insert(0, _src_path)
|
||||||
if os.path.isdir(_services_path):
|
|
||||||
if _services_path not in sys.path:
|
|
||||||
sys.path.insert(0, _services_path)
|
|
||||||
else:
|
|
||||||
print(f"Warning: AITBC_SERVICES_PATH set but not a directory: {_services_path}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
_computed_services = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
|
||||||
if os.path.isdir(_computed_services) and _computed_services not in sys.path:
|
|
||||||
sys.path.insert(0, _computed_services)
|
|
||||||
else:
|
|
||||||
_fallback = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
|
||||||
if os.path.isdir(_fallback) and _fallback not in sys.path:
|
|
||||||
sys.path.insert(0, _fallback)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ai_surveillance import (
|
from app.services.ai_surveillance import (
|
||||||
start_ai_surveillance, stop_ai_surveillance, get_surveillance_summary,
|
start_ai_surveillance, stop_ai_surveillance, get_surveillance_summary,
|
||||||
get_user_risk_profile, list_active_alerts, analyze_behavior_patterns,
|
get_user_risk_profile, list_active_alerts, analyze_behavior_patterns,
|
||||||
ai_surveillance, SurveillanceType, RiskLevel, AlertPriority
|
ai_surveillance, SurveillanceType, RiskLevel, AlertPriority
|
||||||
@@ -43,8 +29,8 @@ except ImportError as e:
|
|||||||
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Required service module 'ai_surveillance' could not be imported: {_import_error}. "
|
f"Required service module 'app.services.ai_surveillance' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api dependencies are installed or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
start_ai_surveillance = stop_ai_surveillance = get_surveillance_summary = _missing
|
start_ai_surveillance = stop_ai_surveillance = get_surveillance_summary = _missing
|
||||||
get_user_risk_profile = list_active_alerts = analyze_behavior_patterns = _missing
|
get_user_risk_profile = list_active_alerts = analyze_behavior_patterns = _missing
|
||||||
|
|||||||
@@ -10,29 +10,15 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
# Import AI trading engine with robust path resolution
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
if _src_path not in sys.path:
|
||||||
if _services_path:
|
sys.path.insert(0, _src_path)
|
||||||
if os.path.isdir(_services_path):
|
|
||||||
if _services_path not in sys.path:
|
|
||||||
sys.path.insert(0, _services_path)
|
|
||||||
else:
|
|
||||||
print(f"Warning: AITBC_SERVICES_PATH set but not a directory: {_services_path}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
_computed_services = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
|
||||||
if os.path.isdir(_computed_services) and _computed_services not in sys.path:
|
|
||||||
sys.path.insert(0, _computed_services)
|
|
||||||
else:
|
|
||||||
_fallback = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
|
||||||
if os.path.isdir(_fallback) and _fallback not in sys.path:
|
|
||||||
sys.path.insert(0, _fallback)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ai_trading_engine import (
|
from app.services.ai_trading_engine import (
|
||||||
initialize_ai_engine, train_strategies, generate_trading_signals,
|
initialize_ai_engine, train_strategies, generate_trading_signals,
|
||||||
get_engine_status, ai_trading_engine, TradingStrategy
|
get_engine_status, ai_trading_engine, TradingStrategy
|
||||||
)
|
)
|
||||||
@@ -42,8 +28,8 @@ except ImportError as e:
|
|||||||
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Required service module 'ai_trading_engine' could not be imported: {_import_error}. "
|
f"Required service module 'app.services.ai_trading_engine' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api dependencies are installed or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
initialize_ai_engine = train_strategies = generate_trading_signals = get_engine_status = _missing
|
initialize_ai_engine = train_strategies = generate_trading_signals = get_engine_status = _missing
|
||||||
ai_trading_engine = None
|
ai_trading_engine = None
|
||||||
|
|||||||
@@ -10,41 +10,32 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
# Import enterprise integration services using importlib to avoid naming conflicts
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import importlib.util
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
|
if _src_path not in sys.path:
|
||||||
|
sys.path.insert(0, _src_path)
|
||||||
|
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
try:
|
||||||
if _services_path:
|
from app.services.enterprise_integration import (
|
||||||
base_dir = _services_path
|
create_tenant, get_tenant_info, generate_api_key,
|
||||||
else:
|
register_integration, get_system_status, list_tenants,
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
list_integrations
|
||||||
base_dir = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
)
|
||||||
if not os.path.isdir(base_dir):
|
# Get EnterpriseAPIGateway if available
|
||||||
base_dir = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
import app.services.enterprise_integration as ei_module
|
||||||
|
EnterpriseAPIGateway = getattr(ei_module, 'EnterpriseAPIGateway', None)
|
||||||
|
_import_error = None
|
||||||
|
except ImportError as e:
|
||||||
|
_import_error = e
|
||||||
|
|
||||||
module_path = os.path.join(base_dir, 'enterprise_integration.py')
|
|
||||||
if os.path.isfile(module_path):
|
|
||||||
spec = importlib.util.spec_from_file_location("enterprise_integration_service", module_path)
|
|
||||||
ei = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(ei)
|
|
||||||
create_tenant = ei.create_tenant
|
|
||||||
get_tenant_info = ei.get_tenant_info
|
|
||||||
generate_api_key = ei.generate_api_key
|
|
||||||
register_integration = ei.register_integration
|
|
||||||
get_system_status = ei.get_system_status
|
|
||||||
list_tenants = ei.list_tenants
|
|
||||||
list_integrations = ei.list_integrations
|
|
||||||
EnterpriseAPIGateway = getattr(ei, 'EnterpriseAPIGateway', None)
|
|
||||||
else:
|
|
||||||
# Provide stubs if module not found
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Could not load enterprise_integration.py from {module_path}. "
|
f"Required service module 'app.services.enterprise_integration' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api services are available or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
create_tenant = get_tenant_info = generate_api_key = _missing
|
create_tenant = get_tenant_info = generate_api_key = register_integration = get_system_status = list_tenants = list_integrations = _missing
|
||||||
register_integration = get_system_status = list_tenants = list_integrations = _missing
|
|
||||||
EnterpriseAPIGateway = None
|
EnterpriseAPIGateway = None
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
|
|||||||
@@ -10,36 +10,17 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
# Import regulatory reporting system with robust path resolution
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
if _src_path not in sys.path:
|
||||||
if _services_path:
|
sys.path.insert(0, _src_path)
|
||||||
if os.path.isdir(_services_path):
|
|
||||||
if _services_path not in sys.path:
|
|
||||||
sys.path.insert(0, _services_path)
|
|
||||||
else:
|
|
||||||
print(f"Warning: AITBC_SERVICES_PATH set but not a directory: {_services_path}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
_computed_services = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
|
||||||
if os.path.isdir(_computed_services) and _computed_services not in sys.path:
|
|
||||||
sys.path.insert(0, _computed_services)
|
|
||||||
else:
|
|
||||||
_fallback = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
|
||||||
if os.path.isdir(_fallback) and _fallback not in sys.path:
|
|
||||||
sys.path.insert(0, _fallback)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from regulatory_reporting import (
|
from app.services.regulatory_reporting import (
|
||||||
generate_sar as generate_sar_svc,
|
generate_sar, generate_compliance_summary, list_reports,
|
||||||
generate_compliance_summary as generate_compliance_summary_svc,
|
regulatory_reporter, ReportType, ReportStatus, RegulatoryBody
|
||||||
list_reports as list_reports_svc,
|
|
||||||
regulatory_reporter,
|
|
||||||
ReportType,
|
|
||||||
ReportStatus,
|
|
||||||
RegulatoryBody
|
|
||||||
)
|
)
|
||||||
_import_error = None
|
_import_error = None
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@@ -47,10 +28,10 @@ except ImportError as e:
|
|||||||
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Required service module 'regulatory_reporting' could not be imported: {_import_error}. "
|
f"Required service module 'app.services.regulatory_reporting' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api dependencies are installed or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
generate_sar_svc = generate_compliance_summary_svc = list_reports_svc = regulatory_reporter = _missing
|
generate_sar = generate_compliance_summary = list_reports = regulatory_reporter = _missing
|
||||||
|
|
||||||
class ReportType:
|
class ReportType:
|
||||||
pass
|
pass
|
||||||
@@ -96,7 +77,7 @@ def generate_sar(ctx, user_id: str, activity_type: str, amount: float, descripti
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Generate SAR
|
# Generate SAR
|
||||||
result = asyncio.run(generate_sar_svc([activity]))
|
result = asyncio.run(generate_sar([activity]))
|
||||||
|
|
||||||
click.echo(f"\n✅ SAR Report Generated Successfully!")
|
click.echo(f"\n✅ SAR Report Generated Successfully!")
|
||||||
click.echo(f"📋 Report ID: {result['report_id']}")
|
click.echo(f"📋 Report ID: {result['report_id']}")
|
||||||
@@ -129,7 +110,7 @@ def compliance_summary(ctx, period_start: str, period_end: str):
|
|||||||
click.echo(f"📈 Duration: {(end_date - start_date).days} days")
|
click.echo(f"📈 Duration: {(end_date - start_date).days} days")
|
||||||
|
|
||||||
# Generate compliance summary
|
# Generate compliance summary
|
||||||
result = asyncio.run(generate_compliance_summary_svc(
|
result = asyncio.run(generate_compliance_summary(
|
||||||
start_date.isoformat(),
|
start_date.isoformat(),
|
||||||
end_date.isoformat()
|
end_date.isoformat()
|
||||||
))
|
))
|
||||||
@@ -174,7 +155,7 @@ def list(ctx, report_type: str, status: str, limit: int):
|
|||||||
try:
|
try:
|
||||||
click.echo(f"📋 Regulatory Reports")
|
click.echo(f"📋 Regulatory Reports")
|
||||||
|
|
||||||
reports = list_reports_svc(report_type, status)
|
reports = list_reports(report_type, status)
|
||||||
|
|
||||||
if not reports:
|
if not reports:
|
||||||
click.echo(f"✅ No reports found")
|
click.echo(f"✅ No reports found")
|
||||||
@@ -459,7 +440,7 @@ def test(ctx, period_start: str, period_end: str):
|
|||||||
|
|
||||||
# Test SAR generation
|
# Test SAR generation
|
||||||
click.echo(f"\n📋 Test 1: SAR Generation")
|
click.echo(f"\n📋 Test 1: SAR Generation")
|
||||||
result = asyncio.run(generate_sar_svc([{
|
result = asyncio.run(generate_sar([{
|
||||||
"id": "test_sar_001",
|
"id": "test_sar_001",
|
||||||
"timestamp": datetime.now().isoformat(),
|
"timestamp": datetime.now().isoformat(),
|
||||||
"user_id": "test_user_123",
|
"user_id": "test_user_123",
|
||||||
@@ -476,13 +457,13 @@ def test(ctx, period_start: str, period_end: str):
|
|||||||
|
|
||||||
# Test compliance summary
|
# Test compliance summary
|
||||||
click.echo(f"\n📊 Test 2: Compliance Summary")
|
click.echo(f"\n📊 Test 2: Compliance Summary")
|
||||||
compliance_result = asyncio.run(generate_compliance_summary_svc(period_start, period_end))
|
compliance_result = asyncio.run(generate_compliance_summary(period_start, period_end))
|
||||||
click.echo(f" ✅ Compliance Summary: {compliance_result['report_id']}")
|
click.echo(f" ✅ Compliance Summary: {compliance_result['report_id']}")
|
||||||
click.echo(f" 📈 Overall Score: {compliance_result['overall_score']:.1%}")
|
click.echo(f" 📈 Overall Score: {compliance_result['overall_score']:.1%}")
|
||||||
|
|
||||||
# Test report listing
|
# Test report listing
|
||||||
click.echo(f"\n📋 Test 3: Report Listing")
|
click.echo(f"\n📋 Test 3: Report Listing")
|
||||||
reports = list_reports_svc()
|
reports = list_reports()
|
||||||
click.echo(f" ✅ Total Reports: {len(reports)}")
|
click.echo(f" ✅ Total Reports: {len(reports)}")
|
||||||
|
|
||||||
# Test export
|
# Test export
|
||||||
|
|||||||
@@ -10,33 +10,16 @@ import json
|
|||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
# Import surveillance system with robust path resolution
|
# Ensure coordinator-api src is on path for app.services imports
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
_src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'apps', 'coordinator-api', 'src'))
|
||||||
# Determine services path: use AITBC_SERVICES_PATH if set, else compute relative to repo layout
|
if _src_path not in sys.path:
|
||||||
_services_path = os.environ.get('AITBC_SERVICES_PATH')
|
sys.path.insert(0, _src_path)
|
||||||
if _services_path:
|
|
||||||
if os.path.isdir(_services_path):
|
|
||||||
if _services_path not in sys.path:
|
|
||||||
sys.path.insert(0, _services_path)
|
|
||||||
else:
|
|
||||||
print(f"Warning: AITBC_SERVICES_PATH set but not a directory: {_services_path}", file=sys.stderr)
|
|
||||||
else:
|
|
||||||
# Compute project root relative to this file: cli/aitbc_cli/commands -> 3 levels up to project root
|
|
||||||
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
_computed_services = os.path.join(_project_root, 'apps', 'coordinator-api', 'src', 'app', 'services')
|
|
||||||
if os.path.isdir(_computed_services) and _computed_services not in sys.path:
|
|
||||||
sys.path.insert(0, _computed_services)
|
|
||||||
else:
|
|
||||||
# Fallback to known hardcoded path if it exists (for legacy deployments)
|
|
||||||
_fallback = '/home/oib/windsurf/aitbc/apps/coordinator-api/src/app/services'
|
|
||||||
if os.path.isdir(_fallback) and _fallback not in sys.path:
|
|
||||||
sys.path.insert(0, _fallback)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from trading_surveillance import (
|
from app.services.trading_surveillance import (
|
||||||
start_surveillance, stop_surveillance, get_alerts,
|
start_surveillance, stop_surveillance, get_alerts,
|
||||||
get_surveillance_summary, AlertLevel
|
get_surveillance_summary, AlertLevel
|
||||||
)
|
)
|
||||||
_import_error = None
|
_import_error = None
|
||||||
@@ -45,8 +28,8 @@ except ImportError as e:
|
|||||||
|
|
||||||
def _missing(*args, **kwargs):
|
def _missing(*args, **kwargs):
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Required service module 'trading_surveillance' could not be imported: {_import_error}. "
|
f"Required service module 'app.services.trading_surveillance' could not be imported: {_import_error}. "
|
||||||
"Ensure coordinator-api dependencies are installed or set AITBC_SERVICES_PATH."
|
"Ensure coordinator-api dependencies are installed and the source directory is accessible."
|
||||||
)
|
)
|
||||||
start_surveillance = stop_surveillance = get_alerts = get_surveillance_summary = _missing
|
start_surveillance = stop_surveillance = get_alerts = get_surveillance_summary = _missing
|
||||||
|
|
||||||
@@ -237,7 +220,7 @@ def resolve(ctx, alert_id: str, resolution: str):
|
|||||||
click.echo(f"🔍 Resolving alert: {alert_id}")
|
click.echo(f"🔍 Resolving alert: {alert_id}")
|
||||||
|
|
||||||
# Import surveillance to access resolve function
|
# Import surveillance to access resolve function
|
||||||
from trading_surveillance import surveillance
|
from app.services.trading_surveillance import surveillance
|
||||||
|
|
||||||
success = surveillance.resolve_alert(alert_id, resolution)
|
success = surveillance.resolve_alert(alert_id, resolution)
|
||||||
|
|
||||||
@@ -263,7 +246,7 @@ def test(ctx, symbols: str, duration: int):
|
|||||||
click.echo(f"⏱️ Duration: {duration} seconds")
|
click.echo(f"⏱️ Duration: {duration} seconds")
|
||||||
|
|
||||||
# Import test function
|
# Import test function
|
||||||
from trading_surveillance import test_trading_surveillance
|
from app.services.trading_surveillance import test_trading_surveillance
|
||||||
|
|
||||||
# Run test
|
# Run test
|
||||||
asyncio.run(test_trading_surveillance())
|
asyncio.run(test_trading_surveillance())
|
||||||
@@ -289,7 +272,7 @@ def test(ctx, symbols: str, duration: int):
|
|||||||
def status(ctx):
|
def status(ctx):
|
||||||
"""Show current surveillance status"""
|
"""Show current surveillance status"""
|
||||||
try:
|
try:
|
||||||
from trading_surveillance import surveillance
|
from app.services.trading_surveillance import surveillance
|
||||||
|
|
||||||
click.echo(f"📊 Trading Surveillance Status")
|
click.echo(f"📊 Trading Surveillance Status")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user