@actuate-media/plugin-ai 0.0.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/dist/admin/AIScorePanel.d.ts +19 -0
- package/dist/admin/AIScorePanel.d.ts.map +1 -0
- package/dist/admin/AIScorePanel.js +35 -0
- package/dist/admin/AIScorePanel.js.map +1 -0
- package/dist/admin/BrandVoiceSettings.d.ts +11 -0
- package/dist/admin/BrandVoiceSettings.d.ts.map +1 -0
- package/dist/admin/BrandVoiceSettings.js +73 -0
- package/dist/admin/BrandVoiceSettings.js.map +1 -0
- package/dist/admin/ContentScoreDashboard.d.ts +15 -0
- package/dist/admin/ContentScoreDashboard.d.ts.map +1 -0
- package/dist/admin/ContentScoreDashboard.js +30 -0
- package/dist/admin/ContentScoreDashboard.js.map +1 -0
- package/dist/admin/WritingAssistantToolbar.d.ts +13 -0
- package/dist/admin/WritingAssistantToolbar.d.ts.map +1 -0
- package/dist/admin/WritingAssistantToolbar.js +49 -0
- package/dist/admin/WritingAssistantToolbar.js.map +1 -0
- package/dist/brand/consistency-checker.d.ts +17 -0
- package/dist/brand/consistency-checker.d.ts.map +1 -0
- package/dist/brand/consistency-checker.js +47 -0
- package/dist/brand/consistency-checker.js.map +1 -0
- package/dist/brand/profile-builder.d.ts +10 -0
- package/dist/brand/profile-builder.d.ts.map +1 -0
- package/dist/brand/profile-builder.js +93 -0
- package/dist/brand/profile-builder.js.map +1 -0
- package/dist/brand/voice-profile.d.ts +30 -0
- package/dist/brand/voice-profile.d.ts.map +1 -0
- package/dist/brand/voice-profile.js +2 -0
- package/dist/brand/voice-profile.js.map +1 -0
- package/dist/collections.d.ts +3 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +2 -0
- package/dist/collections.js.map +1 -0
- package/dist/fields.d.ts +3 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +2 -0
- package/dist/fields.js.map +1 -0
- package/dist/hooks.d.ts +5 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +75 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/links/smart-links.d.ts +18 -0
- package/dist/links/smart-links.d.ts.map +1 -0
- package/dist/links/smart-links.js +44 -0
- package/dist/links/smart-links.js.map +1 -0
- package/dist/media/alt-text-generator.d.ts +16 -0
- package/dist/media/alt-text-generator.d.ts.map +1 -0
- package/dist/media/alt-text-generator.js +50 -0
- package/dist/media/alt-text-generator.js.map +1 -0
- package/dist/media/auto-tagger.d.ts +21 -0
- package/dist/media/auto-tagger.d.ts.map +1 -0
- package/dist/media/auto-tagger.js +43 -0
- package/dist/media/auto-tagger.js.map +1 -0
- package/dist/providers/anthropic.d.ts +8 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +119 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/index.d.ts +34 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +35 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +110 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/scoring/accessibility.d.ts +24 -0
- package/dist/scoring/accessibility.d.ts.map +1 -0
- package/dist/scoring/accessibility.js +87 -0
- package/dist/scoring/accessibility.js.map +1 -0
- package/dist/scoring/brand-alignment.d.ts +12 -0
- package/dist/scoring/brand-alignment.d.ts.map +1 -0
- package/dist/scoring/brand-alignment.js +46 -0
- package/dist/scoring/brand-alignment.js.map +1 -0
- package/dist/scoring/composite.d.ts +19 -0
- package/dist/scoring/composite.d.ts.map +1 -0
- package/dist/scoring/composite.js +38 -0
- package/dist/scoring/composite.js.map +1 -0
- package/dist/scoring/freshness.d.ts +15 -0
- package/dist/scoring/freshness.d.ts.map +1 -0
- package/dist/scoring/freshness.js +37 -0
- package/dist/scoring/freshness.js.map +1 -0
- package/dist/scoring/readability.d.ts +16 -0
- package/dist/scoring/readability.d.ts.map +1 -0
- package/dist/scoring/readability.js +77 -0
- package/dist/scoring/readability.js.map +1 -0
- package/dist/scoring/seo-score.d.ts +26 -0
- package/dist/scoring/seo-score.d.ts.map +1 -0
- package/dist/scoring/seo-score.js +53 -0
- package/dist/scoring/seo-score.js.map +1 -0
- package/dist/search/clustering.d.ts +20 -0
- package/dist/search/clustering.d.ts.map +1 -0
- package/dist/search/clustering.js +91 -0
- package/dist/search/clustering.js.map +1 -0
- package/dist/search/embeddings.d.ts +14 -0
- package/dist/search/embeddings.d.ts.map +1 -0
- package/dist/search/embeddings.js +42 -0
- package/dist/search/embeddings.js.map +1 -0
- package/dist/search/semantic-search.d.ts +31 -0
- package/dist/search/semantic-search.d.ts.map +1 -0
- package/dist/search/semantic-search.js +59 -0
- package/dist/search/semantic-search.js.map +1 -0
- package/dist/translation/translator.d.ts +24 -0
- package/dist/translation/translator.d.ts.map +1 -0
- package/dist/translation/translator.js +42 -0
- package/dist/translation/translator.js.map +1 -0
- package/dist/writing/assistant.d.ts +28 -0
- package/dist/writing/assistant.d.ts.map +1 -0
- package/dist/writing/assistant.js +110 -0
- package/dist/writing/assistant.js.map +1 -0
- package/dist/writing/inline-suggestions.d.ts +18 -0
- package/dist/writing/inline-suggestions.d.ts.map +1 -0
- package/dist/writing/inline-suggestions.js +38 -0
- package/dist/writing/inline-suggestions.js.map +1 -0
- package/dist/writing/meta-generator.d.ts +13 -0
- package/dist/writing/meta-generator.d.ts.map +1 -0
- package/dist/writing/meta-generator.js +41 -0
- package/dist/writing/meta-generator.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readability.js","sourceRoot":"","sources":["../../src/scoring/readability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SEOContent {
|
|
2
|
+
title: string;
|
|
3
|
+
metaDescription: string;
|
|
4
|
+
body: string;
|
|
5
|
+
targetKeyword?: string;
|
|
6
|
+
url?: string;
|
|
7
|
+
headings?: string[];
|
|
8
|
+
images?: Array<{
|
|
9
|
+
src: string;
|
|
10
|
+
alt?: string;
|
|
11
|
+
}>;
|
|
12
|
+
internalLinks?: string[];
|
|
13
|
+
externalLinks?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface SEOScoreResult {
|
|
16
|
+
score: number;
|
|
17
|
+
breakdown: Array<{
|
|
18
|
+
criterion: string;
|
|
19
|
+
score: number;
|
|
20
|
+
maxScore: number;
|
|
21
|
+
suggestion?: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/** Calculates an SEO score (0-100) for the given content against standard on-page criteria. */
|
|
25
|
+
export declare function calculateSEOScore(content: SEOContent): SEOScoreResult;
|
|
26
|
+
//# sourceMappingURL=seo-score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-score.d.ts","sourceRoot":"","sources":["../../src/scoring/seo-score.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,KAAK,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,cAAc,CA4DrE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Calculates an SEO score (0-100) for the given content against standard on-page criteria. */
|
|
2
|
+
export function calculateSEOScore(content) {
|
|
3
|
+
const breakdown = [];
|
|
4
|
+
const titleLen = content.title.length;
|
|
5
|
+
const titleScore = titleLen >= 30 && titleLen <= 60 ? 15 : titleLen > 0 ? 8 : 0;
|
|
6
|
+
breakdown.push({
|
|
7
|
+
criterion: "Title length",
|
|
8
|
+
score: titleScore,
|
|
9
|
+
maxScore: 15,
|
|
10
|
+
suggestion: titleLen < 30 ? "Title is too short — aim for 30-60 characters" : titleLen > 60 ? "Title is too long — keep under 60 characters" : undefined,
|
|
11
|
+
});
|
|
12
|
+
const metaLen = content.metaDescription.length;
|
|
13
|
+
const metaScore = metaLen >= 120 && metaLen <= 160 ? 10 : metaLen > 0 ? 5 : 0;
|
|
14
|
+
breakdown.push({
|
|
15
|
+
criterion: "Meta description length",
|
|
16
|
+
score: metaScore,
|
|
17
|
+
maxScore: 10,
|
|
18
|
+
suggestion: metaLen < 120 ? "Meta description should be 120-160 characters" : metaLen > 160 ? "Meta description is too long" : undefined,
|
|
19
|
+
});
|
|
20
|
+
if (content.targetKeyword) {
|
|
21
|
+
const kw = content.targetKeyword.toLowerCase();
|
|
22
|
+
const titleHasKw = content.title.toLowerCase().includes(kw) ? 10 : 0;
|
|
23
|
+
breakdown.push({ criterion: "Keyword in title", score: titleHasKw, maxScore: 10, suggestion: titleHasKw === 0 ? "Include target keyword in your title" : undefined });
|
|
24
|
+
const bodyKwCount = (content.body.toLowerCase().match(new RegExp(kw, "g")) || []).length;
|
|
25
|
+
const bodyWordCount = content.body.split(/\s+/).length;
|
|
26
|
+
const density = bodyWordCount > 0 ? (bodyKwCount / bodyWordCount) * 100 : 0;
|
|
27
|
+
const densityScore = density >= 0.5 && density <= 2.5 ? 10 : bodyKwCount > 0 ? 5 : 0;
|
|
28
|
+
breakdown.push({ criterion: "Keyword density", score: densityScore, maxScore: 10, suggestion: density < 0.5 ? "Increase keyword usage in body" : density > 2.5 ? "Reduce keyword stuffing" : undefined });
|
|
29
|
+
const headingHasKw = (content.headings ?? []).some((h) => h.toLowerCase().includes(kw));
|
|
30
|
+
breakdown.push({ criterion: "Keyword in headings", score: headingHasKw ? 10 : 0, maxScore: 10, suggestion: headingHasKw ? undefined : "Add target keyword to at least one heading" });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
breakdown.push({ criterion: "Keyword in title", score: 0, maxScore: 10, suggestion: "Set a target keyword" });
|
|
34
|
+
breakdown.push({ criterion: "Keyword density", score: 0, maxScore: 10, suggestion: "Set a target keyword" });
|
|
35
|
+
breakdown.push({ criterion: "Keyword in headings", score: 0, maxScore: 10, suggestion: "Set a target keyword" });
|
|
36
|
+
}
|
|
37
|
+
const headings = content.headings ?? [];
|
|
38
|
+
const headingScore = headings.length >= 2 ? 10 : headings.length === 1 ? 5 : 0;
|
|
39
|
+
breakdown.push({ criterion: "Heading structure", score: headingScore, maxScore: 10, suggestion: headings.length < 2 ? "Use at least two headings to structure content" : undefined });
|
|
40
|
+
const images = content.images ?? [];
|
|
41
|
+
const imagesWithAlt = images.filter((img) => img.alt && img.alt.trim().length > 0).length;
|
|
42
|
+
const altScore = images.length === 0 ? 5 : images.length === imagesWithAlt ? 10 : Math.round((imagesWithAlt / images.length) * 10);
|
|
43
|
+
breakdown.push({ criterion: "Image alt texts", score: altScore, maxScore: 10, suggestion: imagesWithAlt < images.length ? "Add alt text to all images" : undefined });
|
|
44
|
+
const linkCount = (content.internalLinks?.length ?? 0) + (content.externalLinks?.length ?? 0);
|
|
45
|
+
const linkScore = linkCount >= 3 ? 10 : linkCount >= 1 ? 5 : 0;
|
|
46
|
+
breakdown.push({ criterion: "Link count", score: linkScore, maxScore: 10, suggestion: linkCount < 3 ? "Add more internal and external links" : undefined });
|
|
47
|
+
const wordCount = content.body.split(/\s+/).filter(Boolean).length;
|
|
48
|
+
const lengthScore = wordCount >= 300 ? 15 : wordCount >= 150 ? 8 : wordCount > 0 ? 3 : 0;
|
|
49
|
+
breakdown.push({ criterion: "Content length", score: lengthScore, maxScore: 15, suggestion: wordCount < 300 ? "Aim for at least 300 words" : undefined });
|
|
50
|
+
const totalScore = breakdown.reduce((sum, b) => sum + b.score, 0);
|
|
51
|
+
return { score: totalScore, breakdown };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=seo-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-score.js","sourceRoot":"","sources":["../../src/scoring/seo-score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ClusterInput {
|
|
2
|
+
id: string;
|
|
3
|
+
text: string;
|
|
4
|
+
embedding?: number[];
|
|
5
|
+
}
|
|
6
|
+
export interface ClusterOptions {
|
|
7
|
+
numClusters?: number;
|
|
8
|
+
minClusterSize?: number;
|
|
9
|
+
algorithm?: "kmeans" | "dbscan";
|
|
10
|
+
}
|
|
11
|
+
export interface ContentCluster {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
items: ClusterInput[];
|
|
15
|
+
centroid: number[];
|
|
16
|
+
coherence: number;
|
|
17
|
+
}
|
|
18
|
+
/** Clusters content items by semantic similarity using the specified algorithm. */
|
|
19
|
+
export declare function clusterContent(items: ClusterInput[], options?: ClusterOptions): Promise<ContentCluster[]>;
|
|
20
|
+
//# sourceMappingURL=clustering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.d.ts","sourceRoot":"","sources":["../../src/search/clustering.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mFAAmF;AACnF,wBAAsB,cAAc,CAClC,KAAK,EAAE,YAAY,EAAE,EACrB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,EAAE,CAAC,CAwB3B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** Clusters content items by semantic similarity using the specified algorithm. */
|
|
2
|
+
export async function clusterContent(items, options) {
|
|
3
|
+
const algorithm = options?.algorithm ?? "kmeans";
|
|
4
|
+
const numClusters = options?.numClusters ?? Math.max(2, Math.floor(Math.sqrt(items.length)));
|
|
5
|
+
const minClusterSize = options?.minClusterSize ?? 2;
|
|
6
|
+
const embeddedItems = items.filter((item) => item.embedding && item.embedding.length > 0);
|
|
7
|
+
if (embeddedItems.length < minClusterSize) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
id: "cluster-0",
|
|
11
|
+
label: "All Content",
|
|
12
|
+
items,
|
|
13
|
+
centroid: [],
|
|
14
|
+
coherence: 1.0,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
if (algorithm === "kmeans") {
|
|
19
|
+
return kMeansClustering(embeddedItems, numClusters);
|
|
20
|
+
}
|
|
21
|
+
return dbscanClustering(embeddedItems, minClusterSize);
|
|
22
|
+
}
|
|
23
|
+
function kMeansClustering(items, k) {
|
|
24
|
+
const dimensions = items[0].embedding.length;
|
|
25
|
+
const centroids = items
|
|
26
|
+
.slice(0, k)
|
|
27
|
+
.map((item) => [...item.embedding]);
|
|
28
|
+
const assignments = new Array(items.length).fill(0);
|
|
29
|
+
for (let iteration = 0; iteration < 20; iteration++) {
|
|
30
|
+
for (let i = 0; i < items.length; i++) {
|
|
31
|
+
let minDist = Infinity;
|
|
32
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
33
|
+
const dist = euclideanDistance(items[i].embedding, centroids[c]);
|
|
34
|
+
if (dist < minDist) {
|
|
35
|
+
minDist = dist;
|
|
36
|
+
assignments[i] = c;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
41
|
+
const members = items.filter((_, i) => assignments[i] === c);
|
|
42
|
+
if (members.length === 0)
|
|
43
|
+
continue;
|
|
44
|
+
for (let d = 0; d < dimensions; d++) {
|
|
45
|
+
centroids[c][d] = members.reduce((sum, m) => sum + m.embedding[d], 0) / members.length;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const clusters = [];
|
|
50
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
51
|
+
const clusterItems = items.filter((_, i) => assignments[i] === c);
|
|
52
|
+
if (clusterItems.length === 0)
|
|
53
|
+
continue;
|
|
54
|
+
clusters.push({
|
|
55
|
+
id: `cluster-${c}`,
|
|
56
|
+
label: `Cluster ${c + 1}`,
|
|
57
|
+
items: clusterItems,
|
|
58
|
+
centroid: centroids[c],
|
|
59
|
+
coherence: calculateCoherence(clusterItems, centroids[c]),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return clusters;
|
|
63
|
+
}
|
|
64
|
+
function dbscanClustering(items, minSize) {
|
|
65
|
+
void minSize;
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
id: "cluster-0",
|
|
69
|
+
label: "DBSCAN Cluster",
|
|
70
|
+
items,
|
|
71
|
+
centroid: items[0].embedding ?? [],
|
|
72
|
+
coherence: 0.5,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
function euclideanDistance(a, b) {
|
|
77
|
+
let sum = 0;
|
|
78
|
+
for (let i = 0; i < a.length; i++) {
|
|
79
|
+
const diff = a[i] - b[i];
|
|
80
|
+
sum += diff * diff;
|
|
81
|
+
}
|
|
82
|
+
return Math.sqrt(sum);
|
|
83
|
+
}
|
|
84
|
+
function calculateCoherence(items, centroid) {
|
|
85
|
+
if (items.length === 0)
|
|
86
|
+
return 0;
|
|
87
|
+
const avgDist = items.reduce((sum, item) => sum + euclideanDistance(item.embedding, centroid), 0) /
|
|
88
|
+
items.length;
|
|
89
|
+
return 1 / (1 + avgDist);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=clustering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clustering.js","sourceRoot":"","sources":["../../src/search/clustering.ts"],"names":[],"mappings":"AAoBA,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAqB,EACrB,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,QAAQ,CAAC;IACjD,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,CAAC,CAAC;IAEpD,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE1F,IAAI,aAAa,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAC1C,OAAO;YACL;gBACE,EAAE,EAAE,WAAW;gBACf,KAAK,EAAE,aAAa;gBACpB,KAAK;gBACL,QAAQ,EAAE,EAAE;gBACZ,SAAS,EAAE,GAAG;aACf;SACF,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,gBAAgB,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,gBAAgB,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAqB,EAAE,CAAS;IACxD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,SAAU,CAAC,MAAM,CAAC;IAC/C,MAAM,SAAS,GAAe,KAAK;SAChC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,SAAU,CAAC,CAAC,CAAC;IAEvC,MAAM,WAAW,GAAG,IAAI,KAAK,CAAS,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5D,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,OAAO,GAAG,QAAQ,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,SAAU,EAAE,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC;gBACpE,IAAI,IAAI,GAAG,OAAO,EAAE,CAAC;oBACnB,OAAO,GAAG,IAAI,CAAC;oBACf,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAEnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,SAAU,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5F,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAExC,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,WAAW,CAAC,EAAE;YAClB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE;YACzB,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAE;YACvB,SAAS,EAAE,kBAAkB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAE,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAqB,EAAE,OAAe;IAC9D,KAAK,OAAO,CAAC;IACb,OAAO;QACL;YACE,EAAE,EAAE,WAAW;YACf,KAAK,EAAE,gBAAgB;YACvB,KAAK;YACL,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,SAAS,IAAI,EAAE;YACnC,SAAS,EAAE,GAAG;SACf;KACF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAW,EAAE,CAAW;IACjD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC3B,GAAG,IAAI,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAqB,EAAE,QAAkB;IACnE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,MAAM,OAAO,GACX,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,iBAAiB,CAAC,IAAI,CAAC,SAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAClF,KAAK,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AIProvider } from "../providers/index.js";
|
|
2
|
+
export interface EmbeddingResult {
|
|
3
|
+
vector: number[];
|
|
4
|
+
dimensions: number;
|
|
5
|
+
model: string;
|
|
6
|
+
tokenCount: number;
|
|
7
|
+
}
|
|
8
|
+
/** Generates a vector embedding for the given text. */
|
|
9
|
+
export declare function generateEmbedding(text: string, provider?: AIProvider): Promise<EmbeddingResult>;
|
|
10
|
+
/** Generates embeddings for multiple texts in batch. */
|
|
11
|
+
export declare function generateBatchEmbeddings(texts: string[], provider?: AIProvider): Promise<EmbeddingResult[]>;
|
|
12
|
+
/** Computes cosine similarity between two vectors, returning a value in [-1, 1]. */
|
|
13
|
+
export declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
14
|
+
//# sourceMappingURL=embeddings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/search/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uDAAuD;AACvD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,eAAe,CAAC,CAc1B;AAED,wDAAwD;AACxD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAM5B;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAoBjE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Generates a vector embedding for the given text. */
|
|
2
|
+
export async function generateEmbedding(text, provider) {
|
|
3
|
+
if (!provider) {
|
|
4
|
+
throw new Error("Embedding generation requires an AI provider");
|
|
5
|
+
}
|
|
6
|
+
const vector = await provider.generateEmbedding(text);
|
|
7
|
+
const tokenCount = Math.ceil(text.split(/\s+/).length * 1.3);
|
|
8
|
+
return {
|
|
9
|
+
vector,
|
|
10
|
+
dimensions: vector.length,
|
|
11
|
+
model: "provider-default",
|
|
12
|
+
tokenCount,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Generates embeddings for multiple texts in batch. */
|
|
16
|
+
export async function generateBatchEmbeddings(texts, provider) {
|
|
17
|
+
if (!provider) {
|
|
18
|
+
throw new Error("Embedding generation requires an AI provider");
|
|
19
|
+
}
|
|
20
|
+
return Promise.all(texts.map((text) => generateEmbedding(text, provider)));
|
|
21
|
+
}
|
|
22
|
+
/** Computes cosine similarity between two vectors, returning a value in [-1, 1]. */
|
|
23
|
+
export function cosineSimilarity(a, b) {
|
|
24
|
+
if (a.length !== b.length) {
|
|
25
|
+
throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
|
|
26
|
+
}
|
|
27
|
+
if (a.length === 0)
|
|
28
|
+
return 0;
|
|
29
|
+
let dotProduct = 0;
|
|
30
|
+
let magnitudeA = 0;
|
|
31
|
+
let magnitudeB = 0;
|
|
32
|
+
for (let i = 0; i < a.length; i++) {
|
|
33
|
+
dotProduct += a[i] * b[i];
|
|
34
|
+
magnitudeA += a[i] * a[i];
|
|
35
|
+
magnitudeB += b[i] * b[i];
|
|
36
|
+
}
|
|
37
|
+
const magnitude = Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB);
|
|
38
|
+
if (magnitude === 0)
|
|
39
|
+
return 0;
|
|
40
|
+
return dotProduct / magnitude;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/search/embeddings.ts"],"names":[],"mappings":"AASA,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,QAAqB;IAErB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAE7D,OAAO;QACL,MAAM;QACN,UAAU,EAAE,MAAM,CAAC,MAAM;QACzB,KAAK,EAAE,kBAAkB;QACzB,UAAU;KACX,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAe,EACf,QAAqB;IAErB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE7B,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC5B,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QAC5B,UAAU,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IAC9B,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAChE,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAE9B,OAAO,UAAU,GAAG,SAAS,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AIProvider } from "../providers/index";
|
|
2
|
+
export interface SemanticSearchOptions {
|
|
3
|
+
limit?: number;
|
|
4
|
+
threshold?: number;
|
|
5
|
+
collections?: string[];
|
|
6
|
+
filters?: Record<string, unknown>;
|
|
7
|
+
provider?: AIProvider;
|
|
8
|
+
corpus?: Array<{
|
|
9
|
+
id: string;
|
|
10
|
+
collectionSlug: string;
|
|
11
|
+
title: string;
|
|
12
|
+
content: string;
|
|
13
|
+
embedding?: number[];
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export interface SemanticSearchResult {
|
|
17
|
+
id: string;
|
|
18
|
+
collectionSlug: string;
|
|
19
|
+
title: string;
|
|
20
|
+
excerpt: string;
|
|
21
|
+
score: number;
|
|
22
|
+
highlights: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Performs semantic search across content using vector similarity.
|
|
26
|
+
*
|
|
27
|
+
* If `corpus` is provided with pre-computed embeddings, performs in-memory
|
|
28
|
+
* cosine similarity search. Otherwise, requires a vector database integration.
|
|
29
|
+
*/
|
|
30
|
+
export declare function semanticSearch(query: string, options?: SemanticSearchOptions): Promise<SemanticSearchResult[]>;
|
|
31
|
+
//# sourceMappingURL=semantic-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-search.d.ts","sourceRoot":"","sources":["../../src/search/semantic-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA6DjC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { requireAIProvider } from "../providers/index";
|
|
2
|
+
import { generateEmbedding } from "./embeddings";
|
|
3
|
+
import { cosineSimilarity } from "./embeddings";
|
|
4
|
+
/**
|
|
5
|
+
* Performs semantic search across content using vector similarity.
|
|
6
|
+
*
|
|
7
|
+
* If `corpus` is provided with pre-computed embeddings, performs in-memory
|
|
8
|
+
* cosine similarity search. Otherwise, requires a vector database integration.
|
|
9
|
+
*/
|
|
10
|
+
export async function semanticSearch(query, options) {
|
|
11
|
+
const ai = options?.provider ?? requireAIProvider();
|
|
12
|
+
const limit = options?.limit ?? 10;
|
|
13
|
+
const threshold = options?.threshold ?? 0.5;
|
|
14
|
+
const queryEmbedding = await generateEmbedding(query, ai);
|
|
15
|
+
if (options?.corpus) {
|
|
16
|
+
const scored = [];
|
|
17
|
+
for (const item of options.corpus) {
|
|
18
|
+
if (options.collections?.length && !options.collections.includes(item.collectionSlug)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
let itemEmbedding = item.embedding;
|
|
22
|
+
if (!itemEmbedding) {
|
|
23
|
+
const result = await generateEmbedding(item.content.slice(0, 8000), ai);
|
|
24
|
+
itemEmbedding = result.vector;
|
|
25
|
+
}
|
|
26
|
+
const score = cosineSimilarity(queryEmbedding.vector, itemEmbedding);
|
|
27
|
+
if (score >= threshold) {
|
|
28
|
+
const queryTerms = query.toLowerCase().split(/\s+/);
|
|
29
|
+
const contentLower = item.content.toLowerCase();
|
|
30
|
+
const highlights = queryTerms.filter((t) => contentLower.includes(t));
|
|
31
|
+
let excerptStart = 0;
|
|
32
|
+
for (const term of queryTerms) {
|
|
33
|
+
const idx = contentLower.indexOf(term);
|
|
34
|
+
if (idx >= 0) {
|
|
35
|
+
excerptStart = Math.max(0, idx - 50);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
scored.push({
|
|
40
|
+
id: item.id,
|
|
41
|
+
collectionSlug: item.collectionSlug,
|
|
42
|
+
title: item.title,
|
|
43
|
+
excerpt: item.content.slice(excerptStart, excerptStart + 200),
|
|
44
|
+
score,
|
|
45
|
+
highlights,
|
|
46
|
+
_score: score,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
scored.sort((a, b) => b._score - a._score);
|
|
51
|
+
return scored.slice(0, limit).map(({ _score, ...rest }) => rest);
|
|
52
|
+
}
|
|
53
|
+
// For production use with a vector database (pgvector, Pinecone, etc.),
|
|
54
|
+
// the embedding would be passed to the vector store's similarity search API.
|
|
55
|
+
// This requires project-specific setup based on the chosen vector store.
|
|
56
|
+
throw new Error("Semantic search requires either a pre-computed corpus (pass options.corpus) " +
|
|
57
|
+
"or a vector database integration. See docs for pgvector setup.");
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=semantic-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-search.js","sourceRoot":"","sources":["../../src/search/semantic-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BrandVoiceProfile } from "../brand/voice-profile";
|
|
2
|
+
import type { AIProvider } from "../providers/index";
|
|
3
|
+
export interface TranslationOptions {
|
|
4
|
+
sourceLanguage: string;
|
|
5
|
+
targetLanguage: string;
|
|
6
|
+
preserveFormatting?: boolean;
|
|
7
|
+
brandProfile?: BrandVoiceProfile;
|
|
8
|
+
glossary?: Record<string, string>;
|
|
9
|
+
provider?: AIProvider;
|
|
10
|
+
}
|
|
11
|
+
export interface TranslationResult {
|
|
12
|
+
translated: string;
|
|
13
|
+
sourceLanguage: string;
|
|
14
|
+
targetLanguage: string;
|
|
15
|
+
wordCount: number;
|
|
16
|
+
confidence: number;
|
|
17
|
+
glossaryMatches: Array<{
|
|
18
|
+
original: string;
|
|
19
|
+
translated: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
/** Translates content between languages while preserving brand voice and glossary terms. */
|
|
23
|
+
export declare function translateContent(content: string, options: TranslationOptions): Promise<TranslationResult>;
|
|
24
|
+
//# sourceMappingURL=translator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../../src/translation/translator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AAED,4FAA4F;AAC5F,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CA+C5B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { requireAIProvider } from "../providers/index";
|
|
2
|
+
/** Translates content between languages while preserving brand voice and glossary terms. */
|
|
3
|
+
export async function translateContent(content, options) {
|
|
4
|
+
const ai = options.provider ?? requireAIProvider();
|
|
5
|
+
const glossaryMatches = [];
|
|
6
|
+
if (options.glossary) {
|
|
7
|
+
for (const [original, translated] of Object.entries(options.glossary)) {
|
|
8
|
+
if (content.toLowerCase().includes(original.toLowerCase())) {
|
|
9
|
+
glossaryMatches.push({ original, translated });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const glossaryInstruction = glossaryMatches.length > 0
|
|
14
|
+
? `\n\nGlossary (use these exact translations):\n${glossaryMatches.map((g) => `- "${g.original}" → "${g.translated}"`).join("\n")}`
|
|
15
|
+
: "";
|
|
16
|
+
const brandInstruction = options.brandProfile
|
|
17
|
+
? `\nMaintain a ${options.brandProfile.tone.map((t) => t.dimension).join("/")} tone.`
|
|
18
|
+
: "";
|
|
19
|
+
const formatInstruction = options.preserveFormatting !== false
|
|
20
|
+
? "\nPreserve all HTML tags, markdown formatting, and line breaks exactly."
|
|
21
|
+
: "";
|
|
22
|
+
const prompt = `Translate the following content from ${options.sourceLanguage} to ${options.targetLanguage}.${formatInstruction}${brandInstruction}${glossaryInstruction}
|
|
23
|
+
|
|
24
|
+
Content to translate:
|
|
25
|
+
${content}`;
|
|
26
|
+
const result = await ai.generateText(prompt, {
|
|
27
|
+
systemPrompt: "You are a professional translator. Translate accurately while preserving meaning, tone, and formatting. Return only the translated text, no explanations.",
|
|
28
|
+
maxTokens: Math.max(2048, Math.ceil(content.length * 1.5)),
|
|
29
|
+
temperature: 0.3,
|
|
30
|
+
});
|
|
31
|
+
const translated = result.text.trim();
|
|
32
|
+
const wordCount = translated.split(/\s+/).filter(Boolean).length;
|
|
33
|
+
return {
|
|
34
|
+
translated,
|
|
35
|
+
sourceLanguage: options.sourceLanguage,
|
|
36
|
+
targetLanguage: options.targetLanguage,
|
|
37
|
+
wordCount,
|
|
38
|
+
confidence: 0.85,
|
|
39
|
+
glossaryMatches,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=translator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translator.js","sourceRoot":"","sources":["../../src/translation/translator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAoBvD,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAe,EACf,OAA2B;IAE3B,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,EAAE,CAAC;IAEnD,MAAM,eAAe,GAAyC,EAAE,CAAC;IAEjE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtE,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAC3D,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,mBAAmB,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC;QACpD,CAAC,CAAC,iDAAiD,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACnI,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,gBAAgB,GAAG,OAAO,CAAC,YAAY;QAC3C,CAAC,CAAC,gBAAgB,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ;QACrF,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,KAAK,KAAK;QAC5D,CAAC,CAAC,yEAAyE;QAC3E,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,MAAM,GAAG,wCAAwC,OAAO,CAAC,cAAc,OAAO,OAAO,CAAC,cAAc,IAAI,iBAAiB,GAAG,gBAAgB,GAAG,mBAAmB;;;EAGxK,OAAO,EAAE,CAAC;IAEV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE;QAC3C,YAAY,EAAE,2JAA2J;QACzK,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QAC1D,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACtC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAEjE,OAAO;QACL,UAAU;QACV,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,SAAS;QACT,UAAU,EAAE,IAAI;QAChB,eAAe;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BrandVoiceProfile } from "../brand/voice-profile";
|
|
2
|
+
import type { AIProvider } from "../providers/index";
|
|
3
|
+
export interface WritingContext {
|
|
4
|
+
collectionSlug?: string;
|
|
5
|
+
existingContent?: string;
|
|
6
|
+
brandProfile?: BrandVoiceProfile;
|
|
7
|
+
targetAudience?: string;
|
|
8
|
+
provider?: AIProvider;
|
|
9
|
+
}
|
|
10
|
+
export interface ProofreadResult {
|
|
11
|
+
corrected: string;
|
|
12
|
+
changes: Array<{
|
|
13
|
+
original: string;
|
|
14
|
+
corrected: string;
|
|
15
|
+
type: "grammar" | "spelling" | "style" | "clarity";
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** Generates content from a prompt with optional context. */
|
|
19
|
+
export declare function generate(prompt: string, context?: WritingContext): Promise<string>;
|
|
20
|
+
/** Expands the given text with additional detail and context. */
|
|
21
|
+
export declare function expand(text: string, instructions?: string, provider?: AIProvider): Promise<string>;
|
|
22
|
+
/** Compresses the given text to the target length while preserving meaning. */
|
|
23
|
+
export declare function compress(text: string, targetLength?: number, provider?: AIProvider): Promise<string>;
|
|
24
|
+
/** Rewrites the given text in a different style or tone. */
|
|
25
|
+
export declare function rewrite(text: string, style?: string, tone?: string, provider?: AIProvider): Promise<string>;
|
|
26
|
+
/** Proofreads the given text and returns corrections with change types. */
|
|
27
|
+
export declare function proofread(text: string, provider?: AIProvider): Promise<ProofreadResult>;
|
|
28
|
+
//# sourceMappingURL=assistant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant.d.ts","sourceRoot":"","sources":["../../src/writing/assistant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;KACpD,CAAC,CAAC;CACJ;AAkCD,6DAA6D;AAC7D,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,iEAAiE;AACjE,wBAAsB,MAAM,CAC1B,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED,+EAA+E;AAC/E,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,4DAA4D;AAC5D,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,2EAA2E;AAC3E,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,UAAU,GACpB,OAAO,CAAC,eAAe,CAAC,CA2B1B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { requireAIProvider } from "../providers/index";
|
|
2
|
+
function resolveProvider(context) {
|
|
3
|
+
return context?.provider ?? requireAIProvider();
|
|
4
|
+
}
|
|
5
|
+
function buildSystemPrompt(context) {
|
|
6
|
+
const parts = [
|
|
7
|
+
"You are an expert content writer for a CMS.",
|
|
8
|
+
"Do not include passwords, API keys, personal data, or other sensitive information in your output.",
|
|
9
|
+
];
|
|
10
|
+
if (context?.collectionSlug) {
|
|
11
|
+
parts.push(`Content type: ${context.collectionSlug}.`);
|
|
12
|
+
}
|
|
13
|
+
if (context?.targetAudience) {
|
|
14
|
+
parts.push(`Target audience: ${context.targetAudience}.`);
|
|
15
|
+
}
|
|
16
|
+
if (context?.brandProfile) {
|
|
17
|
+
const tone = context.brandProfile.tone
|
|
18
|
+
.map((t) => `${t.dimension}: ${Math.round(t.value * 100)}%`)
|
|
19
|
+
.join(", ");
|
|
20
|
+
parts.push(`Brand tone: ${tone}.`);
|
|
21
|
+
if (context.brandProfile.vocabulary.preferred.length > 0) {
|
|
22
|
+
parts.push(`Preferred terms: ${context.brandProfile.vocabulary.preferred.join(", ")}.`);
|
|
23
|
+
}
|
|
24
|
+
if (context.brandProfile.vocabulary.avoided.length > 0) {
|
|
25
|
+
parts.push(`Avoid these terms: ${context.brandProfile.vocabulary.avoided.join(", ")}.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return parts.join(" ");
|
|
29
|
+
}
|
|
30
|
+
/** Generates content from a prompt with optional context. */
|
|
31
|
+
export async function generate(prompt, context) {
|
|
32
|
+
const provider = resolveProvider(context);
|
|
33
|
+
const systemPrompt = buildSystemPrompt(context);
|
|
34
|
+
let fullPrompt = prompt;
|
|
35
|
+
if (context?.existingContent) {
|
|
36
|
+
fullPrompt += `\n\nExisting content for reference:\n${context.existingContent.slice(0, 2000)}`;
|
|
37
|
+
}
|
|
38
|
+
const result = await provider.generateText(fullPrompt, {
|
|
39
|
+
systemPrompt,
|
|
40
|
+
maxTokens: 2048,
|
|
41
|
+
temperature: 0.7,
|
|
42
|
+
});
|
|
43
|
+
return result.text;
|
|
44
|
+
}
|
|
45
|
+
/** Expands the given text with additional detail and context. */
|
|
46
|
+
export async function expand(text, instructions, provider) {
|
|
47
|
+
const ai = provider ?? requireAIProvider();
|
|
48
|
+
const prompt = `Expand the following text with additional detail and context${instructions ? `. Instructions: ${instructions}` : ""}. Maintain the same voice and style.\n\n${text}`;
|
|
49
|
+
const result = await ai.generateText(prompt, {
|
|
50
|
+
systemPrompt: "You are a content writer. Expand the text while maintaining its voice, style, and accuracy.",
|
|
51
|
+
maxTokens: 2048,
|
|
52
|
+
temperature: 0.7,
|
|
53
|
+
});
|
|
54
|
+
return result.text;
|
|
55
|
+
}
|
|
56
|
+
/** Compresses the given text to the target length while preserving meaning. */
|
|
57
|
+
export async function compress(text, targetLength, provider) {
|
|
58
|
+
const ai = provider ?? requireAIProvider();
|
|
59
|
+
const lengthInstruction = targetLength
|
|
60
|
+
? `Target approximately ${targetLength} characters.`
|
|
61
|
+
: "Make it significantly shorter.";
|
|
62
|
+
const prompt = `Compress the following text while preserving all key information. ${lengthInstruction}\n\n${text}`;
|
|
63
|
+
const result = await ai.generateText(prompt, {
|
|
64
|
+
systemPrompt: "You are an editor. Compress text while preserving meaning, key points, and readability.",
|
|
65
|
+
maxTokens: 1024,
|
|
66
|
+
temperature: 0.3,
|
|
67
|
+
});
|
|
68
|
+
return result.text;
|
|
69
|
+
}
|
|
70
|
+
/** Rewrites the given text in a different style or tone. */
|
|
71
|
+
export async function rewrite(text, style, tone, provider) {
|
|
72
|
+
const ai = provider ?? requireAIProvider();
|
|
73
|
+
const modifiers = [style && `style: ${style}`, tone && `tone: ${tone}`].filter(Boolean).join(", ");
|
|
74
|
+
const prompt = `Rewrite the following text${modifiers ? ` with ${modifiers}` : " to improve clarity and engagement"}:\n\n${text}`;
|
|
75
|
+
const result = await ai.generateText(prompt, {
|
|
76
|
+
systemPrompt: "You are a content editor. Rewrite text as requested while preserving the core message.",
|
|
77
|
+
maxTokens: 2048,
|
|
78
|
+
temperature: 0.7,
|
|
79
|
+
});
|
|
80
|
+
return result.text;
|
|
81
|
+
}
|
|
82
|
+
/** Proofreads the given text and returns corrections with change types. */
|
|
83
|
+
export async function proofread(text, provider) {
|
|
84
|
+
const ai = provider ?? requireAIProvider();
|
|
85
|
+
const prompt = `Proofread the following text. Return a JSON object with:
|
|
86
|
+
- "corrected": the full corrected text
|
|
87
|
+
- "changes": array of objects with "original", "corrected", and "type" (one of: grammar, spelling, style, clarity)
|
|
88
|
+
|
|
89
|
+
Only include actual errors. If the text is correct, return an empty changes array.
|
|
90
|
+
|
|
91
|
+
Text to proofread:
|
|
92
|
+
${text}`;
|
|
93
|
+
const result = await ai.generateText(prompt, {
|
|
94
|
+
systemPrompt: "You are a professional proofreader. Return valid JSON only, no markdown formatting.",
|
|
95
|
+
maxTokens: 2048,
|
|
96
|
+
temperature: 0.2,
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
const cleaned = result.text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
100
|
+
const parsed = JSON.parse(cleaned);
|
|
101
|
+
return {
|
|
102
|
+
corrected: parsed.corrected ?? text,
|
|
103
|
+
changes: Array.isArray(parsed.changes) ? parsed.changes : [],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return { corrected: text, changes: [] };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=assistant.js.map
|