coreason-manifest 0.4.0__py3-none-any.whl → 0.5.0__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.
@@ -7,15 +7,23 @@ dictionaries, normalizing the data, and converting it into Pydantic models.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import inspect
10
11
  from pathlib import Path
11
- from typing import Any, Union
12
+ from typing import Any, Callable, Dict, List, Union, get_type_hints
13
+
14
+ try:
15
+ from typing import get_args, get_origin
16
+ except ImportError: # pragma: no cover
17
+ # For Python < 3.8, though project requires 3.12+
18
+ from typing_extensions import get_args, get_origin # type: ignore
12
19
 
13
20
  import aiofiles
14
21
  import yaml
15
- from pydantic import ValidationError
22
+ from coreason_identity import UserContext
23
+ from pydantic import ValidationError, create_model
16
24
 
17
25
  from coreason_manifest.errors import ManifestSyntaxError
18
- from coreason_manifest.models import AgentDefinition
26
+ from coreason_manifest.models import AgentDefinition, AgentInterface
19
27
 
20
28
 
21
29
  class ManifestLoader:
@@ -156,3 +164,108 @@ class ManifestLoader:
156
164
  while version and version[0] in ("v", "V"):
157
165
  version = version[1:]
158
166
  data["metadata"]["version"] = version
167
+
168
+ @staticmethod
169
+ def inspect_function(func: Callable[..., Any]) -> AgentInterface:
170
+ """Generates an AgentInterface from a Python function.
171
+
172
+ Scans the function signature. If `user_context` (by name) or UserContext (by type)
173
+ is found, it is marked as injected and excluded from the public schema.
174
+
175
+ Args:
176
+ func: The function to inspect.
177
+
178
+ Returns:
179
+ AgentInterface: The generated interface definition.
180
+
181
+ Raises:
182
+ ManifestSyntaxError: If forbidden arguments are found.
183
+ """
184
+ sig = inspect.signature(func)
185
+ try:
186
+ type_hints = get_type_hints(func)
187
+ except Exception:
188
+ # Fallback if get_type_hints fails (e.g. forward refs issues)
189
+ type_hints = {}
190
+
191
+ field_definitions: Dict[str, Any] = {}
192
+ injected: List[str] = []
193
+
194
+ for param_name, param in sig.parameters.items():
195
+ if param_name in ("self", "cls"):
196
+ continue
197
+
198
+ # Determine type annotation
199
+ annotation = type_hints.get(param_name, param.annotation)
200
+ if annotation is inspect.Parameter.empty:
201
+ annotation = Any
202
+
203
+ # Check for forbidden arguments
204
+ if param_name in ("api_key", "token"):
205
+ raise ManifestSyntaxError(f"Function argument '{param_name}' is forbidden. Use UserContext for auth.")
206
+
207
+ # Check for injection
208
+ is_injected = False
209
+ if param_name == "user_context":
210
+ is_injected = True
211
+ else:
212
+ # Check direct type
213
+ if annotation is UserContext:
214
+ is_injected = True
215
+ else:
216
+ # Check for Optional[UserContext], Annotated[UserContext, ...], Union[UserContext, ...]
217
+ origin = get_origin(annotation)
218
+ args = get_args(annotation)
219
+ if origin is not None:
220
+ # Recursively check if UserContext is in args (handles Optional/Union)
221
+ # or if this is Annotated (UserContext might be the first arg)
222
+ # We do a shallow check on args.
223
+ for arg in args:
224
+ if arg is UserContext:
225
+ is_injected = True
226
+ break
227
+
228
+ if is_injected:
229
+ if "user_context" not in injected:
230
+ injected.append("user_context")
231
+ continue
232
+
233
+ # Prepare for Pydantic model creation
234
+ default = param.default
235
+ if default is inspect.Parameter.empty:
236
+ default = ...
237
+
238
+ field_definitions[param_name] = (annotation, default)
239
+
240
+ # Create dynamic model to generate JSON Schema for inputs
241
+ # We assume strict mode or similar is handled by the consumer, here we just describe it.
242
+ try:
243
+ InputsModel = create_model("Inputs", **field_definitions)
244
+ inputs_schema = InputsModel.model_json_schema()
245
+ except Exception as e:
246
+ raise ManifestSyntaxError(f"Failed to generate schema from function signature: {e}") from e
247
+
248
+ # Handle return type for outputs
249
+ return_annotation = type_hints.get("return", sig.return_annotation)
250
+ outputs_schema = {}
251
+ if (
252
+ return_annotation is not inspect.Parameter.empty
253
+ and return_annotation is not None
254
+ and return_annotation is not type(None)
255
+ ):
256
+ try:
257
+ # If return annotation is a Pydantic model, use its schema
258
+ if hasattr(return_annotation, "model_json_schema"):
259
+ outputs_schema = return_annotation.model_json_schema()
260
+ else:
261
+ # Wrap in a model
262
+ OutputsModel = create_model("Outputs", result=(return_annotation, ...))
263
+ outputs_schema = OutputsModel.model_json_schema()
264
+ except Exception:
265
+ pass
266
+
267
+ return AgentInterface(
268
+ inputs=inputs_schema,
269
+ outputs=outputs_schema,
270
+ injected_params=injected,
271
+ )
@@ -20,6 +20,7 @@ from pydantic import (
20
20
  Field,
21
21
  PlainSerializer,
22
22
  field_validator,
23
+ model_validator,
23
24
  )
24
25
  from typing_extensions import Annotated
25
26
 
@@ -86,6 +87,7 @@ class AgentMetadata(BaseModel):
86
87
  name: str = Field(..., min_length=1, description="Name of the Agent.")
87
88
  author: str = Field(..., min_length=1, description="Author of the Agent.")
88
89
  created_at: datetime = Field(..., description="Creation timestamp (ISO 8601).")
90
+ requires_auth: bool = Field(default=False, description="Whether the agent requires user authentication.")
89
91
 
90
92
 
91
93
  class AgentInterface(BaseModel):
@@ -100,6 +102,7 @@ class AgentInterface(BaseModel):
100
102
 
101
103
  inputs: ImmutableDict = Field(..., description="Typed arguments the agent accepts (JSON Schema).")
102
104
  outputs: ImmutableDict = Field(..., description="Typed structure of the result.")
105
+ injected_params: List[str] = Field(default_factory=list, description="List of parameters injected by the system.")
103
106
 
104
107
 
105
108
  class Step(BaseModel):
@@ -218,3 +221,11 @@ class AgentDefinition(BaseModel):
218
221
  pattern=r"^[a-fA-F0-9]{64}$",
219
222
  description="SHA256 hash of the source code.",
220
223
  )
224
+
225
+ @model_validator(mode="after")
226
+ def validate_auth_requirements(self) -> AgentDefinition:
227
+ """Validate that agents requiring auth have user_context injected."""
228
+ if self.metadata.requires_auth:
229
+ if "user_context" not in self.interface.injected_params:
230
+ raise ValueError("Agent requires authentication but 'user_context' is not an injected parameter.")
231
+ return self
@@ -41,6 +41,14 @@
41
41
  "description": "Typed structure of the result.",
42
42
  "title": "Outputs",
43
43
  "type": "object"
44
+ },
45
+ "injected_params": {
46
+ "description": "List of parameters injected by the system.",
47
+ "items": {
48
+ "type": "string"
49
+ },
50
+ "title": "Injected Params",
51
+ "type": "array"
44
52
  }
45
53
  },
46
54
  "required": [
@@ -83,6 +91,12 @@
83
91
  "format": "date-time",
84
92
  "title": "Created At",
85
93
  "type": "string"
94
+ },
95
+ "requires_auth": {
96
+ "default": false,
97
+ "description": "Whether the agent requires user authentication.",
98
+ "title": "Requires Auth",
99
+ "type": "boolean"
86
100
  }
87
101
  },
88
102
  "required": [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreason_manifest
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: This package is the definitive source of truth. If it isn't in the manifest, it doesn't exist. If it violates the manifest, it doesn't run.
5
5
  License: # The Prosperity Public License 3.0.0
6
6
 
@@ -68,9 +68,10 @@ Classifier: License :: Other/Proprietary License
68
68
  Classifier: Programming Language :: Python :: 3.12
69
69
  Classifier: Operating System :: OS Independent
70
70
  Requires-Dist: aiofiles (>=23.2.1,<24.0.0)
71
- Requires-Dist: anyio (>=4.3.0,<5.0.0)
71
+ Requires-Dist: anyio (>=4.12.1,<5.0.0)
72
+ Requires-Dist: coreason-identity (>=0.1.0,<0.2.0)
72
73
  Requires-Dist: fastapi (>=0.111.0,<0.112.0)
73
- Requires-Dist: httpx (>=0.27.0,<0.28.0)
74
+ Requires-Dist: httpx (>=0.28.1,<0.29.0)
74
75
  Requires-Dist: jsonschema (>=4.25.1,<5.0.0)
75
76
  Requires-Dist: loguru (>=0.7.2,<0.8.0)
76
77
  Requires-Dist: pydantic (>=2.12.5,<3.0.0)
@@ -99,6 +100,7 @@ The definitive source of truth for CoReason-AI Asset definitions. "The Blueprint
99
100
  * **Open Agent Specification (OAS) Validation:** Parses and validates agent definitions against a strict schema.
100
101
  * **Compliance Enforcement:** Uses Open Policy Agent (OPA) / Rego to enforce complex business rules and allowlists.
101
102
  * **Integrity Verification:** Calculates and verifies SHA256 hashes of the agent's source code to prevent tampering.
103
+ * **Automatic Schema Generation:** Inspects Python functions to generate Agent Interfaces, automatically handling `UserContext` injection.
102
104
  * **Dependency Pinning:** Enforces strict version pinning for all library dependencies.
103
105
  * **Trusted Bill of Materials (TBOM):** Validates libraries against an approved list.
104
106
  * **Compliance Microservice:** Can be run as a standalone API server (Service C) for centralized validation.
@@ -2,20 +2,20 @@ coreason_manifest/__init__.py,sha256=R07xUHw9RWdWQPh7OWbpMT8kkzkEWfEI5hizu_5UNck
2
2
  coreason_manifest/engine.py,sha256=ENAkRuGFd1uY_5u4EYEsTZUMrqw5HgijKtkF9lk-E-c,8199
3
3
  coreason_manifest/errors.py,sha256=CKFWSucncoPLYUJcsuXtLIfneyr2d89oHRTiWPT4psU,1437
4
4
  coreason_manifest/integrity.py,sha256=CMBaa2A8DpC9hDSO-aj-YOzss6if1CajaeLEmTC703c,5344
5
- coreason_manifest/loader.py,sha256=cc1iV-yKfH3gyPaB-P_7AaTaPYkgj1NoDsJz4PReTgo,5479
5
+ coreason_manifest/loader.py,sha256=WET8HAnSw4dgon_zdiZaaU1dIUrsIQPYZzxlp0crTYo,10027
6
6
  coreason_manifest/main.py,sha256=YQO98w2wxkfNs-DpSLmiDTs9DtUeGsC48GEfbElXVPk,357
7
- coreason_manifest/models.py,sha256=pJIuEtCiiXcxxi2zRwken4ph2GFa66YHnOqHyySR5sU,6828
7
+ coreason_manifest/models.py,sha256=EuaE1Wl5T8f_eI9vF1VFp5hd0y9C7E52zAjB52EulKw,7497
8
8
  coreason_manifest/policies/compliance.rego,sha256=-drMuno6YkGOXKjvdLWawCZWK8iUxY7OcXpoXa_9Oyo,3035
9
9
  coreason_manifest/policies/tbom.json,sha256=rSn4V44_IdFqCC86J3Jc31qQKTV4J5BdmyO0CI4iOu0,167
10
10
  coreason_manifest/policy.py,sha256=vvEivq5HSjv-bMSrZ5VMM3eTomYFp39SBtuFajG9RU4,5009
11
11
  coreason_manifest/schemas/__init__.py,sha256=9TMs6jWKCIewAKkj-u0tq9c6N_-0CU6b5s9q6MTS6v4,17
12
- coreason_manifest/schemas/agent.schema.json,sha256=wdNYmpyCUEKR1UgMmRV4ZSQOm7xvnmSg5lvhQ3JKvSg,6368
12
+ coreason_manifest/schemas/agent.schema.json,sha256=VigUX3ltX7YaW9P7n0DNYGNOf8C6VCuXNy71u3LB9i4,6812
13
13
  coreason_manifest/server.py,sha256=kHrReqoEx4sL5k5GQN7yNwLgvfnOl1kZf-dQehseuO4,4251
14
14
  coreason_manifest/utils/__init__.py,sha256=Q9gXiBtX3mD9GTu4z0JDHSHkbXC-MRHagrOaOmRH_1Q,435
15
15
  coreason_manifest/utils/logger.py,sha256=A7E6Hd_Jk1XDUajNEJQl-WtUv9M2LT76b4_TsbxnILw,1227
16
16
  coreason_manifest/validator.py,sha256=v33EzKroRwLEjZeuRRpTB7cqB38op3DV2EZpZhJ80a0,2240
17
- coreason_manifest-0.4.0.dist-info/METADATA,sha256=aQdNN6nn8Fi4g_eSj17mKrtf_SkO4l0XembzP85TXlE,7229
18
- coreason_manifest-0.4.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
19
- coreason_manifest-0.4.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
20
- coreason_manifest-0.4.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
21
- coreason_manifest-0.4.0.dist-info/RECORD,,
17
+ coreason_manifest-0.5.0.dist-info/METADATA,sha256=GZU4pj0fFgv6x80fJDa71xfQTYCHH1BPnZnSdjv3kBo,7421
18
+ coreason_manifest-0.5.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
19
+ coreason_manifest-0.5.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
20
+ coreason_manifest-0.5.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
21
+ coreason_manifest-0.5.0.dist-info/RECORD,,