cortexhub 0.1.7__tar.gz → 0.1.9__tar.gz

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 (40) hide show
  1. {cortexhub-0.1.7 → cortexhub-0.1.9}/PKG-INFO +5 -1
  2. {cortexhub-0.1.7 → cortexhub-0.1.9}/README.md +3 -0
  3. {cortexhub-0.1.7 → cortexhub-0.1.9}/pyproject.toml +2 -1
  4. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/claude_agents.py +9 -0
  5. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/langgraph.py +2 -0
  6. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/openai_agents.py +2 -0
  7. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/client.py +79 -19
  8. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/pipeline.py +8 -0
  9. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/version.py +1 -1
  10. {cortexhub-0.1.7 → cortexhub-0.1.9}/.gitignore +0 -0
  11. {cortexhub-0.1.7 → cortexhub-0.1.9}/LICENSE +0 -0
  12. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/__init__.py +0 -0
  13. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/__init__.py +0 -0
  14. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/base.py +0 -0
  15. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/adapters/crewai.py +0 -0
  16. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/audit/__init__.py +0 -0
  17. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/audit/events.py +0 -0
  18. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/auto_protect.py +0 -0
  19. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/backend/__init__.py +0 -0
  20. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/backend/client.py +0 -0
  21. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/config.py +0 -0
  22. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/context/__init__.py +0 -0
  23. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/context/enricher.py +0 -0
  24. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/errors.py +0 -0
  25. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/frameworks.py +0 -0
  26. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/guardrails/__init__.py +0 -0
  27. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/guardrails/injection.py +0 -0
  28. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/guardrails/pii.py +0 -0
  29. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/guardrails/secrets.py +0 -0
  30. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/interceptors/__init__.py +0 -0
  31. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/interceptors/llm.py +0 -0
  32. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/interceptors/mcp.py +0 -0
  33. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/__init__.py +0 -0
  34. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/effects.py +0 -0
  35. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/evaluator.py +0 -0
  36. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/loader.py +0 -0
  37. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/models.py +0 -0
  38. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/policy/sync.py +0 -0
  39. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/telemetry/__init__.py +0 -0
  40. {cortexhub-0.1.7 → cortexhub-0.1.9}/src/cortexhub/telemetry/otel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexhub
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: CortexHub Python SDK - Policy-as-Code for AI Agents
5
5
  Project-URL: Homepage, https://cortexhub.ai
6
6
  Project-URL: Documentation, https://docs.cortexhub.ai
@@ -22,6 +22,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Python: <3.14,>=3.10
23
23
  Requires-Dist: cedarpy>=4.0.0
24
24
  Requires-Dist: detect-secrets>=1.5.0
25
+ Requires-Dist: en-core-web-sm>=3.8.0
25
26
  Requires-Dist: httpx>=0.28.0
26
27
  Requires-Dist: opentelemetry-api>=1.20.0
27
28
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20.0
@@ -81,6 +82,9 @@ pip install cortexhub[claude-agents] # Claude Agent SDK
81
82
  pip install cortexhub[all]
82
83
  ```
83
84
 
85
+ Note: The SDK ships with the spaCy `en-core-web-sm` model for Presidio PII detection,
86
+ so it should not download the model at runtime.
87
+
84
88
  ## Quick Start
85
89
 
86
90
  ```python
@@ -18,6 +18,9 @@ pip install cortexhub[claude-agents] # Claude Agent SDK
18
18
  pip install cortexhub[all]
19
19
  ```
20
20
 
21
+ Note: The SDK ships with the spaCy `en-core-web-sm` model for Presidio PII detection,
22
+ so it should not download the model at runtime.
23
+
21
24
  ## Quick Start
22
25
 
23
26
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cortexhub"
3
- version = "0.1.7"
3
+ version = "0.1.9"
4
4
  description = "CortexHub Python SDK - Policy-as-Code for AI Agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10,<3.14"
@@ -37,6 +37,7 @@ dependencies = [
37
37
  "presidio-analyzer>=2.2.360",
38
38
  "presidio-anonymizer>=2.2.360",
39
39
  "detect-secrets>=1.5.0",
40
+ "en-core-web-sm>=3.8.0",
40
41
  # Utils
41
42
  "python-dotenv>=1.0.0",
42
43
  # Required by detect-secrets at runtime
@@ -116,6 +116,14 @@ class ClaudeAgentsAdapter(ToolAdapter):
116
116
  original_handler = original_decorated.handler
117
117
  tool_name = original_decorated.name
118
118
  tool_description = original_decorated.description
119
+ parameters_schema = None
120
+ if isinstance(input_schema, dict):
121
+ parameters_schema = input_schema
122
+ elif hasattr(input_schema, "model_json_schema"):
123
+ try:
124
+ parameters_schema = input_schema.model_json_schema()
125
+ except Exception:
126
+ parameters_schema = None
119
127
 
120
128
  @wraps(original_handler)
121
129
  async def governed_handler(args: dict[str, Any]) -> dict[str, Any]:
@@ -124,6 +132,7 @@ class ClaudeAgentsAdapter(ToolAdapter):
124
132
  "name": tool_name,
125
133
  "description": tool_description,
126
134
  "framework": "claude_agents",
135
+ "parameters_schema": parameters_schema,
127
136
  }
128
137
 
129
138
  governed_fn = govern_execution(
@@ -93,6 +93,7 @@ class LangGraphAdapter(ToolAdapter):
93
93
  """Governed tool invocation."""
94
94
  tool_name = getattr(self, "name", "unknown_tool")
95
95
  tool_description = getattr(self, "description", None)
96
+ parameters_schema = adapter._extract_parameters_schema(self)
96
97
 
97
98
  # Extract args - preserve structure without rewriting
98
99
  if isinstance(input, dict):
@@ -109,6 +110,7 @@ class LangGraphAdapter(ToolAdapter):
109
110
  "name": tool_name,
110
111
  "description": tool_description,
111
112
  "framework": "langgraph",
113
+ "parameters_schema": parameters_schema,
112
114
  }
113
115
  governed_fn = govern_execution(
114
116
  tool_fn=lambda *a, **kw: tool_original_invoke(
@@ -102,6 +102,7 @@ class OpenAIAgentsAdapter(ToolAdapter):
102
102
  original_invoke = tool.on_invoke_tool
103
103
  tool_name = tool.name
104
104
  tool_description = tool.description
105
+ parameters_schema = tool.params_json_schema or tool.strict_json_schema
105
106
 
106
107
  async def governed_invoke(ctx, input_json: str) -> Any:
107
108
  """Governed tool invocation."""
@@ -114,6 +115,7 @@ class OpenAIAgentsAdapter(ToolAdapter):
114
115
  "name": tool_name,
115
116
  "description": tool_description,
116
117
  "framework": "openai_agents",
118
+ "parameters_schema": parameters_schema,
117
119
  }
118
120
 
119
121
  # Create governed function
@@ -439,6 +439,7 @@ class CortexHub:
439
439
  framework: str,
440
440
  call_original: Callable[[], Any],
441
441
  tool_description: str | None = None,
442
+ parameters_schema: dict[str, Any] | None = None,
442
443
  principal: Principal | dict[str, str] | None = None,
443
444
  resource_type: str = "Tool",
444
445
  ) -> Any:
@@ -475,6 +476,10 @@ class CortexHub:
475
476
  PolicyViolationError: If policy denies
476
477
  ApprovalDeniedError: If approval denied
477
478
  """
479
+ expected_types = self._extract_expected_arg_types(parameters_schema)
480
+
481
+ expected_types = self._extract_expected_arg_types(parameters_schema)
482
+
478
483
  # Step 1: Build request (structured, NOT flattened)
479
484
  policy_args = self._sanitize_policy_args(args)
480
485
  request = self.build_request(
@@ -515,6 +520,21 @@ class CortexHub:
515
520
  span.set_attribute("cortexhub.raw.args", json.dumps(args, default=str))
516
521
 
517
522
  try:
523
+ if expected_types and self.enforce and self.evaluator:
524
+ mismatches = self._validate_arg_types(args, expected_types)
525
+ if mismatches:
526
+ message = f"Tool argument type mismatch: {'; '.join(mismatches)}"
527
+ span.add_event(
528
+ "policy.decision",
529
+ attributes={
530
+ "decision.effect": "deny",
531
+ "decision.policy_id": "",
532
+ "decision.reasoning": message,
533
+ "decision.policy_name": "type_mismatch",
534
+ },
535
+ )
536
+ span.set_status(Status(StatusCode.ERROR, message))
537
+ raise PolicyViolationError(message, reasoning=message)
518
538
  # Step 4: Policy evaluation (enforcement mode only)
519
539
  if self.enforce and self.evaluator:
520
540
  if os.getenv("CORTEXHUB_DEBUG_POLICY", "").lower() in ("1", "true", "yes"):
@@ -636,6 +656,7 @@ class CortexHub:
636
656
  framework: str,
637
657
  call_original: Callable[[], Any],
638
658
  tool_description: str | None = None,
659
+ parameters_schema: dict[str, Any] | None = None,
639
660
  principal: Principal | dict[str, str] | None = None,
640
661
  resource_type: str = "Tool",
641
662
  ) -> Any:
@@ -684,6 +705,21 @@ class CortexHub:
684
705
  span.set_attribute("cortexhub.raw.args", json.dumps(args, default=str))
685
706
 
686
707
  try:
708
+ if expected_types and self.enforce and self.evaluator:
709
+ mismatches = self._validate_arg_types(args, expected_types)
710
+ if mismatches:
711
+ message = f"Tool argument type mismatch: {'; '.join(mismatches)}"
712
+ span.add_event(
713
+ "policy.decision",
714
+ attributes={
715
+ "decision.effect": "deny",
716
+ "decision.policy_id": "",
717
+ "decision.reasoning": message,
718
+ "decision.policy_name": "type_mismatch",
719
+ },
720
+ )
721
+ span.set_status(Status(StatusCode.ERROR, message))
722
+ raise PolicyViolationError(message, reasoning=message)
687
723
  # Step 4: Policy evaluation (enforcement mode only)
688
724
  if self.enforce and self.evaluator:
689
725
  decision = self.evaluator.evaluate(request)
@@ -1316,6 +1352,49 @@ class CortexHub:
1316
1352
  return "string"
1317
1353
  return "unknown"
1318
1354
 
1355
+ def _extract_expected_arg_types(self, parameters_schema: dict[str, Any] | None) -> dict[str, str]:
1356
+ if not parameters_schema or not isinstance(parameters_schema, dict):
1357
+ return {}
1358
+ properties = parameters_schema.get("properties")
1359
+ if not isinstance(properties, dict):
1360
+ return {}
1361
+ expected: dict[str, str] = {}
1362
+ for name, schema in properties.items():
1363
+ if not isinstance(schema, dict):
1364
+ continue
1365
+ raw_type = schema.get("type")
1366
+ if isinstance(raw_type, list):
1367
+ raw_type = next((t for t in raw_type if t != "null"), None)
1368
+ if not isinstance(raw_type, str):
1369
+ continue
1370
+ normalized = raw_type.lower()
1371
+ if normalized == "integer":
1372
+ normalized = "number"
1373
+ if normalized in ("number", "string", "boolean"):
1374
+ expected[str(name)] = normalized
1375
+ return expected
1376
+
1377
+ def _validate_arg_types(self, args: dict[str, Any], expected_types: dict[str, str]) -> list[str]:
1378
+ if not isinstance(args, dict):
1379
+ return []
1380
+ mismatches: list[str] = []
1381
+ for name, expected in expected_types.items():
1382
+ if name not in args:
1383
+ continue
1384
+ value = args.get(name)
1385
+ if value is None:
1386
+ continue
1387
+ if expected == "number":
1388
+ if not isinstance(value, int) or isinstance(value, bool):
1389
+ mismatches.append(f"{name} expected number but got {type(value).__name__}")
1390
+ elif expected == "string":
1391
+ if not isinstance(value, str):
1392
+ mismatches.append(f"{name} expected string but got {type(value).__name__}")
1393
+ elif expected == "boolean":
1394
+ if not isinstance(value, bool):
1395
+ mismatches.append(f"{name} expected boolean but got {type(value).__name__}")
1396
+ return mismatches
1397
+
1319
1398
  def trace_llm_call(
1320
1399
  self,
1321
1400
  model: str,
@@ -1785,25 +1864,6 @@ class CortexHub:
1785
1864
  if not isinstance(cleaned_args, dict):
1786
1865
  return {}
1787
1866
 
1788
- if "amount" in cleaned_args:
1789
- from decimal import Decimal, InvalidOperation
1790
-
1791
- amount = cleaned_args.get("amount")
1792
- dec_amount: Decimal | None = None
1793
- if isinstance(amount, int):
1794
- dec_amount = Decimal(amount)
1795
- elif isinstance(amount, str):
1796
- try:
1797
- dec_amount = Decimal(amount)
1798
- except InvalidOperation:
1799
- dec_amount = None
1800
- if dec_amount is not None:
1801
- quantized = dec_amount.quantize(Decimal("0.01"))
1802
- if quantized == dec_amount:
1803
- cleaned_args["amount_cents"] = int(
1804
- (quantized * 100).to_integral_value()
1805
- )
1806
-
1807
1867
  return cleaned_args
1808
1868
 
1809
1869
  def _summarize_policy_args(self, args: dict[str, Any]) -> dict[str, Any]:
@@ -46,6 +46,12 @@ def govern_execution(
46
46
  framework = tool_metadata.get("framework", "unknown")
47
47
  model = tool_metadata.get("model", "unknown")
48
48
  prompt = tool_metadata.get("prompt")
49
+ parameters_schema = (
50
+ tool_metadata.get("parameters_schema")
51
+ or tool_metadata.get("params_json_schema")
52
+ or tool_metadata.get("input_schema")
53
+ or tool_metadata.get("strict_json_schema")
54
+ )
49
55
  call_original = tool_metadata.get("call_original")
50
56
  if call_original is None:
51
57
  call_original = tool_fn
@@ -78,6 +84,7 @@ def govern_execution(
78
84
  args=kwargs, # Use kwargs for structured arguments
79
85
  framework=framework,
80
86
  call_original=lambda: tool_fn(*args, **kwargs),
87
+ parameters_schema=parameters_schema,
81
88
  )
82
89
  return async_wrapper
83
90
  else:
@@ -88,5 +95,6 @@ def govern_execution(
88
95
  args=kwargs, # Use kwargs for structured arguments
89
96
  framework=framework,
90
97
  call_original=lambda: tool_fn(*args, **kwargs),
98
+ parameters_schema=parameters_schema,
91
99
  )
92
100
  return sync_wrapper
@@ -1,3 +1,3 @@
1
1
  """Version information for CortexHub SDK."""
2
2
 
3
- __version__ = "0.1.5"
3
+ __version__ = "0.1.9"
File without changes
File without changes