admina-framework 0.9.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.
Files changed (102) hide show
  1. admina/__init__.py +34 -0
  2. admina/cli/__init__.py +14 -0
  3. admina/cli/commands/__init__.py +14 -0
  4. admina/cli/main.py +1522 -0
  5. admina/cli/templates/admina.yaml.j2 +77 -0
  6. admina/cli/templates/docker-compose.yml.j2 +254 -0
  7. admina/cli/templates/env.j2 +10 -0
  8. admina/cli/templates/main.py.j2 +95 -0
  9. admina/cli/templates/plugin.py.j2 +145 -0
  10. admina/cli/templates/plugin_pyproject.toml.j2 +15 -0
  11. admina/cli/templates/plugin_readme.md.j2 +27 -0
  12. admina/cli/templates/plugin_test.py.j2 +48 -0
  13. admina/core/__init__.py +14 -0
  14. admina/core/config.py +497 -0
  15. admina/core/event_bus.py +112 -0
  16. admina/core/secrets.py +257 -0
  17. admina/core/types.py +146 -0
  18. admina/dashboard/__init__.py +8 -0
  19. admina/dashboard/static/heimdall.png +0 -0
  20. admina/dashboard/static/index.html +1045 -0
  21. admina/dashboard/static/vendor/alpinejs.min.js +5 -0
  22. admina/domains/__init__.py +14 -0
  23. admina/domains/agent_security/__init__.py +41 -0
  24. admina/domains/agent_security/firewall.py +634 -0
  25. admina/domains/agent_security/loop_breaker.py +176 -0
  26. admina/domains/ai_infra/__init__.py +79 -0
  27. admina/domains/ai_infra/llm_engine.py +477 -0
  28. admina/domains/ai_infra/rag.py +817 -0
  29. admina/domains/ai_infra/webui.py +292 -0
  30. admina/domains/compliance/__init__.py +109 -0
  31. admina/domains/compliance/cross_regulation.py +314 -0
  32. admina/domains/compliance/eu_ai_act.py +367 -0
  33. admina/domains/compliance/forensic.py +380 -0
  34. admina/domains/compliance/gdpr.py +331 -0
  35. admina/domains/compliance/nis2.py +258 -0
  36. admina/domains/compliance/oisg.py +658 -0
  37. admina/domains/compliance/otel.py +101 -0
  38. admina/domains/data_sovereignty/__init__.py +42 -0
  39. admina/domains/data_sovereignty/classification.py +102 -0
  40. admina/domains/data_sovereignty/pii.py +260 -0
  41. admina/domains/data_sovereignty/residency.py +121 -0
  42. admina/integrations/__init__.py +14 -0
  43. admina/integrations/_engines.py +63 -0
  44. admina/integrations/cheshirecat/__init__.py +13 -0
  45. admina/integrations/cheshirecat/admina-plugin/admina_governance.py +207 -0
  46. admina/integrations/crewai/__init__.py +13 -0
  47. admina/integrations/crewai/callbacks.py +347 -0
  48. admina/integrations/langchain/__init__.py +13 -0
  49. admina/integrations/langchain/callbacks.py +341 -0
  50. admina/integrations/n8n/__init__.py +14 -0
  51. admina/integrations/openclaw/__init__.py +14 -0
  52. admina/plugins/__init__.py +49 -0
  53. admina/plugins/base.py +633 -0
  54. admina/plugins/builtin/__init__.py +14 -0
  55. admina/plugins/builtin/adapters/__init__.py +14 -0
  56. admina/plugins/builtin/adapters/ollama.py +120 -0
  57. admina/plugins/builtin/adapters/openai.py +138 -0
  58. admina/plugins/builtin/alerts/__init__.py +14 -0
  59. admina/plugins/builtin/alerts/log.py +66 -0
  60. admina/plugins/builtin/alerts/webhook.py +102 -0
  61. admina/plugins/builtin/auth/__init__.py +14 -0
  62. admina/plugins/builtin/auth/apikey.py +138 -0
  63. admina/plugins/builtin/compliance/__init__.py +14 -0
  64. admina/plugins/builtin/compliance/eu_ai_act.py +202 -0
  65. admina/plugins/builtin/connectors/__init__.py +14 -0
  66. admina/plugins/builtin/connectors/chromadb.py +137 -0
  67. admina/plugins/builtin/connectors/filesystem.py +111 -0
  68. admina/plugins/builtin/forensic/__init__.py +14 -0
  69. admina/plugins/builtin/forensic/filesystem.py +163 -0
  70. admina/plugins/builtin/forensic/minio.py +180 -0
  71. admina/plugins/builtin/guards/__init__.py +0 -0
  72. admina/plugins/builtin/guards/guardrailsai_guard.py +172 -0
  73. admina/plugins/builtin/pii/__init__.py +14 -0
  74. admina/plugins/builtin/pii/spacy_regex.py +160 -0
  75. admina/plugins/builtin/transports/__init__.py +14 -0
  76. admina/plugins/builtin/transports/http_rest.py +97 -0
  77. admina/plugins/builtin/transports/mcp.py +173 -0
  78. admina/plugins/registry.py +356 -0
  79. admina/proxy/__init__.py +15 -0
  80. admina/proxy/api/__init__.py +17 -0
  81. admina/proxy/api/dashboard.py +925 -0
  82. admina/proxy/api/integration.py +153 -0
  83. admina/proxy/config.py +214 -0
  84. admina/proxy/engine_bridge.py +306 -0
  85. admina/proxy/governance.py +232 -0
  86. admina/proxy/main.py +1484 -0
  87. admina/proxy/multi_upstream.py +156 -0
  88. admina/proxy/state.py +97 -0
  89. admina/py.typed +0 -0
  90. admina/sdk/__init__.py +34 -0
  91. admina/sdk/_compat.py +43 -0
  92. admina/sdk/compliance_kit.py +359 -0
  93. admina/sdk/governed_agent.py +391 -0
  94. admina/sdk/governed_data.py +434 -0
  95. admina/sdk/governed_model.py +241 -0
  96. admina_framework-0.9.0.dist-info/METADATA +575 -0
  97. admina_framework-0.9.0.dist-info/RECORD +102 -0
  98. admina_framework-0.9.0.dist-info/WHEEL +5 -0
  99. admina_framework-0.9.0.dist-info/entry_points.txt +2 -0
  100. admina_framework-0.9.0.dist-info/licenses/LICENSE +191 -0
  101. admina_framework-0.9.0.dist-info/licenses/NOTICE +16 -0
  102. admina_framework-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,292 @@
1
+ # Copyright © 2025–2026 Stefano Noferi & Admina contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Admina — Web UI module.
16
+
17
+ Open WebUI container configuration, preconfigured Ollama connection,
18
+ and optional multi-user authentication (built-in, OIDC, LDAP).
19
+
20
+ This module produces Docker Compose configuration fragments and
21
+ structured settings — the actual container lifecycle is managed by
22
+ the CLI ``admina dev`` command.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import logging
28
+ from dataclasses import dataclass, field
29
+ from enum import Enum
30
+ from typing import Any
31
+
32
+ logger = logging.getLogger("admina.ai_infra.webui")
33
+
34
+
35
+ # ── Auth configuration ───────────────────────────────────────
36
+
37
+
38
+ class AuthMode(str, Enum):
39
+ """Supported authentication modes for Open WebUI."""
40
+
41
+ BUILTIN = "builtin"
42
+ OIDC = "oidc"
43
+ LDAP = "ldap"
44
+ NONE = "none"
45
+
46
+
47
+ @dataclass
48
+ class OIDCConfig:
49
+ """OpenID Connect authentication settings.
50
+
51
+ Args:
52
+ provider_url: OIDC discovery URL (e.g. ``https://accounts.google.com``).
53
+ client_id: OAuth2 client ID.
54
+ client_secret: OAuth2 client secret.
55
+ scopes: Requested scopes.
56
+ """
57
+
58
+ provider_url: str = ""
59
+ client_id: str = ""
60
+ client_secret: str = ""
61
+ scopes: str = "openid email profile"
62
+
63
+ def to_env(self) -> dict[str, str]:
64
+ """Return environment variables for Open WebUI OIDC."""
65
+ if not self.provider_url or not self.client_id:
66
+ return {}
67
+ return {
68
+ "OAUTH_PROVIDER_URL": self.provider_url,
69
+ "OAUTH_CLIENT_ID": self.client_id,
70
+ "OAUTH_CLIENT_SECRET": self.client_secret,
71
+ "OAUTH_SCOPES": self.scopes,
72
+ "ENABLE_OAUTH_SIGNUP": "true",
73
+ }
74
+
75
+
76
+ @dataclass
77
+ class LDAPConfig:
78
+ """LDAP authentication settings.
79
+
80
+ Args:
81
+ host: LDAP server host.
82
+ port: LDAP server port.
83
+ base_dn: Base distinguished name for search.
84
+ bind_dn: Bind DN for LDAP lookups.
85
+ bind_password: Bind password.
86
+ """
87
+
88
+ host: str = ""
89
+ port: int = 389
90
+ base_dn: str = ""
91
+ bind_dn: str = ""
92
+ bind_password: str = ""
93
+ use_tls: bool = False
94
+
95
+ def to_env(self) -> dict[str, str]:
96
+ """Return environment variables for Open WebUI LDAP."""
97
+ if not self.host or not self.base_dn:
98
+ return {}
99
+ return {
100
+ "LDAP_HOST": self.host,
101
+ "LDAP_PORT": str(self.port),
102
+ "LDAP_BASE_DN": self.base_dn,
103
+ "LDAP_BIND_DN": self.bind_dn,
104
+ "LDAP_BIND_PASSWORD": self.bind_password,
105
+ "LDAP_USE_TLS": str(self.use_tls).lower(),
106
+ }
107
+
108
+
109
+ @dataclass
110
+ class AuthConfig:
111
+ """Aggregated authentication configuration.
112
+
113
+ Args:
114
+ mode: Authentication mode.
115
+ oidc: OIDC settings (used when mode is ``OIDC``).
116
+ ldap: LDAP settings (used when mode is ``LDAP``).
117
+ signup_enabled: Allow new user registration.
118
+ """
119
+
120
+ mode: AuthMode = AuthMode.BUILTIN
121
+ oidc: OIDCConfig = field(default_factory=OIDCConfig)
122
+ ldap: LDAPConfig = field(default_factory=LDAPConfig)
123
+ signup_enabled: bool = True
124
+
125
+ def to_env(self) -> dict[str, str]:
126
+ """Return environment variables for the configured auth mode."""
127
+ env: dict[str, str] = {
128
+ "ENABLE_SIGNUP": str(self.signup_enabled).lower(),
129
+ }
130
+ if self.mode == AuthMode.OIDC:
131
+ env.update(self.oidc.to_env())
132
+ elif self.mode == AuthMode.LDAP:
133
+ env.update(self.ldap.to_env())
134
+ elif self.mode == AuthMode.NONE:
135
+ env["WEBUI_AUTH"] = "false"
136
+ return env
137
+
138
+
139
+ # ── Open WebUI container config ──────────────────────────────
140
+
141
+
142
+ @dataclass
143
+ class OpenWebUIConfig:
144
+ """Container configuration for Open WebUI.
145
+
146
+ Args:
147
+ image: Docker image for Open WebUI.
148
+ container_name: Docker container name.
149
+ port: Host port mapping.
150
+ ollama_base_url: Internal Ollama API URL.
151
+ auth: Authentication configuration.
152
+ extra_env: Additional environment variables.
153
+ """
154
+
155
+ image: str = "ghcr.io/open-webui/open-webui:main"
156
+ container_name: str = "admina-webui"
157
+ port: int = 3080
158
+ ollama_base_url: str = "http://ollama:11434"
159
+ auth: AuthConfig = field(default_factory=AuthConfig)
160
+ extra_env: dict[str, str] = field(default_factory=dict)
161
+
162
+ def to_compose_dict(self) -> dict[str, Any]:
163
+ """Return a docker-compose service fragment.
164
+
165
+ Returns:
166
+ A dict suitable for inclusion in a docker-compose YAML.
167
+ """
168
+ env = {
169
+ "OLLAMA_BASE_URL": self.ollama_base_url,
170
+ "WEBUI_SECRET_KEY": "${WEBUI_SECRET_KEY:?WEBUI_SECRET_KEY must be set}",
171
+ "ENABLE_RAG_WEB_SEARCH": "false",
172
+ }
173
+ env.update(self.auth.to_env())
174
+ env.update(self.extra_env)
175
+
176
+ svc: dict[str, Any] = {
177
+ "image": self.image,
178
+ "container_name": self.container_name,
179
+ "ports": [f"{self.port}:8080"],
180
+ "volumes": ["webui-data:/app/backend/data"],
181
+ "environment": [f"{k}={v}" for k, v in sorted(env.items())],
182
+ "depends_on": {
183
+ "ollama": {"condition": "service_healthy"},
184
+ },
185
+ "healthcheck": {
186
+ "test": ["CMD", "curl", "-f", "http://localhost:8080/"],
187
+ "interval": "30s",
188
+ "timeout": "5s",
189
+ "retries": 3,
190
+ },
191
+ "networks": ["admina"],
192
+ "restart": "unless-stopped",
193
+ }
194
+ return svc
195
+
196
+
197
+ # ── WebUI Engine ─────────────────────────────────────────────
198
+
199
+
200
+ @dataclass
201
+ class WebUIEngine:
202
+ """Manages Open WebUI configuration and Docker Compose generation."""
203
+
204
+ port: int = 3080
205
+ ollama_base_url: str = "http://ollama:11434"
206
+ auth: AuthConfig = field(default_factory=AuthConfig)
207
+
208
+ # ── Factory ──────────────────────────────────────────────
209
+
210
+ @classmethod
211
+ def from_config(
212
+ cls,
213
+ *,
214
+ port: int = 3080,
215
+ ollama_base_url: str = "http://ollama:11434",
216
+ auth_mode: str = "builtin",
217
+ signup_enabled: bool = True,
218
+ oidc_provider_url: str = "",
219
+ oidc_client_id: str = "",
220
+ oidc_client_secret: str = "",
221
+ ldap_host: str = "",
222
+ ldap_port: int = 389,
223
+ ldap_base_dn: str = "",
224
+ ldap_bind_dn: str = "",
225
+ ldap_bind_password: str = "",
226
+ ldap_use_tls: bool = False,
227
+ ) -> WebUIEngine:
228
+ """Create an engine from admina.yaml values.
229
+
230
+ Args:
231
+ port: Host port for the Web UI.
232
+ ollama_base_url: Ollama API URL.
233
+ auth_mode: ``"builtin"``, ``"oidc"``, ``"ldap"``, or ``"none"``.
234
+ signup_enabled: Allow new user registration.
235
+ oidc_provider_url: OIDC discovery URL.
236
+ oidc_client_id: OIDC client ID.
237
+ oidc_client_secret: OIDC client secret.
238
+ ldap_host: LDAP server host.
239
+ ldap_port: LDAP server port.
240
+ ldap_base_dn: LDAP base DN.
241
+ ldap_bind_dn: LDAP bind DN.
242
+ ldap_bind_password: LDAP bind password.
243
+ ldap_use_tls: Use TLS for LDAP.
244
+ """
245
+ auth = AuthConfig(
246
+ mode=AuthMode(auth_mode),
247
+ signup_enabled=signup_enabled,
248
+ oidc=OIDCConfig(
249
+ provider_url=oidc_provider_url,
250
+ client_id=oidc_client_id,
251
+ client_secret=oidc_client_secret,
252
+ ),
253
+ ldap=LDAPConfig(
254
+ host=ldap_host,
255
+ port=ldap_port,
256
+ base_dn=ldap_base_dn,
257
+ bind_dn=ldap_bind_dn,
258
+ bind_password=ldap_bind_password,
259
+ use_tls=ldap_use_tls,
260
+ ),
261
+ )
262
+ return cls(port=port, ollama_base_url=ollama_base_url, auth=auth)
263
+
264
+ # ── Compose generation ───────────────────────────────────
265
+
266
+ def compose_service(
267
+ self,
268
+ project_name: str = "admina",
269
+ ) -> dict[str, Any]:
270
+ """Return the docker-compose service dict for Open WebUI.
271
+
272
+ Args:
273
+ project_name: Used for container naming.
274
+ """
275
+ cfg = OpenWebUIConfig(
276
+ container_name=f"{project_name}-webui",
277
+ port=self.port,
278
+ ollama_base_url=self.ollama_base_url,
279
+ auth=self.auth,
280
+ )
281
+ return cfg.to_compose_dict()
282
+
283
+ # ── Status ───────────────────────────────────────────────
284
+
285
+ def summary(self) -> dict[str, Any]:
286
+ """Return a JSON-serialisable summary of the Web UI config."""
287
+ return {
288
+ "port": self.port,
289
+ "ollama_base_url": self.ollama_base_url,
290
+ "auth_mode": self.auth.mode.value,
291
+ "signup_enabled": self.auth.signup_enabled,
292
+ }
@@ -0,0 +1,109 @@
1
+ # Copyright © 2025–2026 Stefano Noferi & Admina contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Admina — Compliance Domain.
16
+
17
+ Forensic black box, EU AI Act compliance, OISG adequacy score,
18
+ and OpenTelemetry.
19
+
20
+ Heavy or extras-gated symbols (ForensicBlackBox needs ``minio`` from
21
+ the ``[proxy]`` extra) are resolved lazily via PEP 562 ``__getattr__``
22
+ so importing this package never fails on a pure-SDK install.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from typing import TYPE_CHECKING
28
+
29
+ from admina.domains.compliance.cross_regulation import (
30
+ CROSS_REGULATION_MATRIX,
31
+ )
32
+ from admina.domains.compliance.cross_regulation import (
33
+ coverage_summary as cross_regulation_summary,
34
+ )
35
+ from admina.domains.compliance.cross_regulation import to_markdown as cross_regulation_to_markdown
36
+ from admina.domains.compliance.eu_ai_act import (
37
+ EU_AI_ACT_ENFORCEMENT_DEADLINE,
38
+ HIGH_RISK_KEYWORDS,
39
+ HIGH_RISK_SENSITIVE_DATA,
40
+ LIMITED_RISK_KEYWORDS,
41
+ UNACCEPTABLE_RISK_KEYWORDS,
42
+ EUAIActCompliance,
43
+ )
44
+ from admina.domains.compliance.gdpr import (
45
+ DPIA_REQUIRED_CRITERIA,
46
+ ProcessingActivitiesRegistry,
47
+ ProcessingActivity,
48
+ render_dpia_template,
49
+ )
50
+ from admina.domains.compliance.nis2 import (
51
+ NIS2_AREAS,
52
+ NIS2_TRANSPOSITION_DEADLINE,
53
+ NIS2Compliance,
54
+ )
55
+ from admina.domains.compliance.oisg import (
56
+ CRITERIA as OISG_CRITERIA,
57
+ )
58
+ from admina.domains.compliance.oisg import (
59
+ PILLAR_COLORS as OISG_PILLAR_COLORS,
60
+ )
61
+ from admina.domains.compliance.oisg import (
62
+ CriterionResult,
63
+ OISGResult,
64
+ PillarResult,
65
+ compute_oisg_score,
66
+ )
67
+ from admina.domains.compliance.oisg import (
68
+ get_level as oisg_get_level,
69
+ )
70
+ from admina.domains.compliance.otel import OTELGovernanceExporter
71
+
72
+ if TYPE_CHECKING: # pragma: no cover
73
+ from admina.domains.compliance.forensic import ForensicBlackBox
74
+
75
+ __all__ = [
76
+ "ForensicBlackBox",
77
+ "EUAIActCompliance",
78
+ "NIS2Compliance",
79
+ "ProcessingActivitiesRegistry",
80
+ "ProcessingActivity",
81
+ "DPIA_REQUIRED_CRITERIA",
82
+ "render_dpia_template",
83
+ "CROSS_REGULATION_MATRIX",
84
+ "cross_regulation_summary",
85
+ "cross_regulation_to_markdown",
86
+ "OTELGovernanceExporter",
87
+ "EU_AI_ACT_ENFORCEMENT_DEADLINE",
88
+ "UNACCEPTABLE_RISK_KEYWORDS",
89
+ "HIGH_RISK_KEYWORDS",
90
+ "HIGH_RISK_SENSITIVE_DATA",
91
+ "LIMITED_RISK_KEYWORDS",
92
+ "NIS2_AREAS",
93
+ "NIS2_TRANSPOSITION_DEADLINE",
94
+ "compute_oisg_score",
95
+ "oisg_get_level",
96
+ "OISGResult",
97
+ "PillarResult",
98
+ "CriterionResult",
99
+ "OISG_CRITERIA",
100
+ "OISG_PILLAR_COLORS",
101
+ ]
102
+
103
+
104
+ def __getattr__(name: str):
105
+ if name == "ForensicBlackBox":
106
+ from admina.domains.compliance.forensic import ForensicBlackBox
107
+
108
+ return ForensicBlackBox
109
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")