@realtimex/email-automator 2.3.6 → 2.3.8
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 +11 -1
- package/api/server.ts +2 -2
- package/api/src/services/intelligence.ts +16 -17
- package/dist/api/server.js +2 -2
- package/dist/api/src/services/intelligence.js +14 -14
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -60,7 +60,17 @@ npx @realtimex/email-automator-setup
|
|
|
60
60
|
npx @realtimex/email-automator-deploy
|
|
61
61
|
|
|
62
62
|
# Start Email Automator
|
|
63
|
-
npx @realtimex/email-automator
|
|
63
|
+
npx @realtimex/email-automator --port 3004
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Option 1b: Global Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Install globally
|
|
70
|
+
npm install -g @realtimex/email-automator
|
|
71
|
+
|
|
72
|
+
# Then run directly
|
|
73
|
+
email-automator --port 3004
|
|
64
74
|
```
|
|
65
75
|
|
|
66
76
|
### Option 2: Clone and Install
|
package/api/server.ts
CHANGED
|
@@ -69,8 +69,8 @@ app.use('/api', routes);
|
|
|
69
69
|
|
|
70
70
|
// Robust resolution for static assets (dist folder)
|
|
71
71
|
function getDistPath() {
|
|
72
|
-
// 1. Check environment variable override
|
|
73
|
-
if (process.env.ELECTRON_STATIC_PATH && existsSync(process.env.ELECTRON_STATIC_PATH)) {
|
|
72
|
+
// 1. Check environment variable override (must contain index.html)
|
|
73
|
+
if (process.env.ELECTRON_STATIC_PATH && existsSync(join(process.env.ELECTRON_STATIC_PATH, 'index.html'))) {
|
|
74
74
|
return process.env.ELECTRON_STATIC_PATH;
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import Instructor from '@instructor-ai/instructor';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { config } from '../config/index.js';
|
|
5
4
|
import { createLogger } from '../utils/logger.js';
|
|
@@ -48,14 +47,14 @@ export interface EmailContext {
|
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
export class IntelligenceService {
|
|
51
|
-
private client:
|
|
52
|
-
private model: string;
|
|
50
|
+
private client: OpenAI | null = null;
|
|
51
|
+
private model: string = 'gpt-4o-mini';
|
|
53
52
|
private isConfigured: boolean = false;
|
|
54
53
|
|
|
55
54
|
constructor(overrides?: { model?: string; baseUrl?: string; apiKey?: string }) {
|
|
56
55
|
const apiKey = overrides?.apiKey || config.llm.apiKey;
|
|
57
56
|
const baseUrl = overrides?.baseUrl || config.llm.baseUrl;
|
|
58
|
-
this.model = overrides?.model || config.llm.model;
|
|
57
|
+
this.model = overrides?.model || config.llm.model || 'gpt-4o-mini';
|
|
59
58
|
|
|
60
59
|
// Allow local LLM servers (LM Studio, Ollama) or custom endpoints that don't need API keys
|
|
61
60
|
// We assume any custom baseUrl might be a local/private instance.
|
|
@@ -67,16 +66,11 @@ export class IntelligenceService {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
try {
|
|
70
|
-
|
|
69
|
+
this.client = new OpenAI({
|
|
71
70
|
apiKey: apiKey || 'not-needed-for-local', // Placeholder for local LLMs
|
|
72
71
|
baseURL: baseUrl,
|
|
73
72
|
});
|
|
74
73
|
|
|
75
|
-
this.client = Instructor({
|
|
76
|
-
client: oai,
|
|
77
|
-
mode: 'MD_JSON',
|
|
78
|
-
});
|
|
79
|
-
|
|
80
74
|
this.isConfigured = true;
|
|
81
75
|
logger.info('Intelligence service initialized', { model: this.model, baseUrl: baseUrl || 'default' });
|
|
82
76
|
} catch (error) {
|
|
@@ -117,12 +111,16 @@ Do NOT include any greetings, chatter, or special tokens like <|channel|> in you
|
|
|
117
111
|
Return ONLY a valid JSON object.
|
|
118
112
|
|
|
119
113
|
Definitions for Categories:
|
|
120
|
-
- "
|
|
114
|
+
- "spam": Junk, suspicious, unwanted
|
|
115
|
+
- "newsletter": Subscribed content, digests
|
|
121
116
|
- "promotional": Marketing, sales, discounts
|
|
122
117
|
- "transactional": Receipts, shipping, confirmations
|
|
123
118
|
- "social": LinkedIn, friends, social updates
|
|
124
|
-
- "
|
|
125
|
-
- "
|
|
119
|
+
- "support": Help desk, customer service
|
|
120
|
+
- "client": Business clients, customers
|
|
121
|
+
- "internal": Company internal communications
|
|
122
|
+
- "personal": Friends, family, personal matters
|
|
123
|
+
- "other": Anything else
|
|
126
124
|
|
|
127
125
|
Context:
|
|
128
126
|
- Current Date: ${new Date().toISOString()}
|
|
@@ -162,13 +160,14 @@ REQUIRED JSON STRUCTURE:
|
|
|
162
160
|
|
|
163
161
|
let rawResponse = '';
|
|
164
162
|
try {
|
|
165
|
-
//
|
|
166
|
-
const response = await this.client
|
|
163
|
+
// Request JSON response format for reliable parsing
|
|
164
|
+
const response = await this.client!.chat.completions.create({
|
|
167
165
|
model: this.model,
|
|
168
166
|
messages: [
|
|
169
167
|
{ role: 'system', content: systemPrompt },
|
|
170
168
|
{ role: 'user', content: cleanedContent || '[Empty email body]' },
|
|
171
169
|
],
|
|
170
|
+
response_format: { type: 'json_object' },
|
|
172
171
|
temperature: 0.1,
|
|
173
172
|
});
|
|
174
173
|
|
|
@@ -223,7 +222,7 @@ REQUIRED JSON STRUCTURE:
|
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
try {
|
|
226
|
-
const response = await this.client
|
|
225
|
+
const response = await this.client!.chat.completions.create({
|
|
227
226
|
model: this.model,
|
|
228
227
|
messages: [
|
|
229
228
|
{
|
|
@@ -257,7 +256,7 @@ Please write a reply.`,
|
|
|
257
256
|
}
|
|
258
257
|
|
|
259
258
|
try {
|
|
260
|
-
await this.client
|
|
259
|
+
await this.client!.chat.completions.create({
|
|
261
260
|
model: this.model,
|
|
262
261
|
messages: [{ role: 'user', content: 'Say "Connection Successful"' }],
|
|
263
262
|
max_tokens: 5,
|
package/dist/api/server.js
CHANGED
|
@@ -58,8 +58,8 @@ app.use('/api', apiRateLimit);
|
|
|
58
58
|
app.use('/api', routes);
|
|
59
59
|
// Robust resolution for static assets (dist folder)
|
|
60
60
|
function getDistPath() {
|
|
61
|
-
// 1. Check environment variable override
|
|
62
|
-
if (process.env.ELECTRON_STATIC_PATH && existsSync(process.env.ELECTRON_STATIC_PATH)) {
|
|
61
|
+
// 1. Check environment variable override (must contain index.html)
|
|
62
|
+
if (process.env.ELECTRON_STATIC_PATH && existsSync(join(process.env.ELECTRON_STATIC_PATH, 'index.html'))) {
|
|
63
63
|
return process.env.ELECTRON_STATIC_PATH;
|
|
64
64
|
}
|
|
65
65
|
// 2. Try to find dist relative to packageRoot (from config)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import Instructor from '@instructor-ai/instructor';
|
|
3
2
|
import { z } from 'zod';
|
|
4
3
|
import { config } from '../config/index.js';
|
|
5
4
|
import { createLogger } from '../utils/logger.js';
|
|
@@ -26,13 +25,13 @@ export const EmailAnalysisSchema = z.object({
|
|
|
26
25
|
.describe('Action items mentioned in the email'),
|
|
27
26
|
});
|
|
28
27
|
export class IntelligenceService {
|
|
29
|
-
client;
|
|
30
|
-
model;
|
|
28
|
+
client = null;
|
|
29
|
+
model = 'gpt-4o-mini';
|
|
31
30
|
isConfigured = false;
|
|
32
31
|
constructor(overrides) {
|
|
33
32
|
const apiKey = overrides?.apiKey || config.llm.apiKey;
|
|
34
33
|
const baseUrl = overrides?.baseUrl || config.llm.baseUrl;
|
|
35
|
-
this.model = overrides?.model || config.llm.model;
|
|
34
|
+
this.model = overrides?.model || config.llm.model || 'gpt-4o-mini';
|
|
36
35
|
// Allow local LLM servers (LM Studio, Ollama) or custom endpoints that don't need API keys
|
|
37
36
|
// We assume any custom baseUrl might be a local/private instance.
|
|
38
37
|
const isCustomEndpoint = baseUrl && !baseUrl.includes('api.openai.com');
|
|
@@ -41,14 +40,10 @@ export class IntelligenceService {
|
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
42
|
try {
|
|
44
|
-
|
|
43
|
+
this.client = new OpenAI({
|
|
45
44
|
apiKey: apiKey || 'not-needed-for-local', // Placeholder for local LLMs
|
|
46
45
|
baseURL: baseUrl,
|
|
47
46
|
});
|
|
48
|
-
this.client = Instructor({
|
|
49
|
-
client: oai,
|
|
50
|
-
mode: 'MD_JSON',
|
|
51
|
-
});
|
|
52
47
|
this.isConfigured = true;
|
|
53
48
|
logger.info('Intelligence service initialized', { model: this.model, baseUrl: baseUrl || 'default' });
|
|
54
49
|
}
|
|
@@ -89,12 +84,16 @@ Do NOT include any greetings, chatter, or special tokens like <|channel|> in you
|
|
|
89
84
|
Return ONLY a valid JSON object.
|
|
90
85
|
|
|
91
86
|
Definitions for Categories:
|
|
92
|
-
- "
|
|
87
|
+
- "spam": Junk, suspicious, unwanted
|
|
88
|
+
- "newsletter": Subscribed content, digests
|
|
93
89
|
- "promotional": Marketing, sales, discounts
|
|
94
90
|
- "transactional": Receipts, shipping, confirmations
|
|
95
91
|
- "social": LinkedIn, friends, social updates
|
|
96
|
-
- "
|
|
97
|
-
- "
|
|
92
|
+
- "support": Help desk, customer service
|
|
93
|
+
- "client": Business clients, customers
|
|
94
|
+
- "internal": Company internal communications
|
|
95
|
+
- "personal": Friends, family, personal matters
|
|
96
|
+
- "other": Anything else
|
|
98
97
|
|
|
99
98
|
Context:
|
|
100
99
|
- Current Date: ${new Date().toISOString()}
|
|
@@ -133,13 +132,14 @@ REQUIRED JSON STRUCTURE:
|
|
|
133
132
|
}
|
|
134
133
|
let rawResponse = '';
|
|
135
134
|
try {
|
|
136
|
-
//
|
|
137
|
-
const response = await this.client.
|
|
135
|
+
// Request JSON response format for reliable parsing
|
|
136
|
+
const response = await this.client.chat.completions.create({
|
|
138
137
|
model: this.model,
|
|
139
138
|
messages: [
|
|
140
139
|
{ role: 'system', content: systemPrompt },
|
|
141
140
|
{ role: 'user', content: cleanedContent || '[Empty email body]' },
|
|
142
141
|
],
|
|
142
|
+
response_format: { type: 'json_object' },
|
|
143
143
|
temperature: 0.1,
|
|
144
144
|
});
|
|
145
145
|
rawResponse = response.choices[0]?.message?.content || '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realtimex/email-automator",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/api/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -69,7 +69,6 @@
|
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
71
|
"@azure/msal-node": "^3.8.5",
|
|
72
|
-
"@instructor-ai/instructor": "^1.7.0",
|
|
73
72
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
74
73
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
75
74
|
"@radix-ui/react-label": "^2.1.8",
|