@amirdaraee/namewise 0.4.0 → 0.4.1
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/.github/workflows/build.yml +6 -0
- package/.github/workflows/publish.yml +59 -11
- package/.github/workflows/test.yml +1 -0
- package/RELEASE.md +15 -3
- package/dist/services/claude-service.d.ts.map +1 -1
- package/dist/services/claude-service.js +9 -55
- package/dist/services/claude-service.js.map +1 -1
- package/dist/services/lmstudio-service.d.ts.map +1 -1
- package/dist/services/lmstudio-service.js +9 -19
- package/dist/services/lmstudio-service.js.map +1 -1
- package/dist/services/ollama-service.d.ts.map +1 -1
- package/dist/services/ollama-service.js +9 -19
- package/dist/services/ollama-service.js.map +1 -1
- package/dist/services/openai-service.d.ts.map +1 -1
- package/dist/services/openai-service.js +9 -55
- package/dist/services/openai-service.js.map +1 -1
- package/dist/utils/ai-prompts.d.ts +20 -0
- package/dist/utils/ai-prompts.d.ts.map +1 -0
- package/dist/utils/ai-prompts.js +71 -0
- package/dist/utils/ai-prompts.js.map +1 -0
- package/package.json +1 -1
- package/src/services/claude-service.ts +10 -48
- package/src/services/lmstudio-service.ts +11 -20
- package/src/services/ollama-service.ts +11 -20
- package/src/services/openai-service.ts +10 -48
- package/src/utils/ai-prompts.ts +76 -0
- package/tests/integration/ai-prompting.test.ts +386 -0
- package/tests/integration/person-name-extraction.test.ts +440 -0
- package/tests/unit/services/lmstudio-service.test.ts +4 -4
- package/tests/unit/services/ollama-service.test.ts +4 -4
- package/tests/unit/utils/ai-prompts.test.ts +213 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import { AIProvider, FileInfo } from '../types/index.js';
|
|
3
|
-
import { applyNamingConvention,
|
|
4
|
-
import {
|
|
3
|
+
import { applyNamingConvention, NamingConvention } from '../utils/naming-conventions.js';
|
|
4
|
+
import { FileCategory } from '../utils/file-templates.js';
|
|
5
|
+
import { buildFileNamePrompt } from '../utils/ai-prompts.js';
|
|
5
6
|
|
|
6
7
|
export class ClaudeService implements AIProvider {
|
|
7
8
|
name = 'Claude';
|
|
@@ -16,53 +17,14 @@ export class ClaudeService implements AIProvider {
|
|
|
16
17
|
async generateFileName(content: string, originalName: string, namingConvention: string = 'kebab-case', category: string = 'general', fileInfo?: FileInfo): Promise<string> {
|
|
17
18
|
const convention = namingConvention as NamingConvention;
|
|
18
19
|
const fileCategory = category as FileCategory;
|
|
19
|
-
const namingInstructions = getNamingInstructions(convention);
|
|
20
|
-
const templateInstructions = getTemplateInstructions(fileCategory);
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
|
|
30
|
-
- Parent folder: ${fileInfo.parentFolder}
|
|
31
|
-
- Folder path: ${fileInfo.folderPath.join(' > ')}`;
|
|
32
|
-
|
|
33
|
-
if (fileInfo.documentMetadata) {
|
|
34
|
-
const meta = fileInfo.documentMetadata;
|
|
35
|
-
metadataContext += `
|
|
36
|
-
Document Properties:`;
|
|
37
|
-
if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
|
|
38
|
-
if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
|
|
39
|
-
if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
|
|
40
|
-
if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
|
|
41
|
-
if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
|
|
42
|
-
if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
|
|
43
|
-
if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
|
|
44
|
-
if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
|
|
45
|
-
if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const prompt = `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
|
|
50
|
-
- Descriptive and meaningful
|
|
51
|
-
- Professional and clean
|
|
52
|
-
- Between 3-8 words
|
|
53
|
-
- ${namingInstructions}
|
|
54
|
-
- ${templateInstructions}
|
|
55
|
-
- Do not include file extension
|
|
56
|
-
- Do not include personal names, dates, or template variables - just the core content description
|
|
57
|
-
- Only use letters, numbers, and appropriate separators for the naming convention
|
|
58
|
-
- Use all available context (metadata, folder context, document properties) to create the most accurate filename
|
|
59
|
-
|
|
60
|
-
${metadataContext}
|
|
61
|
-
|
|
62
|
-
Document content (first 2000 characters):
|
|
63
|
-
${content.substring(0, 2000)}
|
|
64
|
-
|
|
65
|
-
Respond with only the core filename (without personal info or dates) using the specified naming convention, no explanation.`;
|
|
21
|
+
const prompt = buildFileNamePrompt({
|
|
22
|
+
content,
|
|
23
|
+
originalName,
|
|
24
|
+
namingConvention: convention,
|
|
25
|
+
category: fileCategory,
|
|
26
|
+
fileInfo
|
|
27
|
+
});
|
|
66
28
|
|
|
67
29
|
try {
|
|
68
30
|
const response = await this.client.messages.create({
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { AIProvider, FileInfo } from '../types/index.js';
|
|
2
|
+
import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
|
|
3
|
+
import { NamingConvention } from '../utils/naming-conventions.js';
|
|
4
|
+
import { FileCategory } from '../utils/file-templates.js';
|
|
2
5
|
|
|
3
6
|
interface OpenAICompatibleResponse {
|
|
4
7
|
choices: Array<{
|
|
@@ -55,7 +58,7 @@ export class LMStudioService implements AIProvider {
|
|
|
55
58
|
messages: [
|
|
56
59
|
{
|
|
57
60
|
role: 'system',
|
|
58
|
-
content:
|
|
61
|
+
content: AI_SYSTEM_PROMPT
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
64
|
role: 'user',
|
|
@@ -85,25 +88,13 @@ export class LMStudioService implements AIProvider {
|
|
|
85
88
|
category: string,
|
|
86
89
|
fileInfo?: FileInfo
|
|
87
90
|
): string {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (meta.subject) prompt += `Subject: "${meta.subject}". `;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (fileInfo?.parentFolder && fileInfo.parentFolder !== '.') {
|
|
99
|
-
prompt += `Located in folder: "${fileInfo.parentFolder}". `;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
prompt += `\nOriginal filename: ${originalName}\n`;
|
|
103
|
-
prompt += `\nDocument content (first 2000 characters):\n${content.substring(0, 2000)}`;
|
|
104
|
-
prompt += `\n\nGenerate ONLY a descriptive filename (without extension) using ${namingConvention} format. Do not include any explanation or additional text.`;
|
|
105
|
-
|
|
106
|
-
return prompt;
|
|
91
|
+
return buildFileNamePrompt({
|
|
92
|
+
content,
|
|
93
|
+
originalName,
|
|
94
|
+
namingConvention: namingConvention as NamingConvention,
|
|
95
|
+
category: category as FileCategory,
|
|
96
|
+
fileInfo
|
|
97
|
+
});
|
|
107
98
|
}
|
|
108
99
|
|
|
109
100
|
private sanitizeFilename(filename: string): string {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { AIProvider, FileInfo } from '../types/index.js';
|
|
2
|
+
import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
|
|
3
|
+
import { NamingConvention } from '../utils/naming-conventions.js';
|
|
4
|
+
import { FileCategory } from '../utils/file-templates.js';
|
|
2
5
|
|
|
3
6
|
interface OllamaResponse {
|
|
4
7
|
model: string;
|
|
@@ -43,7 +46,7 @@ export class OllamaService implements AIProvider {
|
|
|
43
46
|
messages: [
|
|
44
47
|
{
|
|
45
48
|
role: 'system',
|
|
46
|
-
content:
|
|
49
|
+
content: AI_SYSTEM_PROMPT
|
|
47
50
|
},
|
|
48
51
|
{
|
|
49
52
|
role: 'user',
|
|
@@ -71,25 +74,13 @@ export class OllamaService implements AIProvider {
|
|
|
71
74
|
category: string,
|
|
72
75
|
fileInfo?: FileInfo
|
|
73
76
|
): string {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (meta.subject) prompt += `Subject: "${meta.subject}". `;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (fileInfo?.parentFolder && fileInfo.parentFolder !== '.') {
|
|
85
|
-
prompt += `Located in folder: "${fileInfo.parentFolder}". `;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
prompt += `\nOriginal filename: ${originalName}\n`;
|
|
89
|
-
prompt += `\nDocument content (first 2000 characters):\n${content.substring(0, 2000)}`;
|
|
90
|
-
prompt += `\n\nGenerate ONLY a descriptive filename (without extension) using ${namingConvention} format. Do not include any explanation or additional text.`;
|
|
91
|
-
|
|
92
|
-
return prompt;
|
|
77
|
+
return buildFileNamePrompt({
|
|
78
|
+
content,
|
|
79
|
+
originalName,
|
|
80
|
+
namingConvention: namingConvention as NamingConvention,
|
|
81
|
+
category: category as FileCategory,
|
|
82
|
+
fileInfo
|
|
83
|
+
});
|
|
93
84
|
}
|
|
94
85
|
|
|
95
86
|
private sanitizeFilename(filename: string): string {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
2
|
import { AIProvider, FileInfo } from '../types/index.js';
|
|
3
|
-
import { applyNamingConvention,
|
|
4
|
-
import {
|
|
3
|
+
import { applyNamingConvention, NamingConvention } from '../utils/naming-conventions.js';
|
|
4
|
+
import { FileCategory } from '../utils/file-templates.js';
|
|
5
|
+
import { buildFileNamePrompt } from '../utils/ai-prompts.js';
|
|
5
6
|
|
|
6
7
|
export class OpenAIService implements AIProvider {
|
|
7
8
|
name = 'OpenAI';
|
|
@@ -16,53 +17,14 @@ export class OpenAIService implements AIProvider {
|
|
|
16
17
|
async generateFileName(content: string, originalName: string, namingConvention: string = 'kebab-case', category: string = 'general', fileInfo?: FileInfo): Promise<string> {
|
|
17
18
|
const convention = namingConvention as NamingConvention;
|
|
18
19
|
const fileCategory = category as FileCategory;
|
|
19
|
-
const namingInstructions = getNamingInstructions(convention);
|
|
20
|
-
const templateInstructions = getTemplateInstructions(fileCategory);
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
|
|
30
|
-
- Parent folder: ${fileInfo.parentFolder}
|
|
31
|
-
- Folder path: ${fileInfo.folderPath.join(' > ')}`;
|
|
32
|
-
|
|
33
|
-
if (fileInfo.documentMetadata) {
|
|
34
|
-
const meta = fileInfo.documentMetadata;
|
|
35
|
-
metadataContext += `
|
|
36
|
-
Document Properties:`;
|
|
37
|
-
if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
|
|
38
|
-
if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
|
|
39
|
-
if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
|
|
40
|
-
if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
|
|
41
|
-
if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
|
|
42
|
-
if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
|
|
43
|
-
if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
|
|
44
|
-
if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
|
|
45
|
-
if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const prompt = `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
|
|
50
|
-
- Descriptive and meaningful
|
|
51
|
-
- Professional and clean
|
|
52
|
-
- Between 3-8 words
|
|
53
|
-
- ${namingInstructions}
|
|
54
|
-
- ${templateInstructions}
|
|
55
|
-
- Do not include file extension
|
|
56
|
-
- Do not include personal names, dates, or template variables - just the core content description
|
|
57
|
-
- Only use letters, numbers, and appropriate separators for the naming convention
|
|
58
|
-
- Use all available context (metadata, folder context, document properties) to create the most accurate filename
|
|
59
|
-
|
|
60
|
-
${metadataContext}
|
|
61
|
-
|
|
62
|
-
Document content (first 2000 characters):
|
|
63
|
-
${content.substring(0, 2000)}
|
|
64
|
-
|
|
65
|
-
Respond with only the core filename (without personal info or dates) using the specified naming convention, no explanation.`;
|
|
21
|
+
const prompt = buildFileNamePrompt({
|
|
22
|
+
content,
|
|
23
|
+
originalName,
|
|
24
|
+
namingConvention: convention,
|
|
25
|
+
category: fileCategory,
|
|
26
|
+
fileInfo
|
|
27
|
+
});
|
|
66
28
|
|
|
67
29
|
try {
|
|
68
30
|
const response = await this.client.chat.completions.create({
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { FileInfo } from '../types/index.js';
|
|
2
|
+
import { getNamingInstructions, NamingConvention } from './naming-conventions.js';
|
|
3
|
+
import { getTemplateInstructions, FileCategory } from './file-templates.js';
|
|
4
|
+
|
|
5
|
+
export interface PromptContext {
|
|
6
|
+
content: string;
|
|
7
|
+
originalName: string;
|
|
8
|
+
namingConvention: NamingConvention;
|
|
9
|
+
category: FileCategory;
|
|
10
|
+
fileInfo?: FileInfo;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Builds a standardized prompt for AI filename generation
|
|
15
|
+
* This prompt is used across all AI providers (Claude, OpenAI, LMStudio, Ollama)
|
|
16
|
+
*/
|
|
17
|
+
export function buildFileNamePrompt(context: PromptContext): string {
|
|
18
|
+
const { content, originalName, namingConvention, category, fileInfo } = context;
|
|
19
|
+
|
|
20
|
+
const namingInstructions = getNamingInstructions(namingConvention);
|
|
21
|
+
const templateInstructions = getTemplateInstructions(category);
|
|
22
|
+
|
|
23
|
+
// Build comprehensive context from all metadata
|
|
24
|
+
let metadataContext = '';
|
|
25
|
+
if (fileInfo) {
|
|
26
|
+
metadataContext += `File Information:
|
|
27
|
+
- Original filename: ${originalName}
|
|
28
|
+
- File size: ${Math.round(fileInfo.size / 1024)}KB
|
|
29
|
+
- Created: ${fileInfo.createdAt.toLocaleDateString()}
|
|
30
|
+
- Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
|
|
31
|
+
- Parent folder: ${fileInfo.parentFolder}
|
|
32
|
+
- Folder path: ${fileInfo.folderPath.join(' > ')}`;
|
|
33
|
+
|
|
34
|
+
if (fileInfo.documentMetadata) {
|
|
35
|
+
const meta = fileInfo.documentMetadata;
|
|
36
|
+
metadataContext += `
|
|
37
|
+
Document Properties:`;
|
|
38
|
+
if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
|
|
39
|
+
if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
|
|
40
|
+
if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
|
|
41
|
+
if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
|
|
42
|
+
if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
|
|
43
|
+
if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
|
|
44
|
+
if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
|
|
45
|
+
if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
|
|
46
|
+
if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
|
|
51
|
+
- Descriptive and meaningful
|
|
52
|
+
- Professional and clean
|
|
53
|
+
- Between 3-10 words
|
|
54
|
+
- ${namingInstructions}
|
|
55
|
+
- ${templateInstructions}
|
|
56
|
+
- Do not include file extension
|
|
57
|
+
- If the document is specifically for/about a person (based on content), include their name at the beginning
|
|
58
|
+
- Include dates only if they are essential to the document's identity (e.g., contracts, certificates)
|
|
59
|
+
- Ignore irrelevant folder names that don't describe the document content
|
|
60
|
+
- Only use letters, numbers, and appropriate separators for the naming convention
|
|
61
|
+
- Focus on the document's actual content and purpose, not just metadata
|
|
62
|
+
|
|
63
|
+
${metadataContext}
|
|
64
|
+
|
|
65
|
+
Document content (first 2000 characters):
|
|
66
|
+
${content.substring(0, 2000)}
|
|
67
|
+
|
|
68
|
+
Important: If this document is specifically for or about a particular person mentioned in the content, start the filename with their name. Otherwise, focus on the document's main purpose and content.
|
|
69
|
+
|
|
70
|
+
Respond with only the filename using the specified naming convention, no explanation.`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* System prompt for AI models that need a separate system message
|
|
75
|
+
*/
|
|
76
|
+
export const AI_SYSTEM_PROMPT = 'You are a helpful assistant that generates descriptive filenames based on document content. Always respond with just the filename, no explanation or additional text.';
|