engineering-argument-language 3.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. eal/__init__.py +3 -0
  2. eal/abstractions.py +144 -0
  3. eal/acquisition_coordination.py +80 -0
  4. eal/api_load_methods.py +77 -0
  5. eal/aspic.py +478 -0
  6. eal/aspic_compiler.py +445 -0
  7. eal/aspic_export.py +309 -0
  8. eal/builtin_methods.py +115 -0
  9. eal/catalogue.py +384 -0
  10. eal/cli.py +204 -0
  11. eal/client_transports.py +69 -0
  12. eal/collection_identity.py +28 -0
  13. eal/collection_scheduler.py +72 -0
  14. eal/command_process.py +118 -0
  15. eal/command_supervisor.py +112 -0
  16. eal/composition.py +531 -0
  17. eal/credentials.py +33 -0
  18. eal/dialectic.py +321 -0
  19. eal/discovery.py +127 -0
  20. eal/evaluator.py +619 -0
  21. eal/expressions.py +404 -0
  22. eal/extensions.py +38 -0
  23. eal/formatter.py +156 -0
  24. eal/generated/EALLexer.py +434 -0
  25. eal/generated/EALParser.py +6559 -0
  26. eal/generated/EALVisitor.py +393 -0
  27. eal/generated/__init__.py +1 -0
  28. eal/host.py +179 -0
  29. eal/host_redaction.py +51 -0
  30. eal/knowledge.py +96 -0
  31. eal/limits.py +88 -0
  32. eal/mcp_guard.py +56 -0
  33. eal/methods.py +463 -0
  34. eal/model.py +266 -0
  35. eal/model_context.py +55 -0
  36. eal/modes.py +121 -0
  37. eal/observation_reuse.py +128 -0
  38. eal/operation_contracts.py +25 -0
  39. eal/operations.py +155 -0
  40. eal/packets.py +604 -0
  41. eal/parser.py +407 -0
  42. eal/planning.py +136 -0
  43. eal/propositions.py +231 -0
  44. eal/reachability.py +85 -0
  45. eal/reasoning/__init__.py +25 -0
  46. eal/reasoning/abductive.py +45 -0
  47. eal/reasoning/analogical.py +41 -0
  48. eal/reasoning/causal.py +46 -0
  49. eal/reasoning/counterfactual.py +64 -0
  50. eal/reasoning/deductive.py +72 -0
  51. eal/reasoning/inductive.py +29 -0
  52. eal/reasoning/strategy.py +18 -0
  53. eal/reasoning/structured.py +11 -0
  54. eal/reasoning/temporal.py +47 -0
  55. eal/reasoning/validation.py +90 -0
  56. eal/registered_assessment.py +181 -0
  57. eal/runtime.py +454 -0
  58. eal/sampled_negative.py +131 -0
  59. eal/scope_transfer.py +59 -0
  60. eal/semantics.py +656 -0
  61. eal/server.py +79 -0
  62. eal/server_auth.py +37 -0
  63. eal/server_settings.py +258 -0
  64. eal/source_printer.py +129 -0
  65. eal/store.py +272 -0
  66. eal/tool_acquisition.py +383 -0
  67. engineering_argument_language-3.2.3.dist-info/METADATA +188 -0
  68. engineering_argument_language-3.2.3.dist-info/RECORD +72 -0
  69. engineering_argument_language-3.2.3.dist-info/WHEEL +5 -0
  70. engineering_argument_language-3.2.3.dist-info/entry_points.txt +4 -0
  71. engineering_argument_language-3.2.3.dist-info/licenses/LICENSE +24 -0
  72. engineering_argument_language-3.2.3.dist-info/top_level.txt +1 -0
eal/operations.py ADDED
@@ -0,0 +1,155 @@
1
+ """Bind the shared MCP operation catalogue to one EAL application service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Collection
6
+ from inspect import Parameter, signature
7
+ from typing import Any
8
+
9
+ from .runtime import ReasoningService
10
+ from .catalogue import WorkspaceKnowledgeCatalogue
11
+ from .registered_assessment import RegisteredAssessmentHost
12
+ from .operation_contracts import OPERATION_FIELDS
13
+
14
+
15
+ def create_operations(service: ReasoningService, *,
16
+ known_entries: Collection[str] | None = None,
17
+ exposure: str | None = None) -> dict[str, Callable[..., dict[str, Any]]]:
18
+ """Expose generic language tools or a selected registered-source catalogue."""
19
+ if (known_entries is not None and
20
+ (isinstance(known_entries, (str, bytes)) or
21
+ not isinstance(known_entries, Collection))):
22
+ raise ValueError("Known entries must be a bounded collection of registered IDs")
23
+ selected_known = tuple(known_entries or ())
24
+ if (len(selected_known) > 128
25
+ or any(not isinstance(identifier, str) or not identifier for identifier in selected_known)
26
+ or len(set(selected_known)) != len(selected_known)):
27
+ raise ValueError("Known entries must contain up to 128 unique registered IDs")
28
+ selected_exposure = exposure or ("registered" if selected_known else "operator")
29
+ if selected_exposure not in {"operator", "registered"}:
30
+ raise ValueError("exposure must be operator or registered")
31
+ if selected_exposure == "registered" and not selected_known:
32
+ raise ValueError("Registered exposure requires at least one known entry")
33
+ if selected_exposure == "operator" and selected_known:
34
+ raise ValueError("Operator exposure conflicts with known entries")
35
+ catalogue = WorkspaceKnowledgeCatalogue(service) if selected_known else None
36
+ for identifier in selected_known:
37
+ catalogue.get(identifier)
38
+ known = RegisteredAssessmentHost(service, catalogue) if catalogue else None
39
+
40
+ operations: dict[str, Callable[..., dict[str, Any]]] = {}
41
+
42
+ def register(handler):
43
+ name = handler.__name__
44
+ short_name = name.removeprefix("eal_")
45
+ required, optional = OPERATION_FIELDS[short_name]
46
+ parameters = signature(handler).parameters
47
+ actual_required = {key for key, value in parameters.items() if value.default is Parameter.empty}
48
+ if actual_required != required or set(parameters) != required | optional:
49
+ raise ValueError(f"Operation signature disagrees with its catalogue: {name}")
50
+ operations[name] = handler
51
+ return handler
52
+
53
+ if selected_known:
54
+ def source_summary(entry: dict[str, Any]) -> dict[str, Any]:
55
+ """Expose discoverable claim metadata without the registered context or source."""
56
+ return {"entry_id": entry["entry_id"], "path": entry["path"],
57
+ "label": entry["label"], "source_digest": entry["source_digest"],
58
+ "claims": entry["claims"], "claim_metadata": entry["claim_metadata"]}
59
+
60
+ @register
61
+ def eal_sources(limit: int = 50, offset: int = 0) -> dict[str, Any]:
62
+ """List developer-registered EAL sources and their selectable claims."""
63
+ if type(limit) is not int or not 1 <= limit <= 100:
64
+ raise ValueError("limit must be an integer from 1 through 100")
65
+ if type(offset) is not int or not 0 <= offset <= 10_000:
66
+ raise ValueError("offset must be an integer from 0 through 10000")
67
+ visible = sorted((catalogue.get(identifier) for identifier in selected_known),
68
+ key=lambda item: item["path"])
69
+ return {"sources": [source_summary(item) for item in visible[offset:offset + limit]],
70
+ "limit": limit, "offset": offset}
71
+
72
+ @register
73
+ def eal_find_claims(query: str | None = None, claim: str | None = None,
74
+ limit: int = 50) -> dict[str, Any]:
75
+ """Find registered claim metadata; use an exact entry ID and claim to assess."""
76
+ if type(limit) is not int or not 1 <= limit <= 100:
77
+ raise ValueError("limit must be an integer from 1 through 100")
78
+ matches = []
79
+ for identifier in selected_known:
80
+ entry = catalogue.get(identifier)
81
+ matches.extend(catalogue.find(query, claim=claim, path=entry["path"], limit=1))
82
+ return {"matches": [source_summary(item) for item in
83
+ sorted(matches, key=lambda item: item["path"])[:limit]]}
84
+
85
+ @register
86
+ def eal_assess_known(entry_id: str, claim: str) -> dict[str, Any]:
87
+ """Reuse compatible observations or collect missing evidence for a registered claim."""
88
+ if entry_id not in selected_known:
89
+ raise ValueError("Registered source is not exposed by this server")
90
+ return known.assess(entry_id, claim)
91
+
92
+ if not selected_known:
93
+
94
+ @register
95
+ def eal_describe() -> dict[str, Any]:
96
+ """Discover supported language versions, syntax, method contracts and interpretation limits."""
97
+ return service.describe()
98
+
99
+ @register
100
+ def eal_format(source: str) -> dict[str, Any]:
101
+ """Return canonical checked EAL source; changed source bytes require new collection bindings."""
102
+ return service.format(source)
103
+
104
+ @register
105
+ def eal_validate(source: str) -> dict[str, Any]:
106
+ """Parse EAL and check names, types and dependency structure without running tools."""
107
+ return service.validate(source)
108
+
109
+ @register
110
+ def eal_plan(source: str, claim: str) -> dict[str, Any]:
111
+ """Plan all evidence for a claim, including alternative supports and objections."""
112
+ return service.plan(source, claim)
113
+
114
+ @register
115
+ def eal_collect(source: str, context: dict, evidence_ids: list[str] | None = None) -> dict[str, Any]:
116
+ """Run the configured collectors for selected evidence; return collection_id."""
117
+ return service.collect(source, context, evidence_ids)
118
+
119
+ @register
120
+ def eal_collect_claim(source: str, context: dict, claim: str) -> dict[str, Any]:
121
+ """Collect the complete declared evidence closure for a claim."""
122
+ return service.collect_claim(source, context, claim)
123
+
124
+ @register
125
+ def eal_reason(source: str, context: dict, collection_id: str | None = None, now: str | None = None) -> dict[str, Any]:
126
+ """Evaluate declared support and objections at an explicit or current UTC time."""
127
+ return service.reason(source, context, collection_id, now)
128
+
129
+ @register
130
+ def eal_compile_aspic(source: str, context: dict, collection_id: str,
131
+ goal: str, now: str | None = None, semantics: str = 'grounded',
132
+ query_mode: str = 'sceptical', preference: str | None = None) -> dict[str, Any]:
133
+ """Compile checked EAL routes to an opt-in bounded ASPIC+ snapshot with a source map."""
134
+ return service.compile_aspic(source, context, collection_id, goal, now,
135
+ semantics=semantics, query_mode=query_mode, preference=preference)
136
+
137
+ @register
138
+ def eal_explain(assessment_id: str, claim: str | None = None) -> dict[str, Any]:
139
+ """Retrieve a persisted reasoning result and its dependency explanations."""
140
+ return service.explain(assessment_id, claim)
141
+
142
+ @register
143
+ def eal_packet(assessment_id: str, claim: str | None = None) -> dict[str, Any]:
144
+ """Return compact claim results with a reference to the full explanation."""
145
+ return service.packet(assessment_id, claim)
146
+
147
+ @register
148
+ def eal_grounded(arguments: list[str], attacks: list[list[str]]) -> dict[str, Any]:
149
+ """Compute Dung grounded semantics for an explicitly supplied finite attack graph."""
150
+ from .dialectic import solve_grounded
151
+ from .limits import using_limits
152
+ with using_limits(service.limits):
153
+ return solve_grounded(arguments, attacks)
154
+
155
+ return operations