Skip to content

Intelligent Packing API

The Intelligent Packing API provides AI-enhanced 3D bin packing built on a three-layer hybrid architecture. It combines a deterministic rule engine, an AI matching/pre-screening layer, and an OR-Tools mathematical solver.

Overview

The service processes each request through three layers:

  • Layer 1 — Rule Engine: handles pre-assigned pallets and filters out unavailable / unfit pallet types.
  • Layer 2 — AI Model: SKU–pallet matching, pallet pre-screening, and solution recommendations.
  • Layer 3 — OR-Tools: mathematically guaranteed bin-packing solve.

Additional capabilities:

  • AI pallet pre-screening (reduce a large pallet catalog to an optimal subset)
  • Parallel and batch processing for large item counts
  • Dynamic entropy tuning
  • LDM (Loading Meter) output for logistics billing

Base URL:

https://api.alaikis.com

All endpoints are mounted under both /api/intelligent-packing and /api/v1/intelligent-packing (v1 alias), and require authentication.

Endpoints

MethodPathDescription
POST/api/intelligent-packing/packRun intelligent packing (three-layer solve)
POST/api/intelligent-packing/pack/previewPreview a plan without solving
POST/api/intelligent-packing/filter-palletsFilter unavailable / unfit pallet types
POST/api/intelligent-packing/prescreen-palletsAI pallet pre-screening (top-K subset)
GET/api/intelligent-packing/healthHealth check

Pack

Run the full three-layer intelligent packing solve.

Endpoint:

POST /api/intelligent-packing/pack

Request Body:

json
{
  "skus": [
    {
      "id": "SKU001",
      "weight": 5.5,
      "weight_unit": "kg",
      "length": 50,
      "width": 30,
      "height": 20,
      "length_unit": "cm",
      "quantity": 10,
      "is_dangerous": false,
      "is_stackable": true
    }
  ],
  "pallets": [
    {
      "id": "PALLET_A",
      "length": 120,
      "width": 100,
      "height": 150,
      "weight": 20,
      "max_weight": 500,
      "length_unit": "cm",
      "weight_unit": "kg",
      "cost": 5.0
    }
  ],
  "use_ai": true,
  "entropy_factor": 1.0,
  "time_limit_ms": 60000,
  "ldm_width": 2.44,
  "ldm_mode": "projection"
}

SKU Fields (skus[])

FieldTypeRequiredDefaultDescription
idstringYesSKU identifier
weightfloatYesItem weight
weight_unitstringNo"kg"Weight unit
length / width / heightfloatYesItem dimensions
length_unitstringNo"cm"Dimension unit
quantityintegerNo1Number of items
is_dangerousbooleanNofalseDangerous goods flag (isolated during packing)
is_stackablebooleanNotrueWhether the item can be stacked

Pallet Fields (pallets[])

FieldTypeRequiredDefaultDescription
idstringYesPallet type identifier
length / width / heightfloatYesPallet dimensions
weightfloatNo0.0Pallet tare weight
max_weightfloatYesMaximum load weight
length_unitstringNo"cm"Dimension unit
weight_unitstringNo"kg"Weight unit
costfloatNo0.0Pallet cost

Request Options

ParameterTypeDefaultRangeDescription
use_aibooleantrueEnable AI enhancement
entropy_factorfloat1.00.8–2.0Packing entropy factor
time_limit_msinteger600001000–300000Solver time limit (ms)
enable_parallelbooleantrueEnable parallel processing
prescreen_palletsbooleantrueEnable AI pallet pre-screening
top_k_palletsinteger155–100Pallet subset size after pre-screening
max_workersinteger81–16Max parallel worker threads
pre_assigned_palletsarraynullPre-assigned pallets (see below)
unavailable_pallet_typesstring[]nullPallet type IDs to exclude
ldm_widthfloatnull> 0LDM normalization vehicle width (m); defaults to 2.4
ldm_modestring"projection"LDM mode: "projection" or "arrangement"

Pre-assigned pallets

pre_assigned_pallets[] items use the shape: { "pallet_type_id": "PALLET_A", "pallet_id": "P-1", "items": [{ "sku_id": "SKU001", "quantity": 4 }], "used_volume": 0.0, "used_weight": 0.0 }.

Response:

json
{
  "pallets": [
    {
      "id": "pallet_1",
      "pallet_id": "pallet_1",
      "pallet_type": "PALLET_A",
      "items": [
        { "sku_id": "SKU001", "quantity": 10 }
      ],
      "total_weight": 75.0,
      "total_volume": 0.347,
      "utilization": 19.3,
      "source": "computed",
      "ldm": 0.492
    }
  ],
  "total_pallets": 1,
  "total_weight": 75.0,
  "total_volume": 0.347,
  "processing_time_ms": 12.3,
  "layers_used": ["L1_RULE_ENGINE", "L2_AI_MODEL", "L3_OR_TOOLS"],
  "ai_recommendations": null,
  "stats": {
    "total_pallets": 1,
    "total_weight": 75.0,
    "total_volume": 0.347,
    "total_ldm": 0.492,
    "ldm_mode": "projection",
    "ldm_width_used": 2.44
  },
  "prescreen_stats": null,
  "performance_stats": {},
  "errors": [],
  "warnings": [],
  "success": true,
  "message": "装托成功",
  "total_ldm": 0.492,
  "ldm_mode": "projection",
  "ldm_width_used": 2.44
}

Response Fields

FieldTypeDescription
pallets[]arraySolved pallets
pallets[].id / pallet_idstringPallet instance ID (kept in sync)
pallets[].pallet_typestringSource pallet type ID
pallets[].itemsarray{ sku_id, quantity } placed on the pallet
pallets[].total_weightfloatLoaded weight (kg)
pallets[].total_volumefloatLoaded volume (m³)
pallets[].utilizationfloatVolume utilization (%)
pallets[].sourcestring"computed" or "pre_assigned"
pallets[].ldmfloatPer-pallet loading meter
total_palletsintegerNumber of pallets used
layers_usedstring[]Processing layers applied
statsobjectAggregated statistics (weights, volumes, LDM, etc.)
total_ldmfloatTotal loading meters for the solution
ldm_modestringLDM mode used
ldm_width_usedfloatActual vehicle width used (m)
successbooleanWhether the solve succeeded
messagestringHuman-readable status

LDM (Loading Meter)

LDM (Loading Meter) measures the equivalent floor length a solution occupies in a vehicle — the standard billing metric for European road freight.

Request Parameters (top-level, alongside skus / pallets)

ParameterTypeDefaultDescription
ldm_widthfloatnullVehicle width in meters. When omitted, defaults to 2.4.
ldm_modestring"projection"Calculation mode: "projection" or "arrangement"

Vehicle width on this endpoint

The /pack request has no vehicle field. Vehicle width therefore resolves as ldm_width (if provided) otherwise the 2.4 m default. Pass ldm_width explicitly to override.

Response Fields

FieldTypeDescription
total_ldmfloatTotal loading meters for the solution
ldm_modestringMode used for calculation
ldm_width_usedfloatActual vehicle width used (meters)
pallets[].ldmfloatPer-pallet loading meter

projection Mode

ldm = (pallet_length_m * pallet_width_m) / ldm_width_m
total_ldm = Σ ldm

Example: a 1.2 m × 1.0 m pallet with ldm_width = 2.44 yields ldm = 1.2 * 1.0 / 2.44 = 0.492.

arrangement Mode

per_row   = max(1, floor(ldm_width_m / pallet_width_m))
rows      = ceil(total_pallets / per_row)
total_ldm = rows * pallet_length_m / ldm_width_m

arrangement assumes pallets are placed side-by-side along the vehicle width.

Example Request with LDM

python
payload = {
    "skus": [
        {"id": "SKU001", "weight": 5.5, "length": 50, "width": 30, "height": 20, "quantity": 10}
    ],
    "pallets": [
        {"id": "PALLET_A", "length": 120, "width": 100, "height": 150, "max_weight": 500}
    ],
    "ldm_width": 2.44,
    "ldm_mode": "projection"
}

Preview

Preview a packing plan without running the full solve. Returns valid SKU / available pallet counts, AI recommendations, and an estimated pallet count.

Endpoint:

POST /api/intelligent-packing/pack/preview

The request body is the same shape as /pack.

Response:

json
{
  "valid_skus": 1,
  "available_pallets": 1,
  "pre_assigned_pallets": 0,
  "ai_recommendations": null,
  "estimated_pallets": 2,
  "warnings": [],
  "preview": true
}

Filter Pallets

Filter out pallet types that are marked unavailable or that no SKU can fit into.

Endpoint:

POST /api/intelligent-packing/filter-pallets

Request Body:

json
{
  "skus": [
    {"id": "SKU001", "weight": 5.5, "length": 50, "width": 30, "height": 20, "quantity": 10}
  ],
  "pallets": [
    {"id": "PALLET_A", "length": 120, "width": 100, "height": 150, "max_weight": 500}
  ],
  "unavailable_pallet_types": ["PALLET_X"]
}

Response:

json
{
  "available_pallets": [
    {"id": "PALLET_A", "length": 120, "width": 100, "height": 150, "max_weight": 500}
  ],
  "filtered_count": 0,
  "total_count": 1,
  "filter_reasons": []
}

filter_reasons[] entries carry a pallet_id and a reason of "marked_unavailable" or "no_sku_fits".


Prescreen Pallets

AI pallet pre-screening — reduce a large pallet catalog to an optimal top-K subset before solving.

Endpoint:

POST /api/intelligent-packing/prescreen-pallets?top_k=20&use_ai=true
Query ParameterTypeDefaultDescription
top_kinteger20Number of pallet types to keep
use_aibooleantrueEnable AI scoring

The request body uses the same skus / pallets / unavailable_pallet_types shape as Filter Pallets.

Response:

json
{
  "original_pallets": [ /* PalletType[] */ ],
  "filtered_pallets": [ /* PalletType[] */ ],
  "prescreen_stats": {},
  "performance_stats": {}
}

Health

Endpoint:

GET /api/intelligent-packing/health

Response:

json
{
  "status": "healthy",
  "service": "intelligent-packing",
  "version": "2.1.0",
  "layers": ["L1_RULE_ENGINE", "L2_AI_MODEL", "L3_OR_TOOLS"],
  "features": ["ai_prescreen", "parallel_processing", "performance_stats"]
}

Example Workflow

python
import requests

headers = {"Authorization": "Bearer <your-jwt-token>"}
base = "https://api.alaikis.com/api/intelligent-packing"

skus = [
    {"id": "SKU001", "weight": 5.5, "length": 50, "width": 30, "height": 20, "quantity": 10}
]
pallets = [
    {"id": "PALLET_A", "length": 120, "width": 100, "height": 150, "max_weight": 500}
]

# 1) Preview to estimate pallet count
preview = requests.post(f"{base}/pack/preview",
                        json={"skus": skus, "pallets": pallets},
                        headers=headers).json()
print("Estimated pallets:", preview["estimated_pallets"])

# 2) Run the full solve with LDM
result = requests.post(f"{base}/pack",
                       json={
                           "skus": skus,
                           "pallets": pallets,
                           "ldm_width": 2.44,
                           "ldm_mode": "projection"
                       },
                       headers=headers).json()

print("Pallets used:", result["total_pallets"])
print("Total LDM:", result["total_ldm"])
for pallet in result["pallets"]:
    print(pallet["pallet_type"], "->", pallet["utilization"], "% | ldm", pallet["ldm"])

Performance Notes

  • Large item counts automatically switch to batch mode; medium counts use parallel processing.
  • Pre-screening (prescreen_pallets) is applied automatically when the pallet catalog exceeds top_k_pallets.
  • Use time_limit_ms to cap solver time for real-time scenarios.