is-it-spam 0.1.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/README.md +73 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +107 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# isSpamLLM
|
|
2
|
+
|
|
3
|
+
Simple LLM-based spam classifier. Works with any OpenAI-compatible API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install spam-classifier
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createClassifier } from "spam-classifier";
|
|
15
|
+
|
|
16
|
+
// Set up once
|
|
17
|
+
const isSpam = createClassifier({
|
|
18
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Use anywhere - returns true/false
|
|
22
|
+
if (await isSpam("Need a quote for your services")) {
|
|
23
|
+
// spam - ignore it
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Or get details
|
|
27
|
+
const result = await isSpam.details("Need a quote");
|
|
28
|
+
// { isSpam: false, confidence: 0.95, reason: "Customer requesting quote" }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
| Option | Type | Default | Description |
|
|
34
|
+
|--------|------|---------|-------------|
|
|
35
|
+
| `apiKey` | string | **required** | API key |
|
|
36
|
+
| `model` | string | `"openai/gpt-4o-mini"` | Model to use |
|
|
37
|
+
| `baseUrl` | string | OpenRouter | API endpoint |
|
|
38
|
+
| `zdr` | boolean | `false` | Enable Zero Data Retention |
|
|
39
|
+
|
|
40
|
+
## Providers
|
|
41
|
+
|
|
42
|
+
Works with any OpenAI-compatible API:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// OpenRouter (default)
|
|
46
|
+
const isSpam = createClassifier({ apiKey: process.env.OPENROUTER_API_KEY! });
|
|
47
|
+
|
|
48
|
+
// OpenAI directly
|
|
49
|
+
const isSpam = createClassifier({
|
|
50
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
51
|
+
baseUrl: "https://api.openai.com/v1/chat/completions",
|
|
52
|
+
model: "gpt-4o-mini",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Local LLM
|
|
56
|
+
const isSpam = createClassifier({
|
|
57
|
+
apiKey: "not-needed",
|
|
58
|
+
baseUrl: "http://localhost:11434/v1/chat/completions",
|
|
59
|
+
model: "llama3",
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Fail-Safe
|
|
64
|
+
|
|
65
|
+
Returns `false` (legitimate) if the API fails, so you never block real customers.
|
|
66
|
+
|
|
67
|
+
## Cost
|
|
68
|
+
|
|
69
|
+
~$0.0002 per classification. 10,000 submissions = ~$2.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* isSpamLLM
|
|
3
|
+
*
|
|
4
|
+
* Simple LLM-based spam classifier.
|
|
5
|
+
* Works with any OpenAI-compatible API.
|
|
6
|
+
*/
|
|
7
|
+
export interface ClassifierOptions {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
model?: string;
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
zdr?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface ClassifyResult {
|
|
14
|
+
isSpam: boolean;
|
|
15
|
+
confidence?: number;
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
type Classifier = ((content: string) => Promise<boolean>) & {
|
|
19
|
+
details: (content: string) => Promise<ClassifyResult>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Create a spam classifier function.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // Set up once
|
|
27
|
+
* const isSpam = createClassifier({
|
|
28
|
+
* apiKey: process.env.OPENROUTER_API_KEY!,
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Use anywhere
|
|
32
|
+
* if (await isSpam("Need a quote")) {
|
|
33
|
+
* // spam
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* // Or get details
|
|
37
|
+
* const result = await isSpam.details("Need a quote");
|
|
38
|
+
* // { isSpam: false, confidence: 0.95, reason: "..." }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function createClassifier(options: ClassifierOptions): Classifier;
|
|
42
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* isSpamLLM
|
|
3
|
+
*
|
|
4
|
+
* Simple LLM-based spam classifier.
|
|
5
|
+
* Works with any OpenAI-compatible API.
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
8
|
+
const DEFAULT_MODEL = "openai/gpt-5.4-nano";
|
|
9
|
+
const SYSTEM_PROMPT = `You are a spam filter.
|
|
10
|
+
Determine if the input is spam or a legitimate message.
|
|
11
|
+
|
|
12
|
+
LEGITIMATE: genuine inquiries, customer questions, support requests, etc.
|
|
13
|
+
SPAM: web design pitches, SEO offers, lead gen, "grow your business", partnership requests, unsolicited sales pitches, etc.
|
|
14
|
+
|
|
15
|
+
Key indicator: Is this a sales pitch TO the recipient or a genuine message FROM someone?
|
|
16
|
+
|
|
17
|
+
Respond with JSON: {"is_spam": boolean, "confidence": number, "reason": string}`;
|
|
18
|
+
async function classify(content, options) {
|
|
19
|
+
const { apiKey, model = DEFAULT_MODEL, baseUrl = DEFAULT_BASE_URL, zdr = false, } = options;
|
|
20
|
+
const body = {
|
|
21
|
+
model,
|
|
22
|
+
messages: [
|
|
23
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
24
|
+
{ role: "user", content: `Classify this:\n\n${content}` },
|
|
25
|
+
],
|
|
26
|
+
response_format: {
|
|
27
|
+
type: "json_schema",
|
|
28
|
+
json_schema: {
|
|
29
|
+
name: "spam_classification",
|
|
30
|
+
strict: true,
|
|
31
|
+
schema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
is_spam: { type: "boolean" },
|
|
35
|
+
confidence: { type: "number" },
|
|
36
|
+
reason: { type: "string" },
|
|
37
|
+
},
|
|
38
|
+
required: ["is_spam", "confidence", "reason"],
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
temperature: 0,
|
|
44
|
+
max_tokens: 200,
|
|
45
|
+
};
|
|
46
|
+
if (zdr) {
|
|
47
|
+
body.provider = { zdr: true };
|
|
48
|
+
}
|
|
49
|
+
const response = await fetch(baseUrl, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: {
|
|
52
|
+
Authorization: `Bearer ${apiKey}`,
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify(body),
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`API error: ${response.status}`);
|
|
59
|
+
}
|
|
60
|
+
const data = (await response.json());
|
|
61
|
+
const parsed = JSON.parse(data.choices[0].message.content);
|
|
62
|
+
return {
|
|
63
|
+
isSpam: parsed.is_spam,
|
|
64
|
+
confidence: parsed.confidence,
|
|
65
|
+
reason: parsed.reason,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a spam classifier function.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // Set up once
|
|
74
|
+
* const isSpam = createClassifier({
|
|
75
|
+
* apiKey: process.env.OPENROUTER_API_KEY!,
|
|
76
|
+
* });
|
|
77
|
+
*
|
|
78
|
+
* // Use anywhere
|
|
79
|
+
* if (await isSpam("Need a quote")) {
|
|
80
|
+
* // spam
|
|
81
|
+
* }
|
|
82
|
+
*
|
|
83
|
+
* // Or get details
|
|
84
|
+
* const result = await isSpam.details("Need a quote");
|
|
85
|
+
* // { isSpam: false, confidence: 0.95, reason: "..." }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function createClassifier(options) {
|
|
89
|
+
const classifier = async (content) => {
|
|
90
|
+
try {
|
|
91
|
+
const result = await classify(content, options);
|
|
92
|
+
return result.isSpam;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false; // Fail open
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
classifier.details = async (content) => {
|
|
99
|
+
try {
|
|
100
|
+
return await classify(content, options);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return { isSpam: false, confidence: 0, reason: "Classification failed" };
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return classifier;
|
|
107
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "is-it-spam",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "LLM-based spam classifier. Works with any OpenAI-compatible API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"test": "bun test"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"spam",
|
|
23
|
+
"classifier",
|
|
24
|
+
"llm",
|
|
25
|
+
"form",
|
|
26
|
+
"openai",
|
|
27
|
+
"openrouter"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.7.0"
|
|
32
|
+
}
|
|
33
|
+
}
|