Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,6 @@ __marimo__/
node_modules/
.venv/
.env
.envrc
.envrc

.vscode
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

60 changes: 46 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,29 @@ OpenBinding is a QoS-aware service composition gateway and solver engine framewo
## 🏗️ Architecture

```mermaid
graph TD
User[User / Frontend] -->|HTTP POST /v1/solve| Gateway[OpenBinding Gateway]
Gateway -->|1. Validate General Schema| Schema[General QoS Schema]
Gateway -->|2. Validate Specialization| Spec[Specialization Schema]
Gateway -->|3. Route Request| Router{Router}

Router -->|engine_id=minizinc-csp| MZ[MiniZinc CSP Engine]
Router -->|engine_id=random-search| RS[Random Search Engine]

MZ -->|Solve via Gecode| Solution
RS -->|Solve via Random Heuristic| Solution

Solution --> Gateway
Gateway -->|HTTP 200| User
flowchart TD
U[User / Frontend]:::client

U -->|HTTP POST /v1/solve| G[OpenBinding Gateway]:::gateway

G --> V0[Validate request envelope]:::step
V0 --> V1[Validate basic composition schema]:::schema
V1 --> R{Root router: route by engine_id}:::router

R -->|minizinc-csp| SZ_MZ[Validate specialization schema: MiniZinc]:::schema
R -->|random-search| SZ_RS[Validate specialization schema: Random Search]:::schema
R -->|many-heuristic| SZ_MH[Validate specialization schema: Many-Heuristic]:::schema

SZ_MZ --> MZ[MiniZinc CSP engine]:::engine
SZ_RS --> RS[Random Search engine]:::engine
SZ_MH --> MH[Many-Heuristic engine]:::engine

MZ -->|solve| SOL[(Solution)]:::solution
RS -->|solve| SOL
MH -->|solve| SOL

SOL --> G
G -->|HTTP 200 result| U
```

## 🧩 Components
Expand All @@ -40,6 +49,11 @@ graph TD
* Uses random search.
* Best for exploring large solution spaces.

4. **Many-Heuristic Engine** (`engines/many-heuristic`):
* Java service (extends Random Search).
* Specialized for **Many-Objective** problems (3+ objectives).
* Returns a **Pareto front** of non-dominated solutions.

4. **Frontend** (`frontend`):
* React + Vite web UI for modeling and submitting problems.
* Multi-page SPA with professional design inspired by modern developer tools.
Expand All @@ -56,6 +70,7 @@ OpenBinding validates incoming requests against two schema layers:

1. **General schema** (engine-agnostic):
- JSON Schema (structural validation): `schemas/general/schema.json`
- Visual model (Mermaid): `schemas/general/schema.mermaid`
- Specification / semantics (human-readable): `schemas/general/schema.specification.md`

The specification document explains the intent and semantics behind the JSON Schema, including:
Expand All @@ -67,6 +82,14 @@ OpenBinding validates incoming requests against two schema layers:
2. **Specialization schemas** (engine-specific constraints):
- `schemas/specializations/minizinc-csp.schema.json`
- `schemas/specializations/random-search.schema.json`
- `schemas/specializations/many-heuristic.schema.json`

Specializations can also include a visual model in Mermaid format (recommended):
- `schemas/specializations/minizinc-csp.schema.mermaid`
- `schemas/specializations/random-search.schema.mermaid`
- `schemas/specializations/many-heuristic.schema.mermaid`

Mermaid models are used by the frontend **Schema Explorer** in the **Model** tab. If a specialization does not provide `.schema.mermaid`, the JSON schema workflow remains fully functional.

Example payloads that follow these schemas live in `examples/`.

Expand All @@ -89,6 +112,7 @@ Example payloads that follow these schemas live in `examples/`.
* **Gateway API**: [http://localhost:8000/docs](http://localhost:8000/docs)
* **MiniZinc Engine**: Port 3000 (Internal)
* **Random Search Engine**: Port 8081 (Internal)
* **Many-Heuristic Engine**: Port 8082 (Internal)

2. **Stop the Stack**:
```bash
Expand Down Expand Up @@ -198,6 +222,14 @@ curl -X POST "http://localhost:8000/v1/solve" \

See `examples/` directory for sample payloads.

## 🧭 Engine Integration Guide

If you are adding a new engine, see [docs/ENGINE_INTEGRATION_GUIDE.md](docs/ENGINE_INTEGRATION_GUIDE.md).

## 🤝 Contributing

See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for branch and PR rules.

## 📄 License

This project is licensed under the **Creative Commons Attribution 4.0 International (CC BY 4.0)**.
Expand Down
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ services:
- PYTHONPATH=/app/src
- ENGINE_MINIZINC_URL=http://engine-minizinc:3000
- ENGINE_RANDOM_SEARCH_URL=http://engine-random-search:8080
- ENGINE_MANY_HEURISTIC_URL=http://engine-many-heuristic:8080
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
interval: 10s
Expand All @@ -26,6 +27,8 @@ services:
condition: service_healthy
engine-random-search:
condition: service_healthy
engine-many-heuristic:
condition: service_healthy

engine-minizinc:
build:
Expand Down Expand Up @@ -53,6 +56,19 @@ services:
retries: 5
start_period: 30s

engine-many-heuristic:
build:
context: ./engines/many-heuristic
dockerfile: Dockerfile
ports:
- "8082:8080"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s

frontend:
build:
context: ./frontend
Expand All @@ -64,3 +80,9 @@ services:
depends_on:
gateway:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
35 changes: 35 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Contributing

Thanks for contributing to OpenBinding.

## Branching model

- Default branches: `develop` and `main`.
- Always create new branches from `develop`.
- Open pull requests into `develop`.
- Admin will merge `develop` into `main` when appropriate.

## Workflow

1. Create a feature branch from `develop`:

```bash
git checkout develop
git pull
git checkout -b feature/my-change
```

2. Make changes with tests and documentation updates as needed.

3. Push the branch and open a PR targeting `develop`.

4. Address review feedback and keep the branch up to date with `develop`. All PRs must pass CI checks before merging.

## Expectations

- Keep changes focused and well tested.
- Update docs when behavior changes.
- Follow existing coding conventions and linting rules.
- Document the rationale for non-trivial changes in the PR description. If possible, create a small video demo of the change in action.
- Be responsive to review feedback and iterate on the PR until it meets the standards for merging. Each PR should be a self-contained unit of work that can be easily reviewed and understood.
- All PRs must be reviewed and approved by at least one other contributor before merging. This ensures code quality and knowledge sharing across the team.
176 changes: 176 additions & 0 deletions docs/ENGINE_INTEGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Engine integration guide

This guide explains how to add a new engine to the OpenBinding gateway. It covers schema specialization, validation hooks, routing, and tests.

## Overview

The gateway validates incoming instances in stages and then routes them to the selected engine.

Validation stages:
1. General schema
2. Specialization schema
3. General semantic rules
4. Engine semantic rules

Engines integrate through the gateway plugin interface and a specialization schema.

## Required artifacts

1. Engine plugin (gateway): implement `EngineValidationPlugin`.
2. Specialization schema: `schemas/specializations/<engine-id>.schema.json`.
3. Specialization model (recommended): `schemas/specializations/<engine-id>.schema.mermaid`.
4. Engine URL in registry + env wiring.
5. Tests for validation and transformation.

## Step-by-step

### 1) Add a specialization schema

Create a specialization schema under `schemas/specializations/`.

### 1.1) Add a specialization model (recommended)

To enable visual exploration in the frontend **Schema Explorer** (`JSON | Model` tabs), add a Mermaid model next to your specialization schema:

- Path: `schemas/specializations/<engine-id>.schema.mermaid`
- Naming must match your engine id exactly (`<engine-id>`)

The Mermaid model is optional, but strongly recommended for maintainability and onboarding.

If the file is missing, the frontend will show **Model not available** while keeping JSON schema validation and all engine workflows fully operational.

#### Good practices

- Keep JSON and Mermaid aligned conceptually (same constraints/capabilities).
- Keep node/edge labels stable and meaningful across versions.
- Prefer modular Mermaid subgraphs for large models.
- Update both files in the same PR when constraints change.
- Avoid changing `<engine-id>` naming once released, to prevent schema/model mismatch.


## Engine options defaults (Playground)

The frontend Playground can prefill the `options` object depending on the selected engine.
To support this, the gateway exposes engine-level defaults at:

- `GET /v1/engines/{engine_id}/options/defaults`

If the engine has no options, the endpoint returns an empty JSON object: `{}`.

### How to define defaults

Defaults are defined in the gateway engine plugin by implementing `get_default_options()`.
Example:

- Return `{}` if your engine does not accept any options.
- Return a JSON object with the gateway defaults (e.g. `{ "iterations_count": 1000 }`) if your engine supports options.

### 2) Implement the engine plugin

Create a new plugin in `openbinding-gateway/src/openbinding_gateway/validation/engine_plugins/`:

```python
from typing import Any, Dict, List, Tuple
import httpx
from .base import EngineValidationPlugin
from ...models.api import ValidationViolation

class MyEnginePlugin(EngineValidationPlugin):
async def check_engine_health(self, base_url: str, client: httpx.AsyncClient) -> bool:
resp = await client.get(f"{base_url.rstrip('/')}/health")
return resp.status_code == 200

def get_capabilities(self) -> Dict[str, Any]:
return {
"qos_features_supported": ["*"],
"composition_nodes_supported": ["TASK", "SEQ"],
"objective_types_supported": ["weighted_sum"],
"constraints_supported": ["attribute_bound"],
"schema_version": "v1",
}

def get_specialization_schema_path(self) -> str:
# Uses SCHEMAS_DIR if available
# e.g. /app/schemas/specializations/my-engine.schema.json
...

def validate_semantics(self, instance: Dict[str, Any]) -> List[ValidationViolation]:
violations: List[ValidationViolation] = []
# Add engine-specific invariants here
return violations

def transform_request(self, instance: Dict[str, Any], options: Dict[str, Any] = {}) -> Tuple[Dict[str, Any], List[str]]:
# Map general instance to engine request payload
return {"instance": instance, "options": options}, []

def transform_response(self, engine_response: Dict[str, Any], original_request: Dict[str, Any]) -> Dict[str, Any]:
# Map engine response to gateway solution format
return engine_response
```

### 3) Register the plugin and URL

Add the plugin to `EngineRegistry`:

- File: `openbinding-gateway/src/openbinding_gateway/registry/engine.py`
- Add env var for the engine URL (e.g. `ENGINE_MY_ENGINE_URL`).
- Register the plugin in the initialization block.

Example:

```python
from ..validation.engine_plugins.my_engine import MyEnginePlugin

_engine_urls = {
"my-engine": os.getenv("ENGINE_MY_ENGINE_URL", "http://engine-my:1234"),
}

EngineRegistry.register("my-engine", MyEnginePlugin())
```

### 4) Ensure schema endpoints work

The gateway exposes:

- `/v1/schemas/general`
- `/v1/schemas/general/model`
- `/v1/schemas/<engine-id>`
- `/v1/schemas/<engine-id>/model`

Your specialization schema must exist and be discoverable via `SCHEMAS_DIR`.
Your specialization model should follow the same directory and naming convention to be discoverable by the `/model` endpoint.

### 5) Add tests

Recommended tests:

- Schema and semantic validation: `openbinding-gateway/tests/test_validation_comprehensive.py`
- Plugin request/response transformation: `openbinding-gateway/tests/test_plugin_transformation.py`
- Integration tests via docker compose (if the engine is available)

### 6) Wire docker compose (if needed)

Add the engine service to `docker-compose.yml` and expose the engine URL to the gateway:

```yaml
environment:
- ENGINE_MY_ENGINE_URL=http://engine-my:1234
```

## Validation expectations

The gateway uses schema defaults and semantic checks before engine-specific validation. If your engine depends on implicit rules, enforce them in `validate_semantics`.

Common checks:

- Unsupported composition nodes
- Unsupported constraint types or objective types
- Missing candidates or missing QoS values
- Attribute bounds on missing features

## Troubleshooting

- Check `/v1/engines` to confirm the engine is registered and reachable.
- Use `/v1/analyze` for validation errors and warnings.
- Ensure `SCHEMAS_DIR` resolves to the folder containing your specialization schema.
- If the Model tab shows unavailable, confirm `<engine-id>.schema.mermaid` exists under `schemas/specializations/` and matches engine id naming exactly.
Loading