diff options
author | bobzel <zzzman@gmail.com> | 2025-06-23 15:44:31 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-06-23 15:44:31 -0400 |
commit | 0c716d87a3b9ece5b0fa77ffb8f9c008ecf78fda (patch) | |
tree | 9d1b0a4bdebab07363b79eac17913c1b64280419 | |
parent | 35bd9e51f7cef551382025a5459d68eddd8f028b (diff) |
fixed gptpopup to do something more reasonable when asked to tag documents.
-rw-r--r-- | src/client/apis/gpt/GPT.ts | 50 | ||||
-rw-r--r-- | src/client/views/pdf/GPTPopup/GPTPopup.tsx | 24 |
2 files changed, 43 insertions, 31 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 7878e9bfe..af9cdd2d1 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -11,10 +11,12 @@ export enum GPTDocCommand { export const DescriptionSeperator = '======'; export const DocSeperator = '------'; +export const DataSeperator = '>>>>>>'; export enum TextClassifications { - Title = 'word', //a few words + Title = 'word', //a few words Caption = 'sentence', //few sentences - LengthyDescription = 'paragraphs' } + LengthyDescription = 'paragraphs', +} enum GPTCallType { SUMMARY = 'summary', @@ -38,11 +40,12 @@ enum GPTCallType { MAKERUBRIC = 'make_rubric', // create a definition rubric for a document to be used when quizzing the user COMMANDTYPE = 'command_type', // Determine the type of command being made (GPTQueryType - eg., AssignTags, Sort, Filter, DocInfo, GenInfo) and possibly some parameters (eg, Tag type for Tags) SUBSETDOCS = 'subset_docs', // select a subset of documents based on their descriptions + TAGDOCS = 'tag_docs', // choose a tags for each Doc DOCINFO = 'doc_info', // provide information about a document SORTDOCS = 'sort_docs', CLASSIFYTEXTMINIMAL = 'classify_text_minimal', // classify text into one of the three categories: title, caption, lengthy description CLASSIFYTEXTFULL = 'classify_text_full', //tags pdf content - GENERATESCRAPBOOK = 'generate_scrapbook' + GENERATESCRAPBOOK = 'generate_scrapbook', } type GPTCallOpts = { @@ -55,14 +58,12 @@ type GPTCallOpts = { const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { // newest model: gpt-4 summary: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Summarize the text given in simpler terms.' }, - - + sort_docs: { model: 'gpt-4o', maxTokens: 2048, temp: 0.25, - prompt: - `The user is going to give you a list of descriptions. + prompt: `The user is going to give you a list of descriptions. Each one is separated by '${DescriptionSeperator}' on either side. Descriptions will vary in length, so make sure to only separate when you see '${DescriptionSeperator}'. Sort them by the user's specifications. @@ -71,7 +72,6 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { It is VERY important that you format it exactly as described, ensuring the proper number of '${DescriptionSeperator[0]}' and '${DocSeperator[0]}' (${DescriptionSeperator.length} of each) and NO commas`, }, - edit: { model: 'gpt-4-turbo', maxTokens: 256, temp: 0.5, prompt: 'Reword the text.' }, stack: { model: 'gpt-4o', @@ -100,7 +100,7 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { temp: 0.25, prompt: `Based on the content of the the text, classify it into the most appropriate category: '${TextClassifications.Title}' if it is a few words, '${TextClassifications.Caption}' if it is a couple sentences or less, or '${TextClassifications.LengthyDescription}' if it is a lengthy description. Output exclusively the classification in your response. - ` + `, }, classify_text_full: { model: 'gpt-4o', @@ -109,7 +109,7 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { prompt: `Based on the content of the the text, classify it into the most appropriate category: '${TextClassifications.Title}', '${TextClassifications.Caption}', or '${TextClassifications.LengthyDescription}'. Then provide five more descriptive tags (single words) separated by spaces. - Finally, include a more detailed summary phrase tag using underscores, for a total of seven tags.` + Finally, include a more detailed summary phrase tag using underscores, for a total of seven tags.`, }, describe: { model: 'gpt-4-vision-preview', maxTokens: 2048, temp: 0, prompt: 'Describe these images in 3-5 words' }, flashcard: { @@ -174,7 +174,7 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { model: 'gpt-4o', maxTokens: 2048, temp: 0.5, - prompt: `Generate an aesthetically pleasing scrapbook layout preset based on these items. + prompt: `Generate an aesthetically pleasing scrapbook layout preset based on these items. Return your response as JSON in the format: [{ "type": rich text or image or pdf or video or collection @@ -191,22 +191,32 @@ const callTypeMap: { [type in GPTCallType]: GPTCallOpts } = { tag: x: , y: , width: , height: } - ] ` - - }, + ] `, + }, command_type: { model: 'gpt-4-turbo', maxTokens: 1024, temp: 0, - prompt: `I'm going to provide you with a question. - Based on the question, is the user asking you to - ${GPTDocCommand.AssignTags}. Assigns docs with tags(like star / heart etc)/labels. + prompt: `Is the provided command/question asking you to: + ${GPTDocCommand.AssignTags}. Choose a descriptive tag/label. ${GPTDocCommand.GetInfo}. Provide information about a specific doc. ${GPTDocCommand.Filter}. Filter docs based on a question/information. ${GPTDocCommand.Sort}. Put docs in a specific order. - Answer with only the number for ${GPTDocCommand.GetInfo}-${GPTDocCommand.Sort}. - For number one, provide the number (${GPTDocCommand.AssignTags}) and the appropriate tag`, + Answer with only the number, do not add any other punctuation or formatting of any kind`, + }, + tag_docs: { + model: 'gpt-4-turbo', + maxTokens: 1024, + temp: 0, + prompt: `I'm going to give you a list of descriptions. + Each one is separated by '${DescriptionSeperator}' on either side. + Descriptions will vary in length, so make sure to only separate when you see '${DescriptionSeperator}'. + Based on the question/command the user asks, provide a tag label of the given descriptions that best matches the user's specifications. + Format your response precisely as a single string that prints each description followed by '${DataSeperator}' followed by the label followed by '${DescriptionSeperator}'. + Do not use any additional formatting marks or punctuation'. + Immediately afterward, surrounded by '${DocSeperator}' on BOTH SIDES, provide some insight into your reasoning for the way you sorted (and mention nothing about the formatting details given in this description). + `, }, subset_docs: { model: 'gpt-4-turbo', @@ -284,7 +294,7 @@ const gptImageCall = async (prompt: string, n?: number) => { n: n ?? 1, size: '1024x1024', }); - return response.data.map((data: Image) => data.url); + return (response.data ?? []).map((data: Image) => data.url); // return response.data.data[0].url; } catch (err) { console.error(err); diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 660284397..ec269cc6c 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -11,7 +11,7 @@ import { ClientUtils } from '../../../../ClientUtils'; import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; -import { DescriptionSeperator, DocSeperator, GPTCallType, GPTDocCommand, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; +import { DataSeperator, DescriptionSeperator, DocSeperator, GPTCallType, GPTDocCommand, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs } from '../../../documents/Documents'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -145,20 +145,22 @@ export class GPTPopup extends ObservableReactComponent<object> { break; } // prettier-ignore - gptOutput.split('======').filter(item => item.trim() !== '') // Split output into individual document contents - .map(docContentRaw => textToDocMap.get(docContentRaw.replace(/\n/g, ' ').trim())) // the find the corresponding Doc using textToDoc map - .filter(doc => doc).map(doc => doc!) // filter out undefined values - .forEach((doc, index) => { + gptOutput.split(DescriptionSeperator).filter(item => item.trim() !== '') // Split output into individual document contents + .map(docContentRaw => docContentRaw.replace(/\n/g, ' ').trim()) + .map(docContentRaw => ({doc: textToDocMap.get(docContentRaw.split(DataSeperator)[0]), data: docContentRaw.split(DataSeperator)[1] })) // the find the corresponding Doc using textToDoc map + .filter(({doc}) => doc).map(({doc, data}) => ({doc:doc!, data})) // filter out undefined values + .forEach(({doc, data}, index) => { switch (questionType) { case GPTDocCommand.Sort: doc[ChatSortField] = index; break; case GPTDocCommand.AssignTags: - if (args) { - const hashTag = args.startsWith('#') ? args : '#' + args[0].toLowerCase() + args.slice(1); - const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(args)) ?? hashTag; - TagItem.addTagToDoc(doc, filterTag); - } + TagItem.addTagToDoc(doc, data.startsWith('#') ? data : '#'+data); + // if (args) { + // const hashTag = args.startsWith('#') ? args : '#' + args[0].toLowerCase() + args.slice(1); + // const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(args)) ?? hashTag; + // TagItem.addTagToDoc(doc, filterTag); + // } break; case GPTDocCommand.Filter: TagItem.addTagToDoc(doc, GPTPopup.ChatTag); @@ -241,7 +243,7 @@ export class GPTPopup extends ObservableReactComponent<object> { gptAPICall(userPrompt, GPTCallType.COMMANDTYPE, undefined, true).then((commandType, args = commandType.split(' ').slice(1).join(' ')) => (async () => { switch (this.NumberToCommandType(commandType)) { - case GPTDocCommand.AssignTags: + case GPTDocCommand.AssignTags:return this._documentDescriptions?.then(descs => gptAPICall(userPrompt, GPTCallType.TAGDOCS, descs)) ?? ""; case GPTDocCommand.Filter: return this._documentDescriptions?.then(descs => gptAPICall(userPrompt, GPTCallType.SUBSETDOCS, descs)) ?? ""; case GPTDocCommand.Sort: return this._documentDescriptions?.then(descs => gptAPICall(userPrompt, GPTCallType.SORTDOCS, descs)) ?? ""; default: return Doc.getDescription(DocumentView.SelectedDocs().lastElement()).then(desc => gptAPICall(userPrompt, GPTCallType.DOCINFO, desc)); |