Upload folder using huggingface_hub

#6
Dockerfile CHANGED
@@ -1,11 +1,7 @@
1
  FROM python:3.12-slim
2
  RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
3
  WORKDIR /app
4
-
5
- ARG CLASSIC_TOKEN=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
6
- RUN git config --global url."https://x-access-token:${CLASSIC_TOKEN}@github.com/".insteadOf "https://github.com/"
7
-
8
  COPY requirements.txt .
9
  RUN pip install --no-cache-dir -r requirements.txt
10
  COPY . .
11
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
1
  FROM python:3.12-slim
2
  RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
3
  WORKDIR /app
 
 
 
 
4
  COPY requirements.txt .
5
  RUN pip install --no-cache-dir -r requirements.txt
6
  COPY . .
7
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,9 +1,3 @@
1
- ---
2
- title: ARF API Control Plane
3
- sdk: docker
4
- colorFrom: blue
5
- colorTo: green
6
- ---
7
  # arf-api
8
 
9
  ARF API Control Plane (FastAPI)
@@ -87,7 +81,7 @@ curl -X POST "http://localhost:8000/api/v1/v1/incidents/evaluate" -H "Content-
87
  "justification": "Causal: If we apply restart_container instead of no_action, latency would change from 600.00 to 510.00 (Δ = -90.00). Based on heuristic causal model.",
88
  "confidence": 0.85,
89
  "risk_score": 0.54,
90
- "status": "oss_advisory_only"
91
  },
92
  "causal_explanation": {
93
  "factual_outcome": 600,
@@ -123,4 +117,5 @@ Notes
123
  -----
124
 
125
  - The governance endpoints use an in-process `RiskEngine` initialized at startup.
126
- - The outcome recording endpoint is not implemented in this repository and returns HTTP 501.
 
 
 
 
 
 
 
 
1
  # arf-api
2
 
3
  ARF API Control Plane (FastAPI)
 
81
  "justification": "Causal: If we apply restart_container instead of no_action, latency would change from 600.00 to 510.00 (Δ = -90.00). Based on heuristic causal model.",
82
  "confidence": 0.85,
83
  "risk_score": 0.54,
84
+ "status": "success"
85
  },
86
  "causal_explanation": {
87
  "factual_outcome": 600,
 
117
  -----
118
 
119
  - The governance endpoints use an in-process `RiskEngine` initialized at startup.
120
+ - The outcome recording endpoint is not implemented in this repository and returns HTTP 501.
121
+
app/api/routes_incidents.py CHANGED
@@ -198,7 +198,7 @@ async def evaluate_incident(
198
  ),
199
  "confidence": 1.0 - result.get("uncertainty", 0.0),
200
  "risk_score": result["risk_score"],
201
- "status": "oss_advisory_only",
202
  }
203
 
204
  response_data = {
 
198
  ),
199
  "confidence": 1.0 - result.get("uncertainty", 0.0),
200
  "risk_score": result["risk_score"],
201
+ "status": "success",
202
  }
203
 
204
  response_data = {
app/database/models_intents.py CHANGED
@@ -1,50 +1,149 @@
1
- from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, JSON, Float, ForeignKey, UniqueConstraint
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from sqlalchemy.orm import relationship
3
  import datetime
4
  from .base import Base
5
 
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  class IntentDB(Base):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  __tablename__ = "intents"
 
9
  id = Column(Integer, primary_key=True, index=True)
10
- deterministic_id = Column(
11
- String(64),
12
- unique=True,
13
- index=True,
14
- nullable=False)
15
  intent_type = Column(String(64), nullable=False)
16
  payload = Column(JSON, nullable=False)
17
  oss_payload = Column(JSON, nullable=True)
18
  environment = Column(String(32), nullable=True)
19
- created_at = Column(
20
- DateTime,
21
- default=datetime.datetime.utcnow,
22
- nullable=False)
23
  evaluated_at = Column(DateTime, nullable=True)
24
  risk_score = Column(String(32), nullable=True)
25
- outcomes = relationship(
26
- "OutcomeDB",
27
- back_populates="intent",
28
- cascade="all, delete-orphan")
29
 
30
 
31
  class OutcomeDB(Base):
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  __tablename__ = "intent_outcomes"
 
33
  id = Column(Integer, primary_key=True, index=True)
34
- intent_id = Column(
35
- Integer,
36
- ForeignKey(
37
- "intents.id",
38
- ondelete="CASCADE"),
39
- nullable=False)
40
  success = Column(Boolean, nullable=False)
41
  recorded_by = Column(String(128), nullable=True)
42
  notes = Column(Text, nullable=True)
43
- recorded_at = Column(
44
- DateTime,
45
- default=datetime.datetime.utcnow,
46
- nullable=False)
47
  idempotency_key = Column(String(128), unique=True, nullable=True)
 
48
  intent = relationship("IntentDB", back_populates="outcomes")
49
 
50
  __table_args__ = (
@@ -52,24 +151,83 @@ class OutcomeDB(Base):
52
  )
53
 
54
 
55
- # ---------------------------------------------------------------------------
56
- # NEW: Persistence for the conjugate Bayesian state
57
- # ---------------------------------------------------------------------------
 
58
  class BetaStateDB(Base):
59
  """
60
- Stores the per‑category posterior parameters (α, β) of the BetaStore
61
- so that online learning survives API restarts.
 
62
 
63
- Only one row per ActionCategory is expected; the 'category' column is
64
- unique. Updates are performed via merge / upsert.
 
 
 
 
 
65
  """
66
  __tablename__ = "beta_state"
67
 
68
  id = Column(Integer, primary_key=True, index=True)
69
- category = Column(String(32), unique=True, nullable=False, index=True)
 
70
  alpha = Column(Float, nullable=False)
71
  beta = Column(Float, nullable=False)
72
- updated_at = Column(
73
- DateTime,
74
- default=datetime.datetime.utcnow,
75
- onupdate=datetime.datetime.utcnow)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Database models for the ARF API Control Plane.
3
+
4
+ This module defines the SQLAlchemy ORM models for:
5
+ - Intents (InfrastructureIntent evaluations)
6
+ - Outcomes (recorded results of executed intents)
7
+ - Beta state (conjugate Bayesian posteriors per tenant and category)
8
+ - Audit logs (immutable decision records for compliance)
9
+ - Tenants (multi‑tenant isolation)
10
+
11
+ All tables include a `tenant_id` column to enforce data partitioning.
12
+ The BetaStateDB now stores parameters per (tenant, category) pair.
13
+ """
14
+
15
+ from sqlalchemy import (
16
+ Column, Integer, String, DateTime, Boolean, Text, JSON,
17
+ Float, ForeignKey, UniqueConstraint, Index
18
+ )
19
  from sqlalchemy.orm import relationship
20
  import datetime
21
  from .base import Base
22
 
23
 
24
+ # ============================================================================
25
+ # Tenant table – root of multi‑tenancy
26
+ # ============================================================================
27
+
28
+ class TenantDB(Base):
29
+ """
30
+ Represents a customer tenant (organisation). All other tables
31
+ reference this table via a foreign key `tenant_id`.
32
+
33
+ Attributes:
34
+ id (str): UUID of the tenant (primary key).
35
+ name (str): Human‑readable organisation name.
36
+ created_at (datetime): UTC timestamp of creation.
37
+ created_by (str, optional): Email or user ID of the creator.
38
+ """
39
+ __tablename__ = "tenants"
40
+
41
+ id = Column(String(64), primary_key=True, index=True)
42
+ name = Column(String(256), nullable=False)
43
+ created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
44
+ created_by = Column(String(128), nullable=True)
45
+
46
+ # Relationships
47
+ api_keys = relationship("APIKeyDB", back_populates="tenant", cascade="all, delete-orphan")
48
+ intents = relationship("IntentDB", back_populates="tenant")
49
+ beta_states = relationship("BetaStateDB", back_populates="tenant")
50
+ audit_logs = relationship("DecisionAuditLogDB", back_populates="tenant")
51
+
52
+
53
+ # ============================================================================
54
+ # API keys (extended with tenant_id)
55
+ # ============================================================================
56
+
57
+ class APIKeyDB(Base):
58
+ """
59
+ Stores API keys for authentication and tiered quota. Each key belongs
60
+ to exactly one tenant. The `tier` determines monthly evaluation limits.
61
+
62
+ Attributes:
63
+ key (str): The hashed API key (primary key).
64
+ tenant_id (str): Foreign key to `tenants.id`.
65
+ tier (str): Tier enumeration value (free, pro, premium, enterprise).
66
+ created_at (datetime): UTC creation time.
67
+ last_used_at (datetime, optional): Timestamp of last successful request.
68
+ is_active (bool): Soft‑delete flag.
69
+ """
70
+ __tablename__ = "api_keys"
71
+
72
+ key = Column(String(256), primary_key=True, index=True)
73
+ tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
74
+ tier = Column(String(32), nullable=False)
75
+ created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
76
+ last_used_at = Column(DateTime, nullable=True)
77
+ is_active = Column(Boolean, default=True, nullable=False)
78
+
79
+ # Relationships
80
+ tenant = relationship("TenantDB", back_populates="api_keys")
81
+ usage_logs = relationship("UsageLogDB", back_populates="api_key")
82
+
83
+
84
+ # ============================================================================
85
+ # Intents (evaluations) – now tenant‑scoped
86
+ # ============================================================================
87
+
88
  class IntentDB(Base):
89
+ """
90
+ Stores each InfrastructureIntent evaluation request and its resulting
91
+ risk score. One‑to‑many with OutcomeDB.
92
+
93
+ Attributes:
94
+ id (int): Auto‑increment primary key.
95
+ deterministic_id (str): Client‑provided idempotency identifier (unique).
96
+ tenant_id (str): Tenant that owns this intent.
97
+ intent_type (str): Type of intent (e.g., "provision_resource").
98
+ payload (JSON): Original API request payload.
99
+ oss_payload (JSON): Canonical OSS intent representation.
100
+ environment (str, optional): Environment label (prod, staging, etc.).
101
+ created_at (datetime): UTC timestamp of evaluation.
102
+ evaluated_at (datetime, optional): When the risk engine processed it.
103
+ risk_score (str, optional): String representation of the risk score.
104
+ """
105
  __tablename__ = "intents"
106
+
107
  id = Column(Integer, primary_key=True, index=True)
108
+ deterministic_id = Column(String(64), unique=True, index=True, nullable=False)
109
+ tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
 
 
 
110
  intent_type = Column(String(64), nullable=False)
111
  payload = Column(JSON, nullable=False)
112
  oss_payload = Column(JSON, nullable=True)
113
  environment = Column(String(32), nullable=True)
114
+ created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
 
 
 
115
  evaluated_at = Column(DateTime, nullable=True)
116
  risk_score = Column(String(32), nullable=True)
117
+
118
+ # Relationships
119
+ tenant = relationship("TenantDB", back_populates="intents")
120
+ outcomes = relationship("OutcomeDB", back_populates="intent", cascade="all, delete-orphan")
121
 
122
 
123
  class OutcomeDB(Base):
124
+ """
125
+ Records the outcome (success/failure) of a previously evaluated intent.
126
+ Only one outcome per intent is allowed (unique constraint on intent_id).
127
+
128
+ Attributes:
129
+ id (int): Primary key.
130
+ intent_id (int): Foreign key to `intents.id`.
131
+ success (bool): Whether the executed action succeeded.
132
+ recorded_by (str, optional): Identity of the caller (e.g., API key owner).
133
+ notes (str, optional): Free‑text notes.
134
+ recorded_at (datetime): UTC timestamp.
135
+ idempotency_key (str, optional): Unique idempotency key for this outcome.
136
+ """
137
  __tablename__ = "intent_outcomes"
138
+
139
  id = Column(Integer, primary_key=True, index=True)
140
+ intent_id = Column(Integer, ForeignKey("intents.id", ondelete="CASCADE"), nullable=False)
 
 
 
 
 
141
  success = Column(Boolean, nullable=False)
142
  recorded_by = Column(String(128), nullable=True)
143
  notes = Column(Text, nullable=True)
144
+ recorded_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
 
 
 
145
  idempotency_key = Column(String(128), unique=True, nullable=True)
146
+
147
  intent = relationship("IntentDB", back_populates="outcomes")
148
 
149
  __table_args__ = (
 
151
  )
152
 
153
 
154
+ # ============================================================================
155
+ # Bayesian conjugate state now per tenant and per category
156
+ # ============================================================================
157
+
158
  class BetaStateDB(Base):
159
  """
160
+ Stores the posterior parameters (α, β) of the conjugate Beta model
161
+ for each (tenant, category) pair. This allows online learning to be
162
+ isolated per customer.
163
 
164
+ Attributes:
165
+ id (int): Primary key.
166
+ tenant_id (str): Tenant that owns this state.
167
+ category (str): ActionCategory value (e.g., "database", "compute").
168
+ alpha (float): α parameter of the Beta distribution.
169
+ beta (float): β parameter of the Beta distribution.
170
+ updated_at (datetime): Last update timestamp (auto‑set).
171
  """
172
  __tablename__ = "beta_state"
173
 
174
  id = Column(Integer, primary_key=True, index=True)
175
+ tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
176
+ category = Column(String(32), nullable=False, index=True)
177
  alpha = Column(Float, nullable=False)
178
  beta = Column(Float, nullable=False)
179
+ updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
180
+
181
+ # Composite unique constraint: (tenant_id, category)
182
+ __table_args__ = (
183
+ UniqueConstraint("tenant_id", "category", name="uq_beta_state_tenant_category"),
184
+ )
185
+
186
+ # Relationships
187
+ tenant = relationship("TenantDB", back_populates="beta_states")
188
+
189
+
190
+ # ============================================================================
191
+ # NEW: Audit log for compliance (immutable decision records)
192
+ # ============================================================================
193
+
194
+ class DecisionAuditLogDB(Base):
195
+ """
196
+ Immutable, tamper‑evident record of every governance decision.
197
+ Designed for compliance (SOC2, ISO) and forensic analysis.
198
+
199
+ Attributes:
200
+ id (str): UUID primary key.
201
+ tenant_id (str): Tenant that owns the decision.
202
+ deterministic_id (str): Intent identifier (idempotency key).
203
+ timestamp (datetime): UTC decision time.
204
+ risk_score (float): Fused Bayesian risk score (0‑1).
205
+ action (str): Selected action (approve, deny, escalate).
206
+ justification (str): Human‑readable explanation.
207
+ memory_success_rate (float, optional): Memory‑based correction value.
208
+ memory_weight (float, optional): Weight assigned to memory.
209
+ counterfactual (JSON, optional): Structured counterfactual explanation.
210
+ trace_id (str, optional): OpenTelemetry trace ID for debugging.
211
+ signature (str, optional): Ed25519 signature for tamper‑proofing.
212
+ """
213
+ __tablename__ = "decision_audit_log"
214
+
215
+ id = Column(String(64), primary_key=True, default=lambda: str(uuid.uuid4()))
216
+ tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
217
+ deterministic_id = Column(String(64), nullable=False, index=True)
218
+ timestamp = Column(DateTime, default=datetime.datetime.utcnow, nullable=False, index=True)
219
+ risk_score = Column(Float, nullable=False)
220
+ action = Column(String(32), nullable=False)
221
+ justification = Column(Text, nullable=False)
222
+ memory_success_rate = Column(Float, nullable=True)
223
+ memory_weight = Column(Float, nullable=True)
224
+ counterfactual = Column(JSON, nullable=True)
225
+ trace_id = Column(String(128), nullable=True)
226
+ signature = Column(String(256), nullable=True)
227
+
228
+ # Composite index for fast filtered queries
229
+ __table_args__ = (
230
+ Index("idx_audit_tenant_time", "tenant_id", "timestamp"),
231
+ )
232
+
233
+ tenant = relationship("TenantDB", back_populates="audit_logs")