Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/lib/aws-comprehend/aws-comprehend.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, it, expect, vi } from "vitest";
import { AWSComprehendPIIRedactor } from "./aws-comprehend";

// Mocking AWS SDK secara total dengan factory function
vi.mock("@aws-sdk/client-comprehend", () => {
return {
DetectPiiEntitiesCommand: vi.fn(),
ComprehendClient: class {
send = vi.fn().mockResolvedValue({
Entities: [
{ Type: "NAME", BeginOffset: 10, EndOffset: 13 },
{ Type: "PHONE", BeginOffset: 27, EndOffset: 38 }
]
});
}
};
});

describe("AWSComprehendPIIRedactor", () => {
it("harus menyunting teks dengan placeholder yang benar", async () => {
const redactor = new AWSComprehendPIIRedactor("us-east-1", "fake-key", "fake-secret");
const input = "Nama saya Sky dan nomor hp 08123456789";
const result = await redactor.redact(input);

expect(result).toContain("[REDACTED_NAME]");
expect(result).toContain("[REDACTED_PHONE]");
console.log("SUCCESS! HASIL REDACT:", result);
});
});

60 changes: 60 additions & 0 deletions src/lib/aws-comprehend/aws-comprehend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ComprehendClient, DetectPiiEntitiesCommand } from "@aws-sdk/client-comprehend";

/**
* AWSComprehendPIIRedactor
* Utilitas untuk mendeteksi dan menyunting data sensitif (PII) menggunakan AWS Comprehend.
*/
export class AWSComprehendPIIRedactor {
private client: ComprehendClient;

constructor(region: string, accessKeyId: string, secretAccessKey: string) {
this.client = new ComprehendClient({
region,
credentials: {
accessKeyId,
secretAccessKey,
},
});
}

/**
* Menyunting teks dengan mengganti entitas PII yang terdeteksi dengan placeholder.
*/
async redact(text: string, languageCode: string = "en"): Promise<string> {
const entities = await this.detectPii(text, languageCode);

// Kita urutkan entitas dari offset TERBESAR ke terkecil agar index tidak rusak saat editing
const sortedEntities = [...entities].sort((a, b) => b.BeginOffset - a.BeginOffset);

let redactedText = text;

for (const entity of sortedEntities) {
const placeholder = `[REDACTED_${entity.Type}]`;
redactedText =
redactedText.slice(0, entity.BeginOffset) +
placeholder +
redactedText.slice(entity.EndOffset);
}

return redactedText;
}

/**
* Memanggil AWS Comprehend untuk mendeteksi entitas PII.
*/
async detectPii(text: string, languageCode: string) {
const command = new DetectPiiEntitiesCommand({
Text: text,
LanguageCode: languageCode,
});

try {
const response = await this.client.send(command);
return response.Entities || [];
} catch (error) {
console.error("AWS Comprehend Error:", error);
throw error;
}
}
}

11 changes: 11 additions & 0 deletions src/lib/smart-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { bandit } from '@oraclaw/bandit';

const provider = bandit({
arms: [
{ id: 'openai', name: 'OpenAI', pulls: tokensUsed.openai, totalReward: success.openai },
{ id: 'palm', name: 'Google Palm', pulls: tokensUsed.palm, totalReward: success.palm },
{ id: 'cohere', name: 'Cohere', pulls: tokensUsed.cohere, totalReward: success.cohere },
],
strategy: 'ucb1' // Ini kunci buat dapetin $200!
});

Loading