Reduce machine max MCP response size

This commit is contained in:
2026-09-26 13:42:00 +02:00
parent 90c28fb2f5
commit eca151a465
4 changed files with 121 additions and 14 deletions
+4 -1
View File
@@ -157,7 +157,10 @@ Tool calling semantics:
weight, and the domain returns the governing constraint(s), unrounded. weight, and the domain returns the governing constraint(s), unrounded.
Core and width incompatibility cannot be corrected by shortening and is Core and width incompatibility cannot be corrected by shortening and is
returned structurally. No manufacturing increment, core/packaging/gross returned structurally. No manufacturing increment, core/packaging/gross
weight, or conditional production rule is applied in V1.1. weight, or conditional production rule is applied in V1.1. Its successful
MCP response is a compact projection: machine identity, configured maximum
diameter/material-weight limits, production maxima, material weights, and
warnings. Flask and other domain consumers retain the full diagnostic result.
- `calculate_product_length` calculates required roll length from a target outer - `calculate_product_length` calculates required roll length from a target outer
diameter, core diameter, and material thickness. Its optional thickness diameter, core diameter, and material thickness. Its optional thickness
stddev returns minimum/average/maximum length ranges; optional width and area stddev returns minimum/average/maximum length ranges; optional width and area
+31 -2
View File
@@ -69,8 +69,8 @@ def calculate_machine_max_product_length_result(
product_width_m: float | None = None, product_width_m: float | None = None,
area_weight_g_m2: float | None = None, area_weight_g_m2: float | None = None,
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Delegate machine-aware maximum product length to the shared domain.""" """Return the compact MCP view of the machine-aware domain calculation."""
return calculate_machine_max_product_length( result = calculate_machine_max_product_length(
machine=machine, machine=machine,
core_diameter_mm=core_diameter_mm, core_diameter_mm=core_diameter_mm,
thickness_mm=thickness_mm, thickness_mm=thickness_mm,
@@ -78,6 +78,35 @@ def calculate_machine_max_product_length_result(
product_width_m=product_width_m, product_width_m=product_width_m,
area_weight_g_m2=area_weight_g_m2, area_weight_g_m2=area_weight_g_m2,
) )
return project_machine_max_product_length_result(result)
def project_machine_max_product_length_result(result: dict[str, Any]) -> dict[str, Any]:
"""Project successful maximum-length results for MCP without diagnostics.
The shared domain result deliberately contains detailed verification reports.
They remain available to Flask and other consumers, but duplicate machine
metadata and checks are unnecessary in the MCP response.
"""
if result.get("status") != "success":
return result
machine = result["machine"]
constraints = machine["constraints"]
return {
"status": result["status"],
"machine": {"id": machine["id"], "name": machine["name"]},
"weight_scope": result["weight_scope"],
"machine_limits": {
"maximum_roll_diameter_mm": constraints["roll_diameter_mm"]["maximum"]
if constraints["roll_diameter_mm"] is not None else None,
"maximum_material_roll_weight_kg": constraints["roll_weight_kg"]["maximum"]
if constraints["roll_weight_kg"] is not None else None,
},
"production_maximums": result["production_maximums"],
"material_weights_kg": result["material_weights_kg"],
"warnings": result["warnings"],
}
def calculate_material_weight_result( def calculate_material_weight_result(
+12 -8
View File
@@ -5,6 +5,7 @@ import unittest
from unittest.mock import patch from unittest.mock import patch
import app as rollcalc_app import app as rollcalc_app
from machine_constraints import calculate_machine_max_product_length
class CalculationApiTests(unittest.TestCase): class CalculationApiTests(unittest.TestCase):
@@ -140,21 +141,24 @@ class CalculationApiTests(unittest.TestCase):
self.assertEqual(result["warning_constraints"], ["roll_diameter_mm"]) self.assertEqual(result["warning_constraints"], ["roll_diameter_mm"])
def test_machine_maximum_length_endpoint_is_authenticated_and_structured(self): def test_machine_maximum_length_endpoint_is_authenticated_and_structured(self):
payload = {
"machine": "K7",
"core_diameter_mm": 133,
"thickness_mm": 4.311611,
"thickness_stddev_mm": 0.249999,
"product_width_m": 6,
"area_weight_g_m2": 495.395833,
}
response = self.client.post( response = self.client.post(
"/api/calculations/machine-max-product-length", "/api/calculations/machine-max-product-length",
json={ json=payload,
"machine": "K7",
"core_diameter_mm": 133,
"thickness_mm": 4.311611,
"thickness_stddev_mm": 0.249999,
"product_width_m": 6,
"area_weight_g_m2": 495.395833,
},
headers=self.headers, headers=self.headers,
) )
result = response.get_json() result = response.get_json()
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(result["status"], "success") self.assertEqual(result["status"], "success")
self.assertEqual(result, calculate_machine_max_product_length(**payload))
self.assertIn("final_feasibility", result)
self.assertEqual( self.assertEqual(
result["production_maximums"]["nominal_governing_constraints"], result["production_maximums"]["nominal_governing_constraints"],
["roll_diameter_nominal"], ["roll_diameter_nominal"],
+74 -3
View File
@@ -1,3 +1,4 @@
import copy
import inspect import inspect
import sys import sys
import types import types
@@ -21,6 +22,7 @@ from rollcalc_mcp_tools import (
calculate_roll_diameter_result, calculate_roll_diameter_result,
get_article_result, get_article_result,
get_machine_result, get_machine_result,
project_machine_max_product_length_result,
search_articles_result, search_articles_result,
) )
from machine_constraints import ( from machine_constraints import (
@@ -103,7 +105,7 @@ class McpAdapterTests(unittest.TestCase):
"feasible_with_warnings", "feasible_with_warnings",
) )
def test_machine_maximum_length_adapter_delegates_to_shared_domain(self): def test_machine_maximum_length_adapter_projects_success_without_mutating_domain(self):
arguments = { arguments = {
"machine": "K7", "machine": "K7",
"core_diameter_mm": 133.0, "core_diameter_mm": 133.0,
@@ -112,10 +114,79 @@ class McpAdapterTests(unittest.TestCase):
"product_width_m": 6.0, "product_width_m": 6.0,
"area_weight_g_m2": 495.395833, "area_weight_g_m2": 495.395833,
} }
domain_result = calculate_machine_max_product_length(**arguments)
domain_before_projection = copy.deepcopy(domain_result)
result = project_machine_max_product_length_result(domain_result)
self.assertEqual(domain_result, domain_before_projection)
self.assertIn("final_feasibility", domain_result)
self.assertNotIn("final_feasibility", result)
self.assertEqual( self.assertEqual(
calculate_machine_max_product_length_result(**arguments), calculate_machine_max_product_length_result(**arguments), result
calculate_machine_max_product_length(**arguments),
) )
self.assertEqual(result["machine"], {"id": "k7", "name": "K7"})
self.assertEqual(result["weight_scope"], "material_only")
self.assertEqual(result["machine_limits"], {
"maximum_roll_diameter_mm": 1100.0,
"maximum_material_roll_weight_kg": 700.0,
})
self.assertEqual(result["production_maximums"], {
"nominal_length_m": 217.1900177447768,
"nominal_governing_constraints": ["roll_diameter_nominal"],
"conservative_length_m": 194.62073281485985,
"conservative_governing_constraints": ["roll_diameter_conservative"],
})
self.assertEqual(result["material_weights_kg"], {
"at_nominal_production_maximum": 645.570178559751,
"at_conservative_production_maximum": 578.4858003113276,
})
self.assertEqual(result["warnings"], {"nominal": ["roll_diameter_mm"]})
def test_machine_maximum_length_adapter_preserves_non_success_diagnostics(self):
base = {
"machine": "K7",
"core_diameter_mm": 133.0,
"thickness_mm": 4.311611,
"thickness_stddev_mm": 0.249999,
"product_width_m": 6.0,
"area_weight_g_m2": 495.395833,
}
for changes, status in (
({"core_diameter_mm": 194.0}, "incompatible"),
({"product_width_m": 3.0}, "incompatible"),
(
{"product_width_m": None, "area_weight_g_m2": None},
"needs_clarification",
),
({"thickness_mm": -1.0}, "invalid_parameter"),
({"machine": "not configured"}, "machine_not_found"),
):
with self.subTest(changes=changes):
arguments = {**base, **changes}
domain_result = calculate_machine_max_product_length(**arguments)
adapter_result = calculate_machine_max_product_length_result(**arguments)
self.assertEqual(adapter_result, domain_result)
self.assertEqual(adapter_result["status"], status)
missing = calculate_machine_max_product_length_result(
**{**base, "product_width_m": None, "area_weight_g_m2": None}
)
self.assertEqual(
missing["missing_required_inputs"],
["product_width_m", "area_weight_g_m2"],
)
core = calculate_machine_max_product_length_result(
**{**base, "core_diameter_mm": 194.0}
)
self.assertEqual(
core["failed_compatibility_constraints"],
["allowed_core_diameters_mm"],
)
width = calculate_machine_max_product_length_result(
**{**base, "product_width_m": 3.0}
)
self.assertEqual(width["failed_compatibility_constraints"], ["product_width_m"])
def test_material_weight_adapter_delegates_without_a_core_diameter(self): def test_material_weight_adapter_delegates_without_a_core_diameter(self):
arguments = { arguments = {