Compare commits
2
Commits
90c28fb2f5
...
0c18c86a47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c18c86a47 | ||
|
|
eca151a465 |
@@ -150,14 +150,18 @@ Tool calling semantics:
|
|||||||
width, and area weight. It requires no core diameter, does not infer one, and
|
width, and area weight. It requires no core diameter, does not infer one, and
|
||||||
cannot return a roll diameter or a core-inclusive total weight.
|
cannot return a roll diameter or a core-inclusive total weight.
|
||||||
- `calculate_machine_max_product_length` composes configured machine limits
|
- `calculate_machine_max_product_length` composes configured machine limits
|
||||||
with the shared calculation domain. Resolve an article first, then pass its
|
with the shared calculation domain. With an exact article number, MCP resolves
|
||||||
properties unchanged. Its nominal maximum uses the nominal diameter limit;
|
its properties internally; otherwise callers pass explicit properties. Its
|
||||||
|
nominal maximum uses the nominal diameter limit;
|
||||||
its conservative/no-warning maximum uses RollCalc's existing maximum-diameter
|
its conservative/no-warning maximum uses RollCalc's existing maximum-diameter
|
||||||
thickness variation. Both also respect configured material-only maximum roll
|
thickness variation. Both also respect configured material-only maximum roll
|
||||||
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
|
||||||
|
|||||||
+4
-2
@@ -67,15 +67,17 @@ def create_server() -> Any:
|
|||||||
def calculate_machine_max_product_length(
|
def calculate_machine_max_product_length(
|
||||||
machine: str,
|
machine: str,
|
||||||
core_diameter_mm: float,
|
core_diameter_mm: float,
|
||||||
thickness_mm: float,
|
article_number: str | None = None,
|
||||||
|
thickness_mm: float | None = None,
|
||||||
thickness_stddev_mm: float | None = None,
|
thickness_stddev_mm: float | None = None,
|
||||||
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]:
|
||||||
"""Calculate deterministic maximum product length for a configured production machine. Use this for maximum roll/product length, how much product fits on a machine roll, or maximum length without a diameter warning. Resolve the article first with search_articles/get_article when needed, then pass its thickness, thickness standard deviation, width, and area weight exactly. Do not supply or invent machine limits, rearrange roll or weight formulas, calculate a weight-limited length, or compare candidate limits yourself: this tool returns nominal and conservative/no-warning production maxima and their governing constraints. Report incompatibility rather than substituting another core or width, and ask for returned missing inputs rather than inventing them."""
|
"""Calculate deterministic maximum product length for a configured production machine. With an exact article_number, call this directly with machine and caller-supplied core_diameter_mm; do not call get_article merely to copy properties. Otherwise supply explicit material properties. Never mix article_number with material properties. For a product name, use search_articles; if candidates are multiple, ask the user to select an article number. Do not supply or invent machine limits or substitute a core or width; this returns nominal and conservative/no-warning production maxima and governing constraints."""
|
||||||
return calculate_machine_max_product_length_result(
|
return calculate_machine_max_product_length_result(
|
||||||
machine=machine,
|
machine=machine,
|
||||||
core_diameter_mm=core_diameter_mm,
|
core_diameter_mm=core_diameter_mm,
|
||||||
|
article_number=article_number,
|
||||||
thickness_mm=thickness_mm,
|
thickness_mm=thickness_mm,
|
||||||
thickness_stddev_mm=thickness_stddev_mm,
|
thickness_stddev_mm=thickness_stddev_mm,
|
||||||
product_width_m=product_width_m,
|
product_width_m=product_width_m,
|
||||||
|
|||||||
+78
-3
@@ -64,13 +64,49 @@ def calculate_machine_max_product_length_result(
|
|||||||
*,
|
*,
|
||||||
machine: str,
|
machine: str,
|
||||||
core_diameter_mm: float,
|
core_diameter_mm: float,
|
||||||
thickness_mm: float,
|
article_number: str | None = None,
|
||||||
|
thickness_mm: float | None = None,
|
||||||
thickness_stddev_mm: float | None = None,
|
thickness_stddev_mm: float | None = None,
|
||||||
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, resolving one exact article when supplied."""
|
||||||
return calculate_machine_max_product_length(
|
explicit_material = {
|
||||||
|
"thickness_mm": thickness_mm,
|
||||||
|
"thickness_stddev_mm": thickness_stddev_mm,
|
||||||
|
"product_width_m": product_width_m,
|
||||||
|
"area_weight_g_m2": area_weight_g_m2,
|
||||||
|
}
|
||||||
|
if article_number is not None:
|
||||||
|
mixed_fields = [
|
||||||
|
field for field, value in explicit_material.items() if value is not None
|
||||||
|
]
|
||||||
|
if mixed_fields:
|
||||||
|
return {
|
||||||
|
"status": "invalid_parameter",
|
||||||
|
"invalid": [{
|
||||||
|
"field": "article_number",
|
||||||
|
"message": (
|
||||||
|
"cannot be combined with explicit material properties: "
|
||||||
|
+ ", ".join(mixed_fields)
|
||||||
|
),
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
article_result = get_article(article_number)
|
||||||
|
if article_result["status"] != "resolved":
|
||||||
|
return article_result
|
||||||
|
article = article_result["article"]
|
||||||
|
result = calculate_machine_max_product_length(
|
||||||
|
machine=machine,
|
||||||
|
core_diameter_mm=core_diameter_mm,
|
||||||
|
thickness_mm=article["thickness_mm"],
|
||||||
|
thickness_stddev_mm=article["thickness_stddev_mm"],
|
||||||
|
product_width_m=article["width_m"],
|
||||||
|
area_weight_g_m2=article["area_weight_g_m2"],
|
||||||
|
)
|
||||||
|
return project_machine_max_product_length_result(result, article=article)
|
||||||
|
|
||||||
|
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 +114,45 @@ 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],
|
||||||
|
*,
|
||||||
|
article: dict[str, Any] | None = None,
|
||||||
|
) -> 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"]
|
||||||
|
projection = {
|
||||||
|
"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"],
|
||||||
|
}
|
||||||
|
if article is not None:
|
||||||
|
projection["article"] = {
|
||||||
|
"number": article["number"],
|
||||||
|
"name": article["name"],
|
||||||
|
}
|
||||||
|
return projection
|
||||||
|
|
||||||
|
|
||||||
def calculate_material_weight_result(
|
def calculate_material_weight_result(
|
||||||
|
|||||||
@@ -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,26 +141,49 @@ 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):
|
||||||
response = self.client.post(
|
payload = {
|
||||||
"/api/calculations/machine-max-product-length",
|
|
||||||
json={
|
|
||||||
"machine": "K7",
|
"machine": "K7",
|
||||||
"core_diameter_mm": 133,
|
"core_diameter_mm": 133,
|
||||||
"thickness_mm": 4.311611,
|
"thickness_mm": 4.311611,
|
||||||
"thickness_stddev_mm": 0.249999,
|
"thickness_stddev_mm": 0.249999,
|
||||||
"product_width_m": 6,
|
"product_width_m": 6,
|
||||||
"area_weight_g_m2": 495.395833,
|
"area_weight_g_m2": 495.395833,
|
||||||
},
|
}
|
||||||
|
response = self.client.post(
|
||||||
|
"/api/calculations/machine-max-product-length",
|
||||||
|
json=payload,
|
||||||
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"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_machine_maximum_length_endpoint_does_not_accept_mcp_article_convenience(self):
|
||||||
|
response = self.client.post(
|
||||||
|
"/api/calculations/machine-max-product-length",
|
||||||
|
json={
|
||||||
|
"article_number": "218500",
|
||||||
|
"machine": "K7",
|
||||||
|
"core_diameter_mm": 133,
|
||||||
|
},
|
||||||
|
headers=self.headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.get_json(), {
|
||||||
|
"status": "invalid_parameter",
|
||||||
|
"invalid": [{
|
||||||
|
"field": "request",
|
||||||
|
"message": "unknown request fields: article_number",
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
|
||||||
def test_machine_maximum_length_endpoint_preserves_domain_outcomes(self):
|
def test_machine_maximum_length_endpoint_preserves_domain_outcomes(self):
|
||||||
incompatible = self.client.post(
|
incompatible = self.client.post(
|
||||||
"/api/calculations/machine-max-product-length",
|
"/api/calculations/machine-max-product-length",
|
||||||
|
|||||||
+152
-9
@@ -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,149 @@ 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,
|
||||||
}
|
}
|
||||||
self.assertEqual(
|
domain_result = calculate_machine_max_product_length(**arguments)
|
||||||
calculate_machine_max_product_length_result(**arguments),
|
domain_before_projection = copy.deepcopy(domain_result)
|
||||||
calculate_machine_max_product_length(**arguments),
|
|
||||||
|
self.assertNotIn(
|
||||||
|
"article_number",
|
||||||
|
inspect.signature(calculate_machine_max_product_length).parameters,
|
||||||
)
|
)
|
||||||
|
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(
|
||||||
|
calculate_machine_max_product_length_result(**arguments), result
|
||||||
|
)
|
||||||
|
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_resolves_exact_article_compactly(self):
|
||||||
|
result = calculate_machine_max_product_length_result(
|
||||||
|
article_number="218500",
|
||||||
|
machine="K7",
|
||||||
|
core_diameter_mm=133.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(result["status"], "success")
|
||||||
|
self.assertEqual(result["article"], {
|
||||||
|
"number": "218500",
|
||||||
|
"name": "Stex AS 501, 6,00 x 75 m",
|
||||||
|
})
|
||||||
|
self.assertNotIn("thickness_mm", result)
|
||||||
|
self.assertNotIn("final_feasibility", result)
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_machine_maximum_length_adapter_article_lookup_is_exact_and_core_is_caller_supplied(self):
|
||||||
|
missing = calculate_machine_max_product_length_result(
|
||||||
|
article_number="does-not-exist",
|
||||||
|
machine="K7",
|
||||||
|
core_diameter_mm=133.0,
|
||||||
|
)
|
||||||
|
incompatible_core = calculate_machine_max_product_length_result(
|
||||||
|
article_number="218500",
|
||||||
|
machine="K7",
|
||||||
|
core_diameter_mm=194.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(missing, {
|
||||||
|
"status": "article_not_found",
|
||||||
|
"article_number": "does-not-exist",
|
||||||
|
})
|
||||||
|
self.assertEqual(incompatible_core["status"], "incompatible")
|
||||||
|
self.assertEqual(
|
||||||
|
incompatible_core["failed_compatibility_constraints"],
|
||||||
|
["allowed_core_diameters_mm"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_machine_maximum_length_adapter_rejects_mixed_article_and_material_inputs(self):
|
||||||
|
result = calculate_machine_max_product_length_result(
|
||||||
|
article_number="218500",
|
||||||
|
machine="K7",
|
||||||
|
core_diameter_mm=133.0,
|
||||||
|
thickness_mm=4.311611,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(result, {
|
||||||
|
"status": "invalid_parameter",
|
||||||
|
"invalid": [{
|
||||||
|
"field": "article_number",
|
||||||
|
"message": (
|
||||||
|
"cannot be combined with explicit material properties: "
|
||||||
|
"thickness_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 = {
|
||||||
@@ -273,17 +414,19 @@ class McpAdapterTests(unittest.TestCase):
|
|||||||
self.assertIn("Missing configured V1 roll inputs", feasibility_tool.description)
|
self.assertIn("Missing configured V1 roll inputs", feasibility_tool.description)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
maximum_tool.parameters["required"],
|
maximum_tool.parameters["required"],
|
||||||
["machine", "core_diameter_mm", "thickness_mm"],
|
["machine", "core_diameter_mm"],
|
||||||
)
|
)
|
||||||
|
self.assertIn("article_number", maximum_tool.parameters["properties"])
|
||||||
self.assertIn("product_width_m", maximum_tool.parameters["properties"])
|
self.assertIn("product_width_m", maximum_tool.parameters["properties"])
|
||||||
self.assertIn("area_weight_g_m2", maximum_tool.parameters["properties"])
|
self.assertIn("area_weight_g_m2", maximum_tool.parameters["properties"])
|
||||||
for text in (
|
for text in (
|
||||||
"Resolve the article first with search_articles/get_article",
|
"exact article_number",
|
||||||
|
"do not call get_article merely to copy properties",
|
||||||
|
"Never mix article_number with material properties",
|
||||||
|
"For a product name, use search_articles",
|
||||||
|
"if candidates are multiple, ask the user to select an article number",
|
||||||
"Do not supply or invent machine limits",
|
"Do not supply or invent machine limits",
|
||||||
"rearrange roll or weight formulas",
|
|
||||||
"compare candidate limits yourself",
|
|
||||||
"nominal and conservative/no-warning production maxima",
|
"nominal and conservative/no-warning production maxima",
|
||||||
"incompatibility rather than substituting another core or width",
|
|
||||||
):
|
):
|
||||||
self.assertIn(text, maximum_tool.description)
|
self.assertIn(text, maximum_tool.description)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
Reference in New Issue
Block a user