frenum 0.1.0__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.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ *.egg
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ .venv/
frenum-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Terry Li
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
frenum-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: frenum
3
+ Version: 0.1.0
4
+ Summary: Deterministic, config-driven guardrails for LLM agent tool calls
5
+ Project-URL: Homepage, https://github.com/terry-li-hm/frenum
6
+ Project-URL: Repository, https://github.com/terry-li-hm/frenum
7
+ Author-email: Terry Li <terry.li.hm@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,ai,audit,compliance,guardrails,llm,tool-calls
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Security
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.10
22
+ Provides-Extra: all
23
+ Requires-Dist: langchain-core>=0.3; extra == 'all'
24
+ Requires-Dist: langgraph>=0.2; extra == 'all'
25
+ Requires-Dist: pyyaml>=6.0; extra == 'all'
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8.0; extra == 'dev'
28
+ Requires-Dist: pyyaml>=6.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.8; extra == 'dev'
30
+ Provides-Extra: langgraph
31
+ Requires-Dist: langchain-core>=0.3; extra == 'langgraph'
32
+ Requires-Dist: langgraph>=0.2; extra == 'langgraph'
33
+ Provides-Extra: yaml
34
+ Requires-Dist: pyyaml>=6.0; extra == 'yaml'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # frenum
38
+
39
+ > Deterministic, config-driven guardrails for LLM agent tool calls.
40
+ > Zero-LLM enforcement. Compliance audit trail built in.
41
+
42
+ Named after the Latin word for *bridle* or *restraint*. The rein that keeps your LLM agent in check.
43
+
44
+ ## Why
45
+
46
+ LLM agents can now call tools autonomously — execute SQL, send emails, make API calls. Existing guardrail frameworks either use LLM calls in the enforcement path (non-deterministic, slow, expensive) or require writing Python code that compliance teams can't review.
47
+
48
+ Frenum is different:
49
+
50
+ - **YAML config** — rules live in config files that compliance teams can review, version, and audit without reading Python
51
+ - **Zero-LLM enforcement** — every decision is deterministic and reproducible. No AI in the guardrail path.
52
+ - **Compliance-first** — OPA-inspired audit trail as a first-class feature, not an afterthought
53
+ - **Framework-agnostic** — works standalone or with LangGraph, CrewAI, or any agent framework
54
+
55
+ | Feature | Frenum | NeMo Guardrails | LangChain Middleware | OpenAI Agents SDK |
56
+ |---|---|---|---|---|
57
+ | Config-driven (YAML) | Yes | Colang DSL | Python code | Python decorators |
58
+ | Zero-LLM enforcement | Yes | No (LLM in loop) | Yes | Yes |
59
+ | Audit trail | Built-in | Logging only | No | No |
60
+ | Framework-agnostic | Yes | LangChain | LangChain only | OpenAI only |
61
+ | Dependencies | Zero (stdlib) | Heavy | LangChain | OpenAI SDK |
62
+
63
+ ## Quick Start
64
+
65
+ ```bash
66
+ pip install frenum[yaml]
67
+ ```
68
+
69
+ ```python
70
+ from frenum import Engine, ToolCall
71
+
72
+ engine = Engine.from_yaml("rules.yaml")
73
+
74
+ # Evaluate a tool call
75
+ result = engine.evaluate(
76
+ ToolCall(name="execute_sql", args={"query": "DROP TABLE users"})
77
+ )
78
+ print(result.decision) # Decision.BLOCK
79
+ print(result.reason) # "Pattern matched in field 'query': DROP TABLE"
80
+ ```
81
+
82
+ ## YAML Config
83
+
84
+ ```yaml
85
+ version: "1.0"
86
+ policy_version: "1.0.0"
87
+
88
+ rules:
89
+ # Block dangerous SQL patterns
90
+ - name: block_sql_injection
91
+ type: regex_block
92
+ applies_to: ["execute_sql", "run_query"]
93
+ params:
94
+ fields: ["query"]
95
+ patterns:
96
+ - "(?i)(DROP|DELETE|TRUNCATE)\\s+TABLE"
97
+
98
+ # Require confirmation IDs on sensitive operations
99
+ - name: require_confirmation
100
+ type: regex_require
101
+ applies_to: ["send_email", "transfer_funds"]
102
+ params:
103
+ fields: ["confirmation_id"]
104
+ pattern: "^CONF-[A-Z0-9]{8}$"
105
+
106
+ # Scan all tool calls for PII leakage
107
+ - name: detect_pii
108
+ type: pii_detect
109
+ applies_to: ["*"]
110
+ params:
111
+ detectors: [email, phone_intl, hk_id, credit_card]
112
+ action: block
113
+
114
+ # Role-based tool access
115
+ - name: tool_entitlement
116
+ type: entitlement
117
+ applies_to: ["*"]
118
+ params:
119
+ roles:
120
+ analyst: ["search", "get_data", "summarize"]
121
+ admin: ["*"]
122
+ default: block
123
+ ```
124
+
125
+ ### Rule Types
126
+
127
+ | Type | Purpose | Key Params |
128
+ |---|---|---|
129
+ | `regex_block` | Block if field matches pattern | `fields`, `patterns` |
130
+ | `regex_require` | Block if required field is missing/invalid | `fields`, `pattern` |
131
+ | `pii_detect` | Scan args for PII (email, phone, HKID, etc.) | `detectors`, `action` |
132
+ | `entitlement` | Role-based tool access control | `roles`, `default` |
133
+
134
+ ## Audit Trail
135
+
136
+ Every evaluation produces a structured JSON record (JSONL format, OPA-inspired):
137
+
138
+ ```python
139
+ from frenum import AuditLogger, Engine
140
+
141
+ logger = AuditLogger("audit.jsonl")
142
+ engine = Engine.from_yaml("rules.yaml", audit_logger=logger.log)
143
+ ```
144
+
145
+ Each record includes: `decision_id`, `timestamp`, `policy_version`, `tool_name`, `tool_args` (redacted), `decision`, `rules_evaluated`, `blocking_rule`, `human_override`, `trace_id`.
146
+
147
+ ## Audit Reports
148
+
149
+ Generate compliance-ready summaries from audit logs:
150
+
151
+ ```python
152
+ from frenum import AuditReporter
153
+
154
+ reporter = AuditReporter("audit.jsonl")
155
+ report = reporter.generate()
156
+ print(report.to_text())
157
+ ```
158
+
159
+ ```
160
+ ========================================
161
+ OBEX AUDIT REPORT
162
+ ========================================
163
+ Period: 2026-02-28 10:00 to 2026-02-28 16:00
164
+ Policy versions: 1.0.0
165
+
166
+ Total evaluations: 500
167
+ Allow: 450 (90.0%)
168
+ Block: 50 (10.0%)
169
+
170
+ Top blocked tools:
171
+ 1. execute_sql — 25 blocks
172
+ 2. send_email — 15 blocks
173
+
174
+ Top triggered rules:
175
+ 1. block_sql_injection — 20 triggers
176
+ 2. detect_pii — 18 triggers
177
+
178
+ Human override rate: 4.0% (2 of 50 blocks overridden)
179
+ ========================================
180
+ ```
181
+
182
+ ## LangGraph Integration
183
+
184
+ ```bash
185
+ pip install frenum[langgraph]
186
+ ```
187
+
188
+ ```python
189
+ from langgraph.prebuilt import ToolNode
190
+ from frenum import Engine
191
+ from frenum.adapters.langgraph import guarded_tool_node
192
+
193
+ tools = [search, calculator]
194
+ engine = Engine.from_yaml("rules.yaml")
195
+ safe_tools = guarded_tool_node(ToolNode(tools), engine)
196
+
197
+ builder.add_node("tools", safe_tools)
198
+ ```
199
+
200
+ Blocked tool calls return a `ToolMessage` with the block reason — the LLM sees why its call was rejected and can adjust.
201
+
202
+ ## Programmatic Use (No YAML)
203
+
204
+ ```python
205
+ from frenum import Engine, ToolCall
206
+ from frenum._types import RuleConfig, RuleType
207
+
208
+ engine = Engine(rules=[
209
+ RuleConfig(
210
+ name="block_drops",
211
+ rule_type=RuleType.REGEX_BLOCK,
212
+ params={"fields": ["query"], "patterns": [r"(?i)DROP\s+TABLE"]},
213
+ applies_to=["execute_sql"],
214
+ ),
215
+ ])
216
+
217
+ result = engine.evaluate(ToolCall(name="execute_sql", args={"query": "SELECT 1"}))
218
+ assert result.decision.value == "allow"
219
+ ```
220
+
221
+ Zero dependencies — the core engine runs on stdlib alone.
222
+
223
+ ## Design Philosophy
224
+
225
+ - **Hooks > prompts** for mechanical rules. If a rule is regex-matchable, enforce it in code, not in the system prompt.
226
+ - **Fail closed.** If a rule errors, the tool call is blocked.
227
+ - **No LLM in the enforcement path.** Every decision is deterministic and reproducible.
228
+ - **Config is reviewable.** Compliance teams review YAML, not Python.
229
+ - **Audit schema inspired by OPA.** Decision logs follow established policy-engine conventions.
230
+
231
+ ## License
232
+
233
+ MIT
frenum-0.1.0/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # frenum
2
+
3
+ > Deterministic, config-driven guardrails for LLM agent tool calls.
4
+ > Zero-LLM enforcement. Compliance audit trail built in.
5
+
6
+ Named after the Latin word for *bridle* or *restraint*. The rein that keeps your LLM agent in check.
7
+
8
+ ## Why
9
+
10
+ LLM agents can now call tools autonomously — execute SQL, send emails, make API calls. Existing guardrail frameworks either use LLM calls in the enforcement path (non-deterministic, slow, expensive) or require writing Python code that compliance teams can't review.
11
+
12
+ Frenum is different:
13
+
14
+ - **YAML config** — rules live in config files that compliance teams can review, version, and audit without reading Python
15
+ - **Zero-LLM enforcement** — every decision is deterministic and reproducible. No AI in the guardrail path.
16
+ - **Compliance-first** — OPA-inspired audit trail as a first-class feature, not an afterthought
17
+ - **Framework-agnostic** — works standalone or with LangGraph, CrewAI, or any agent framework
18
+
19
+ | Feature | Frenum | NeMo Guardrails | LangChain Middleware | OpenAI Agents SDK |
20
+ |---|---|---|---|---|
21
+ | Config-driven (YAML) | Yes | Colang DSL | Python code | Python decorators |
22
+ | Zero-LLM enforcement | Yes | No (LLM in loop) | Yes | Yes |
23
+ | Audit trail | Built-in | Logging only | No | No |
24
+ | Framework-agnostic | Yes | LangChain | LangChain only | OpenAI only |
25
+ | Dependencies | Zero (stdlib) | Heavy | LangChain | OpenAI SDK |
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ pip install frenum[yaml]
31
+ ```
32
+
33
+ ```python
34
+ from frenum import Engine, ToolCall
35
+
36
+ engine = Engine.from_yaml("rules.yaml")
37
+
38
+ # Evaluate a tool call
39
+ result = engine.evaluate(
40
+ ToolCall(name="execute_sql", args={"query": "DROP TABLE users"})
41
+ )
42
+ print(result.decision) # Decision.BLOCK
43
+ print(result.reason) # "Pattern matched in field 'query': DROP TABLE"
44
+ ```
45
+
46
+ ## YAML Config
47
+
48
+ ```yaml
49
+ version: "1.0"
50
+ policy_version: "1.0.0"
51
+
52
+ rules:
53
+ # Block dangerous SQL patterns
54
+ - name: block_sql_injection
55
+ type: regex_block
56
+ applies_to: ["execute_sql", "run_query"]
57
+ params:
58
+ fields: ["query"]
59
+ patterns:
60
+ - "(?i)(DROP|DELETE|TRUNCATE)\\s+TABLE"
61
+
62
+ # Require confirmation IDs on sensitive operations
63
+ - name: require_confirmation
64
+ type: regex_require
65
+ applies_to: ["send_email", "transfer_funds"]
66
+ params:
67
+ fields: ["confirmation_id"]
68
+ pattern: "^CONF-[A-Z0-9]{8}$"
69
+
70
+ # Scan all tool calls for PII leakage
71
+ - name: detect_pii
72
+ type: pii_detect
73
+ applies_to: ["*"]
74
+ params:
75
+ detectors: [email, phone_intl, hk_id, credit_card]
76
+ action: block
77
+
78
+ # Role-based tool access
79
+ - name: tool_entitlement
80
+ type: entitlement
81
+ applies_to: ["*"]
82
+ params:
83
+ roles:
84
+ analyst: ["search", "get_data", "summarize"]
85
+ admin: ["*"]
86
+ default: block
87
+ ```
88
+
89
+ ### Rule Types
90
+
91
+ | Type | Purpose | Key Params |
92
+ |---|---|---|
93
+ | `regex_block` | Block if field matches pattern | `fields`, `patterns` |
94
+ | `regex_require` | Block if required field is missing/invalid | `fields`, `pattern` |
95
+ | `pii_detect` | Scan args for PII (email, phone, HKID, etc.) | `detectors`, `action` |
96
+ | `entitlement` | Role-based tool access control | `roles`, `default` |
97
+
98
+ ## Audit Trail
99
+
100
+ Every evaluation produces a structured JSON record (JSONL format, OPA-inspired):
101
+
102
+ ```python
103
+ from frenum import AuditLogger, Engine
104
+
105
+ logger = AuditLogger("audit.jsonl")
106
+ engine = Engine.from_yaml("rules.yaml", audit_logger=logger.log)
107
+ ```
108
+
109
+ Each record includes: `decision_id`, `timestamp`, `policy_version`, `tool_name`, `tool_args` (redacted), `decision`, `rules_evaluated`, `blocking_rule`, `human_override`, `trace_id`.
110
+
111
+ ## Audit Reports
112
+
113
+ Generate compliance-ready summaries from audit logs:
114
+
115
+ ```python
116
+ from frenum import AuditReporter
117
+
118
+ reporter = AuditReporter("audit.jsonl")
119
+ report = reporter.generate()
120
+ print(report.to_text())
121
+ ```
122
+
123
+ ```
124
+ ========================================
125
+ OBEX AUDIT REPORT
126
+ ========================================
127
+ Period: 2026-02-28 10:00 to 2026-02-28 16:00
128
+ Policy versions: 1.0.0
129
+
130
+ Total evaluations: 500
131
+ Allow: 450 (90.0%)
132
+ Block: 50 (10.0%)
133
+
134
+ Top blocked tools:
135
+ 1. execute_sql — 25 blocks
136
+ 2. send_email — 15 blocks
137
+
138
+ Top triggered rules:
139
+ 1. block_sql_injection — 20 triggers
140
+ 2. detect_pii — 18 triggers
141
+
142
+ Human override rate: 4.0% (2 of 50 blocks overridden)
143
+ ========================================
144
+ ```
145
+
146
+ ## LangGraph Integration
147
+
148
+ ```bash
149
+ pip install frenum[langgraph]
150
+ ```
151
+
152
+ ```python
153
+ from langgraph.prebuilt import ToolNode
154
+ from frenum import Engine
155
+ from frenum.adapters.langgraph import guarded_tool_node
156
+
157
+ tools = [search, calculator]
158
+ engine = Engine.from_yaml("rules.yaml")
159
+ safe_tools = guarded_tool_node(ToolNode(tools), engine)
160
+
161
+ builder.add_node("tools", safe_tools)
162
+ ```
163
+
164
+ Blocked tool calls return a `ToolMessage` with the block reason — the LLM sees why its call was rejected and can adjust.
165
+
166
+ ## Programmatic Use (No YAML)
167
+
168
+ ```python
169
+ from frenum import Engine, ToolCall
170
+ from frenum._types import RuleConfig, RuleType
171
+
172
+ engine = Engine(rules=[
173
+ RuleConfig(
174
+ name="block_drops",
175
+ rule_type=RuleType.REGEX_BLOCK,
176
+ params={"fields": ["query"], "patterns": [r"(?i)DROP\s+TABLE"]},
177
+ applies_to=["execute_sql"],
178
+ ),
179
+ ])
180
+
181
+ result = engine.evaluate(ToolCall(name="execute_sql", args={"query": "SELECT 1"}))
182
+ assert result.decision.value == "allow"
183
+ ```
184
+
185
+ Zero dependencies — the core engine runs on stdlib alone.
186
+
187
+ ## Design Philosophy
188
+
189
+ - **Hooks > prompts** for mechanical rules. If a rule is regex-matchable, enforce it in code, not in the system prompt.
190
+ - **Fail closed.** If a rule errors, the tool call is blocked.
191
+ - **No LLM in the enforcement path.** Every decision is deterministic and reproducible.
192
+ - **Config is reviewable.** Compliance teams review YAML, not Python.
193
+ - **Audit schema inspired by OPA.** Decision logs follow established policy-engine conventions.
194
+
195
+ ## License
196
+
197
+ MIT
@@ -0,0 +1,51 @@
1
+ """Basic usage of frenum — evaluate tool calls against YAML rules."""
2
+
3
+ from frenum import AuditLogger, AuditReporter, Engine, ToolCall
4
+
5
+ # Load rules from YAML config
6
+ engine = Engine.from_yaml("examples/config.yaml", audit_logger=AuditLogger("audit.jsonl").log)
7
+
8
+ # A safe query by an authorized user — passes through
9
+ safe = ToolCall(
10
+ name="execute_sql",
11
+ args={"query": "SELECT * FROM users WHERE id = 1"},
12
+ metadata={"role": "operator"},
13
+ )
14
+ result = engine.evaluate(safe)
15
+ print(f"Safe query: {result.decision.value}") # allow
16
+
17
+ # A dangerous query — blocked by regex rule
18
+ dangerous = ToolCall(
19
+ name="execute_sql",
20
+ args={"query": "DROP TABLE users"},
21
+ metadata={"role": "operator"},
22
+ )
23
+ result = engine.evaluate(dangerous)
24
+ print(f"DROP TABLE: {result.decision.value}") # block
25
+ print(f" Reason: {result.reason}")
26
+
27
+ # PII in arguments — blocked
28
+ pii = ToolCall(
29
+ name="search",
30
+ args={"query": "Contact alice@example.com"},
31
+ metadata={"role": "analyst"},
32
+ )
33
+ result = engine.evaluate(pii)
34
+ print(f"PII leak: {result.decision.value}") # block
35
+ print(f" Reason: {result.reason}")
36
+
37
+ # Unauthorized tool access — analyst can't run SQL
38
+ unauthorized = ToolCall(
39
+ name="execute_sql",
40
+ args={"query": "SELECT 1"},
41
+ metadata={"role": "analyst"},
42
+ )
43
+ result = engine.evaluate(unauthorized)
44
+ print(f"Unauthed: {result.decision.value}") # block
45
+ print(f" Reason: {result.reason}")
46
+
47
+ # Generate audit report
48
+ print()
49
+ reporter = AuditReporter("audit.jsonl")
50
+ report = reporter.generate()
51
+ print(report.to_text())
@@ -0,0 +1,48 @@
1
+ # limen configuration example
2
+ # YAML rules that compliance teams can review without reading Python.
3
+
4
+ version: "1.0"
5
+ policy_version: "1.0.0"
6
+
7
+ rules:
8
+ # Block dangerous SQL patterns
9
+ - name: block_sql_injection
10
+ type: regex_block
11
+ applies_to: ["execute_sql", "run_query"]
12
+ params:
13
+ fields: ["query"]
14
+ patterns:
15
+ - "(?i)(DROP|DELETE|TRUNCATE)\\s+TABLE"
16
+ - "(?i);\\s*--"
17
+ - "(?i)UNION\\s+SELECT"
18
+
19
+ # Require confirmation IDs on sensitive operations
20
+ - name: require_confirmation
21
+ type: regex_require
22
+ applies_to: ["send_email", "transfer_funds"]
23
+ params:
24
+ fields: ["confirmation_id"]
25
+ pattern: "^CONF-[A-Z0-9]{8}$"
26
+
27
+ # Scan all tool outputs for PII
28
+ - name: detect_pii
29
+ type: pii_detect
30
+ applies_to: ["*"]
31
+ params:
32
+ detectors:
33
+ - email
34
+ - phone_intl
35
+ - hk_id
36
+ - credit_card
37
+ action: block
38
+
39
+ # Role-based tool access
40
+ - name: tool_entitlement
41
+ type: entitlement
42
+ applies_to: ["*"]
43
+ params:
44
+ roles:
45
+ analyst: ["search", "get_data", "summarize"]
46
+ admin: ["*"]
47
+ operator: ["search", "get_data", "execute_sql"]
48
+ default: block
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "frenum"
3
+ version = "0.1.0"
4
+ description = "Deterministic, config-driven guardrails for LLM agent tool calls"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.10"
8
+ authors = [{ name = "Terry Li", email = "terry.li.hm@gmail.com" }]
9
+ keywords = ["ai", "agent", "guardrails", "llm", "tool-calls", "compliance", "audit"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Security",
20
+ "Topic :: Software Development :: Libraries",
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/terry-li-hm/frenum"
25
+ Repository = "https://github.com/terry-li-hm/frenum"
26
+
27
+ [project.optional-dependencies]
28
+ yaml = ["pyyaml>=6.0"]
29
+ langgraph = ["langgraph>=0.2", "langchain-core>=0.3"]
30
+ all = ["frenum[yaml,langgraph]"]
31
+ dev = ["pytest>=8.0", "ruff>=0.8", "pyyaml>=6.0"]
32
+
33
+ [build-system]
34
+ requires = ["hatchling"]
35
+ build-backend = "hatchling.build"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/frenum"]
39
+
40
+ [tool.ruff]
41
+ target-version = "py310"
42
+ line-length = 100
43
+
44
+ [tool.ruff.lint]
45
+ select = ["E", "F", "I", "UP", "B"]
46
+
47
+ [tool.pytest.ini_options]
48
+ testpaths = ["tests"]
49
+ pythonpath = ["src"]
@@ -0,0 +1,25 @@
1
+ """frenum — Deterministic, config-driven guardrails for LLM agent tool calls.
2
+
3
+ Named after the Latin word for 'bridle' or 'restraint'.
4
+ The rein that keeps your LLM agent in check.
5
+ """
6
+
7
+ from frenum._types import Decision, EvalResult, RuleConfig, RuleResult, ToolCall, ToolCallBlocked
8
+ from frenum.audit import AuditLogger
9
+ from frenum.engine import Engine
10
+ from frenum.reporter import AuditReporter, Report
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ __all__ = [
15
+ "AuditLogger",
16
+ "AuditReporter",
17
+ "Decision",
18
+ "Engine",
19
+ "EvalResult",
20
+ "Report",
21
+ "RuleConfig",
22
+ "RuleResult",
23
+ "ToolCall",
24
+ "ToolCallBlocked",
25
+ ]