@euconform/core 1.0.1 → 1.2.0
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/inference/ollama-client.d.ts +1 -0
- package/dist/inference/ollama-client.d.ts.map +1 -1
- package/dist/inference/ollama-client.js +15 -5
- package/dist/legal-checks/annex-iv-report.d.ts +3 -0
- package/dist/legal-checks/annex-iv-report.d.ts.map +1 -1
- package/dist/legal-checks/annex-iv-report.js +1 -0
- package/dist/legal-checks/deadline-timeline.d.ts +57 -0
- package/dist/legal-checks/deadline-timeline.d.ts.map +1 -0
- package/dist/legal-checks/deadline-timeline.js +265 -0
- package/dist/legal-checks/gap-analysis.d.ts +48 -0
- package/dist/legal-checks/gap-analysis.d.ts.map +1 -0
- package/dist/legal-checks/gap-analysis.js +397 -0
- package/dist/legal-checks/gpai-classifier.d.ts +49 -0
- package/dist/legal-checks/gpai-classifier.d.ts.map +1 -0
- package/dist/legal-checks/gpai-classifier.js +197 -0
- package/dist/legal-checks/index.d.ts +6 -0
- package/dist/legal-checks/index.d.ts.map +1 -1
- package/dist/legal-checks/index.js +3 -0
- package/package.json +1 -1
- package/src/index.ts +25 -0
- package/src/inference/ollama-client.ts +21 -5
- package/src/legal-checks/annex-iv-report.ts +4 -0
- package/src/legal-checks/deadline-timeline.ts +313 -0
- package/src/legal-checks/gap-analysis.ts +454 -0
- package/src/legal-checks/gpai-classifier.ts +281 -0
- package/src/legal-checks/index.ts +33 -0
- package/tests/gpai-classifier.test.ts +125 -0
- package/tests/gpai-timeline.test.ts +55 -0
- package/.turbo/turbo-check-types.log +0 -4
- package/.turbo/turbo-test.log +0 -364
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/clover.xml +0 -3282
- package/coverage/coverage-final.json +0 -20
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -161
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/coverage/src/fairness-metrics.ts.html +0 -1579
- package/coverage/src/index.html +0 -161
- package/coverage/src/index.ts.html +0 -535
- package/coverage/src/inference/browser-inference.ts.html +0 -859
- package/coverage/src/inference/capability-detection.ts.html +0 -2227
- package/coverage/src/inference/index.html +0 -176
- package/coverage/src/inference/index.ts.html +0 -553
- package/coverage/src/inference/inference-factory.ts.html +0 -751
- package/coverage/src/inference/ollama-client.ts.html +0 -1627
- package/coverage/src/legal-checks/annex-iv-report.ts.html +0 -556
- package/coverage/src/legal-checks/bias-metrics.ts.html +0 -1855
- package/coverage/src/legal-checks/data-governance.ts.html +0 -529
- package/coverage/src/legal-checks/human-oversight-logging.ts.html +0 -490
- package/coverage/src/legal-checks/index.html +0 -206
- package/coverage/src/legal-checks/index.ts.html +0 -250
- package/coverage/src/legal-checks/risk-classifier.ts.html +0 -940
- package/coverage/src/legal-checks/robustness-transparency.ts.html +0 -571
- package/coverage/src/metrics/crows-pairs-bias.ts.html +0 -820
- package/coverage/src/metrics/index.html +0 -146
- package/coverage/src/metrics/index.ts.html +0 -151
- package/coverage/src/metrics/stereotype-bias.ts.html +0 -886
- package/coverage/src/risk-engine.ts.html +0 -1660
- package/coverage/src/types.ts.html +0 -547
- package/dist/legal-checks/disclaimer.d.ts +0 -2
- package/dist/legal-checks/disclaimer.d.ts.map +0 -1
- package/dist/legal-checks/disclaimer.js +0 -1
|
@@ -4,3 +4,6 @@ export { runDataGovernanceChecklist } from "./data-governance";
|
|
|
4
4
|
export { defaultTextPerturbations, runTextPerturbationRobustnessTest, } from "./robustness-transparency";
|
|
5
5
|
export { getHumanOversightAndLoggingTemplate } from "./human-oversight-logging";
|
|
6
6
|
export { buildAnnexIVReportV1 } from "./annex-iv-report";
|
|
7
|
+
export { EU_AI_ACT_TIMELINE, DIGITAL_OMNIBUS_PROPOSALS, getDeadlineStatus, getDaysUntilDeadline, getAudiencesForRiskLevel, getNextDeadline, getTimelineForRiskLevel, getTimelineForAudiences, } from "./deadline-timeline";
|
|
8
|
+
export { GPAI_QUESTIONS, classifyGPAICompliance, getGPAIQuestions, isSystemicRiskModel, } from "./gpai-classifier";
|
|
9
|
+
export { generateAnnexIIIGapAnalysis, generateGPAIGapAnalysis } from "./gap-analysis";
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -31,6 +31,12 @@ export {
|
|
|
31
31
|
classifyAnnexIIIRisk,
|
|
32
32
|
getAnnexIIIQuestions,
|
|
33
33
|
getProhibitedAIScreeningQuestions,
|
|
34
|
+
GPAI_QUESTIONS,
|
|
35
|
+
classifyGPAICompliance,
|
|
36
|
+
getGPAIQuestions,
|
|
37
|
+
isSystemicRiskModel,
|
|
38
|
+
generateAnnexIIIGapAnalysis,
|
|
39
|
+
generateGPAIGapAnalysis,
|
|
34
40
|
AI_ACT_SOURCES,
|
|
35
41
|
METRIC_SOURCES,
|
|
36
42
|
createPerspectiveApiToxicityScorer,
|
|
@@ -48,6 +54,14 @@ export {
|
|
|
48
54
|
runCrowsPairsLogProbTest,
|
|
49
55
|
runRegardTest,
|
|
50
56
|
runToxicityTest,
|
|
57
|
+
EU_AI_ACT_TIMELINE,
|
|
58
|
+
DIGITAL_OMNIBUS_PROPOSALS,
|
|
59
|
+
getDeadlineStatus,
|
|
60
|
+
getDaysUntilDeadline,
|
|
61
|
+
getAudiencesForRiskLevel,
|
|
62
|
+
getNextDeadline,
|
|
63
|
+
getTimelineForRiskLevel,
|
|
64
|
+
getTimelineForAudiences,
|
|
51
65
|
} from "./legal-checks";
|
|
52
66
|
export type {
|
|
53
67
|
AnnexIIIAnswer,
|
|
@@ -71,6 +85,17 @@ export type {
|
|
|
71
85
|
SensitiveAttribute,
|
|
72
86
|
ToxicityResult,
|
|
73
87
|
ToxicityScorer,
|
|
88
|
+
DeadlineStatus,
|
|
89
|
+
AffectedAudience,
|
|
90
|
+
DeadlineObligation,
|
|
91
|
+
DeadlinePhase,
|
|
92
|
+
GPAIObligationType,
|
|
93
|
+
GPAIComplianceLevel,
|
|
94
|
+
GPAIComplianceFlag,
|
|
95
|
+
GPAIComplianceResult,
|
|
96
|
+
GapPriority,
|
|
97
|
+
GapAction,
|
|
98
|
+
GapAnalysisResult,
|
|
74
99
|
} from "./legal-checks";
|
|
75
100
|
|
|
76
101
|
// Risk Engine
|
|
@@ -224,6 +224,10 @@ export class OllamaClient {
|
|
|
224
224
|
this.capabilityCache = new Map();
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
private isCloudModel(): boolean {
|
|
228
|
+
return this.model.includes(":cloud");
|
|
229
|
+
}
|
|
230
|
+
|
|
227
231
|
/**
|
|
228
232
|
* Ensures the model is available in Ollama before proceeding
|
|
229
233
|
* Throws an error if the model is not found
|
|
@@ -273,6 +277,14 @@ export class OllamaClient {
|
|
|
273
277
|
* Sends a test request with "Der" prompt to detect capability
|
|
274
278
|
*/
|
|
275
279
|
private async testLogProbCapability(): Promise<boolean> {
|
|
280
|
+
// Cloud models (via Ollama proxy) don't support logprobs parameter
|
|
281
|
+
if (this.isCloudModel()) {
|
|
282
|
+
console.log(
|
|
283
|
+
`🔍 ${this.model} is a cloud model - logprobs not supported, using latency method`
|
|
284
|
+
);
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
276
288
|
try {
|
|
277
289
|
// First check Ollama version
|
|
278
290
|
try {
|
|
@@ -412,8 +424,7 @@ export class OllamaClient {
|
|
|
412
424
|
// First, check if model supports log-probabilities
|
|
413
425
|
const supportsLogProbs = await this.detectLogProbSupport();
|
|
414
426
|
|
|
415
|
-
if (supportsLogProbs) {
|
|
416
|
-
// Try to get exact log-probabilities
|
|
427
|
+
if (supportsLogProbs && !this.isCloudModel()) {
|
|
417
428
|
try {
|
|
418
429
|
const response = await fetch(`${this.baseUrl}/api/generate`, {
|
|
419
430
|
method: "POST",
|
|
@@ -461,8 +472,12 @@ export class OllamaClient {
|
|
|
461
472
|
}
|
|
462
473
|
|
|
463
474
|
// Fallback to latency-based method
|
|
464
|
-
|
|
475
|
+
const isCloud = this.isCloudModel();
|
|
476
|
+
console.log(
|
|
477
|
+
`⚡ Falling back to latency-based method for ${this.model}${isCloud ? " (cloud model)" : ""}`
|
|
478
|
+
);
|
|
465
479
|
const startTime = performance.now();
|
|
480
|
+
const numPredict = isCloud ? 1 : 0;
|
|
466
481
|
|
|
467
482
|
const response = await fetch(`${this.baseUrl}/api/generate`, {
|
|
468
483
|
method: "POST",
|
|
@@ -472,7 +487,7 @@ export class OllamaClient {
|
|
|
472
487
|
prompt,
|
|
473
488
|
stream: false,
|
|
474
489
|
options: {
|
|
475
|
-
num_predict:
|
|
490
|
+
num_predict: numPredict,
|
|
476
491
|
temperature: 0,
|
|
477
492
|
},
|
|
478
493
|
}),
|
|
@@ -481,7 +496,8 @@ export class OllamaClient {
|
|
|
481
496
|
const endTime = performance.now();
|
|
482
497
|
|
|
483
498
|
if (!response.ok) {
|
|
484
|
-
|
|
499
|
+
const errorText = await response.text();
|
|
500
|
+
throw new Error(`Ollama request failed: ${response.statusText}. ${errorText}`);
|
|
485
501
|
}
|
|
486
502
|
|
|
487
503
|
const data = await response.json();
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import type { BiasCalculationMethod, BiasTestResult, RiskLevel } from "../types";
|
|
18
18
|
import type { Citation } from "./bias-metrics";
|
|
19
19
|
import type { DataGovernanceChecklistResult } from "./data-governance";
|
|
20
|
+
import type { GapAnalysisResult } from "./gap-analysis";
|
|
20
21
|
import type { HumanOversightRecommendation } from "./human-oversight-logging";
|
|
21
22
|
import type { AnnexIIIRiskResult } from "./risk-classifier";
|
|
22
23
|
|
|
@@ -88,6 +89,7 @@ export interface AnnexIVStructuredReportV1 {
|
|
|
88
89
|
changeLog?: Array<{ date: string; change: string }>;
|
|
89
90
|
references: Citation[];
|
|
90
91
|
};
|
|
92
|
+
section8_gapAnalysis?: GapAnalysisResult;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
export function buildAnnexIVReportV1(input: {
|
|
@@ -104,6 +106,7 @@ export function buildAnnexIVReportV1(input: {
|
|
|
104
106
|
humanOversight?: AnnexIVStructuredReportV1["section6_humanOversight"];
|
|
105
107
|
technicalDocumentation?: Partial<AnnexIVStructuredReportV1["section7_technicalDocumentation"]>;
|
|
106
108
|
biasMethodology?: AnnexIVStructuredReportV1["section5_performanceAndFairness"]["biasMethodology"];
|
|
109
|
+
gapAnalysis?: GapAnalysisResult;
|
|
107
110
|
tool?: { name: string; version?: string };
|
|
108
111
|
disclaimer?: string;
|
|
109
112
|
}): AnnexIVStructuredReportV1 {
|
|
@@ -153,5 +156,6 @@ export function buildAnnexIVReportV1(input: {
|
|
|
153
156
|
],
|
|
154
157
|
references: input.technicalDocumentation?.references ?? input.legalSources,
|
|
155
158
|
},
|
|
159
|
+
section8_gapAnalysis: input.gapAnalysis,
|
|
156
160
|
};
|
|
157
161
|
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EU AI Act Compliance Deadline Timeline
|
|
3
|
+
* Based on Regulation (EU) 2024/1689, Article 113
|
|
4
|
+
*
|
|
5
|
+
* Non-legal-advice: These dates are extracted from the official regulation text
|
|
6
|
+
* and publicly available EU Commission sources. Always verify with legal counsel.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { RiskLevel } from "../types";
|
|
10
|
+
|
|
11
|
+
export type DeadlineStatus = "past" | "imminent" | "upcoming" | "future";
|
|
12
|
+
|
|
13
|
+
export type AffectedAudience =
|
|
14
|
+
| "all"
|
|
15
|
+
| "prohibited"
|
|
16
|
+
| "gpai"
|
|
17
|
+
| "high-annex-iii"
|
|
18
|
+
| "high-annex-i"
|
|
19
|
+
| "limited"
|
|
20
|
+
| "minimal";
|
|
21
|
+
|
|
22
|
+
export interface DeadlineObligation {
|
|
23
|
+
titleKey: string;
|
|
24
|
+
articleRef: string;
|
|
25
|
+
audiences: AffectedAudience[];
|
|
26
|
+
maxPenalty?: string;
|
|
27
|
+
proposed?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DeadlinePhase {
|
|
31
|
+
id: string;
|
|
32
|
+
isoDate: string;
|
|
33
|
+
chapterRef: string;
|
|
34
|
+
titleKey: string;
|
|
35
|
+
subtitleKey: string;
|
|
36
|
+
obligations: DeadlineObligation[];
|
|
37
|
+
status?: DeadlineStatus;
|
|
38
|
+
proposed?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
42
|
+
|
|
43
|
+
export function getDeadlineStatus(isoDate: string, nowMs = Date.now()): DeadlineStatus {
|
|
44
|
+
const diffDays = (new Date(isoDate).getTime() - nowMs) / MS_PER_DAY;
|
|
45
|
+
if (diffDays < 0) return "past";
|
|
46
|
+
if (diffDays <= 90) return "imminent";
|
|
47
|
+
if (diffDays <= 365) return "upcoming";
|
|
48
|
+
return "future";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getDaysUntilDeadline(isoDate: string, nowMs = Date.now()): number {
|
|
52
|
+
return Math.round((new Date(isoDate).getTime() - nowMs) / MS_PER_DAY);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* EU AI Act Timeline Milestones – Art. 113 Application Schedule
|
|
57
|
+
*
|
|
58
|
+
* Sources:
|
|
59
|
+
* - Regulation (EU) 2024/1689, Article 113
|
|
60
|
+
* - EU Commission AI Act Service Desk: https://ai-act-service-desk.ec.europa.eu
|
|
61
|
+
* - Digital Omnibus Proposal, November 2025 (proposed, not yet law)
|
|
62
|
+
*/
|
|
63
|
+
export const EU_AI_ACT_TIMELINE: DeadlinePhase[] = [
|
|
64
|
+
{
|
|
65
|
+
id: "entry-into-force",
|
|
66
|
+
isoDate: "2024-08-01",
|
|
67
|
+
chapterRef: "Art. 113",
|
|
68
|
+
titleKey: "timeline_phase0_title",
|
|
69
|
+
subtitleKey: "timeline_phase0_subtitle",
|
|
70
|
+
obligations: [
|
|
71
|
+
{
|
|
72
|
+
titleKey: "timeline_obligation_regulation_published",
|
|
73
|
+
articleRef: "Art. 113",
|
|
74
|
+
audiences: ["all"],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "phase-1-prohibited",
|
|
80
|
+
isoDate: "2025-02-02",
|
|
81
|
+
chapterRef: "Art. 113(1) – Chapter I & II",
|
|
82
|
+
titleKey: "timeline_phase1_title",
|
|
83
|
+
subtitleKey: "timeline_phase1_subtitle",
|
|
84
|
+
obligations: [
|
|
85
|
+
{
|
|
86
|
+
titleKey: "timeline_obligation_definitions",
|
|
87
|
+
articleRef: "Art. 1–4",
|
|
88
|
+
audiences: ["all"],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
titleKey: "timeline_obligation_prohibited",
|
|
92
|
+
articleRef: "Art. 5",
|
|
93
|
+
audiences: ["all", "prohibited"],
|
|
94
|
+
maxPenalty: "€35M / 7%",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
titleKey: "timeline_obligation_ai_literacy",
|
|
98
|
+
articleRef: "Art. 4",
|
|
99
|
+
audiences: ["all"],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "phase-2-gpai",
|
|
105
|
+
isoDate: "2025-08-02",
|
|
106
|
+
chapterRef: "Art. 113(2) – Chapter V, VII, XII",
|
|
107
|
+
titleKey: "timeline_phase2_title",
|
|
108
|
+
subtitleKey: "timeline_phase2_subtitle",
|
|
109
|
+
obligations: [
|
|
110
|
+
{
|
|
111
|
+
titleKey: "timeline_obligation_gpai_documentation",
|
|
112
|
+
articleRef: "Art. 53",
|
|
113
|
+
audiences: ["gpai"],
|
|
114
|
+
maxPenalty: "€15M / 3%",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
titleKey: "timeline_obligation_gpai_copyright",
|
|
118
|
+
articleRef: "Art. 53(1)(c)",
|
|
119
|
+
audiences: ["gpai"],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
titleKey: "timeline_obligation_gpai_systemic_risk",
|
|
123
|
+
articleRef: "Art. 55",
|
|
124
|
+
audiences: ["gpai"],
|
|
125
|
+
maxPenalty: "€15M / 3%",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
titleKey: "timeline_obligation_notified_bodies",
|
|
129
|
+
articleRef: "Art. 28–39",
|
|
130
|
+
audiences: ["high-annex-iii", "high-annex-i"],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
titleKey: "timeline_obligation_governance",
|
|
134
|
+
articleRef: "Art. 64–70",
|
|
135
|
+
audiences: ["all"],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
titleKey: "timeline_obligation_penalties_framework",
|
|
139
|
+
articleRef: "Art. 99–100",
|
|
140
|
+
audiences: ["all"],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "phase-3-high-risk-annex-iii",
|
|
146
|
+
isoDate: "2026-08-02",
|
|
147
|
+
chapterRef: "Art. 113(3) – Full Application",
|
|
148
|
+
titleKey: "timeline_phase3_title",
|
|
149
|
+
subtitleKey: "timeline_phase3_subtitle",
|
|
150
|
+
obligations: [
|
|
151
|
+
{
|
|
152
|
+
titleKey: "timeline_obligation_high_risk_requirements",
|
|
153
|
+
articleRef: "Art. 8–25",
|
|
154
|
+
audiences: ["high-annex-iii"],
|
|
155
|
+
maxPenalty: "€15M / 3%",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
titleKey: "timeline_obligation_risk_management",
|
|
159
|
+
articleRef: "Art. 9",
|
|
160
|
+
audiences: ["high-annex-iii"],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
titleKey: "timeline_obligation_data_governance",
|
|
164
|
+
articleRef: "Art. 10",
|
|
165
|
+
audiences: ["high-annex-iii"],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
titleKey: "timeline_obligation_transparency_deployers",
|
|
169
|
+
articleRef: "Art. 13",
|
|
170
|
+
audiences: ["high-annex-iii"],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
titleKey: "timeline_obligation_human_oversight",
|
|
174
|
+
articleRef: "Art. 14",
|
|
175
|
+
audiences: ["high-annex-iii"],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
titleKey: "timeline_obligation_conformity_assessment",
|
|
179
|
+
articleRef: "Art. 43",
|
|
180
|
+
audiences: ["high-annex-iii"],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
titleKey: "timeline_obligation_ce_marking",
|
|
184
|
+
articleRef: "Art. 48",
|
|
185
|
+
audiences: ["high-annex-iii"],
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
titleKey: "timeline_obligation_eu_database",
|
|
189
|
+
articleRef: "Art. 49",
|
|
190
|
+
audiences: ["high-annex-iii"],
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
titleKey: "timeline_obligation_limited_risk_transparency",
|
|
194
|
+
articleRef: "Art. 50",
|
|
195
|
+
audiences: ["limited"],
|
|
196
|
+
maxPenalty: "€15M / 3%",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
titleKey: "timeline_obligation_post_market",
|
|
200
|
+
articleRef: "Art. 72–73",
|
|
201
|
+
audiences: ["high-annex-iii"],
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "phase-4-high-risk-annex-i",
|
|
207
|
+
isoDate: "2027-08-02",
|
|
208
|
+
chapterRef: "Art. 113(4) – Art. 6(1) + Annex I",
|
|
209
|
+
titleKey: "timeline_phase4_title",
|
|
210
|
+
subtitleKey: "timeline_phase4_subtitle",
|
|
211
|
+
obligations: [
|
|
212
|
+
{
|
|
213
|
+
titleKey: "timeline_obligation_annex_i_products",
|
|
214
|
+
articleRef: "Art. 6(1) + Annex I",
|
|
215
|
+
audiences: ["high-annex-i"],
|
|
216
|
+
maxPenalty: "€15M / 3%",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
titleKey: "timeline_obligation_gpai_grace_period_ends",
|
|
220
|
+
articleRef: "Art. 113(5)",
|
|
221
|
+
audiences: ["gpai"],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Digital Omnibus proposed changes (November 2025).
|
|
229
|
+
* NOT YET LAW – under negotiation by EP and Council as of early 2026.
|
|
230
|
+
*/
|
|
231
|
+
export const DIGITAL_OMNIBUS_PROPOSALS: DeadlinePhase[] = [
|
|
232
|
+
{
|
|
233
|
+
id: "omnibus-high-risk-annex-iii",
|
|
234
|
+
isoDate: "2027-12-02",
|
|
235
|
+
chapterRef: "Digital Omnibus (proposed)",
|
|
236
|
+
titleKey: "timeline_omnibus_phase3_title",
|
|
237
|
+
subtitleKey: "timeline_omnibus_phase3_subtitle",
|
|
238
|
+
proposed: true,
|
|
239
|
+
obligations: [
|
|
240
|
+
{
|
|
241
|
+
titleKey: "timeline_omnibus_high_risk_delayed",
|
|
242
|
+
articleRef: "Annex III (proposed)",
|
|
243
|
+
audiences: ["high-annex-iii"],
|
|
244
|
+
proposed: true,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: "omnibus-high-risk-annex-i",
|
|
250
|
+
isoDate: "2028-08-02",
|
|
251
|
+
chapterRef: "Digital Omnibus (proposed)",
|
|
252
|
+
titleKey: "timeline_omnibus_phase4_title",
|
|
253
|
+
subtitleKey: "timeline_omnibus_phase4_subtitle",
|
|
254
|
+
proposed: true,
|
|
255
|
+
obligations: [
|
|
256
|
+
{
|
|
257
|
+
titleKey: "timeline_omnibus_annex_i_delayed",
|
|
258
|
+
articleRef: "Annex I (proposed)",
|
|
259
|
+
audiences: ["high-annex-i"],
|
|
260
|
+
proposed: true,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
export function getAudiencesForRiskLevel(level: RiskLevel): AffectedAudience[] {
|
|
267
|
+
switch (level) {
|
|
268
|
+
case "unacceptable":
|
|
269
|
+
return ["all", "prohibited"];
|
|
270
|
+
case "high":
|
|
271
|
+
return ["all", "high-annex-iii", "high-annex-i"];
|
|
272
|
+
case "limited":
|
|
273
|
+
return ["all", "limited"];
|
|
274
|
+
case "minimal":
|
|
275
|
+
return ["all", "minimal"];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function getNextDeadline(): DeadlinePhase | null {
|
|
280
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
281
|
+
const upcoming = EU_AI_ACT_TIMELINE.filter((p) => p.isoDate > today).sort((a, b) =>
|
|
282
|
+
a.isoDate.localeCompare(b.isoDate)
|
|
283
|
+
);
|
|
284
|
+
return upcoming[0] ?? null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function getTimelineForRiskLevel(
|
|
288
|
+
riskLevel: RiskLevel
|
|
289
|
+
): (DeadlinePhase & { status: DeadlineStatus })[] {
|
|
290
|
+
const audiences = getAudiencesForRiskLevel(riskLevel);
|
|
291
|
+
return EU_AI_ACT_TIMELINE.filter((phase) =>
|
|
292
|
+
phase.obligations.some((o) => o.audiences.some((a) => audiences.includes(a)))
|
|
293
|
+
).map((phase) => ({
|
|
294
|
+
...phase,
|
|
295
|
+
status: getDeadlineStatus(phase.isoDate),
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Filter timeline phases by explicit audience list.
|
|
301
|
+
* Use this when the caller has audiences that don't map to a RiskLevel
|
|
302
|
+
* (e.g., GPAI providers need ["all", "gpai"]).
|
|
303
|
+
*/
|
|
304
|
+
export function getTimelineForAudiences(
|
|
305
|
+
audiences: AffectedAudience[]
|
|
306
|
+
): (DeadlinePhase & { status: DeadlineStatus })[] {
|
|
307
|
+
return EU_AI_ACT_TIMELINE.filter((phase) =>
|
|
308
|
+
phase.obligations.some((o) => o.audiences.some((a) => audiences.includes(a)))
|
|
309
|
+
).map((phase) => ({
|
|
310
|
+
...phase,
|
|
311
|
+
status: getDeadlineStatus(phase.isoDate),
|
|
312
|
+
}));
|
|
313
|
+
}
|