aboutsummaryrefslogtreecommitdiffstats
path: root/packages/shared
diff options
context:
space:
mode:
Diffstat (limited to 'packages/shared')
-rw-r--r--packages/shared/prompts.ts37
-rw-r--r--packages/shared/types/users.ts15
-rw-r--r--packages/shared/utils/tag.ts24
3 files changed, 71 insertions, 5 deletions
diff --git a/packages/shared/prompts.ts b/packages/shared/prompts.ts
index 5a6a705e..af2f07f1 100644
--- a/packages/shared/prompts.ts
+++ b/packages/shared/prompts.ts
@@ -1,5 +1,8 @@
import type { Tiktoken } from "js-tiktoken";
+import type { ZTagStyle } from "./types/users";
+import { getTagStylePrompt } from "./utils/tag";
+
let encoding: Tiktoken | null = null;
/**
@@ -40,15 +43,22 @@ async function truncateContent(
return enc.decode(truncatedTokens);
}
-export function buildImagePrompt(lang: string, customPrompts: string[]) {
+export function buildImagePrompt(
+ lang: string,
+ customPrompts: string[],
+ tagStyle: ZTagStyle,
+) {
+ const tagStyleInstruction = getTagStylePrompt(tagStyle);
+
return `
-You are an expert whose responsibility is to help with automatic text tagging for a read-it-later app.
+You are an expert whose responsibility is to help with automatic text tagging for a read-it-later/bookmarking app.
Please analyze the attached image and suggest relevant tags that describe its key themes, topics, and main ideas. The rules are:
- Aim for a variety of tags, including broad categories, specific keywords, and potential sub-genres.
- The tags must be in ${lang}.
- If the tag is not generic enough, don't include it.
- Aim for 10-15 tags.
- If there are no good tags, don't emit any.
+${tagStyleInstruction}
${customPrompts && customPrompts.map((p) => `- ${p}`).join("\n")}
You must respond in valid JSON with the key "tags" and the value is list of tags. Don't wrap the response in a markdown code.`;
}
@@ -60,9 +70,12 @@ function constructTextTaggingPrompt(
lang: string,
customPrompts: string[],
content: string,
+ tagStyle: ZTagStyle,
): string {
+ const tagStyleInstruction = getTagStylePrompt(tagStyle);
+
return `
-You are an expert whose responsibility is to help with automatic tagging for a read-it-later app.
+You are an expert whose responsibility is to help with automatic tagging for a read-it-later/bookmarking app.
Please analyze the TEXT_CONTENT below and suggest relevant tags that describe its key themes, topics, and main ideas. The rules are:
- Aim for a variety of tags, including broad categories, specific keywords, and potential sub-genres.
- The tags must be in ${lang}.
@@ -70,6 +83,7 @@ Please analyze the TEXT_CONTENT below and suggest relevant tags that describe it
- The content can include text for cookie consent and privacy policy, ignore those while tagging.
- Aim for 3-5 tags.
- If there are no good tags, leave the array empty.
+${tagStyleInstruction}
${customPrompts && customPrompts.map((p) => `- ${p}`).join("\n")}
<TEXT_CONTENT>
@@ -101,11 +115,13 @@ export function buildTextPromptUntruncated(
lang: string,
customPrompts: string[],
content: string,
+ tagStyle: ZTagStyle,
): string {
return constructTextTaggingPrompt(
lang,
customPrompts,
preprocessContent(content),
+ tagStyle,
);
}
@@ -114,15 +130,26 @@ export async function buildTextPrompt(
customPrompts: string[],
content: string,
contextLength: number,
+ tagStyle: ZTagStyle,
): Promise<string> {
content = preprocessContent(content);
- const promptTemplate = constructTextTaggingPrompt(lang, customPrompts, "");
+ const promptTemplate = constructTextTaggingPrompt(
+ lang,
+ customPrompts,
+ "",
+ tagStyle,
+ );
const promptSize = await calculateNumTokens(promptTemplate);
const truncatedContent = await truncateContent(
content,
contextLength - promptSize,
);
- return constructTextTaggingPrompt(lang, customPrompts, truncatedContent);
+ return constructTextTaggingPrompt(
+ lang,
+ customPrompts,
+ truncatedContent,
+ tagStyle,
+ );
}
export async function buildSummaryPrompt(
diff --git a/packages/shared/types/users.ts b/packages/shared/types/users.ts
index 7338ee15..3ba56583 100644
--- a/packages/shared/types/users.ts
+++ b/packages/shared/types/users.ts
@@ -5,6 +5,17 @@ import { zBookmarkSourceSchema } from "./bookmarks";
export const PASSWORD_MIN_LENGTH = 8;
export const PASSWORD_MAX_LENGTH = 100;
+export const zTagStyleSchema = z.enum([
+ "lowercase-hyphens",
+ "lowercase-spaces",
+ "lowercase-underscores",
+ "titlecase-spaces",
+ "titlecase-hyphens",
+ "camelCase",
+ "as-generated",
+]);
+export type ZTagStyle = z.infer<typeof zTagStyleSchema>;
+
export const zSignUpSchema = z
.object({
name: z.string().min(1, { message: "Name can't be empty" }),
@@ -123,6 +134,8 @@ export const zUserSettingsSchema = z.object({
// AI settings (nullable = opt-in, null means use server default)
autoTaggingEnabled: z.boolean().nullable(),
autoSummarizationEnabled: z.boolean().nullable(),
+ tagStyle: zTagStyleSchema,
+ inferredTagLang: z.string().nullable(),
});
export type ZUserSettings = z.infer<typeof zUserSettingsSchema>;
@@ -139,6 +152,8 @@ export const zUpdateUserSettingsSchema = zUserSettingsSchema.partial().pick({
readerFontFamily: true,
autoTaggingEnabled: true,
autoSummarizationEnabled: true,
+ tagStyle: true,
+ inferredTagLang: true,
});
export const zUpdateBackupSettingsSchema = zUpdateUserSettingsSchema.pick({
diff --git a/packages/shared/utils/tag.ts b/packages/shared/utils/tag.ts
index 8e1bd105..4dc7c696 100644
--- a/packages/shared/utils/tag.ts
+++ b/packages/shared/utils/tag.ts
@@ -1,6 +1,30 @@
+import type { ZTagStyle } from "../types/users";
+
/**
* Ensures exactly ONE leading #
*/
export function normalizeTagName(raw: string): string {
return raw.trim().replace(/^#+/, ""); // strip every leading #
}
+
+export type TagStyle = ZTagStyle;
+
+export function getTagStylePrompt(style: TagStyle): string {
+ switch (style) {
+ case "lowercase-hyphens":
+ return "- Use lowercase letters with hyphens between words (e.g., 'machine-learning', 'web-development')";
+ case "lowercase-spaces":
+ return "- Use lowercase letters with spaces between words (e.g., 'machine learning', 'web development')";
+ case "lowercase-underscores":
+ return "- Use lowercase letters with underscores between words (e.g., 'machine_learning', 'web_development')";
+ case "titlecase-spaces":
+ return "- Use title case with spaces between words (e.g., 'Machine Learning', 'Web Development')";
+ case "titlecase-hyphens":
+ return "- Use title case with hyphens between words (e.g., 'Machine-Learning', 'Web-Development')";
+ case "camelCase":
+ return "- Use camelCase format (e.g., 'machineLearning', 'webDevelopment')";
+ case "as-generated":
+ default:
+ return "";
+ }
+}